diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 646aa887c4..1ff2ed1841 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -1,128 +1,132 @@ project (krita-and-all-its-deps) # # Build all dependencies for Krita and finally Krita itself. # Parameters: EXTERNALS_DOWNLOAD_DIR place to download all packages # INSTALL_ROOT place to install everything to # MXE_TOOLCHAIN: the toolchain file to cross-compile using MXE # # Example usage: cmake ..\kritadeposx -DEXTERNALS_DOWNLOAD_DIR=/dev2/d -DINSTALL_ROOT=/dev2/i -DWIN64_BUILD=TRUE -DBOOST_LIBRARYDIR=/dev2/i/lib -G "Visual Studio 11 Win64" cmake_minimum_required(VERSION 2.8.6) if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) message(FATAL_ERROR "Compiling in the source directory is not supported. Use for example 'mkdir build; cd build; cmake ..'.") endif (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) # Tools must be obtained to work with: include (ExternalProject) # allow specification of a directory with pre-downloaded # requirements if(NOT IS_DIRECTORY ${EXTERNALS_DOWNLOAD_DIR}) message(FATAL_ERROR "No externals download dir set. Use -DEXTERNALS_DOWNLOAD_DIR") endif() if(NOT IS_DIRECTORY ${INSTALL_ROOT}) message(FATAL_ERROR "No install dir set. Use -DINSTALL_ROOT") endif() set(TOP_INST_DIR ${INSTALL_ROOT}) set(EXTPREFIX "${TOP_INST_DIR}") set(CMAKE_PREFIX_PATH "${EXTPREFIX}") if (${CMAKE_GENERATOR} STREQUAL "Visual Studio 14 2015 Win64") SET(GLOBAL_PROFILE -DCMAKE_MODULE_LINKER_FLAGS=/machine:x64 -DCMAKE_EXE_LINKER_FLAGS=/machine:x64 -DCMAKE_SHARED_LINKER_FLAGS=/machine:x64 -DCMAKE_STATIC_LINKER_FLAGS=/machine:x64 ) endif () message( STATUS "CMAKE_GENERATOR: ${CMAKE_GENERATOR}") message( STATUS "CMAKE_CL_64: ${CMAKE_CL_64}") set(GLOBAL_BUILD_TYPE RelWithDebInfo) set(GLOBAL_PROFILE ${GLOBAL_PROFILE} -DBUILD_TESTING=false) +if (DEFINED EP_PREFIX) + set_directory_properties(PROPERTIES EP_PREFIX ${EP_PREFIX}) +endif (DEFINED EP_PREFIX) + if (MSVC) set(GLOBAL_PROFILE ${GLOBAL_PROFILE} -DCMAKE_EXE_LINKER_FLAGS=/PROFILE -DCMAKE_SHARED_LINKER_FLAGS=/PROFILE) set(PATCH_COMMAND myptch) endif() if (MINGW) set(PATCH_COMMAND myptch) endif() if (MSYS) set(PATCH_COMMAND patch) set(GLOBAL_PROFILE ${GLOBAL_PROFILE} -DCMAKE_TOOLCHAIN_FILE=${MXE_TOOLCHAIN} -DCMAKE_FIND_PREFIX_PATH=${CMAKE_PREFIX_PATH} -DCMAKE_SYSTEM_INCLUDE_PATH=${CMAKE_PREFIX_PATH}/include -DCMAKE_INCLUDE_PATH=${CMAKE_PREFIX_PATH}/include -DCMAKE_LIBRARY_PATH=${CMAKE_PREFIX_PATH}/lib -DZLIB_ROOT=${CMAKE_PREFIX_PATH} ) set(GLOBAL_AUTOMAKE_PROFILE --host=i686-pc-mingw32 ) endif() if (APPLE) set(GLOBAL_PROFILE ${GLOBAL_PROFILE} -DCMAKE_MACOSX_RPATH=ON -DKDE_SKIP_RPATH_SETTINGS=ON -DBUILD_WITH_INSTALL_RPATH=ON -DAPPLE_SUPPRESS_X11_WARNING=ON) set(PATCH_COMMAND patch) endif () if (UNIX AND NOT APPLE) set(LINUX true) set(PATCH_COMMAND patch) endif () # this list must be dependency-ordered if (UNIX) add_subdirectory( ext_python ) add_subdirectory( ext_llvm ) endif () if (MSVC) add_subdirectory( ext_patch ) add_subdirectory( ext_png2ico ) endif (MSVC) if (MINGW) add_subdirectory( ext_patch ) add_subdirectory( ext_png2ico ) endif (MINGW) add_subdirectory( ext_iconv ) add_subdirectory( ext_gettext ) add_subdirectory( ext_zlib ) add_subdirectory( ext_libxml2 ) add_subdirectory( ext_libxslt ) add_subdirectory( ext_boost ) add_subdirectory( ext_jpeg ) add_subdirectory( ext_tiff ) add_subdirectory( ext_png ) add_subdirectory( ext_eigen3 ) add_subdirectory( ext_expat ) # for exiv2 add_subdirectory( ext_exiv2 ) add_subdirectory( ext_ilmbase ) add_subdirectory( ext_lcms2 ) add_subdirectory( ext_openexr ) add_subdirectory( ext_vc ) add_subdirectory( ext_gsl ) add_subdirectory( ext_fftw3 ) add_subdirectory( ext_ocio ) if (MSVC) add_subdirectory( ext_pthreads ) endif (MSVC) add_subdirectory( ext_fontconfig) add_subdirectory( ext_freetype) add_subdirectory( ext_qt ) add_subdirectory( ext_poppler ) add_subdirectory( ext_libraw ) add_subdirectory( ext_frameworks ) add_subdirectory( ext_sip ) add_subdirectory( ext_pyqt ) if (MSVC OR MINGW) add_subdirectory( ext_drmingw ) endif (MSVC OR MINGW) diff --git a/3rdparty/ext_qt/CMakeLists.txt b/3rdparty/ext_qt/CMakeLists.txt index a7b99bb7a1..81fd754f82 100644 --- a/3rdparty/ext_qt/CMakeLists.txt +++ b/3rdparty/ext_qt/CMakeLists.txt @@ -1,186 +1,186 @@ SET(EXTPREFIX_qt "${EXTPREFIX}") if (WIN32) ExternalProject_Add( ext_qt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL https://download.qt.io/official_releases/qt/5.6/5.6.1-1/single/qt-everywhere-opensource-src-5.6.1-1.zip URL_MD5 9d7ea0cadcec7b5a63e8e83686756978 PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/disable-wintab.diff COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/qtgui-private-headers.diff COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/0001-Don-t-request-the-MIME-image-every-time-Windows-asks.patch COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/0002-Hack-always-return-we-support-DIBV5.patch COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/0003-Hack-for-fullscreen-workaround.patch INSTALL_DIR ${EXTPREFIX_qt} - CONFIGURE_COMMAND /configure.bat -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtdoc -skip qtenginio -skip qtgraphicaleffects -skip qtlocation -skip qtmultimedia -skip qtsensors -skip qtserialport -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtxmlpatterns -no-sql-sqlite -nomake examples -nomake tools -no-compile-examples -no-dbus -no-iconv -no-angle -no-ssl -no-openssl -no-wmf-backend -no-qml-debug -no-libproxy -no-system-proxies -no-nis -no-icu -no-mtdev -opensource -confirm-license -release -opengl desktop -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -prefix ${EXTPREFIX_qt} -platform win32-g++ + CONFIGURE_COMMAND /configure.bat -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtdoc -skip qtenginio -skip qtgraphicaleffects -skip qtlocation -skip qtsensors -skip qtserialport -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtxmlpatterns -no-sql-sqlite -nomake examples -nomake tools -no-compile-examples -no-dbus -no-iconv -no-angle -no-ssl -no-openssl -no-wmf-backend -no-qml-debug -no-libproxy -no-system-proxies -no-nis -no-icu -no-mtdev -opensource -confirm-license -release -opengl desktop -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -prefix ${EXTPREFIX_qt} -platform win32-g++ # use this line for building Qt with debugging info enabled #CONFIGURE_COMMAND /configure.bat -release -force-debug-info -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtdoc -skip qtenginio -skip qtgraphicaleffects -skip qtlocation -skip qtmultimedia -skip qtsensors -skip qtserialport -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtxmlpatterns -no-sql-sqlite -nomake examples -nomake tools -no-compile-examples -no-dbus -no-iconv -no-angle -no-ssl -no-openssl -no-wmf-backend -no-qml-debug -no-libproxy -no-system-proxies -no-nis -no-icu -no-mtdev -opensource -confirm-license -release -opengl desktop -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -prefix ${EXTPREFIX_qt} -platform win32-g++ BUILD_COMMAND mingw32-make INSTALL_COMMAND mingw32-make install UPDATE_COMMAND "" BUILD_IN_SOURCE 1 ALWAYS 0 DEPENDS ext_patch ) elseif (NOT APPLE) ExternalProject_Add( ext_qt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL https://download.qt.io/official_releases/qt/5.6/5.6.1-1/single/qt-everywhere-opensource-src-5.6.1-1.tar.gz URL_MD5 8fdec6d657bc370bd3183d8fe8e9c47a PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/qt-no-motion-compression.diff INSTALL_DIR ${EXTPREFIX_qt} CONFIGURE_COMMAND /configure -prefix ${EXTPREFIX_qt} -opensource -confirm-license -nomake examples -no-sql-sqlite -no-openssl -no-qml-debug -no-mtdev -no-journald -no-syslog -no-nis -no-cups -no-tslib -no-directfb -no-linuxfb -no-libproxy -no-pch -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -qt-harfbuzz -qt-freetype -qt-xcb -qt-xkbcommon-x11 -optimized-qmake -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtenginio -skip qtgraphicaleffects -skip qtlocation -skip qtmultimedia -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtandroidextras -skip qtserialport BUILD_COMMAND $(MAKE) INSTALL_COMMAND $(MAKE) install UPDATE_COMMAND "" BUILD_IN_SOURCE 1 ALWAYS 0 ) else( APPLE ) # XCODE_VERSION is set by CMake when using the Xcode generator, otherwise we need # to detect it manually here. if (NOT XCODE_VERSION) execute_process( COMMAND xcodebuild -version OUTPUT_VARIABLE xcodebuild_version OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_FILE /dev/null ) string(REGEX MATCH "Xcode ([0-9]([.][0-9])+)" version_match ${xcodebuild_version}) if (version_match) message(STATUS "${EXTPREFIX_qt}:Identified Xcode Version: ${CMAKE_MATCH_1}") set(XCODE_VERSION ${CMAKE_MATCH_1}) else() # If detecting Xcode version failed, set a crazy high version so we default # to the newest. set(XCODE_VERSION 99) message(WARNING "${EXTPREFIX_qt}:Failed to detect the version of an installed copy of Xcode, falling back to highest supported version. Set XCODE_VERSION to override.") endif(version_match) endif(NOT XCODE_VERSION) # ------------------------------------------------------------------------------- # Verify the Xcode installation on Mac OS like Qt5.7 does/will # If not stop now, the system isn't configured correctly for Qt. # No reason to even proceed. # ------------------------------------------------------------------------------- set(XCSELECT_OUTPUT) find_program(XCSELECT_PROGRAM "xcode-select") if(XCSELECT_PROGRAM) message(STATUS "${EXTPREFIX_qt}:Found XCSELECT_PROGRAM as ${XCSELECT_PROGRAM}") set(XCSELECT_COMMAND ${XCSELECT_PROGRAM} "--print-path") execute_process( COMMAND ${XCSELECT_COMMAND} RESULT_VARIABLE XCSELECT_COMMAND_RESULT OUTPUT_VARIABLE XCSELECT_COMMAND_OUTPUT ERROR_FILE /dev/null ) if(NOT XCSELECT_COMMAND_RESULT) # returned 0, we're ok. string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" XCSELECT_COMMAND_OUTPUT ${XCSELECT_COMMAND_OUTPUT}) else() string(REPLACE ";" " " XCSELECT_COMMAND_STR "${XCSELECT_COMMAND}") # message(STATUS "${XCSELECT_COMMAND_STR}") message(FATAL_ERROR "${EXTPREFIX_qt}:${XCSELECT_PROGRAM} test failed with status ${XCSELECT_COMMAND_RESULT}") endif() else() message(FATAL_ERROR "${EXTPREFIX_qt}:${XCSELECT_PROGRAM} not found. No Xcode is selected. Use xcode-select -switch to choose an Xcode version") endif() # Belts and suspenders # Beyond all the Xcode and Qt version checking, the proof of the pudding # lies in the success/failure of this command: xcrun --find xcrun. # On failure a patch is necessary, otherwise we're ok # So hard check xcrun now... set(XCRUN_OUTPUT) find_program(XCRUN_PROGRAM "xcrun") if(XCRUN_PROGRAM) message(STATUS "${EXTPREFIX_qt}:Found XCRUN_PROGRAM as ${XCRUN_PROGRAM}") set(XCRUN_COMMAND ${XCRUN_PROGRAM} "--find xcrun") execute_process( COMMAND ${XCRUN_COMMAND} RESULT_VARIABLE XCRUN_COMMAND_RESULT OUTPUT_VARIABLE XCRUN_COMMAND_OUTPUT ERROR_FILE /dev/null ) if(NOT XCRUN_COMMAND_RESULT) # returned 0, we're ok. string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" XCRUN_COMMAND_OUTPUT ${XCRUN_COMMAND_OUTPUT}) else() string(REPLACE ";" " " XCRUN_COMMAND_STR "${XCRUN_COMMAND}") # message(STATUS "${XCRUN_COMMAND_STR}") message(STATUS "${EXTPREFIX_qt}:xcrun test failed with status ${XCRUN_COMMAND_RESULT}") endif() else() message(STATUS "${EXTPREFIX_qt}:xcrun not found -- ${XCRUN_PROGRAM}") endif() # # Now configure ext_qt accordingly # if ((XCRUN_COMMAND_RESULT) AND (NOT (XCODE_VERSION VERSION_LESS 8.0.0))) # Fix Xcode xcrun related issue. # NOTE: This should be fixed by Qt 5.7.1 see here: http://code.qt.io/cgit/qt/qtbase.git/commit/?h=dev&id=77a71c32c9d19b87f79b208929e71282e8d8b5d9 # NOTE: but no one's holding their breath. set(ext_qt_PATCH_COMMAND $${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/gerrit-166202.diff COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/macdeploy-qt.diff COMMAND ${PATCH_COMMAND} -p1 -b -d /qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/qtbase-configure.patch COMMAND ${PATCH_COMMAND} -p1 -b -d /qtbase/mkspecs/features/mac -i ${CMAKE_CURRENT_SOURCE_DIR}/mac-default.patch) message(STATUS "${EXTPREFIX_qt}:Additional patches injected.") else() # No extra patches will be applied # NOTE: defaults for some untested scenarios like xcrun fails and xcode_version < 8. # NOTE: that is uncharted territory and (hopefully) a very unlikely scenario... set(ext_qt_PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/gerrit-166202.diff COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/macdeploy-qt.diff) endif() # Qt is big - try and parallelize if at all possible include(ProcessorCount) ProcessorCount(NUM_CORES) if(NOT NUM_CORES EQUAL 0) if (NUM_CORES GREATER 2) # be nice... MATH( EXPR NUM_CORES "${NUM_CORES} - 2" ) endif() set(PARALLEL_MAKE "make;-j${NUM_CORES}") message(STATUS "${EXTPREFIX_qt}:Parallelized make: ${PARALLEL_MAKE}") else() set(PARALLEL_MAKE "make") endif() ExternalProject_Add(ext_qt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} LOG_DOWNLOAD ON LOG_UPDATE ON LOG_CONFIGURE ON LOG_BUILD ON LOG_TEST ON LOG_INSTALL ON BUILD_IN_SOURCE ON URL https://download.qt.io/official_releases/qt/5.7/5.7.0/single/qt-everywhere-opensource-src-5.7.0.tar.gz URL_MD5 9a46cce61fc64c20c3ac0a0e0fa41b42 PATCH_COMMAND ${ext_qt_PATCH_COMMAND} INSTALL_DIR ${EXTPREFIX_qt} CONFIGURE_COMMAND /configure -confirm-license -opensource -nomake examples -no-openssl -no-compile-examples -qt-freetype -qt-harfbuzz -opengl desktop -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtgraphicaleffects -skip qtlocation -skip qtmultimedia -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtxmlpatterns -prefix ${EXTPREFIX_qt} BUILD_COMMAND ${PARALLEL_MAKE} INSTALL_COMMAND make install UPDATE_COMMAND "" BUILD_IN_SOURCE 1 ALWAYS 0 ) endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 628f0661d2..e21280a154 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,655 +1,667 @@ project(krita) message(STATUS "Using CMake version: ${CMAKE_VERSION}") cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) set(MIN_QT_VERSION 5.6.0) option(OVERRIDE_QT_VERSION "Use this to make it possible to build with Qt < 5.6.0. There will be bugs." OFF) if (OVERRIDE_QT_VERSION) set(MIN_QT_VERSION 5.4.0) endif() set(MIN_FRAMEWORKS_VERSION 5.7.0) 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 (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.9 -Wno-macro-redefined -Wno-deprecated-register) endif() ###################### ####################### ## Constants defines ## ####################### ###################### # define common versions of Krita applications, used to generate kritaversion.h # update these version for every release: set(KRITA_VERSION_STRING "3.1.88") set(KRITA_STABLE_VERSION_MAJOR 3) # 3 for 3.x, 4 for 4.x, etc. set(KRITA_STABLE_VERSION_MINOR 1) # 0 for 3.0, 1 for 3.1, etc. set(KRITA_VERSION_RELEASE 88) # 88 for pre-alpha, 89 for Alpha, increase for next test releases, set 0 for first Stable, etc. 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 2017) # 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. if(KRITA_STABLE_VERSION_MAJOR EQUAL 3) math(EXPR GENERIC_KRITA_LIB_VERSION_MAJOR "${KRITA_STABLE_VERSION_MINOR} + 15") else() # let's make sure we won't forget to update the "15" message(FATAL_ERROR "Reminder: please update offset == 15 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_revision(GIT_REFSPEC GIT_SHA1) get_git_branch(GIT_BRANCH) if(GIT_SHA1 AND GIT_BRANCH) string(SUBSTRING ${GIT_SHA1} 0 7 GIT_SHA1) set(KRITA_GIT_SHA1_STRING ${GIT_SHA1}) set(KRITA_GIT_BRANCH_STRING ${GIT_BRANCH}) endif() if(NOT DEFINED RELEASE_BUILD) # estimate mode by CMAKE_BUILD_TYPE content if not set on cmdline string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_TOLOWER) set(RELEASE_BUILD_TYPES "release" "relwithdebinfo" "minsizerel") list(FIND RELEASE_BUILD_TYPES "${CMAKE_BUILD_TYPE_TOLOWER}" INDEX) if (INDEX EQUAL -1) set(RELEASE_BUILD FALSE) else() set(RELEASE_BUILD TRUE) endif() endif() message(STATUS "Release build: ${RELEASE_BUILD}") # 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) option(PACKAGERS_BUILD "Build support of multiple CPU architectures in one binary. Should be used by packagers only or Krita developers. Only switch off when you're an artist optimizing a build for your very own machine." ON) add_feature_info("Packagers' Build" PACKAGERS_BUILD "Support several CPU arch in one binary. Recommended for packages. Switch this off to make a build for only your machine.") 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("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(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).") include(MacroJPEG) ######################## ######################### ## Look for KDE and Qt ## ######################### ######################## find_package(ECM 1.7.0 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 Archive Config WidgetsAddons Completion CoreAddons GuiAddons I18n ItemModels ItemViews WindowSystem ) # KConfig deprecated authorizeKAction. In order to be warning free, # compile with the updated function when the dependency is new enough. # Remove this (and the uses of the define) when the minimum KF5 # version is >= 5.24.0. if (${KF5Config_VERSION} VERSION_LESS "5.24.0" ) message("Old KConfig (< 5.24.0) found.") add_definitions(-DKCONFIG_BEFORE_5_24) endif() find_package(Qt5 ${MIN_QT_VERSION} REQUIRED COMPONENTS Core Gui Widgets Xml Network PrintSupport Svg Test Concurrent ) 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 WIN32 AND NOT APPLE) find_package(Qt5 ${MIN_QT_VERSION} REQUIRED X11Extras) - find_package(Qt5DBus ${MIN_QT_VERSION} QUIET) + 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(KF5KIO ${MIN_FRAMEWORKS_VERSION} QUIET) + find_package(KF5KIO ${MIN_FRAMEWORKS_VERSION}) macro_bool_to_01(KF5KIO_FOUND HAVE_KIO) set_package_properties(KF5KIO PROPERTIES DESCRIPTION "KDE's KIO Framework" URL "http://api.kde.org/frameworks-api/frameworks5-apidocs/kio/html/index.html" TYPE OPTIONAL PURPOSE "Optionally used for recent document handling") - find_package(KF5Crash ${MIN_FRAMEWORKS_VERSION} QUIET) + 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) find_package(XCB COMPONENTS XCB ATOM) set(HAVE_XCB ${XCB_FOUND}) else() set(HAVE_DBUS FALSE) set(HAVE_X11 FALSE) set(HAVE_XCB FALSE) endif() + add_definitions( -DQT_USE_QSTRINGBUILDER -DQT_STRICT_ITERATORS -DQT_NO_SIGNALS_SLOTS_KEYWORDS -DQT_USE_FAST_OPERATOR_PLUS -DQT_USE_FAST_CONCATENATION -DQT_NO_URL_CAST_FROM_STRING -DQT_DISABLE_DEPRECATED_BEFORE=0 ) 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_KRITADEVS "-O3 -g" CACHE STRING "" FORCE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals") 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() # enable exceptions globally kde_enable_exceptions() # only with this definition will all the FOO_TEST_EXPORT macro do something # TODO: check if this can be moved to only those places which make use of it, # to reduce global compiler definitions that would trigger a recompile of # everything on a change (like adding/removing tests to/from the build) if(BUILD_TESTING) add_definitions(-DCOMPILING_TESTS) endif() 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 set(KRITA_PLUGIN_INSTALL_DIR ${LIB_INSTALL_DIR}/kritaplugins) ########################### ############################ ## Required dependencies ## ############################ ########################### find_package(PNG REQUIRED) 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 REQUIRED COMPONENTS system) # for pigment and stage include_directories(${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 ) ########################### ############################ ## Optional dependencies ## ############################ ########################### ## ## Check for OpenEXR ## 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") 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") 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) 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) ## ## 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 REQUIRED "3.0") set_package_properties(Eigen3 PROPERTIES DESCRIPTION "C++ template library for linear algebra" URL "http://eigen.tuxfamily.org" TYPE REQUIRED) ## ## Test for exiv2 ## set(EXIV2_MIN_VERSION "0.16") find_package(Exiv2 REQUIRED) set_package_properties(Exiv2 PROPERTIES DESCRIPTION "Image metadata library and tools" URL "http://www.exiv2.org" PURPOSE "Required by Krita") ## ## Test for lcms ## find_package(LCMS2 REQUIRED "2.4") 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() ## ## 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 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) macro_bool_to_01(PACKAGERS_BUILD DO_PACKAGERS_BUILD) 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 "-Wabi -ffp-contract=fast -fPIC") elseif (NOT MSVC) set(ADDITIONAL_VC_FLAGS "-Wabi -fabi-version=0 -ffp-contract=fast -fPIC") endif() #Handle Vc master if(Vc_COMPILER_IS_GCC OR Vc_COMPILER_IS_CLANG) AddCompilerFlag("-std=c++11" _ok) if(NOT _ok) AddCompilerFlag("-std=c++0x" _ok) endif() endif() macro(ko_compile_for_all_implementations_no_scalar _objs _src) if(PACKAGERS_BUILD) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) else() set(${_objs} ${_src}) endif() endmacro() macro(ko_compile_for_all_implementations _objs _src) if(PACKAGERS_BUILD) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY Scalar SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) else() set(${_objs} ${_src}) endif() endmacro() if (NOT PACKAGERS_BUILD) # Optimize everything for the current architecture set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Vc_DEFINITIONS}") endif () endif() set(CMAKE_MODULE_PATH ${OLD_CMAKE_MODULE_PATH} ) add_definitions(${QT_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS}) if(WIN32) set(LIB_INSTALL_DIR ${LIB_INSTALL_DIR} RUNTIME DESTINATION ${BIN_INSTALL_DIR} LIBRARY ${INSTALL_TARGETS_DEFAULT_ARGS} ARCHIVE ${INSTALL_TARGETS_DEFAULT_ARGS} ) endif() ## ## Test endianess ## include (TestBigEndian) test_big_endian(CMAKE_WORDS_BIGENDIAN) ## ## Test for qt-poppler ## find_package(Poppler) 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 pthreads (for G'Mic) ## find_package(Threads) set_package_properties(Threads PROPERTIES DESCRIPTION "PThreads - A low-level threading library" TYPE OPTIONAL PURPOSE "Optionally used by the G'Mic plugin") ## ## Test for OpenMP (for G'Mic) ## find_package(OpenMP) set_package_properties(OpenMP PROPERTIES DESCRIPTION "A low-level parallel execution library" URL "http://openmp.org/wp/" TYPE OPTIONAL PURPOSE "Optionally used by the G'Mic plugin") ## ## Test for Curl (for G'Mic) ## find_package(CURL) set_package_properties(CURL PROPERTIES DESCRIPTION "A tool to fetch remote data" URL "http://curl.haxx.se/" TYPE OPTIONAL PURPOSE "Optionally used by the G'Mic plugin") ############################ ############################# ## 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) add_subdirectory(benchmarks) 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) message("\nBroken tests:") foreach(tst ${KRITA_BROKEN_TESTS}) message(" * ${tst}") endforeach() feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/config-qtmultimedia.h.cmake b/config-qtmultimedia.h.cmake new file mode 100644 index 0000000000..ca655089f5 --- /dev/null +++ b/config-qtmultimedia.h.cmake @@ -0,0 +1,4 @@ +/* config-qtmultimedia.h. Generated by cmake from config-gsl.h.cmake */ + +/* Defines if you have Qt Multimedia component */ +#cmakedefine HAVE_QT_MULTIMEDIA 1 diff --git a/krita/data/kritarc b/krita/data/kritarc index fd0cb3e373..e4685bffb3 100644 --- a/krita/data/kritarc +++ b/krita/data/kritarc @@ -1,467 +1,469 @@ ArtColorSel.ColorSpace=0 ArtColorSel.InversedSaturation=false ArtColorSel.Light=0.5 ArtColorSel.LightPieces=19 ArtColorSel.NumRings=11 ArtColorSel.RelativeLight=false ArtColorSel.RingAngles=0,0,0,0,0,0,0,0,0,0,0 ArtColorSel.RingPieces=12 ArtColorSel.SelColorA=1 ArtColorSel.SelColorH=0 ArtColorSel.SelColorS=0 ArtColorSel.SelColorX=0.5 BackgroundColorForNewImage=255,255,255 BackgroundOpacityForNewImage=255 BackgroundStyleForNewImage=0 Krita/Ocio/OcioColorManagementMode=0 Krita/Ocio/OcioLockColorVisualRepresentation=false Krita/Ocio/UseOcio=false LastBackGroundColor=\n\n \n\n LastForeGroundColor=\n\n \n\n LastPreset=Basic_circle LastPreset_-1=Basic_circle LineSmoothingDelayDistance=50 LineSmoothingDistance=50 LineSmoothingFinishStabilizedCurve=true LineSmoothingStabilizeSensors=true LineSmoothingTailAggressiveness=0.14999999999999999 LineSmoothingType=1 LineSmoothingUseDelayDistance=true NumberOfLayersForNewImage=2 PaintopPopupDetached=false SpecificColorSelector/ShowColorSpaceSelector=false baseLength=50 colorDepthDef=U8 colorModelDef=RGBA colorProfileDef=sRGB-elle-V2-srgbtrc.icc favoritePresetsTag=demo globalSnapBoundingBox=false globalSnapExtension=false globalSnapImageBounds=true globalSnapImageCenter=true globalSnapIntersection=false globalSnapNode=false globalSnapOrthogonal=false gridmaincolor=99,99,99 gridmainstyle=0 gridsubdivisioncolor=150,150,150 gridsubdivisionstyle=1 guidesColor=99,99,99 guidesLineStyle=0 imageHeightDef=1200 imageResolutionDef=300 imageWidthDef=1600 levelOfDetailEnabled=true numberOfOnionSkins=10 oninSkinTintColorForward=0,255,0 onionSkinOpacity_-1=173 onionSkinOpacity_-10=22 onionSkinOpacity_-2=163 onionSkinOpacity_-3=147 onionSkinOpacity_-4=127 onionSkinOpacity_-5=107 onionSkinOpacity_-6=84 onionSkinOpacity_-7=63 onionSkinOpacity_-8=48 onionSkinOpacity_-9=33 onionSkinOpacity_0=175 onionSkinOpacity_1=173 onionSkinOpacity_10=22 onionSkinOpacity_2=163 onionSkinOpacity_3=147 onionSkinOpacity_4=127 onionSkinOpacity_5=107 onionSkinOpacity_6=84 onionSkinOpacity_7=63 onionSkinOpacity_8=48 onionSkinOpacity_9=33 onionSkinState_-1=true onionSkinState_-10=false onionSkinState_-2=true onionSkinState_-3=false onionSkinState_-4=false onionSkinState_-5=false onionSkinState_-6=false onionSkinState_-7=false onionSkinState_-8=false onionSkinState_-9=false onionSkinState_0=true onionSkinState_1=true onionSkinState_10=false onionSkinState_2=true onionSkinState_3=false onionSkinState_4=false onionSkinState_5=false onionSkinState_6=false onionSkinState_7=false onionSkinState_8=false onionSkinState_9=false onionSkinTintColorBackward=255,0,0 onionSkinTintFactor=191 presethistory=Basic_tip_default showAdditionalOnionSkinsSettings=true toolbarslider_1=opacity toolbarslider_2=size toolbarslider_3=flow [advancedColorSelector] allowHorizontalLayout=true colorSelectorConfiguration=3|0|5|0 commonColorsAlignment=false commonColorsAutoUpdate=false commonColorsCount=12 commonColorsHeight=16 commonColorsNumCols=1 commonColorsNumRows=1 commonColorsScrolling=false commonColorsShow=true commonColorsWidth=16 customColorSpaceDepthID=U8 customColorSpaceModel=RGBA customColorSpaceProfile=sRGB built-in lastUsedColorsAlignment=true lastUsedColorsCount=20 lastUsedColorsHeight=16 lastUsedColorsNumCols=1 lastUsedColorsNumRows=1 lastUsedColorsScrolling=true lastUsedColorsShow=true lastUsedColorsWidth=16 minimalShadeSelectorAsGradient=true minimalShadeSelectorLineConfig=0|0.2|0|0|0|0|0;1|0|1|1|0|0|0;2|0|-1|1|0|0|0; minimalShadeSelectorLineHeight=10 minimalShadeSelectorPatchCount=10 popupOnMouseClick=true popupOnMouseOver=false shadeSelectorHideable=false shadeSelectorType=Minimal shadeSelectorUpdateOnBackground=true shadeSelectorUpdateOnForeground=true shadeSelectorUpdateOnLeftClick=false shadeSelectorUpdateOnRightClick=false useCustomColorSpace=false zoomSize=280 [DockWidget sharedtooldocker] TabbedMode=false [KisToolTransform] filterId=Bicubic [MainWindow] Height 1080=720 Width 1920=1256 ko_geometry=AdnQywACAAAAAAE6AAAAtAAABikAAAOkAAABPgAAANEAAAYlAAADoAAAAAAAAAAAB4A= ko_windowstate=AAAA/wAAAAD9AAAABAAAAAAAAAA/AAACdvwCAAAAA/sAAAAOAFQAbwBvAGwAQgBvAHgBAAAAPAAAAnYAAAAVAP////sAAAAkAEYAbABvAHcAUwBoAGEAcABlAEIAbwB4AEQAbwBjAGsAZQByAAAAA2oAAADHAAAAAAAAAAD7AAAAKABGAGwAbwB3AFMAdABlAG4AYwBpAGwAQgBvAHgARABvAGMAawBlAHIAAAADfQAAAMcAAAAAAAAAAAAAAAEAAAEZAAACdvwCAAAAOvsAAAAaAEsAaQBzAEIAaQByAGQAZQB5AGUAQgBvAHgAAAAAAP////8AAAAAAAAAAPsAAAAgAEsAaQBzAFAAYQBsAGUAdAB0AGUARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAPsAAAAaAEsAbwBDAG8AbABvAHIARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAPsAAAAwAEsAaQBzAFQAcgBpAGEAbgBnAGwAZQBDAG8AbABvAHIAUwBlAGwAZQBjAHQAbwByAAAAAAD/////AAAAAAAAAAD7AAAAIgBTAGgAYQBkAG8AdwAgAFAAcgBvAHAAZQByAHQAaQBlAHMAAAAAAP////8AAAAAAAAAAPsAAAAgAFMAaABhAHAAZQAgAFAAcgBvAHAAZQByAHQAaQBlAHMAAAAAAP////8AAAAUAP////sAAAAaAFMAaABhAHAAZQBTAGUAbABlAGMAdABvAHIAAAAASAAAAEQAAAAAAAAAAPsAAAAkAFMAaQBtAHAAbABlACAAVABlAHgAdAAgAEUAZABpAHQAbwByAAAAAAD/////AAAAAAAAAAD8AAAAPAAAAN0AAACkAQAAGPoAAAAAAQAAAAX7AAAAHgBDAG8AbABvAHIAUwBlAGwAZQBjAHQAbwByAE4AZwEAAAAA/////wAAANUA////+wAAACoAUwBwAGUAYwBpAGYAaQBjAEMAbwBsAG8AcgBTAGUAbABlAGMAdABvAHIBAAAAAP////8AAADKAP////sAAAAWAEMAbwBsAG8AcgBTAGwAaQBkAGUAcgEAAAAA/////wAAAJMA////+wAAABYASQBtAGEAZwBlAEQAbwBjAGsAZQByAAAAAAD/////AAAAwAD////7AAAAKgBTAGgAYQBwAGUAQwBvAGwAbABlAGMAdABpAG8AbgBEAG8AYwBrAGUAcgAAAAZIAAABKAAAAIkAAACJ+wAAAEYASwByAGkAdABhAFMAaABhAHAAZQAvAEsAaQBzAFQAbwBvAGwARAB5AG4AYQBvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AQAAAFIAAAASAAAAAAAAAAD7AAAALABLAHIAaQB0AGEAUwBoAGEAcABlAC8ASwBpAHMAVABvAG8AbABMAGkAbgBlAQAAADwAAABpAAAAAAAAAAD7AAAAMgBLAHIAaQB0AGEAUwBoAGEAcABlAC8ASwBpAHMAVABvAG8AbABFAGwAbABpAHAAcwBlAQAAAJEAAAASAAAAAAAAAAD7AAAAHABLAGkAcwBUAG8AbwBsAFAAbwBsAHkAZwBvAG4BAAAApgAAABIAAAAAAAAAAPsAAAAeAEsAaQBzAFQAbwBvAGwAUABvAGwAeQBsAGkAbgBlAQAAALsAAAASAAAAAAAAAAD7AAAAFgBLAGkAcwBUAG8AbwBsAFMAdABhAHIBAAAA0AAAABMAAAAAAAAAAPsAAAAqAFMAbgBhAHAARwB1AGkAZABlAEMAbwBuAGYAaQBnAFcAaQBkAGcAZQB0AAAAAO8AAABxAAAAAAAAAAD7AAAAMgBLAGkAcwBUAG8AbwBsAEMAcgBvAHAAIABvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AQAAAPsAAAASAAAAAAAAAAD7AAAAUABLAHIAaQB0AGEAVAByAGEAbgBzAGYAbwByAG0ALwBLAGkAcwBUAG8AbwBsAE0AbwB2AGUAIABPAHAAdABpAG8AbgAgAFcAaQBkAGcAZQB0AQAAARAAAAASAAAAAAAAAAD7AAAAPABLAGkAcwBUAG8AbwBsAFQAcgBhAG4AcwBmAG8AcgBtACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAA8AAAALwAAAAAAAAAA+wAAAE4ASwByAGkAdABhAFMAaABhAHAAZQAvAEsAaQBzAFQAbwBvAGwATQBlAGEAcwB1AHIAZQAgAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAAAPAAAAEIAAAAAAAAAAPsAAABcAEsAcgBpAHQAYQBTAGUAbABlAGMAdABlAGQALwBLAGkAcwBUAG8AbwBsAEMAbwBsAG8AcgBQAGkAYwBrAGUAcgAgAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAAAPAAAAP8AAAAAAAAAAPsAAABGAEsAaQBzAFIAdQBsAGUAcgBBAHMAcwBpAHMAdABhAG4AdABUAG8AbwBsACAATwBwAHQAaQBvAG4AIABXAGkAZABnAGUAdAEAAAA8AAAAEgAAAAAAAAAA+wAAAEgASwBpAHMAVABvAG8AbABQAGUAcgBzAHAAZQBjAHQAaQB2AGUARwByAGkAZAAgAE8AcAB0AGkAbwBuACAAVwBpAGQAZwBlAHQBAAABowAAABIAAAAAAAAAAPsAAAAyAEsAaQBzAFQAbwBvAGwARwByAGkAZAAgAE8AcAB0AGkAbwBuACAAVwBpAGQAZwBlAHQBAAABuAAAABMAAAAAAAAAAPsAAABMAEsAaQBzAFQAbwBvAGwAUwBlAGwAZQBjAHQAUgBlAGMAdABhAG4AZwB1AGwAYQByACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAHOAAAAEgAAAAAAAAAA+wAAAEoASwBpAHMAVABvAG8AbABTAGUAbABlAGMAdABFAGwAbABpAHAAdABpAGMAYQBsACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAHjAAAAEgAAAAAAAAAA+wAAAEgASwBpAHMAVABvAG8AbABTAGUAbABlAGMAdABQAG8AbAB5AGcAbwBuAGEAbAAgAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAAB+AAAABIAAAAAAAAAAPsAAABEAEsAaQBzAFQAbwBvAGwAUwBlAGwAZQBjAHQATwB1AHQAbABpAG4AZQAgAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAACDQAAABIAAAAAAAAAAPsAAABKAEsAaQBzAFQAbwBvAGwAUwBlAGwAZQBjAHQAQwBvAG4AdABpAGcAdQBvAHUAcwAgAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAACIgAAABIAAAAAAAAAAPsAAABEAEsAaQBzAFQAbwBvAGwAUwBlAGwAZQBjAHQAUwBpAG0AaQBsAGEAcgAgAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAACNwAAABIAAAAAAAAAAPwAAAG2AAAAWgAAAAAA////+gAAAAABAAAAAvsAAAAuAEsAbwBTAGgAYQBwAGUAQwBvAGwAbABlAGMAdABpAG8AbgBEAG8AYwBrAGUAcgEAAAAA/////wAAAAAAAAAA+wAAACQAUwBtAGEAbABsAEMAbwBsAG8AcgBTAGUAbABlAGMAdABvAHIAAAADbgAAAQQAAAC9AP////wAAAEfAAABkwAAAL4BAAAY+gAAAAABAAAABfsAAAAWAEsAaQBzAEwAYQB5AGUAcgBCAG8AeAEAAAAA/////wAAAQIA////+wAAABoAQwBoAGEAbgBuAGUAbABEAG8AYwBrAGUAcgAAAAAA/////wAAAIEA////+wAAABgAUAByAGUAcwBlAHQARABvAGMAawBlAHIBAAAAAP////8AAACaAP////sAAAAgAHMAaABhAHIAZQBkAHQAbwBvAGwAZABvAGMAawBlAHIBAAAAAP////8AAACBAP////sAAAAuAEsAaQBzAFAAYQBpAG4AdABlAHIAbAB5AE0AaQB4AGUAcgBEAG8AYwBrAGUAcgAAAAAA/////wAAAAAAAAAA+wAAAEgASwByAGkAdABhAFMAaABhAHAAZQAvAEsAaQBzAFQAbwBvAGwAQgByAHUAcwBoAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAAD3AAAAGgAAAAAAAAAAPsAAAAiAFMAdAByAG8AawBlACAAUAByAG8AcABlAHIAdABpAGUAcwAAAAAA/////wAAAAAAAAAA+wAAABYAUwB0AHkAbABlAEQAbwBjAGsAZQByAAAAAAD/////AAAAAAAAAAD7AAAAIABLAGkAcwBIAGkAcwB0AG8AZwByAGEAbQBEAG8AYwBrAAAAAAD/////AAAAAAAAAAD7AAAAEgBTAGMAcgBpAHAAdABpAG4AZwAAAAAA/////wAAAAAAAAAA+wAAADAARABlAGYAYQB1AGwAdABUAG8AbwBsAEEAcgByAGEAbgBnAGUAVwBpAGQAZwBlAHQAAAACvAAAAFIAAAAAAAAAAPsAAAAiAEQAZQBmAGEAdQBsAHQAVABvAG8AbABXAGkAZABnAGUAdAAAAAMRAAAAWwAAAAAAAAAA+wAAACQASwBpAHMASABpAHMAdABvAGcAcgBhAG0ARABvAGMAawBlAHIAAAACQgAAAHsAAAAAAAAAAPsAAAAYAEQAaQBnAGkAdABhAGwATQBpAHgAZQByAAAAAAD/////AAAAkQD////7AAAADgBIAGkAcwB0AG8AcgB5AAAAA5AAAAC0AAAAWgD////7AAAATgBLAHIAaQB0AGEARgBpAGwAbAAvAEsAaQBzAFQAbwBvAGwARwByAGEAZABpAGUAbgB0ACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAAAAAQoAAAAHAAAAAAAAAAA+wAAAEYASwByAGkAdABhAEYAaQBsAGwALwBLAGkAcwBUAG8AbwBsAEYAaQBsAGwAIABvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AAAAA1AAAAAcAAAAAAAAAAD7AAAANgBLAHIAaQB0AGEAUwBoAGEAcABlAC8ASwBpAHMAVABvAG8AbABSAGUAYwB0AGEAbgBnAGwAZQAAAAMFAAAAZwAAAAAAAAAA+wAAACIAQwBvAG0AcABvAHMAaQB0AGkAbwBuAEQAbwBjAGsAZQByAAAAAAD/////AAAAegD////7AAAAKgBBAHIAdABpAHMAdABpAGMAQwBvAGwAbwByAFMAZQBsAGUAYwB0AG8AcgAAAAAA/////wAAAHgA////+wAAABoAUABhAHQAdABlAHIAbgBEAG8AYwBrAGUAcgAAAALZAAABSQAAAT8A////+wAAABoAVABhAHMAawBzAGUAdABEAG8AYwBrAGUAcgAAAAAA/////wAAAHoA////+wAAACgAUwBuAGEAcABHAHUAaQBkAGUAIABQAHIAbwBwAGUAcgB0AGkAZQBzAAAAAAD/////AAAAAAAAAAD7AAAAOABUAGUAeAB0AEQAbwBjAHUAbQBlAG4AdABJAG4AcwBwAGUAYwB0AGkAbwBuAEQAbwBjAGsAZQByAgAABJoAAAIVAAABKgAAAK77AAAAEgBMAHUAdABEAG8AYwBrAGUAcgAAAAAA/////wAAATkA////+wAAABwATwB2AGUAcgB2AGkAZQB3AEQAbwBjAGsAZQByAAAAAAD/////AAAASAD////7AAAAGgBQAGEAbABlAHQAdABlAEQAbwBjAGsAZQByAAAAAAD/////AAAAPwD////7AAAAGgBQAHIAZQBzAGUAdABIAGkAcwB0AG8AcgB5AAAAAAD/////AAAAWgD////7AAAAFABHAHIAaQBkAEQAbwBjAGsAZQByAAAAAAD/////AAABLgD////7AAAAHgBIAGkAcwB0AG8AZwByAGEAbQBEAG8AYwBrAGUAcgAAAAAA/////wAAAEgA////+wAAACoAQQBuAGkAbQBhAHQAaQBvAG4AQwB1AHIAdgBlAHMARABvAGMAawBlAHIAAAAAAP////8AAAB5AP///wAAAAIAAAeAAAAAvPwBAAAAAfsAAAAaAFQAbwBvAGwAQgBhAHIARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAAAAAAMAAAAAAAAAAPwBAAAABPsAAAAcAEYAbABpAHAAYgBvAG8AawBEAG8AYwBrAGUAcgAAAAAA/////wAAAAAAAAAA+wAAAB4AQQBuAGkAbQBhAHQAaQBvAG4ARABvAGMAawBlAHIAAAAAAP////8AAAELAP////sAAAAgAE8AbgBpAG8AbgBTAGsAaQBuAHMARABvAGMAawBlAHIAAAAAAP////8AAAEtAP////sAAAAcAFQAaQBtAGUAbABpAG4AZQBEAG8AYwBrAGUAcgAAAAAA/////wAAAH0A////AAADhAAAAnYAAAAEAAAABAAAAAgAAAAI/AAAAAEAAAACAAAAAgAAABYAbQBhAGkAbgBUAG8AbwBsAEIAYQByAAAAAAD/////AAAAAAAAAAAAAAAeAEIAcgB1AHMAaABlAHMAQQBuAGQAUwB0AHUAZgBmAQAAAAD/////AAAAAAAAAAA= [advancedColorSelector] gamma=2.2000000000000002 hidePopupOnClickCheck=false hsxSettingType=0 lumaB=0.0722 lumaG=0.71519999999999995 lumaR=0.21260000000000001 onDockerResize=0 shadeMyPaintType=HSV zoomSelectorOptions=0 [calligra] ColorSpaceExtensionsPlugins=\\0 ColorSpaceExtensionsPluginsDisabled= ColorSpacePlugins=\\0 ColorSpacePluginsDisabled= DockerPlugins=\\0 DockerPluginsDisabled=textdocumentinspection FlakePlugins=, ShapePlugins=, ToolsBlacklist=CreatePathTool,KoPencilTool,ConnectionTool,KarbonFilterEffectsTool ToolPlugins=,, ToolPluginsDisabled= [KoShapeCollection] QuickShapes=ArtisticText,TextShapeID,EllipseShape,RectangleShape [colorhotkeys] steps_blueyellow=10 steps_hue=36 steps_lightness=10 steps_redgreen=10 steps_saturation=10 [crashprevention] CreatingCanvas=false [hsxColorSlider] hsiH=false hsiI=false hsiS=false hslH=true hslL=true hslS=true hsvH=false hsvS=false hsvV=false hsyH=false hsyS=false hsyY=false [krita] State=AAAA/wAAAAD9AAAABAAAAAAAAAA/AAACdvwCAAAAA/sAAAAOAFQAbwBvAGwAQgBvAHgBAAAAPAAAAnYAAAAVAP////sAAAAkAEYAbABvAHcAUwBoAGEAcABlAEIAbwB4AEQAbwBjAGsAZQByAAAAA2oAAADHAAAAAAAAAAD7AAAAKABGAGwAbwB3AFMAdABlAG4AYwBpAGwAQgBvAHgARABvAGMAawBlAHIAAAADfQAAAMcAAAAAAAAAAAAAAAEAAAEZAAACdvwCAAAAOvsAAAAaAEsAaQBzAEIAaQByAGQAZQB5AGUAQgBvAHgAAAAAAP////8AAAAAAAAAAPsAAAAgAEsAaQBzAFAAYQBsAGUAdAB0AGUARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAPsAAAAaAEsAbwBDAG8AbABvAHIARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAPsAAAAwAEsAaQBzAFQAcgBpAGEAbgBnAGwAZQBDAG8AbABvAHIAUwBlAGwAZQBjAHQAbwByAAAAAAD/////AAAAAAAAAAD7AAAAIgBTAGgAYQBkAG8AdwAgAFAAcgBvAHAAZQByAHQAaQBlAHMAAAAAAP////8AAAAAAAAAAPsAAAAgAFMAaABhAHAAZQAgAFAAcgBvAHAAZQByAHQAaQBlAHMAAAAAAP////8AAAAUAP////sAAAAaAFMAaABhAHAAZQBTAGUAbABlAGMAdABvAHIAAAAASAAAAEQAAAAAAAAAAPsAAAAkAFMAaQBtAHAAbABlACAAVABlAHgAdAAgAEUAZABpAHQAbwByAAAAAAD/////AAAAAAAAAAD8AAAAPAAAAN0AAACkAQAAGPoAAAAAAQAAAAX7AAAAHgBDAG8AbABvAHIAUwBlAGwAZQBjAHQAbwByAE4AZwEAAAAA/////wAAANUA////+wAAACoAUwBwAGUAYwBpAGYAaQBjAEMAbwBsAG8AcgBTAGUAbABlAGMAdABvAHIBAAAAAP////8AAADKAP////sAAAAWAEMAbwBsAG8AcgBTAGwAaQBkAGUAcgEAAAAA/////wAAAJMA////+wAAABYASQBtAGEAZwBlAEQAbwBjAGsAZQByAAAAAAD/////AAAAwAD////7AAAAKgBTAGgAYQBwAGUAQwBvAGwAbABlAGMAdABpAG8AbgBEAG8AYwBrAGUAcgAAAAZIAAABKAAAAIkAAACJ+wAAAEYASwByAGkAdABhAFMAaABhAHAAZQAvAEsAaQBzAFQAbwBvAGwARAB5AG4AYQBvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AQAAAFIAAAASAAAAAAAAAAD7AAAALABLAHIAaQB0AGEAUwBoAGEAcABlAC8ASwBpAHMAVABvAG8AbABMAGkAbgBlAQAAADwAAABpAAAAAAAAAAD7AAAAMgBLAHIAaQB0AGEAUwBoAGEAcABlAC8ASwBpAHMAVABvAG8AbABFAGwAbABpAHAAcwBlAQAAAJEAAAASAAAAAAAAAAD7AAAAHABLAGkAcwBUAG8AbwBsAFAAbwBsAHkAZwBvAG4BAAAApgAAABIAAAAAAAAAAPsAAAAeAEsAaQBzAFQAbwBvAGwAUABvAGwAeQBsAGkAbgBlAQAAALsAAAASAAAAAAAAAAD7AAAAFgBLAGkAcwBUAG8AbwBsAFMAdABhAHIBAAAA0AAAABMAAAAAAAAAAPsAAAAqAFMAbgBhAHAARwB1AGkAZABlAEMAbwBuAGYAaQBnAFcAaQBkAGcAZQB0AAAAAO8AAABxAAAAAAAAAAD7AAAAMgBLAGkAcwBUAG8AbwBsAEMAcgBvAHAAIABvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AQAAAPsAAAASAAAAAAAAAAD7AAAAUABLAHIAaQB0AGEAVAByAGEAbgBzAGYAbwByAG0ALwBLAGkAcwBUAG8AbwBsAE0AbwB2AGUAIABPAHAAdABpAG8AbgAgAFcAaQBkAGcAZQB0AQAAARAAAAASAAAAAAAAAAD7AAAAPABLAGkAcwBUAG8AbwBsAFQAcgBhAG4AcwBmAG8AcgBtACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAA8AAAALwAAAAAAAAAA+wAAAE4ASwByAGkAdABhAFMAaABhAHAAZQAvAEsAaQBzAFQAbwBvAGwATQBlAGEAcwB1AHIAZQAgAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAAAPAAAAEIAAAAAAAAAAPsAAABcAEsAcgBpAHQAYQBTAGUAbABlAGMAdABlAGQALwBLAGkAcwBUAG8AbwBsAEMAbwBsAG8AcgBQAGkAYwBrAGUAcgAgAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAAAPAAAAP8AAAAAAAAAAPsAAABGAEsAaQBzAFIAdQBsAGUAcgBBAHMAcwBpAHMAdABhAG4AdABUAG8AbwBsACAATwBwAHQAaQBvAG4AIABXAGkAZABnAGUAdAEAAAA8AAAAEgAAAAAAAAAA+wAAAEgASwBpAHMAVABvAG8AbABQAGUAcgBzAHAAZQBjAHQAaQB2AGUARwByAGkAZAAgAE8AcAB0AGkAbwBuACAAVwBpAGQAZwBlAHQBAAABowAAABIAAAAAAAAAAPsAAAAyAEsAaQBzAFQAbwBvAGwARwByAGkAZAAgAE8AcAB0AGkAbwBuACAAVwBpAGQAZwBlAHQBAAABuAAAABMAAAAAAAAAAPsAAABMAEsAaQBzAFQAbwBvAGwAUwBlAGwAZQBjAHQAUgBlAGMAdABhAG4AZwB1AGwAYQByACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAHOAAAAEgAAAAAAAAAA+wAAAEoASwBpAHMAVABvAG8AbABTAGUAbABlAGMAdABFAGwAbABpAHAAdABpAGMAYQBsACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAHjAAAAEgAAAAAAAAAA+wAAAEgASwBpAHMAVABvAG8AbABTAGUAbABlAGMAdABQAG8AbAB5AGcAbwBuAGEAbAAgAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAAB+AAAABIAAAAAAAAAAPsAAABEAEsAaQBzAFQAbwBvAGwAUwBlAGwAZQBjAHQATwB1AHQAbABpAG4AZQAgAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAACDQAAABIAAAAAAAAAAPsAAABKAEsAaQBzAFQAbwBvAGwAUwBlAGwAZQBjAHQAQwBvAG4AdABpAGcAdQBvAHUAcwAgAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAACIgAAABIAAAAAAAAAAPsAAABEAEsAaQBzAFQAbwBvAGwAUwBlAGwAZQBjAHQAUwBpAG0AaQBsAGEAcgAgAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAACNwAAABIAAAAAAAAAAPwAAAG2AAAAWgAAAAAA////+gAAAAABAAAAAvsAAAAuAEsAbwBTAGgAYQBwAGUAQwBvAGwAbABlAGMAdABpAG8AbgBEAG8AYwBrAGUAcgEAAAAA/////wAAAAAAAAAA+wAAACQAUwBtAGEAbABsAEMAbwBsAG8AcgBTAGUAbABlAGMAdABvAHIAAAADbgAAAQQAAAC9AP////wAAAEfAAABkwAAAL4BAAAY+gAAAAABAAAABfsAAAAWAEsAaQBzAEwAYQB5AGUAcgBCAG8AeAEAAAAA/////wAAAQIA////+wAAABoAQwBoAGEAbgBuAGUAbABEAG8AYwBrAGUAcgAAAAAA/////wAAAIEA////+wAAABgAUAByAGUAcwBlAHQARABvAGMAawBlAHIBAAAAAP////8AAACaAP////sAAAAgAHMAaABhAHIAZQBkAHQAbwBvAGwAZABvAGMAawBlAHIBAAAAAP////8AAACBAP////sAAAAuAEsAaQBzAFAAYQBpAG4AdABlAHIAbAB5AE0AaQB4AGUAcgBEAG8AYwBrAGUAcgAAAAAA/////wAAAAAAAAAA+wAAAEgASwByAGkAdABhAFMAaABhAHAAZQAvAEsAaQBzAFQAbwBvAGwAQgByAHUAcwBoAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAAD3AAAAGgAAAAAAAAAAPsAAAAiAFMAdAByAG8AawBlACAAUAByAG8AcABlAHIAdABpAGUAcwAAAAAA/////wAAAAAAAAAA+wAAABYAUwB0AHkAbABlAEQAbwBjAGsAZQByAAAAAAD/////AAAAAAAAAAD7AAAAIABLAGkAcwBIAGkAcwB0AG8AZwByAGEAbQBEAG8AYwBrAAAAAAD/////AAAAAAAAAAD7AAAAEgBTAGMAcgBpAHAAdABpAG4AZwAAAAAA/////wAAAAAAAAAA+wAAADAARABlAGYAYQB1AGwAdABUAG8AbwBsAEEAcgByAGEAbgBnAGUAVwBpAGQAZwBlAHQAAAACvAAAAFIAAAAAAAAAAPsAAAAiAEQAZQBmAGEAdQBsAHQAVABvAG8AbABXAGkAZABnAGUAdAAAAAMRAAAAWwAAAAAAAAAA+wAAACQASwBpAHMASABpAHMAdABvAGcAcgBhAG0ARABvAGMAawBlAHIAAAACQgAAAHsAAAAAAAAAAPsAAAAYAEQAaQBnAGkAdABhAGwATQBpAHgAZQByAAAAAAD/////AAAAkQD////7AAAADgBIAGkAcwB0AG8AcgB5AAAAA5AAAAC0AAAAWgD////7AAAATgBLAHIAaQB0AGEARgBpAGwAbAAvAEsAaQBzAFQAbwBvAGwARwByAGEAZABpAGUAbgB0ACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAAAAAQoAAAAHAAAAAAAAAAA+wAAAEYASwByAGkAdABhAEYAaQBsAGwALwBLAGkAcwBUAG8AbwBsAEYAaQBsAGwAIABvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AAAAA1AAAAAcAAAAAAAAAAD7AAAANgBLAHIAaQB0AGEAUwBoAGEAcABlAC8ASwBpAHMAVABvAG8AbABSAGUAYwB0AGEAbgBnAGwAZQAAAAMFAAAAZwAAAAAAAAAA+wAAACIAQwBvAG0AcABvAHMAaQB0AGkAbwBuAEQAbwBjAGsAZQByAAAAAAD/////AAAAegD////7AAAAKgBBAHIAdABpAHMAdABpAGMAQwBvAGwAbwByAFMAZQBsAGUAYwB0AG8AcgAAAAAA/////wAAAHgA////+wAAABoAUABhAHQAdABlAHIAbgBEAG8AYwBrAGUAcgAAAALZAAABSQAAAT8A////+wAAABoAVABhAHMAawBzAGUAdABEAG8AYwBrAGUAcgAAAAAA/////wAAAHoA////+wAAACgAUwBuAGEAcABHAHUAaQBkAGUAIABQAHIAbwBwAGUAcgB0AGkAZQBzAAAAAAD/////AAAAAAAAAAD7AAAAOABUAGUAeAB0AEQAbwBjAHUAbQBlAG4AdABJAG4AcwBwAGUAYwB0AGkAbwBuAEQAbwBjAGsAZQByAgAABJoAAAIVAAABKgAAAK77AAAAEgBMAHUAdABEAG8AYwBrAGUAcgAAAAAA/////wAAATkA////+wAAABwATwB2AGUAcgB2AGkAZQB3AEQAbwBjAGsAZQByAAAAAAD/////AAAASAD////7AAAAGgBQAGEAbABlAHQAdABlAEQAbwBjAGsAZQByAAAAAAD/////AAAAPwD////7AAAAGgBQAHIAZQBzAGUAdABIAGkAcwB0AG8AcgB5AAAAAAD/////AAAAWgD////7AAAAFABHAHIAaQBkAEQAbwBjAGsAZQByAAAAAAD/////AAABLgD////7AAAAHgBIAGkAcwB0AG8AZwByAGEAbQBEAG8AYwBrAGUAcgAAAAAA/////wAAAEgA////+wAAACoAQQBuAGkAbQBhAHQAaQBvAG4AQwB1AHIAdgBlAHMARABvAGMAawBlAHIAAAAAAP////8AAAB5AP///wAAAAIAAAeAAAAAvPwBAAAAAfsAAAAaAFQAbwBvAGwAQgBhAHIARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAAAAAAMAAAAAAAAAAPwBAAAABPsAAAAcAEYAbABpAHAAYgBvAG8AawBEAG8AYwBrAGUAcgAAAAAA/////wAAAAAAAAAA+wAAAB4AQQBuAGkAbQBhAHQAaQBvAG4ARABvAGMAawBlAHIAAAAAAP////8AAAELAP////sAAAAgAE8AbgBpAG8AbgBTAGsAaQBuAHMARABvAGMAawBlAHIAAAAAAP////8AAAEtAP////sAAAAcAFQAaQBtAGUAbABpAG4AZQBEAG8AYwBrAGUAcgAAAAAA/////wAAAH0A////AAADhAAAAnYAAAAEAAAABAAAAAgAAAAI/AAAAAEAAAACAAAAAgAAABYAbQBhAGkAbgBUAG8AbwBsAEIAYQByAAAAAAD/////AAAAAAAAAAAAAAAeAEIAcgB1AHMAaABlAHMAQQBuAGQAUwB0AHUAZgBmAQAAAAD/////AAAAAAAAAAA= ToolBarsMovable=Disabled [krita][DockWidget AnimationCurvesDocker] Collapsed=false DockArea=2 Locked=false height=421 width=448 xPosition=0 yPosition=0 [krita][DockWidget AnimationDocker] Collapsed=false DockArea=8 Locked=false height=160 width=280 xPosition=0 yPosition=0 [krita][DockWidget ArtisticColorSelector] Collapsed=false DockArea=2 Locked=false height=294 width=337 xPosition=0 yPosition=0 [krita][DockWidget ChannelDocker] Collapsed=false DockArea=2 Locked=false height=30 width=100 xPosition=0 yPosition=0 [krita][DockWidget ColorSelectorNg] Collapsed=false DockArea=2 Locked=false height=176 width=281 xPosition=0 yPosition=20 [krita][DockWidget ColorSlider] Collapsed=false DockArea=2 Locked=false height=460 width=640 xPosition=0 yPosition=20 [krita][DockWidget CompositionDocker] Collapsed=false DockArea=2 Locked=false height=300 width=400 xPosition=0 yPosition=0 [krita][DockWidget DigitalMixer] Collapsed=false DockArea=2 Locked=false height=30 width=100 xPosition=0 yPosition=0 [krita][DockWidget GridDocker] Collapsed=false DockArea=2 Locked=false height=342 width=441 xPosition=0 yPosition=0 [krita][DockWidget HistogramDocker] Collapsed=false DockArea=2 Locked=false height=91 width=281 xPosition=0 yPosition=20 [krita][DockWidget History] Collapsed=false DockArea=2 Locked=false height=460 width=640 xPosition=0 yPosition=20 [krita][DockWidget ImageDocker] Collapsed=false DockArea=2 Locked=false height=300 width=399 xPosition=0 yPosition=0 [krita][DockWidget KisLayerBox] DockArea=2 Locked=false height=358 width=281 xPosition=0 yPosition=20 [krita][DockWidget LutDocker] Collapsed=false DockArea=2 Locked=false height=286 width=357 xPosition=0 yPosition=0 [krita][DockWidget OnionSkinsDocker] Collapsed=false DockArea=8 Locked=false height=210 width=356 xPosition=0 yPosition=0 [krita][DockWidget OverviewDocker] Collapsed=false DockArea=2 Locked=false height=30 width=100 xPosition=0 yPosition=0 [krita][DockWidget PaletteDocker] Collapsed=false DockArea=2 Locked=false height=219 width=256 xPosition=0 yPosition=0 [krita][DockWidget PatternDocker] Collapsed=false DockArea=2 Locked=false height=30 width=100 xPosition=0 yPosition=0 [krita][DockWidget PresetDocker] Collapsed=false DockArea=2 Locked=false height=460 width=640 xPosition=0 yPosition=20 [krita][DockWidget PresetHistory] Collapsed=false DockArea=2 Locked=false height=30 width=100 xPosition=0 yPosition=0 [krita][DockWidget Shape Properties] DockArea=2 Locked=false height=480 width=640 xPosition=0 yPosition=0 [krita][DockWidget ShapeCollectionDocker] Collapsed=false DockArea=2 Locked=false height=0 width=0 xPosition=0 yPosition=20 [krita][DockWidget SmallColorSelector] DockArea=2 Locked=false height=460 width=640 xPosition=0 yPosition=20 [krita][DockWidget SpecificColorSelector] DockArea=2 Locked=false height=460 width=640 xPosition=0 yPosition=20 [krita][DockWidget TasksetDocker] Collapsed=false DockArea=2 Locked=false height=300 width=400 xPosition=0 yPosition=0 [krita][DockWidget TimelineDocker] Collapsed=false DockArea=8 Locked=false height=30 width=100 xPosition=0 yPosition=0 [krita][DockWidget ToolBox] DockArea=1 Locked=false height=610 width=63 xPosition=0 yPosition=20 [krita][DockWidget sharedtooldocker] Collapsed=false DockArea=2 Locked=false height=460 width=640 xPosition=0 yPosition=20 [krita][Toolbar mainToolBar] ToolButtonStyle=IconOnly [TemplateChooserDialog] ShowCustomDocumentWidgetByDefault=true LastReturnType=Custom Document [theme] Theme=Krita dark + +favoriteCompositeOps=normal,erase,multiply,burn,darken,add,dodge,screen,overlay,soft_light_svg,luminize,lighten,saturation,color diff --git a/krita/data/paintoppresets/kis_paintoppresets_tags.xml b/krita/data/paintoppresets/kis_paintoppresets_tags.xml index 9ea6708fa7..cc7337730a 100644 --- a/krita/data/paintoppresets/kis_paintoppresets_tags.xml +++ b/krita/data/paintoppresets/kis_paintoppresets_tags.xml @@ -1,62 +1,227 @@ ink paint sketch sketch sketch demo ink demo demo demo demo paint demo demo demo ink paint demo paint demo + + Block + + + Block + Wet + + + Block + Mix + + + FX + + + Mix + + + FX + + + Ink + + + Erasers + + + Erasers + + + Wet + + + Circle + + + Ink + + + Block + + + Block + Smudge + + + Mix + + + Circle + + + Wet + + + FX + + + Mix + + + FX + + + Circle + + + Smudge + + + Block + + + Mix + + + Circle + + + Block + + + Block + + + Ink + + + Wet + + + Ink + + + Erasers + + + Ink + + + Smudge + + + Smudge + + + Block + + + Ink + + + Circle + Ink + + + Circle + Ink + + + Wet + + + Erasers + + + Erasers + + + Smudge + + + Wet + + + Circle + + + FX + + + Ink + + + Wet + + + Circle + + + FX + + + PixelArt + + + PixelArt + + + PixelArt + + + PixelArt + + diff --git a/krita/pics/svg/dark_audio-none.svg b/krita/pics/svg/dark_audio-none.svg new file mode 100644 index 0000000000..6b08d8c5e7 --- /dev/null +++ b/krita/pics/svg/dark_audio-none.svg @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/krita/pics/svg/dark_audio-volume-high.svg b/krita/pics/svg/dark_audio-volume-high.svg new file mode 100644 index 0000000000..5b9d6cb5b6 --- /dev/null +++ b/krita/pics/svg/dark_audio-volume-high.svg @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/krita/pics/svg/dark_audio-volume-mute.svg b/krita/pics/svg/dark_audio-volume-mute.svg new file mode 100644 index 0000000000..315644bb39 --- /dev/null +++ b/krita/pics/svg/dark_audio-volume-mute.svg @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/krita/pics/svg/light_audio-none.svg b/krita/pics/svg/light_audio-none.svg new file mode 100644 index 0000000000..00358b4844 --- /dev/null +++ b/krita/pics/svg/light_audio-none.svg @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/krita/pics/svg/light_audio-volume-high.svg b/krita/pics/svg/light_audio-volume-high.svg new file mode 100644 index 0000000000..e9a282df99 --- /dev/null +++ b/krita/pics/svg/light_audio-volume-high.svg @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/krita/pics/svg/light_audio-volume-mute.svg b/krita/pics/svg/light_audio-volume-mute.svg new file mode 100644 index 0000000000..e1f9c7ba15 --- /dev/null +++ b/krita/pics/svg/light_audio-volume-mute.svg @@ -0,0 +1,40 @@ + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/krita/pics/svg/svg-icons.qrc b/krita/pics/svg/svg-icons.qrc index c579ff9085..68f4046f84 100644 --- a/krita/pics/svg/svg-icons.qrc +++ b/krita/pics/svg/svg-icons.qrc @@ -1,118 +1,124 @@ broken-preset.svgz dark_addblankframe.svg dark_addcolor.svg dark_addduplicateframe.svg dark_deletekeyframe.svg dark_docker_lock_a.svg dark_docker_lock_b.svg dark_layer-locked.svg dark_layer-unlocked.svg dark_nextframe.svg dark_nextkeyframe.svg dark_lastframe.svg dark_prevkeyframe.svg dark_firstframe.svg dark_pallete_librarysvg.svg dark_passthrough-disabled.svg dark_passthrough-enabled.svg dark_prevframe.svg dark_selection-mode_ants.svg dark_selection-mode_invisible.svg dark_selection-mode_mask.svg dark_transparency-disabled.svg dark_transparency-enabled.svg dark_trim-to-image.svg delete.svgz layer-style-disabled.svg layer-style-enabled.svg light_addblankframe.svg light_addcolor.svg light_addduplicateframe.svg light_deletekeyframe.svg light_docker_lock_a.svg light_docker_lock_b.svg light_layer-locked.svg light_layer-unlocked.svg light_nextframe.svg light_pallete_library.svg light_passthrough-disabled.svgz light_passthrough-enabled.svgz light_prevframe.svg light_nextkeyframe.svg light_lastframe.svg light_prevkeyframe.svg light_firstframe.svg light_selection-mode_ants.svg light_selection-mode_invisible.svg light_selection-mode_mask.svg light_timeline_keyframe.svg light_transparency-disabled.svg light_transparency-enabled.svg light_trim-to-image.svg paintop_presets_disabled.svgz paintop_settings_01.svgz selection-info.svg selection-mode_invisible.svg svg-icons.qrc transparency-locked.svg transparency-unlocked.svg workspace-chooser.svg light_lazyframeOn.svg light_lazyframeOff.svg dark_lazyframeOn.svg dark_lazyframeOff.svg dark_animation_play.svg dark_animation_stop.svg dark_dropframe.svg dark_droppedframes.svg light_animation_play.svg light_animation_stop.svg light_dropframe.svg light_droppedframes.svg dark_landscape.svg dark_portrait.svg light_landscape.svg light_portrait.svg dark_interpolation_constant.svg dark_interpolation_linear.svg dark_interpolation_bezier.svg dark_interpolation_sharp.svg dark_interpolation_smooth.svg light_interpolation_bezier.svg light_interpolation_constant.svg light_interpolation_linear.svg light_interpolation_sharp.svg light_interpolation_smooth.svg + dark_audio-none.svg + dark_audio-volume-high.svg + dark_audio-volume-mute.svg dark_keyframe-add.svg dark_keyframe-remove.svg dark_zoom-fit.svg dark_zoom-horizontal.svg dark_zoom-vertical.svg + light_audio-none.svg + light_audio-volume-high.svg + light_audio-volume-mute.svg light_keyframe-add.svg light_keyframe-remove.svg light_zoom-fit.svg light_zoom-horizontal.svg light_zoom-vertical.svg dark_showColoring.svg dark_showMarks.svg dark_showColoringOff.svg dark_showMarksOff.svg dark_updateColorize.svg light_showColoring.svg light_showMarks.svg light_showColoringOff.svg light_showMarksOff.svg light_updateColorize.svg diff --git a/libs/image/kis_image_animation_interface.cpp b/libs/image/kis_image_animation_interface.cpp index 09cdc21f3e..5d2cd3fd29 100644 --- a/libs/image/kis_image_animation_interface.cpp +++ b/libs/image/kis_image_animation_interface.cpp @@ -1,354 +1,401 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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. */ #include "kis_image_animation_interface.h" +#include + #include "kis_global.h" #include "kis_image.h" #include "kis_regenerate_frame_stroke_strategy.h" #include "kis_switch_time_stroke_strategy.h" #include "kis_keyframe_channel.h" #include "kis_time_range.h" #include "kis_post_execution_undo_adapter.h" #include "commands_new/kis_switch_current_time_command.h" #include "kis_layer_utils.h" struct KisImageAnimationInterface::Private { Private() : image(0), externalFrameActive(false), frameInvalidationBlocked(false), cachedLastFrameValue(-1), + audioChannelMuted(false), + audioChannelVolume(0.5), m_currentTime(0), m_currentUITime(0) { } Private(const Private &rhs, KisImage *newImage) : image(newImage), externalFrameActive(false), frameInvalidationBlocked(false), fullClipRange(rhs.fullClipRange), playbackRange(rhs.playbackRange), framerate(rhs.framerate), cachedLastFrameValue(-1), + audioChannelFileName(rhs.audioChannelFileName), + audioChannelMuted(rhs.audioChannelMuted), + audioChannelVolume(rhs.audioChannelVolume), m_currentTime(rhs.m_currentTime), m_currentUITime(rhs.m_currentUITime) { } KisImage *image; bool externalFrameActive; bool frameInvalidationBlocked; KisTimeRange fullClipRange; KisTimeRange playbackRange; int framerate; int cachedLastFrameValue; + QString audioChannelFileName; + bool audioChannelMuted; + qreal audioChannelVolume; KisSwitchTimeStrokeStrategy::SharedTokenWSP switchToken; inline int currentTime() const { return m_currentTime; } inline int currentUITime() const { return m_currentUITime; } inline void setCurrentTime(int value) { m_currentTime = value; } inline void setCurrentUITime(int value) { m_currentUITime = value; } private: int m_currentTime; int m_currentUITime; }; KisImageAnimationInterface::KisImageAnimationInterface(KisImage *image) : m_d(new Private) { m_d->image = image; m_d->framerate = 24; m_d->fullClipRange = KisTimeRange::fromTime(0, 100); connect(this, SIGNAL(sigInternalRequestTimeSwitch(int, bool)), SLOT(switchCurrentTimeAsync(int, bool))); } KisImageAnimationInterface::KisImageAnimationInterface(const KisImageAnimationInterface &rhs, KisImage *newImage) : m_d(new Private(*rhs.m_d, newImage)) { connect(this, SIGNAL(sigInternalRequestTimeSwitch(int, bool)), SLOT(switchCurrentTimeAsync(int, bool))); } KisImageAnimationInterface::~KisImageAnimationInterface() { } bool KisImageAnimationInterface::hasAnimation() const { bool hasAnimation = false; KisLayerUtils::recursiveApplyNodes( m_d->image->root(), [&hasAnimation](KisNodeSP node) { hasAnimation |= node->isAnimated(); }); return hasAnimation; } int KisImageAnimationInterface::currentTime() const { return m_d->currentTime(); } int KisImageAnimationInterface::currentUITime() const { return m_d->currentUITime(); } const KisTimeRange& KisImageAnimationInterface::fullClipRange() const { return m_d->fullClipRange; } void KisImageAnimationInterface::setFullClipRange(const KisTimeRange range) { m_d->fullClipRange = range; emit sigFullClipRangeChanged(); } const KisTimeRange& KisImageAnimationInterface::playbackRange() const { return m_d->playbackRange.isValid() ? m_d->playbackRange : m_d->fullClipRange; } void KisImageAnimationInterface::setPlaybackRange(const KisTimeRange range) { m_d->playbackRange = range; emit sigPlaybackRangeChanged(); } int KisImageAnimationInterface::framerate() const { return m_d->framerate; } +QString KisImageAnimationInterface::audioChannelFileName() const +{ + return m_d->audioChannelFileName; +} + +void KisImageAnimationInterface::setAudioChannelFileName(const QString &fileName) +{ + QFileInfo info(fileName); + + KIS_SAFE_ASSERT_RECOVER_NOOP(fileName.isEmpty() || info.isAbsolute()); + m_d->audioChannelFileName = fileName.isEmpty() ? fileName : info.absoluteFilePath(); + + emit sigAudioChannelChanged(); +} + +bool KisImageAnimationInterface::isAudioMuted() const +{ + return m_d->audioChannelMuted; +} + +void KisImageAnimationInterface::setAudioMuted(bool value) +{ + m_d->audioChannelMuted = value; + emit sigAudioChannelChanged(); +} + +qreal KisImageAnimationInterface::audioVolume() const +{ + return m_d->audioChannelVolume; +} + +void KisImageAnimationInterface::setAudioVolume(qreal value) +{ + m_d->audioChannelVolume = value; + emit sigAudioVolumeChanged(); +} + void KisImageAnimationInterface::setFramerate(int fps) { m_d->framerate = fps; emit sigFramerateChanged(); } KisImageWSP KisImageAnimationInterface::image() const { return m_d->image; } bool KisImageAnimationInterface::externalFrameActive() const { return m_d->externalFrameActive; } void KisImageAnimationInterface::requestTimeSwitchWithUndo(int time) { if (currentUITime() == time) return; requestTimeSwitchNonGUI(time, true); } void KisImageAnimationInterface::setDefaultProjectionColor(const KoColor &color) { int savedTime = 0; saveAndResetCurrentTime(currentTime(), &savedTime); m_d->image->setDefaultProjectionColor(color); restoreCurrentTime(&savedTime); } void KisImageAnimationInterface::requestTimeSwitchNonGUI(int time, bool useUndo) { emit sigInternalRequestTimeSwitch(time, useUndo); } void KisImageAnimationInterface::explicitlySetCurrentTime(int frameId) { m_d->setCurrentTime(frameId); } void KisImageAnimationInterface::switchCurrentTimeAsync(int frameId, bool useUndo) { if (currentUITime() == frameId) return; KisTimeRange range = KisTimeRange::infinite(0); KisTimeRange::calculateTimeRangeRecursive(m_d->image->root(), currentUITime(), range, true); const bool needsRegeneration = !range.contains(frameId); KisSwitchTimeStrokeStrategy::SharedTokenSP token = m_d->switchToken.toStrongRef(); if (!token || !token->tryResetDestinationTime(frameId, needsRegeneration)) { { KisPostExecutionUndoAdapter *undoAdapter = useUndo ? m_d->image->postExecutionUndoAdapter() : 0; KisSwitchTimeStrokeStrategy *strategy = new KisSwitchTimeStrokeStrategy(frameId, needsRegeneration, this, undoAdapter); m_d->switchToken = strategy->token(); KisStrokeId stroke = m_d->image->startStroke(strategy); m_d->image->endStroke(stroke); } if (needsRegeneration) { KisStrokeStrategy *strategy = new KisRegenerateFrameStrokeStrategy(this); KisStrokeId strokeId = m_d->image->startStroke(strategy); m_d->image->endStroke(strokeId); } } m_d->setCurrentUITime(frameId); emit sigUiTimeChanged(frameId); } void KisImageAnimationInterface::requestFrameRegeneration(int frameId, const QRegion &dirtyRegion) { KisStrokeStrategy *strategy = new KisRegenerateFrameStrokeStrategy(frameId, dirtyRegion, this); QList jobs = KisRegenerateFrameStrokeStrategy::createJobsData(m_d->image); KisStrokeId stroke = m_d->image->startStroke(strategy); Q_FOREACH (KisStrokeJobData* job, jobs) { m_d->image->addJob(stroke, job); } m_d->image->endStroke(stroke); } void KisImageAnimationInterface::saveAndResetCurrentTime(int frameId, int *savedValue) { m_d->externalFrameActive = true; *savedValue = m_d->currentTime(); m_d->setCurrentTime(frameId); } void KisImageAnimationInterface::restoreCurrentTime(int *savedValue) { m_d->setCurrentTime(*savedValue); m_d->externalFrameActive = false; } void KisImageAnimationInterface::notifyFrameReady() { emit sigFrameReady(m_d->currentTime()); } void KisImageAnimationInterface::notifyFrameCancelled() { emit sigFrameCancelled(); } KisUpdatesFacade* KisImageAnimationInterface::updatesFacade() const { return m_d->image; } void KisImageAnimationInterface::notifyNodeChanged(const KisNode *node, const QRect &rect, bool recursive) { if (externalFrameActive() || m_d->frameInvalidationBlocked) return; if (node->inherits("KisSelectionMask")) return; KisKeyframeChannel *channel = node->getKeyframeChannel(KisKeyframeChannel::Content.id()); if (recursive) { KisTimeRange affectedRange; KisTimeRange::calculateTimeRangeRecursive(node, currentTime(), affectedRange, false); invalidateFrames(affectedRange, rect); } else if (channel) { const int currentTime = m_d->currentTime(); invalidateFrames(channel->affectedFrames(currentTime), rect); } else { invalidateFrames(KisTimeRange::infinite(0), rect); } } void KisImageAnimationInterface::invalidateFrames(const KisTimeRange &range, const QRect &rect) { m_d->cachedLastFrameValue = -1; emit sigFramesChanged(range, rect); } void KisImageAnimationInterface::blockFrameInvalidation(bool value) { m_d->frameInvalidationBlocked = value; } int findLastKeyframeTimeRecursive(KisNodeSP node) { int time = 0; KisKeyframeChannel *channel; Q_FOREACH (channel, node->keyframeChannels()) { KisKeyframeSP keyframe = channel->lastKeyframe(); if (keyframe) { time = std::max(time, keyframe->time()); } } KisNodeSP child = node->firstChild(); while (child) { time = std::max(time, findLastKeyframeTimeRecursive(child)); child = child->nextSibling(); } return time; } int KisImageAnimationInterface::totalLength() { if (m_d->cachedLastFrameValue < 0) { m_d->cachedLastFrameValue = findLastKeyframeTimeRecursive(m_d->image->root()); } int lastKey = m_d->cachedLastFrameValue; lastKey = std::max(lastKey, m_d->fullClipRange.end()); lastKey = std::max(lastKey, m_d->currentUITime()); return lastKey + 1; } diff --git a/libs/image/kis_image_animation_interface.h b/libs/image/kis_image_animation_interface.h index b596c69292..f9af07ccff 100644 --- a/libs/image/kis_image_animation_interface.h +++ b/libs/image/kis_image_animation_interface.h @@ -1,166 +1,208 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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. */ #ifndef __KIS_IMAGE_ANIMATION_INTERFACE_H #define __KIS_IMAGE_ANIMATION_INTERFACE_H #include #include #include "kis_types.h" #include "kritaimage_export.h" class KisUpdatesFacade; class KisTimeRange; class KoColor; namespace KisLayerUtils { struct SwitchFrameCommand; } class KRITAIMAGE_EXPORT KisImageAnimationInterface : public QObject { Q_OBJECT public: KisImageAnimationInterface(KisImage *image); KisImageAnimationInterface(const KisImageAnimationInterface &rhs, KisImage *newImage); ~KisImageAnimationInterface(); /** * Returns true of the image has at least one animated layer */ bool hasAnimation() const; /** * Returns currently active frame of the underlying image. Some strokes * can override this value and it will report a different value. */ int currentTime() const; /** * Same as currentTime, except it isn't changed when background strokes * are running. */ int currentUITime() const; /** * While any non-current frame is being regenerated by the * strategy, the image is kept in a special state, named * 'externalFrameActive'. Is this state the following applies: * * 1) All the animated paint devices switch its state into * frameId() defined by global time. * * 2) All animation-not-capable devices switch to a temporary * content device, which *is in undefined state*. The stroke * should regenerate the image projection manually. */ bool externalFrameActive() const; void requestTimeSwitchWithUndo(int time); void requestTimeSwitchNonGUI(int time, bool useUndo = false); public Q_SLOTS: /** * Switches current frame (synchronously) and starts an * asynchronous regeneration of the entire image. */ void switchCurrentTimeAsync(int frameId, bool useUndo = false); public: /** * Start a backgroud thread that will recalculate some extra frame. * The result will be reported using two types of signals: * * 1) KisImage::sigImageUpdated() will be emitted for every chunk * of updated area. * * 2) sigFrameReady() will be emitted in the end of the operation. * IMPORTANT: to get the result you must connect to this signal * with Qt::DirectConnection and fetch the result from * frameProjection(). After the signal handler is exited, the * data will no longer be available. */ void requestFrameRegeneration(int frameId, const QRegion &dirtyRegion); void notifyNodeChanged(const KisNode *node, const QRect &rect, bool recursive); void invalidateFrames(const KisTimeRange &range, const QRect &rect); /** * Changes the default color of the "external frame" projection of * the image's root layer. Please note that this command should be * executed from a context of an exclusive job! */ void setDefaultProjectionColor(const KoColor &color); /** * The current time range selected by user. * @return current time range */ const KisTimeRange& fullClipRange() const; void setFullClipRange(const KisTimeRange range); const KisTimeRange &playbackRange() const; void setPlaybackRange(const KisTimeRange range); int framerate() const; + /** + * @return **absolute** file name of the audio channel file + */ + QString audioChannelFileName() const; + + /** + * Sets **absolute** file name of the audio channel file. Dont' try to pass + * a relative path, it'll assert! + */ + void setAudioChannelFileName(const QString &fileName); + + /** + * @return is the audio channel is currently muted + */ + bool isAudioMuted() const; + + /** + * Mutes the audio channel + */ + void setAudioMuted(bool value); + + /** + * Returns the preferred audio value in rangle [0, 1] + */ + qreal audioVolume() const; + + /** + * Set the preferred volume for the audio channel in range [0, 1] + */ + void setAudioVolume(qreal value); + public Q_SLOTS: void setFramerate(int fps); public: KisImageWSP image() const; int totalLength(); private: // interface for: friend class KisRegenerateFrameStrokeStrategy; friend class KisAnimationFrameCacheTest; friend struct KisLayerUtils::SwitchFrameCommand; friend class KisImageTest; void saveAndResetCurrentTime(int frameId, int *savedValue); void restoreCurrentTime(int *savedValue); void notifyFrameReady(); void notifyFrameCancelled(); KisUpdatesFacade* updatesFacade() const; void blockFrameInvalidation(bool value); friend class KisSwitchTimeStrokeStrategy; void explicitlySetCurrentTime(int frameId); Q_SIGNALS: void sigFrameReady(int time); void sigFrameCancelled(); void sigUiTimeChanged(int newTime); void sigFramesChanged(const KisTimeRange &range, const QRect &rect); void sigInternalRequestTimeSwitch(int frameId, bool useUndo); void sigFramerateChanged(); void sigFullClipRangeChanged(); void sigPlaybackRangeChanged(); + /** + * Emitted when the audio channel of the document is changed + */ + void sigAudioChannelChanged(); + + /** + * Emitted when audion volume changes. Please note that it doesn't change + * when you mute the channel! When muting, sigAudioChannelChanged() is used instead! + */ + void sigAudioVolumeChanged(); + private: struct Private; const QScopedPointer m_d; }; #endif /* __KIS_IMAGE_ANIMATION_INTERFACE_H */ diff --git a/libs/image/kis_signal_compressor_with_param.h b/libs/image/kis_signal_compressor_with_param.h index 94c751a8ff..6b01235345 100644 --- a/libs/image/kis_signal_compressor_with_param.h +++ b/libs/image/kis_signal_compressor_with_param.h @@ -1,136 +1,140 @@ /* * Copyright (c) 2014 Dmitry Kazakov * * 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. */ #ifndef __KIS_SIGNAL_COMPRESSOR_WITH_PARAM_H #define __KIS_SIGNAL_COMPRESSOR_WITH_PARAM_H #include #include /** * A special class that converts a Qt signal into a std::function call. * * Example: * * std::function destinationFunctionCall(std::bind(someNiceFunc, firstParam, secondParam)); * SignalToFunctionProxy proxy(destinationFunctionCall); * connect(srcObject, SIGNAL(sigSomethingChanged()), &proxy, SLOT(start())); * * Now every time sigSomethingChanged() is emitted, someNiceFunc is * called. std::bind allows us to call any method of any class without * changing signature of the class or creating special wrappers. */ class KRITAIMAGE_EXPORT SignalToFunctionProxy : public QObject { Q_OBJECT public: using TrivialFunction = std::function; public: SignalToFunctionProxy(TrivialFunction function) : m_function(function) { } public Q_SLOTS: void start() { m_function(); } private: TrivialFunction m_function; }; /** * A special class for deferring and comressing events with one * parameter of type T. This works like KisSignalCompressor but can * handle events with one parameter. Due to limitation of the Qt this * doesn't allow signal/slots, so it uses std::function instead. * * In the end (after a timeout) the latest param value is returned to * the callback. * * Usage: * * \code{.cpp} * * using namespace std::placeholders; // For _1 placeholder * * // prepare the callback function * std::function callback( * std::bind(&LutDockerDock::setCurrentExposureImpl, this, _1)); * * // Create the compressor object * KisSignalCompressorWithParam compressor(40, callback); * * // When event comes: * compressor.start(0.123456); * * \endcode */ template class KisSignalCompressorWithParam { public: using CallbackFunction = std::function; public: KisSignalCompressorWithParam(int delay, CallbackFunction function, KisSignalCompressor::Mode mode = KisSignalCompressor::FIRST_ACTIVE) : m_compressor(delay, mode), m_function(function) { std::function callback( std::bind(&KisSignalCompressorWithParam::fakeSlotTimeout, this)); m_signalProxy.reset(new SignalToFunctionProxy(callback)); m_compressor.connect(&m_compressor, SIGNAL(timeout()), m_signalProxy.data(), SLOT(start())); } ~KisSignalCompressorWithParam() { } void start(T param) { m_currentParamValue = param; m_compressor.start(); } void stop() { m_compressor.stop(); } bool isActive() const { return m_compressor.isActive(); } + void setDelay(int value) { + m_compressor.setDelay(value); + } + private: void fakeSlotTimeout() { m_function(m_currentParamValue); } private: KisSignalCompressor m_compressor; CallbackFunction m_function; QScopedPointer m_signalProxy; T m_currentParamValue; }; #endif /* __KIS_SIGNAL_COMPRESSOR_WITH_PARAM_H */ diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt index 31c0dd1d3f..3cf92dbe6e 100644 --- a/libs/ui/CMakeLists.txt +++ b/libs/ui/CMakeLists.txt @@ -1,533 +1,539 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/qtlockedfile ${EXIV2_INCLUDE_DIR} ) include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR} ${OCIO_INCLUDE_DIR} ${Boost_INCLUDE_DIRS} ) add_subdirectory( tests ) if (APPLE) find_library(FOUNDATION_LIBRARY Foundation) endif () set(kritaui_LIB_SRCS canvas/kis_canvas_widget_base.cpp canvas/kis_canvas2.cpp canvas/kis_canvas_updates_compressor.cpp canvas/kis_canvas_controller.cpp canvas/kis_paintop_transformation_connector.cpp canvas/kis_display_color_converter.cpp canvas/kis_display_filter.cpp canvas/kis_exposure_gamma_correction_interface.cpp canvas/kis_tool_proxy.cpp canvas/kis_canvas_decoration.cc canvas/kis_coordinates_converter.cpp canvas/kis_grid_manager.cpp canvas/kis_grid_decoration.cpp canvas/kis_grid_config.cpp canvas/kis_prescaled_projection.cpp canvas/kis_qpainter_canvas.cpp canvas/kis_projection_backend.cpp canvas/kis_update_info.cpp canvas/kis_image_patch.cpp canvas/kis_image_pyramid.cpp canvas/kis_infinity_manager.cpp canvas/kis_change_guides_command.cpp canvas/kis_guides_decoration.cpp canvas/kis_guides_manager.cpp canvas/kis_guides_config.cpp canvas/kis_snap_config.cpp canvas/kis_snap_line_strategy.cpp dialogs/kis_about_application.cpp dialogs/kis_dlg_adj_layer_props.cc dialogs/kis_dlg_adjustment_layer.cc dialogs/kis_dlg_filter.cpp dialogs/kis_dlg_generator_layer.cpp dialogs/kis_dlg_file_layer.cpp dialogs/kis_dlg_filter.cpp dialogs/kis_dlg_stroke_selection_properties.cpp dialogs/kis_dlg_image_properties.cc dialogs/kis_dlg_layer_properties.cc dialogs/kis_dlg_preferences.cc dialogs/slider_and_spin_box_sync.cpp dialogs/kis_dlg_blacklist_cleanup.cpp dialogs/kis_dlg_layer_style.cpp dialogs/kis_dlg_png_import.cpp dialogs/kis_dlg_import_image_sequence.cpp dialogs/kis_delayed_save_dialog.cpp dialogs/kis_dlg_internal_color_selector.cpp flake/kis_node_dummies_graph.cpp flake/kis_dummies_facade_base.cpp flake/kis_dummies_facade.cpp flake/kis_node_shapes_graph.cpp flake/kis_node_shape.cpp flake/kis_shape_controller.cpp flake/kis_shape_layer.cc flake/kis_shape_layer_canvas.cpp flake/kis_shape_selection.cpp flake/kis_shape_selection_canvas.cpp flake/kis_shape_selection_model.cpp flake/kis_take_all_shapes_command.cpp brushhud/kis_uniform_paintop_property_widget.cpp brushhud/kis_brush_hud.cpp brushhud/kis_round_hud_button.cpp brushhud/kis_dlg_brush_hud_config.cpp brushhud/kis_brush_hud_properties_list.cpp brushhud/kis_brush_hud_properties_config.cpp kis_aspect_ratio_locker.cpp kis_autogradient.cc kis_bookmarked_configurations_editor.cc kis_bookmarked_configurations_model.cc kis_bookmarked_filter_configurations_model.cc kis_base_option.cpp kis_canvas_resource_provider.cpp kis_derived_resources.cpp kis_categories_mapper.cpp kis_categorized_list_model.cpp kis_categorized_item_delegate.cpp kis_clipboard.cc kis_config.cc kis_config_notifier.cpp kis_control_frame.cpp kis_composite_ops_model.cc kis_paint_ops_model.cpp kis_cursor.cc kis_cursor_cache.cpp kis_custom_pattern.cc kis_file_layer.cpp kis_safe_document_loader.cpp kis_splash_screen.cpp kis_filter_manager.cc kis_filters_model.cc kis_histogram_view.cc kis_image_manager.cc kis_image_view_converter.cpp kis_import_catcher.cc kis_layer_manager.cc kis_mask_manager.cc kis_mimedata.cpp kis_node_commands_adapter.cpp kis_node_manager.cpp kis_node_juggler_compressed.cpp kis_node_selection_adapter.cpp kis_node_insertion_adapter.cpp kis_node_model.cpp kis_node_filter_proxy_model.cpp kis_model_index_converter_base.cpp kis_model_index_converter.cpp kis_model_index_converter_show_all.cpp kis_painting_assistant.cc kis_painting_assistants_decoration.cpp kis_painting_assistants_manager.cpp kis_paintop_box.cc kis_paintop_option.cpp kis_paintop_options_model.cpp kis_paintop_settings_widget.cpp kis_popup_palette.cpp kis_png_converter.cpp kis_preference_set_registry.cpp kis_script_manager.cpp kis_resource_server_provider.cpp kis_selection_decoration.cc kis_selection_manager.cc kis_statusbar.cc kis_zoom_manager.cc kis_favorite_resource_manager.cpp kis_workspace_resource.cpp kis_action.cpp kis_action_manager.cpp kis_view_plugin.cpp kis_canvas_controls_manager.cpp kis_tooltip_manager.cpp kis_multinode_property.cpp kis_stopgradient_editor.cpp kisexiv2/kis_exif_io.cpp kisexiv2/kis_exiv2.cpp kisexiv2/kis_iptc_io.cpp kisexiv2/kis_xmp_io.cpp opengl/kis_opengl.cpp opengl/kis_opengl_canvas2.cpp opengl/kis_opengl_canvas_debugger.cpp opengl/kis_opengl_image_textures.cpp opengl/kis_texture_tile.cpp opengl/kis_opengl_shader_loader.cpp kis_fps_decoration.cpp recorder/kis_node_query_path_editor.cc recorder/kis_recorded_action_creator.cc recorder/kis_recorded_action_creator_factory.cc recorder/kis_recorded_action_creator_factory_registry.cc recorder/kis_recorded_action_editor_factory.cc recorder/kis_recorded_action_editor_factory_registry.cc recorder/kis_recorded_filter_action_editor.cc recorder/kis_recorded_filter_action_creator.cpp recorder/kis_recorded_paint_action_editor.cc tool/kis_selection_tool_helper.cpp tool/kis_selection_tool_config_widget_helper.cpp tool/kis_rectangle_constraint_widget.cpp tool/kis_shape_tool_helper.cpp tool/kis_tool.cc tool/kis_delegated_tool_policies.cpp tool/kis_tool_freehand.cc tool/kis_speed_smoother.cpp tool/kis_painting_information_builder.cpp tool/kis_stabilized_events_sampler.cpp tool/kis_tool_freehand_helper.cpp tool/kis_tool_multihand_helper.cpp tool/kis_figure_painting_tool_helper.cpp tool/kis_recording_adapter.cpp tool/kis_tool_paint.cc tool/kis_tool_shape.cc tool/kis_tool_ellipse_base.cpp tool/kis_tool_rectangle_base.cpp tool/kis_tool_polyline_base.cpp tool/kis_tool_utils.cpp tool/kis_resources_snapshot.cpp tool/kis_smoothing_options.cpp tool/KisStabilizerDelayedPaintHelper.cpp tool/strokes/freehand_stroke.cpp tool/strokes/kis_painter_based_stroke_strategy.cpp tool/strokes/kis_filter_stroke_strategy.cpp tool/strokes/kis_color_picker_stroke_strategy.cpp widgets/kis_cmb_composite.cc widgets/kis_cmb_contour.cpp widgets/kis_cmb_gradient.cpp widgets/kis_paintop_list_widget.cpp widgets/kis_cmb_idlist.cc widgets/kis_color_space_selector.cc widgets/kis_advanced_color_space_selector.cc widgets/kis_cie_tongue_widget.cpp widgets/kis_tone_curve_widget.cpp widgets/kis_curve_widget.cpp widgets/kis_custom_image_widget.cc widgets/kis_image_from_clipboard_widget.cpp widgets/kis_double_widget.cc widgets/kis_filter_selector_widget.cc widgets/kis_gradient_chooser.cc widgets/kis_gradient_slider_widget.cc widgets/kis_gradient_slider.cpp widgets/kis_iconwidget.cc widgets/kis_mask_widgets.cpp widgets/kis_meta_data_merge_strategy_chooser_widget.cc widgets/kis_multi_bool_filter_widget.cc widgets/kis_multi_double_filter_widget.cc widgets/kis_multi_integer_filter_widget.cc widgets/kis_multipliers_double_slider_spinbox.cpp widgets/kis_paintop_presets_popup.cpp widgets/kis_tool_options_popup.cpp widgets/kis_paintop_presets_chooser_popup.cpp widgets/kis_pattern_chooser.cc widgets/kis_popup_button.cc widgets/kis_preset_chooser.cpp widgets/kis_progress_widget.cpp widgets/kis_selection_options.cc widgets/kis_scratch_pad.cpp widgets/kis_scratch_pad_event_filter.cpp widgets/kis_preset_selector_strip.cpp widgets/kis_slider_spin_box.cpp widgets/kis_size_group.cpp widgets/kis_size_group_p.cpp widgets/kis_wdg_generator.cpp widgets/kis_workspace_chooser.cpp widgets/squeezedcombobox.cpp widgets/kis_categorized_list_view.cpp widgets/kis_widget_chooser.cpp widgets/kis_tool_button.cpp widgets/kis_floating_message.cpp widgets/kis_lod_availability_widget.cpp widgets/kis_color_label_selector_widget.cpp widgets/kis_color_filter_combo.cpp widgets/kis_elided_label.cpp widgets/kis_stopgradient_slider_widget.cpp widgets/kis_spinbox_color_selector.cpp widgets/kis_screen_color_picker.cpp widgets/KoDualColorButton.cpp widgets/kis_color_input.cpp widgets/kis_color_button.cpp widgets/KisVisualColorSelector.cpp widgets/KisVisualColorSelectorShape.cpp widgets/KisVisualEllipticalSelectorShape.cpp widgets/KisVisualRectangleSelectorShape.cpp widgets/KisVisualTriangleSelectorShape.cpp input/kis_input_manager.cpp input/kis_input_manager_p.cpp input/kis_extended_modifiers_mapper.cpp input/kis_abstract_input_action.cpp input/kis_tool_invocation_action.cpp input/kis_pan_action.cpp input/kis_alternate_invocation_action.cpp input/kis_rotate_canvas_action.cpp input/kis_zoom_action.cpp input/kis_change_frame_action.cpp input/kis_gamma_exposure_action.cpp input/kis_show_palette_action.cpp input/kis_change_primary_setting_action.cpp input/kis_abstract_shortcut.cpp input/kis_single_action_shortcut.cpp input/kis_stroke_shortcut.cpp input/kis_shortcut_matcher.cpp input/kis_select_layer_action.cpp operations/kis_operation.cpp operations/kis_operation_configuration.cpp operations/kis_operation_registry.cpp operations/kis_operation_ui_factory.cpp operations/kis_operation_ui_widget.cpp operations/kis_filter_selection_operation.cpp actions/kis_selection_action_factories.cpp input/kis_touch_shortcut.cpp kis_document_undo_store.cpp kis_transaction_based_command.cpp kis_gui_context_command.cpp kis_gui_context_command_p.cpp input/kis_tablet_debugger.cpp input/kis_input_profile_manager.cpp input/kis_input_profile.cpp input/kis_shortcut_configuration.cpp input/config/kis_input_configuration_page.cpp input/config/kis_edit_profiles_dialog.cpp input/config/kis_input_profile_model.cpp input/config/kis_input_configuration_page_item.cpp input/config/kis_action_shortcuts_model.cpp input/config/kis_input_type_delegate.cpp input/config/kis_input_mode_delegate.cpp input/config/kis_input_button.cpp input/config/kis_input_editor_delegate.cpp input/config/kis_mouse_input_editor.cpp input/config/kis_wheel_input_editor.cpp input/config/kis_key_input_editor.cpp processing/fill_processing_visitor.cpp kis_asl_layer_style_serializer.cpp kis_psd_layer_style_resource.cpp canvas/kis_mirror_axis.cpp kis_abstract_perspective_grid.cpp KisApplication.cpp KisAutoSaveRecoveryDialog.cpp KisDetailsPane.cpp KisDocument.cpp KisNodeDelegate.cpp kis_node_view_visibility_delegate.cpp KisNodeToolTip.cpp KisNodeView.cpp kis_node_view_color_scheme.cpp KisImportExportFilter.cpp KisFilterEntry.cpp KisImportExportManager.cpp KisMainWindow.cpp KisOpenPane.cpp KisPart.cpp KisPrintJob.cpp KisTemplate.cpp KisTemplateCreateDia.cpp KisTemplateGroup.cpp KisTemplates.cpp KisTemplatesPane.cpp KisTemplateTree.cpp KisUndoStackAction.cpp KisView.cpp thememanager.cpp kis_mainwindow_observer.cpp KisViewManager.cpp kis_mirror_manager.cpp qtlockedfile/qtlockedfile.cpp qtsingleapplication/qtlocalpeer.cpp qtsingleapplication/qtsingleapplication.cpp KisResourceBundle.cpp KisResourceBundleManifest.cpp kis_md5_generator.cpp KisApplicationArguments.cpp KisNetworkAccessManager.cpp KisMultiFeedRSSModel.cpp KisRemoteFileFetcher.cpp KisPaletteModel.cpp kis_palette_delegate.cpp kis_palette_view.cpp KisColorsetChooser.cpp KisSaveGroupVisitor.cpp ) if(WIN32) if (NOT Qt5Gui_PRIVATE_INCLUDE_DIRS) message(FATAL_ERROR "Qt5Gui Private header are missing!") endif() set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/kis_tablet_event.cpp input/wintab/kis_tablet_support_win.cpp input/wintab/kis_screen_size_choice_dialog.cpp qtlockedfile/qtlockedfile_win.cpp ) include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS}) endif() + set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} kis_animation_frame_cache.cpp kis_animation_cache_populator.cpp canvas/kis_animation_player.cpp kis_animation_exporter.cpp kis_animation_importer.cpp + KisSyncedAudioPlayback.cpp ) if(UNIX) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/kis_tablet_event.cpp input/wintab/kis_tablet_support.cpp qtlockedfile/qtlockedfile_unix.cpp ) if(NOT APPLE) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/wintab/kis_tablet_support_x11.cpp input/wintab/qxcbconnection_xi2.cpp input/wintab/qxcbconnection.cpp input/wintab/kis_xi2_event_filter.cpp ) endif() endif() ki18n_wrap_ui(kritaui_LIB_SRCS forms/wdgdlgpngimport.ui forms/wdgfullscreensettings.ui forms/wdgautogradient.ui forms/wdggeneralsettings.ui forms/wdgperformancesettings.ui forms/wdggenerators.ui forms/wdgbookmarkedconfigurationseditor.ui forms/wdgapplyprofile.ui forms/wdgcustompattern.ui forms/wdglayerproperties.ui forms/wdgcolorsettings.ui forms/wdgtabletsettings.ui forms/wdgcolorspaceselector.ui forms/wdgcolorspaceselectoradvanced.ui forms/wdgdisplaysettings.ui forms/kis_previewwidgetbase.ui forms/kis_matrix_widget.ui forms/wdgselectionoptions.ui forms/wdggeometryoptions.ui forms/wdgnewimage.ui forms/wdgimageproperties.ui forms/wdgmaskfromselection.ui forms/wdgmasksource.ui forms/wdgfilterdialog.ui forms/wdgmetadatamergestrategychooser.ui forms/wdgpaintoppresets.ui forms/wdgpaintopsettings.ui forms/wdgdlggeneratorlayer.ui forms/wdgdlgfilelayer.ui forms/wdgfilterselector.ui forms/wdgfilternodecreation.ui forms/wdgpaintactioneditor.ui forms/wdgmultipliersdoublesliderspinbox.ui forms/wdgnodequerypatheditor.ui forms/wdgpresetselectorstrip.ui forms/wdgdlgblacklistcleanup.ui forms/wdgrectangleconstraints.ui forms/wdgimportimagesequence.ui forms/wdgstrokeselectionproperties.ui forms/KisDetailsPaneBase.ui forms/KisOpenPaneBase.ui forms/wdgstopgradienteditor.ui brushhud/kis_dlg_brush_hud_config.ui forms/wdgdlginternalcolorselector.ui dialogs/kis_delayed_save_dialog.ui input/config/kis_input_configuration_page.ui input/config/kis_edit_profiles_dialog.ui input/config/kis_input_configuration_page_item.ui input/config/kis_mouse_input_editor.ui input/config/kis_wheel_input_editor.ui input/config/kis_key_input_editor.ui layerstyles/wdgBevelAndEmboss.ui layerstyles/wdgblendingoptions.ui layerstyles/WdgColorOverlay.ui layerstyles/wdgContour.ui layerstyles/wdgdropshadow.ui layerstyles/WdgGradientOverlay.ui layerstyles/wdgInnerGlow.ui layerstyles/wdglayerstyles.ui layerstyles/WdgPatternOverlay.ui layerstyles/WdgSatin.ui layerstyles/WdgStroke.ui layerstyles/wdgstylesselector.ui layerstyles/wdgTexture.ui wdgsplash.ui input/wintab/kis_screen_size_choice_dialog.ui ) QT5_WRAP_CPP(kritaui_HEADERS_MOC KisNodePropertyAction_p.h) add_library(kritaui SHARED ${kritaui_HEADERS_MOC} ${kritaui_LIB_SRCS} ) generate_export_header(kritaui BASE_NAME kritaui) target_link_libraries(kritaui KF5::CoreAddons KF5::Completion KF5::I18n KF5::ItemViews Qt5::Network kritaimpex kritacolor kritaimage kritalibbrush kritawidgets kritawidgetutils ${PNG_LIBRARIES} ${EXIV2_LIBRARIES} ) +if (HAVE_QT_MULTIMEDIA) + target_link_libraries(kritaui Qt5::Multimedia) +endif() + if (HAVE_KIO) target_link_libraries(kritaui KF5::KIOCore) endif() if (NOT WIN32 AND NOT APPLE) target_link_libraries(kritaui ${X11_X11_LIB} ${X11_Xinput_LIB} ${XCB_LIBRARIES}) endif() if(APPLE) target_link_libraries(kritaui ${FOUNDATION_LIBRARY}) endif () target_link_libraries(kritaui ${OPENEXR_LIBRARIES}) # Add VSync disable workaround if(NOT WIN32 AND NOT APPLE) target_link_libraries(kritaui ${CMAKE_DL_LIBS} Qt5::X11Extras) endif() if(X11_FOUND) target_link_libraries(kritaui Qt5::X11Extras ${X11_LIBRARIES}) endif() target_include_directories(kritaui PUBLIC $ $ $ $ $ $ $ ) set_target_properties(kritaui PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritaui ${INSTALL_TARGETS_DEFAULT_ARGS}) if (APPLE) install(FILES osx.stylesheet DESTINATION ${DATA_INSTALL_DIR}/krita) endif () diff --git a/libs/ui/KisImportExportManager.cpp b/libs/ui/KisImportExportManager.cpp index 36b6c2e278..bd2772370f 100644 --- a/libs/ui/KisImportExportManager.cpp +++ b/libs/ui/KisImportExportManager.cpp @@ -1,434 +1,456 @@ /* * Copyright (C) 2016 Boudewijn Rempt * * 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; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisImportExportManager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include "kis_config.h" #include "KisImportExportFilter.h" #include "KisDocument.h" #include #include #include "kis_guides_config.h" #include "kis_grid_config.h" #include "kis_popup_button.h" #include // static cache for import and export mimetypes QStringList KisImportExportManager::m_importMimeTypes; QStringList KisImportExportManager::m_exportMimeTypes; class Q_DECL_HIDDEN KisImportExportManager::Private { public: bool batchMode {false}; QPointer progressUpdater {0}; }; KisImportExportManager::KisImportExportManager(KisDocument* document) : m_document(document) , d(new Private) { } KisImportExportManager::~KisImportExportManager() { delete d; } KisImportExportFilter::ConversionStatus KisImportExportManager::importDocument(const QString& location, const QString& mimeType) { return convert(Import, location, location, mimeType, false, 0); } KisImportExportFilter::ConversionStatus KisImportExportManager::exportDocument(const QString& location, const QString& realLocation, QByteArray& mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration) { return convert(Export, location, realLocation, mimeType, showWarnings, exportConfiguration); } // The static method to figure out to which parts of the // graph this mimetype has a connection to. QStringList KisImportExportManager::mimeFilter(Direction direction) { // Find the right mimetype by the extension QSet mimeTypes; // mimeTypes << KisDocument::nativeFormatMimeType() << "application/x-krita-paintoppreset" << "image/openraster"; if (direction == KisImportExportManager::Import) { if (m_importMimeTypes.isEmpty()) { KoJsonTrader trader; QListlist = trader.query("Krita/FileFilter", ""); Q_FOREACH(QPluginLoader *loader, list) { QJsonObject json = loader->metaData().value("MetaData").toObject(); Q_FOREACH(const QString &mimetype, json.value("X-KDE-Import").toString().split(",", QString::SkipEmptyParts)) { //qDebug() << "Adding import mimetype" << mimetype << KisMimeDatabase::descriptionForMimeType(mimetype) << "from plugin" << loader; mimeTypes << mimetype; } } qDeleteAll(list); m_importMimeTypes = mimeTypes.toList(); } return m_importMimeTypes; } else if (direction == KisImportExportManager::Export) { if (m_exportMimeTypes.isEmpty()) { KoJsonTrader trader; QListlist = trader.query("Krita/FileFilter", ""); Q_FOREACH(QPluginLoader *loader, list) { QJsonObject json = loader->metaData().value("MetaData").toObject(); Q_FOREACH(const QString &mimetype, json.value("X-KDE-Export").toString().split(",", QString::SkipEmptyParts)) { //qDebug() << "Adding export mimetype" << mimetype << KisMimeDatabase::descriptionForMimeType(mimetype) << "from plugin" << loader; mimeTypes << mimetype; } } qDeleteAll(list); m_exportMimeTypes = mimeTypes.toList(); } return m_exportMimeTypes; } return QStringList(); } KisImportExportFilter *KisImportExportManager::filterForMimeType(const QString &mimetype, KisImportExportManager::Direction direction) { int weight = -1; KisImportExportFilter *filter = 0; KoJsonTrader trader; QListlist = trader.query("Krita/FileFilter", ""); Q_FOREACH(QPluginLoader *loader, list) { QJsonObject json = loader->metaData().value("MetaData").toObject(); QString directionKey = direction == Export ? "X-KDE-Export" : "X-KDE-Import"; if (json.value(directionKey).toString().split(",", QString::SkipEmptyParts).contains(mimetype)) { KLibFactory *factory = qobject_cast(loader->instance()); if (!factory) { warnUI << loader->errorString(); continue; } QObject* obj = factory->create(0); if (!obj || !obj->inherits("KisImportExportFilter")) { delete obj; continue; } KisImportExportFilter *f = qobject_cast(obj); if (!f) { delete obj; continue; } int w = json.value("X-KDE-Weight").toInt(); if (w > weight) { delete filter; filter = f; f->setObjectName(loader->fileName()); weight = w; } } } qDeleteAll(list); filter->setMimeType(mimetype); return filter; } void KisImportExportManager::setBatchMode(const bool batch) { d->batchMode = batch; } bool KisImportExportManager::batchMode(void) const { return d->batchMode; } void KisImportExportManager::setProgresUpdater(KoProgressUpdater *updater) { d->progressUpdater = updater; } +QString KisImportExportManager::askForAudioFileName(const QString &defaultDir, QWidget *parent) +{ + KoFileDialog dialog(parent, KoFileDialog::ImportFiles, "ImportAudio"); + + if (!defaultDir.isEmpty()) { + dialog.setDefaultDir(defaultDir); + } + + QStringList mimeTypes; + mimeTypes << "audio/mpeg"; + mimeTypes << "audio/ogg"; + mimeTypes << "audio/vorbis"; + mimeTypes << "audio/vnd.wave"; + mimeTypes << "audio/flac"; + + dialog.setMimeTypeFilters(mimeTypes); + dialog.setCaption(i18nc("@titile:window", "Open Audio")); + + return dialog.filename(); +} + KisImportExportFilter::ConversionStatus KisImportExportManager::convert(KisImportExportManager::Direction direction, const QString &location, const QString& realLocation, const QString &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration) { QString typeName = mimeType; if (typeName.isEmpty()) { typeName = KisMimeDatabase::mimeTypeForFile(location); } QSharedPointer filter(filterForMimeType(typeName, direction)); if (!filter) { return KisImportExportFilter::FilterCreationError; } filter->setFilename(location); filter->setRealFilename(realLocation); filter->setBatchMode(batchMode()); filter->setMimeType(typeName); if (d->progressUpdater) { filter->setUpdater(d->progressUpdater->startSubtask()); } QByteArray from, to; if (direction == Export) { from = m_document->nativeFormatMimeType(); to = mimeType.toLatin1(); } else { from = mimeType.toLatin1(); to = m_document->nativeFormatMimeType(); } if (!exportConfiguration) { exportConfiguration = filter->lastSavedConfiguration(from, to); if (exportConfiguration) { // Fill with some meta information about the image KisImageWSP image = m_document->image(); KisPaintDeviceSP pd = image->projection(); bool isThereAlpha = false; KisSequentialConstIterator it(pd, image->bounds()); const KoColorSpace* cs = pd->colorSpace(); do { if (cs->opacityU8(it.oldRawData()) != OPACITY_OPAQUE_U8) { isThereAlpha = true; break; } } while (it.nextPixel()); exportConfiguration->setProperty("ImageContainsTransparency", isThereAlpha); exportConfiguration->setProperty("ColorModelID", cs->colorModelId().id()); exportConfiguration->setProperty("ColorDepthID", cs->colorDepthId().id()); bool sRGB = (cs->profile()->name().contains(QLatin1String("srgb"), Qt::CaseInsensitive) && !cs->profile()->name().contains(QLatin1String("g10"))); exportConfiguration->setProperty("sRGB", sRGB); } } KisPreExportChecker checker; if (direction == Export) { checker.check(m_document->image(), filter->exportChecks()); } KisConfigWidget *wdg = filter->createConfigurationWidget(0, from, to); bool alsoAsKra = false; QStringList warnings = checker.warnings(); QStringList errors = checker.errors(); // Extra checks that cannot be done by the checker, because the checker only has access to the image. if (!m_document->assistants().isEmpty() && typeName != m_document->nativeFormatMimeType()) { warnings.append(i18nc("image conversion warning", "The image contains assistants. The assistants will not be saved.")); } if (m_document->guidesConfig().hasGuides() && typeName != m_document->nativeFormatMimeType()) { warnings.append(i18nc("image conversion warning", "The image contains guides. The guides will not be saved.")); } if (!m_document->gridConfig().isDefault() && typeName != m_document->nativeFormatMimeType()) { warnings.append(i18nc("image conversion warning", "The image contains a custom grid configuration. The configuration will not be saved.")); } if (!batchMode() && !errors.isEmpty()) { QString error = "

" + i18n("Error: cannot save this image as a %1.", KisMimeDatabase::descriptionForMimeType(typeName)) + " Reasons:

" + "

    "; Q_FOREACH(const QString &w, errors) { error += "\n
  • " + w + "
  • "; } error += "
"; QMessageBox::critical(KisPart::instance()->currentMainwindow(), i18nc("@title:window", "Krita: Export Error"), error); return KisImportExportFilter::UserCancelled; } if (!batchMode() && (wdg || !warnings.isEmpty())) { KoDialog dlg; dlg.setButtons(KoDialog::Ok | KoDialog::Cancel); dlg.setWindowTitle(KisMimeDatabase::descriptionForMimeType(mimeType)); QWidget *page = new QWidget(&dlg); QVBoxLayout *layout = new QVBoxLayout(page); if (!checker.warnings().isEmpty()) { if (showWarnings) { QHBoxLayout *hLayout = new QHBoxLayout(); QLabel *labelWarning = new QLabel(); labelWarning->setPixmap(KisIconUtils::loadIcon("dialog-warning").pixmap(32, 32)); hLayout->addWidget(labelWarning); KisPopupButton *bn = new KisPopupButton(0); bn->setText(i18nc("Keep the extra space at the end of the sentence, please", "Warning: saving as %1 will lose information from your image. ", KisMimeDatabase::descriptionForMimeType(mimeType))); hLayout->addWidget(bn); layout->addLayout(hLayout); QTextBrowser *browser = new QTextBrowser(); browser->setMinimumWidth(bn->width()); bn->setPopupWidget(browser); QString warning = "

" + i18n("You will lose information when saving this image as a %1.", KisMimeDatabase::descriptionForMimeType(typeName)); if (warnings.size() == 1) { warning += " Reason:

"; } else { warning += " Reasons:

"; } warning += "

    "; Q_FOREACH(const QString &w, warnings) { warning += "\n
  • " + w + "
  • "; } warning += "
"; browser->setHtml(warning); } } if (wdg) { QGroupBox *box = new QGroupBox(i18n("Options")); QVBoxLayout *boxLayout = new QVBoxLayout(box); wdg->setConfiguration(exportConfiguration); boxLayout->addWidget(wdg); layout->addWidget(box); } QCheckBox *chkAlsoAsKra = 0; if (showWarnings) { if (!checker.warnings().isEmpty()) { chkAlsoAsKra = new QCheckBox(i18n("Also save your image as a Krita file.")); chkAlsoAsKra->setChecked(KisConfig().readEntry("AlsoSaveAsKra", false)); layout->addWidget(chkAlsoAsKra); } } dlg.setMainWidget(page); dlg.resize(dlg.minimumSize()); if (showWarnings || wdg) { if (!dlg.exec()) { return KisImportExportFilter::UserCancelled; } } if (chkAlsoAsKra) { KisConfig().writeEntry("AlsoSaveAsKra", chkAlsoAsKra->isChecked()); alsoAsKra = chkAlsoAsKra->isChecked(); } if (wdg) { exportConfiguration = wdg->configuration(); } } QFile io(location); if (direction == Import) { if (!io.exists()) { return KisImportExportFilter::FileNotFound; } if (!io.open(QFile::ReadOnly)) { return KisImportExportFilter::FileNotFound; } } else if (direction == Export) { if (!io.open(QFile::WriteOnly)) { return KisImportExportFilter::CreationError; } } else { return KisImportExportFilter::BadConversionGraph; } if (!batchMode()) { QApplication::setOverrideCursor(Qt::WaitCursor); } KisImportExportFilter::ConversionStatus status = filter->convert(m_document, &io, exportConfiguration); io.close(); if (exportConfiguration) { KisConfig().setExportConfiguration(typeName, exportConfiguration); } if (alsoAsKra) { QString l = location + ".kra"; QByteArray ba = m_document->nativeFormatMimeType(); KisImportExportFilter *filter = filterForMimeType(QString::fromLatin1(ba), Export); QFile f(l); f.open(QIODevice::WriteOnly); if (filter) { filter->setFilename(l); filter->convert(m_document, &f); } f.close(); delete filter; } if (!batchMode()) { QApplication::restoreOverrideCursor(); } return status; } #include diff --git a/libs/ui/KisImportExportManager.h b/libs/ui/KisImportExportManager.h index 2535d29882..bd07d42ad7 100644 --- a/libs/ui/KisImportExportManager.h +++ b/libs/ui/KisImportExportManager.h @@ -1,144 +1,146 @@ /* * Copyright (C) 2016 Boudewijn Rempt * * 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; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_IMPORT_EXPORT_MANAGER_H #define KIS_IMPORT_EXPORT_MANAGER_H #include #include #include #include #include "KisImportExportFilter.h" #include "kritaui_export.h" class KisDocument; class KoProgressUpdater; /** * @brief The class managing all the filters. * * This class manages all filters for a %Calligra application. Normally * you will not have to use it, since KisMainWindow takes care of loading * and saving documents. * * @ref KisFilter * * @author Kalle Dalheimer * @author Torben Weis * @author Werner Trobin */ class KRITAUI_EXPORT KisImportExportManager : public QObject { Q_OBJECT public: /** * This enum is used to distinguish the import/export cases */ enum Direction { Import = 1, Export = 2 }; /** * Create a filter manager for a document */ explicit KisImportExportManager(KisDocument *document); public: virtual ~KisImportExportManager(); /** * Imports the specified document and returns the resultant filename * (most likely some file in /tmp). * @p path can be either a URL or a filename. * @p documentMimeType gives importDocument a hint about what type * the document may be. It can be left empty. * * @return status signals the success/error of the conversion. * If the QString which is returned isEmpty() and the status is OK, * then we imported the file directly into the document. */ KisImportExportFilter::ConversionStatus importDocument(const QString &location, const QString &mimeType); /** * @brief Exports the given file/document to the specified URL/mimetype. * * If @p mimeType is empty, then the closest matching Calligra part is searched * and when the method returns @p mimeType contains this mimetype. * Oh, well, export is a C++ keyword ;) */ KisImportExportFilter::ConversionStatus exportDocument(const QString &location, const QString& realLocation, QByteArray &mimeType, bool showWarnings = true, KisPropertiesConfigurationSP exportConfiguration = 0); ///@name Static API //@{ /** * Suitable for passing to KoFileDialog::setMimeTypeFilters. The default mime * gets set by the "users" of this method, as we do not have enough * information here. * Optionally, @p extraNativeMimeTypes are added after the native mimetype. */ static QStringList mimeFilter(Direction direction); /** * @brief filterForMimeType loads the relevant import/export plugin and returns it. The caller * is responsible for deleting it! * @param mimetype the mimetype we want to import/export. If there's more than one plugin, the one * with the highest weight as defined in the json description will be taken * @param direction import or export * @return a pointer to the filter plugin or 0 if none could be found */ static KisImportExportFilter *filterForMimeType(const QString &mimetype, Direction direction); /** * Set the filter manager is batch mode (no dialog shown) * instead of the interactive mode (dialog shown) */ void setBatchMode(const bool batch); /** * Get if the filter manager is batch mode (true) * or in interactive mode (true) */ bool batchMode(void) const; void setProgresUpdater(KoProgressUpdater *updater); + static QString askForAudioFileName(const QString &defaultDir, QWidget *parent); + private Q_SLOTS: private: KisImportExportFilter::ConversionStatus convert(Direction direction, const QString &location, const QString& realLocation, const QString &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration); // Private API KisImportExportManager(const KisImportExportManager& rhs); KisImportExportManager &operator=(const KisImportExportManager& rhs); KisDocument *m_document; /// A static cache for the availability checks of filters static QStringList m_importMimeTypes; static QStringList m_exportMimeTypes; class Private; Private * const d; }; #endif // __KO_FILTER_MANAGER_H__ diff --git a/libs/ui/KisSyncedAudioPlayback.cpp b/libs/ui/KisSyncedAudioPlayback.cpp new file mode 100644 index 0000000000..8a3b815b65 --- /dev/null +++ b/libs/ui/KisSyncedAudioPlayback.cpp @@ -0,0 +1,143 @@ +#include "KisSyncedAudioPlayback.h" + +#include "config-qtmultimedia.h" + +#ifdef HAVE_QT_MULTIMEDIA +#include +#else +class QIODevice; + +#include + +namespace { + + class QMediaPlayer : public QObject { + Q_OBJECT + public: + + enum Error + { + NoError, + ResourceError, + FormatError, + NetworkError, + AccessDeniedError, + ServiceMissingError, + MediaIsPlaylist + }; + + enum State + { + StoppedState, + PlayingState, + PausedState + }; + + State state() const { return StoppedState; } + + void play() {} + void stop() {} + + qint64 position() const { return 0; } + qreal playbackRate() const { return 1.0; } + void setPosition(qint64) {} + void setPlaybackRate(qreal) {} + void setVolume(int) {} + void setMedia(const QUrl&, QIODevice * device = 0) { Q_UNUSED(device);} + QString errorString() const { return QString(); } + + Q_SIGNALS: + void error(Error value); + }; +} +#endif + + + +#include + + + +struct KisSyncedAudioPlayback::Private +{ + QMediaPlayer player; + qint64 tolerance = 200; +}; + + +KisSyncedAudioPlayback::KisSyncedAudioPlayback(const QString &fileName) + : QObject(0), + m_d(new Private) +{ + QFileInfo fileInfo(fileName); + Q_ASSERT(fileInfo.exists()); + + m_d->player.setMedia(QUrl::fromLocalFile(fileInfo.absoluteFilePath())); + m_d->player.setVolume(50); + + connect(&m_d->player, SIGNAL(error(QMediaPlayer::Error)), SLOT(slotOnError())); +} + +KisSyncedAudioPlayback::~KisSyncedAudioPlayback() +{ +} + +void KisSyncedAudioPlayback::setSoundOffsetTolerance(qint64 value) +{ + m_d->tolerance = value; +} + +void KisSyncedAudioPlayback::syncWithVideo(qint64 position) +{ + if (qAbs(position - m_d->player.position()) > m_d->tolerance) { + m_d->player.setPosition(position); + } +} + +bool KisSyncedAudioPlayback::isPlaying() const +{ + return m_d->player.state() == QMediaPlayer::PlayingState; +} + +void KisSyncedAudioPlayback::setVolume(qreal value) +{ + m_d->player.setVolume(qRound(100.0 * value)); +} + +void KisSyncedAudioPlayback::setSpeed(qreal value) +{ + if (qFuzzyCompare(value, m_d->player.playbackRate())) return; + + if (m_d->player.state() == QMediaPlayer::PlayingState) { + const qint64 oldPosition = m_d->player.position(); + + m_d->player.stop(); + m_d->player.setPlaybackRate(value); + m_d->player.setPosition(oldPosition); + m_d->player.play(); + } else { + m_d->player.setPlaybackRate(value); + } +} + +void KisSyncedAudioPlayback::play(qint64 startPosition) +{ + m_d->player.setPosition(startPosition); + m_d->player.play(); +} + +void KisSyncedAudioPlayback::stop() +{ + m_d->player.stop(); +} + +void KisSyncedAudioPlayback::slotOnError() +{ +#ifdef HAVE_QT_MULTIMEDIA + emit error(m_d->player.media().canonicalUrl().toLocalFile(), m_d->player.errorString()); +#endif +} + +#ifndef HAVE_QT_MULTIMEDIA +#include "KisSyncedAudioPlayback.moc" +#endif diff --git a/libs/ui/KisSyncedAudioPlayback.h b/libs/ui/KisSyncedAudioPlayback.h new file mode 100644 index 0000000000..2286a934f0 --- /dev/null +++ b/libs/ui/KisSyncedAudioPlayback.h @@ -0,0 +1,37 @@ +#ifndef KISSYNCEDAUDIOPLAYBACK_H +#define KISSYNCEDAUDIOPLAYBACK_H + +#include +#include + +class KisSyncedAudioPlayback : public QObject +{ + Q_OBJECT +public: + KisSyncedAudioPlayback(const QString &fileName); + virtual ~KisSyncedAudioPlayback(); + + void setSoundOffsetTolerance(qint64 value); + void syncWithVideo(qint64 position); + + bool isPlaying() const; + + void setVolume(qreal value); + +public Q_SLOTS: + void setSpeed(qreal value); + void play(qint64 startPosition); + void stop(); + +Q_SIGNALS: + void error(const QString &filename, const QString &message); + +private Q_SLOTS: + void slotOnError(); + +private: + struct Private; + const QScopedPointer m_d; +}; + +#endif // KISSYNCEDAUDIOPLAYBACK_H diff --git a/libs/ui/canvas/kis_animation_player.cpp b/libs/ui/canvas/kis_animation_player.cpp index 116a7f3569..5641c603de 100644 --- a/libs/ui/canvas/kis_animation_player.cpp +++ b/libs/ui/canvas/kis_animation_player.cpp @@ -1,383 +1,529 @@ /* * Copyright (c) 2015 Jouni Pentikäinen * * 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. */ #include "kis_animation_player.h" #include #include #include //#define PLAYER_DEBUG_FRAMERATE #include "kis_global.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_image.h" #include "kis_canvas2.h" #include "kis_animation_frame_cache.h" #include "kis_signal_auto_connection.h" #include "kis_image_animation_interface.h" #include "kis_time_range.h" #include "kis_signal_compressor.h" +#include +#include #include #include #include +#include "KisSyncedAudioPlayback.h" +#include "kis_signal_compressor_with_param.h" + +#include "KisViewManager.h" +#include "kis_icon_utils.h" + + using namespace boost::accumulators; typedef accumulator_set > FpsAccumulator; struct KisAnimationPlayer::Private { public: Private(KisAnimationPlayer *_q) : q(_q), realFpsAccumulator(tag::rolling_window::window_size = 24), droppedFpsAccumulator(tag::rolling_window::window_size = 24), droppedFramesPortion(tag::rolling_window::window_size = 24), dropFramesMode(true), nextFrameExpectedTime(0), expectedInterval(0), expectedFrame(0), lastTimerInterval(0), lastPaintedFrame(0), - playbackStatisticsCompressor(1000, KisSignalCompressor::FIRST_INACTIVE) + playbackStatisticsCompressor(1000, KisSignalCompressor::FIRST_INACTIVE), + stopAudioOnScrubbingCompressor(100, KisSignalCompressor::POSTPONE), + audioOffsetTolerance(-1) {} KisAnimationPlayer *q; bool useFastFrameUpload; bool playing; QTimer *timer; int initialFrame; int firstFrame; int lastFrame; - int fps; qreal playbackSpeed; KisCanvas2 *canvas; KisSignalAutoConnectionsStore cancelStrokeConnections; QElapsedTimer realFpsTimer; FpsAccumulator realFpsAccumulator; FpsAccumulator droppedFpsAccumulator; FpsAccumulator droppedFramesPortion; bool dropFramesMode; QElapsedTimer playbackTime; int nextFrameExpectedTime; int expectedInterval; int expectedFrame; int lastTimerInterval; int lastPaintedFrame; KisSignalCompressor playbackStatisticsCompressor; + QScopedPointer syncedAudio; + QScopedPointer > audioSyncScrubbingCompressor; + KisSignalCompressor stopAudioOnScrubbingCompressor; + + int audioOffsetTolerance; + void stopImpl(bool doUpdates); int incFrame(int frame, int inc) { frame += inc; if (frame > lastFrame) { frame = firstFrame + frame - lastFrame - 1; } return frame; } + + qint64 frameToMSec(int value, int fps) { + return qreal(value) / fps * 1000.0; + } + int msecToFrame(qint64 value, int fps) { + return qreal(value) * fps / 1000.0; + } }; KisAnimationPlayer::KisAnimationPlayer(KisCanvas2 *canvas) : QObject(canvas) , m_d(new Private(this)) { m_d->useFastFrameUpload = false; m_d->playing = false; - m_d->fps = 15; m_d->canvas = canvas; m_d->playbackSpeed = 1.0; m_d->timer = new QTimer(this); connect(m_d->timer, SIGNAL(timeout()), this, SLOT(slotUpdate())); m_d->timer->setSingleShot(true); connect(KisConfigNotifier::instance(), SIGNAL(dropFramesModeChanged()), SLOT(slotUpdateDropFramesMode())); slotUpdateDropFramesMode(); connect(&m_d->playbackStatisticsCompressor, SIGNAL(timeout()), this, SIGNAL(sigPlaybackStatisticsUpdated())); + + using namespace std::placeholders; + std::function callback( + std::bind(&KisAnimationPlayer::slotSyncScrubbingAudio, this, _1)); + + const int defaultScrubbingUdpatesDelay = 40; /* 40 ms == 25 fps */ + + m_d->audioSyncScrubbingCompressor.reset( + new KisSignalCompressorWithParam(defaultScrubbingUdpatesDelay, callback, KisSignalCompressor::FIRST_ACTIVE)); + + m_d->stopAudioOnScrubbingCompressor.setDelay(defaultScrubbingUdpatesDelay); + connect(&m_d->stopAudioOnScrubbingCompressor, SIGNAL(timeout()), SLOT(slotTryStopScrubbingAudio())); + + connect(m_d->canvas->image()->animationInterface(), SIGNAL(sigFramerateChanged()), SLOT(slotUpdateAudioChunkLength())); + slotUpdateAudioChunkLength(); + + connect(m_d->canvas->image()->animationInterface(), SIGNAL(sigAudioChannelChanged()), SLOT(slotAudioChannelChanged())); + connect(m_d->canvas->image()->animationInterface(), SIGNAL(sigAudioVolumeChanged()), SLOT(slotAudioVolumeChanged())); + slotAudioChannelChanged(); } KisAnimationPlayer::~KisAnimationPlayer() {} void KisAnimationPlayer::slotUpdateDropFramesMode() { KisConfig cfg; m_d->dropFramesMode = cfg.animationDropFrames(); } +void KisAnimationPlayer::slotSyncScrubbingAudio(int msecTime) +{ + KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->syncedAudio); + + if (!m_d->syncedAudio->isPlaying()) { + m_d->syncedAudio->play(msecTime); + } else { + m_d->syncedAudio->syncWithVideo(msecTime); + } + + if (!isPlaying()) { + m_d->stopAudioOnScrubbingCompressor.start(); + } +} + +void KisAnimationPlayer::slotTryStopScrubbingAudio() +{ + KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->syncedAudio); + if (m_d->syncedAudio && !isPlaying()) { + m_d->syncedAudio->stop(); + } +} + +void KisAnimationPlayer::slotAudioChannelChanged() +{ + + KisImageAnimationInterface *interface = m_d->canvas->image()->animationInterface(); + QString fileName = interface->audioChannelFileName(); + QFileInfo info(fileName); + if (info.exists() && !interface->isAudioMuted()) { + m_d->syncedAudio.reset(new KisSyncedAudioPlayback(info.absoluteFilePath())); + m_d->syncedAudio->setVolume(interface->audioVolume()); + m_d->syncedAudio->setSoundOffsetTolerance(m_d->audioOffsetTolerance); + + connect(m_d->syncedAudio.data(), SIGNAL(error(const QString &, const QString &)), SLOT(slotOnAudioError(const QString &, const QString &))); + } else { + m_d->syncedAudio.reset(); + } +} + +void KisAnimationPlayer::slotAudioVolumeChanged() +{ + KisImageAnimationInterface *interface = m_d->canvas->image()->animationInterface(); + if (m_d->syncedAudio) { + m_d->syncedAudio->setVolume(interface->audioVolume()); + } +} + +void KisAnimationPlayer::slotOnAudioError(const QString &fileName, const QString &message) +{ + QString errorMessage(i18nc("floating on-canvas message", "Cannot open audio: \"%1\"\nError: %2", fileName, message)); + m_d->canvas->viewManager()->showFloatingMessage(errorMessage, KisIconUtils::loadIcon("dialog-warning")); +} + void KisAnimationPlayer::connectCancelSignals() { m_d->cancelStrokeConnections.addConnection( m_d->canvas->image().data(), SIGNAL(sigUndoDuringStrokeRequested()), this, SLOT(slotCancelPlayback())); m_d->cancelStrokeConnections.addConnection( m_d->canvas->image().data(), SIGNAL(sigStrokeCancellationRequested()), this, SLOT(slotCancelPlayback())); m_d->cancelStrokeConnections.addConnection( m_d->canvas->image().data(), SIGNAL(sigStrokeEndRequested()), this, SLOT(slotCancelPlaybackSafe())); m_d->cancelStrokeConnections.addConnection( m_d->canvas->image()->animationInterface(), SIGNAL(sigFramerateChanged()), this, SLOT(slotUpdatePlaybackTimer())); m_d->cancelStrokeConnections.addConnection( m_d->canvas->image()->animationInterface(), SIGNAL(sigFullClipRangeChanged()), this, SLOT(slotUpdatePlaybackTimer())); m_d->cancelStrokeConnections.addConnection( m_d->canvas->image()->animationInterface(), SIGNAL(sigPlaybackRangeChanged()), this, SLOT(slotUpdatePlaybackTimer())); } void KisAnimationPlayer::disconnectCancelSignals() { m_d->cancelStrokeConnections.clear(); } +void KisAnimationPlayer::slotUpdateAudioChunkLength() +{ + const KisImageAnimationInterface *animation = m_d->canvas->image()->animationInterface(); + const int animationFramePeriod = qMax(1, 1000 / animation->framerate()); + + KisConfig cfg; + int scrubbingAudioUdpatesDelay = cfg.scrubbingAudioUpdatesDelay(); + + if (scrubbingAudioUdpatesDelay < 0) { + + scrubbingAudioUdpatesDelay = qMax(1, animationFramePeriod); + } + + m_d->audioSyncScrubbingCompressor->setDelay(scrubbingAudioUdpatesDelay); + m_d->stopAudioOnScrubbingCompressor.setDelay(scrubbingAudioUdpatesDelay); + + m_d->audioOffsetTolerance = cfg.audioOffsetTolerance(); + if (m_d->audioOffsetTolerance < 0) { + m_d->audioOffsetTolerance = animationFramePeriod; + } + + if (m_d->syncedAudio) { + m_d->syncedAudio->setSoundOffsetTolerance(m_d->audioOffsetTolerance); + } +} + void KisAnimationPlayer::slotUpdatePlaybackTimer() { m_d->timer->stop(); const KisImageAnimationInterface *animation = m_d->canvas->image()->animationInterface(); const KisTimeRange &range = animation->playbackRange(); if (!range.isValid()) return; - m_d->fps = animation->framerate(); + const int fps = animation->framerate(); m_d->initialFrame = animation->currentUITime(); m_d->firstFrame = range.start(); m_d->lastFrame = range.end(); m_d->expectedFrame = qBound(m_d->firstFrame, m_d->expectedFrame, m_d->lastFrame); - m_d->expectedInterval = qreal(1000) / m_d->fps / m_d->playbackSpeed; + m_d->expectedInterval = qreal(1000) / fps / m_d->playbackSpeed; m_d->lastTimerInterval = m_d->expectedInterval; + + if (m_d->syncedAudio) { + m_d->syncedAudio->setSpeed(m_d->playbackSpeed); + } + m_d->timer->start(m_d->expectedInterval); if (m_d->playbackTime.isValid()) { m_d->playbackTime.restart(); } else { m_d->playbackTime.start(); } m_d->nextFrameExpectedTime = m_d->playbackTime.elapsed() + m_d->expectedInterval; } void KisAnimationPlayer::play() { m_d->playing = true; slotUpdatePlaybackTimer(); m_d->expectedFrame = m_d->firstFrame; m_d->lastPaintedFrame = m_d->firstFrame; connectCancelSignals(); + + if (m_d->syncedAudio) { + KisImageAnimationInterface *animationInterface = m_d->canvas->image()->animationInterface(); + m_d->syncedAudio->play(m_d->frameToMSec(m_d->firstFrame, animationInterface->framerate())); + } } void KisAnimationPlayer::Private::stopImpl(bool doUpdates) { + if (syncedAudio) { + syncedAudio->stop(); + } + q->disconnectCancelSignals(); timer->stop(); playing = false; if (doUpdates) { KisImageAnimationInterface *animation = canvas->image()->animationInterface(); if (animation->currentUITime() == initialFrame) { canvas->refetchDataFromImage(); } else { animation->switchCurrentTimeAsync(initialFrame); } } emit q->sigPlaybackStopped(); } void KisAnimationPlayer::stop() { m_d->stopImpl(true); } void KisAnimationPlayer::forcedStopOnExit() { m_d->stopImpl(false); } bool KisAnimationPlayer::isPlaying() { return m_d->playing; } int KisAnimationPlayer::currentTime() { return m_d->lastPaintedFrame; } void KisAnimationPlayer::displayFrame(int time) { uploadFrame(time); } void KisAnimationPlayer::slotUpdate() { uploadFrame(-1); } void KisAnimationPlayer::uploadFrame(int frame) { + KisImageAnimationInterface *animationInterface = m_d->canvas->image()->animationInterface(); + if (frame < 0) { const int currentTime = m_d->playbackTime.elapsed(); const int framesDiff = currentTime - m_d->nextFrameExpectedTime; const qreal framesDiffNorm = qreal(framesDiff) / m_d->expectedInterval; // qDebug() << ppVar(framesDiff) // << ppVar(m_d->expectedFrame) // << ppVar(framesDiffNorm) // << ppVar(m_d->lastTimerInterval); if (m_d->dropFramesMode) { const int numDroppedFrames = qMax(0, qRound(framesDiffNorm)); frame = m_d->incFrame(m_d->expectedFrame, numDroppedFrames); } else { frame = m_d->expectedFrame; } m_d->nextFrameExpectedTime = currentTime + m_d->expectedInterval; m_d->lastTimerInterval = qMax(0.0, m_d->lastTimerInterval - 0.5 * framesDiff); m_d->expectedFrame = m_d->incFrame(frame, 1); m_d->timer->start(m_d->lastTimerInterval); m_d->playbackStatisticsCompressor.start(); } + if (m_d->syncedAudio) { + const int msecTime = m_d->frameToMSec(frame, animationInterface->framerate()); + if (isPlaying()) { + slotSyncScrubbingAudio(msecTime); + } else { + m_d->audioSyncScrubbingCompressor->start(msecTime); + } + } + if (m_d->canvas->frameCache() && m_d->canvas->frameCache()->uploadFrame(frame)) { m_d->canvas->updateCanvas(); m_d->useFastFrameUpload = true; emit sigFrameChanged(); } else { m_d->useFastFrameUpload = false; m_d->canvas->image()->barrierLock(true); m_d->canvas->image()->unlock(); // no OpenGL cache or the frame just not cached yet - m_d->canvas->image()->animationInterface()->switchCurrentTimeAsync(frame); + animationInterface->switchCurrentTimeAsync(frame); emit sigFrameChanged(); } if (!m_d->realFpsTimer.isValid()) { m_d->realFpsTimer.start(); } else { const int elapsed = m_d->realFpsTimer.restart(); m_d->realFpsAccumulator(elapsed); int numFrames = frame - m_d->lastPaintedFrame; if (numFrames < 0) { numFrames += m_d->lastFrame - m_d->firstFrame + 1; } m_d->droppedFramesPortion(qreal(int(numFrames != 1))); if (numFrames > 0) { m_d->droppedFpsAccumulator(qreal(elapsed) / numFrames); } #ifdef PLAYER_DEBUG_FRAMERATE qDebug() << " RFPS:" << 1000.0 / rolling_mean(m_d->realFpsAccumulator) << "DFPS:" << 1000.0 / rolling_mean(m_d->droppedFpsAccumulator) << ppVar(numFrames); #endif /* PLAYER_DEBUG_FRAMERATE */ } m_d->lastPaintedFrame = frame; } qreal KisAnimationPlayer::effectiveFps() const { return 1000.0 / rolling_mean(m_d->droppedFpsAccumulator); } qreal KisAnimationPlayer::realFps() const { return 1000.0 / rolling_mean(m_d->realFpsAccumulator); } qreal KisAnimationPlayer::framesDroppedPortion() const { return rolling_mean(m_d->droppedFramesPortion); } void KisAnimationPlayer::slotCancelPlayback() { stop(); } void KisAnimationPlayer::slotCancelPlaybackSafe() { /** * If there is no openGL support, then we have no (!) cache at * all. Therefore we should regenerate frame on every time switch, * which, yeah, can be very slow. What is more important, when * regenerating a frame animation interface will emit a * sigStrokeEndRequested() signal and we should ignore it. That is * not an ideal solution, because the user will be able to paint * on random frames while playing, but it lets users with faulty * GPUs see at least some preview of their animation. */ if (m_d->useFastFrameUpload) { stop(); } } qreal KisAnimationPlayer::playbackSpeed() { return m_d->playbackSpeed; } void KisAnimationPlayer::slotUpdatePlaybackSpeed(double value) { m_d->playbackSpeed = value; if (m_d->playing) { slotUpdatePlaybackTimer(); } } diff --git a/libs/ui/canvas/kis_animation_player.h b/libs/ui/canvas/kis_animation_player.h index 06e1d35716..a7374eb0dd 100644 --- a/libs/ui/canvas/kis_animation_player.h +++ b/libs/ui/canvas/kis_animation_player.h @@ -1,77 +1,85 @@ /* * Copyright (c) 2015 Jouni Pentikäinen * * 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. */ #ifndef KIS_ANIMATION_PLAYER_H #define KIS_ANIMATION_PLAYER_H #include #include #include "kritaui_export.h" class KisCanvas2; class KRITAUI_EXPORT KisAnimationPlayer : public QObject { Q_OBJECT public: KisAnimationPlayer(KisCanvas2 *canvas); ~KisAnimationPlayer(); void play(); void stop(); void displayFrame(int time); bool isPlaying(); int currentTime(); qreal playbackSpeed(); void forcedStopOnExit(); qreal effectiveFps() const; qreal realFps() const; qreal framesDroppedPortion() const; public Q_SLOTS: void slotUpdate(); void slotCancelPlayback(); void slotCancelPlaybackSafe(); void slotUpdatePlaybackSpeed(double value); void slotUpdatePlaybackTimer(); void slotUpdateDropFramesMode(); +private Q_SLOTS: + void slotSyncScrubbingAudio(int msecTime); + void slotTryStopScrubbingAudio(); + void slotUpdateAudioChunkLength(); + void slotAudioChannelChanged(); + void slotAudioVolumeChanged(); + void slotOnAudioError(const QString &fileName, const QString &message); + Q_SIGNALS: void sigFrameChanged(); void sigPlaybackStopped(); void sigPlaybackStatisticsUpdated(); private: void connectCancelSignals(); void disconnectCancelSignals(); void uploadFrame(int time); private: struct Private; QScopedPointer m_d; }; #endif diff --git a/libs/ui/dialogs/kis_dlg_import_image_sequence.cpp b/libs/ui/dialogs/kis_dlg_import_image_sequence.cpp index ba565435da..d518cfbc09 100644 --- a/libs/ui/dialogs/kis_dlg_import_image_sequence.cpp +++ b/libs/ui/dialogs/kis_dlg_import_image_sequence.cpp @@ -1,158 +1,163 @@ /* * Copyright (c) 2016 Jouni Pentikäinen * * 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. */ #include "kis_dlg_import_image_sequence.h" #include "KisDocument.h" #include "KisMainWindow.h" #include "kis_image.h" #include "kis_image_animation_interface.h" #include "KisImportExportManager.h" #include "KoFileDialog.h" #include class KisDlgImportImageSequence::ListItem : QListWidgetItem { public: ListItem(const QString &text, QListWidget *view, QCollator *collator) : QListWidgetItem(text, view), collator(collator) {} bool operator <(const QListWidgetItem &other) const override { int cmp = collator->compare(this->text(), other.text()); return cmp < 0; } private: QCollator *collator; }; KisDlgImportImageSequence::KisDlgImportImageSequence(KisMainWindow *mainWindow, KisDocument *document) : KoDialog(mainWindow), m_mainWindow(mainWindow), m_document(document) { setButtons(Ok | Cancel); setDefaultButton(Ok); QWidget * page = new QWidget(this); m_ui.setupUi(page); setMainWidget(page); enableButtonOk(false); m_ui.cmbOrder->addItem(i18n("Ascending"), Ascending); m_ui.cmbOrder->addItem(i18n("Descending"), Descending); m_ui.cmbOrder->setCurrentIndex(0); m_ui.cmbSortMode->addItem(i18n("Alphabetical"), Natural); m_ui.cmbSortMode->addItem(i18n("Numerical"), Numerical); m_ui.cmbSortMode->setCurrentIndex(1); m_ui.lstFiles->setSelectionMode(QAbstractItemView::ExtendedSelection); connect(m_ui.btnAddImages, &QAbstractButton::clicked, this, &KisDlgImportImageSequence::slotAddFiles); connect(m_ui.btnRemove, &QAbstractButton::clicked, this, &KisDlgImportImageSequence::slotRemoveFiles); connect(m_ui.spinStep, SIGNAL(valueChanged(int)), this, SLOT(slotSkipChanged(int))); connect(m_ui.cmbOrder, SIGNAL(currentIndexChanged(int)), this, SLOT(slotOrderOptionsChanged(int))); connect(m_ui.cmbSortMode, SIGNAL(currentIndexChanged(int)), this, SLOT(slotOrderOptionsChanged(int))); + + // cold initialization of the controls + slotSkipChanged(m_ui.spinStep->value()); + slotOrderOptionsChanged(m_ui.cmbOrder->currentIndex()); + slotOrderOptionsChanged(m_ui.cmbSortMode->currentIndex()); } QStringList KisDlgImportImageSequence::files() { QStringList list; for (int i=0; i < m_ui.lstFiles->count(); i++) { list.append(m_ui.lstFiles->item(i)->text()); } return list; } int KisDlgImportImageSequence::firstFrame() { return m_ui.spinFirstFrame->value(); } int KisDlgImportImageSequence::step() { return m_ui.spinStep->value(); } void KisDlgImportImageSequence::slotAddFiles() { QStringList urls = showOpenFileDialog(); if (!urls.isEmpty()) { Q_FOREACH(QString url, urls) { new ListItem(url, m_ui.lstFiles, &m_collator); } sortFileList(); } enableButtonOk(m_ui.lstFiles->count() > 0); } QStringList KisDlgImportImageSequence::showOpenFileDialog() { KoFileDialog dialog(this, KoFileDialog::ImportFiles, "OpenDocument"); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import)); dialog.setCaption(i18n("Import Images")); return dialog.filenames(); } void KisDlgImportImageSequence::slotRemoveFiles() { QList selected = m_ui.lstFiles->selectedItems(); Q_FOREACH(QListWidgetItem *item, selected) { delete item; } enableButtonOk(m_ui.lstFiles->count() > 0); } void KisDlgImportImageSequence::slotSkipChanged(int) { int documentFps = m_document->image()->animationInterface()->framerate(); float sourceFps = 1.0f * documentFps / m_ui.spinStep->value(); m_ui.lblFramerate->setText(i18n("Source fps: %1", sourceFps)); } void KisDlgImportImageSequence::slotOrderOptionsChanged(int) { sortFileList(); } void KisDlgImportImageSequence::sortFileList() { int order = m_ui.cmbOrder->currentData().toInt(); bool numeric = m_ui.cmbSortMode->currentData().toInt() == Numerical; m_collator.setNumericMode(numeric); m_ui.lstFiles->sortItems((order == Ascending) ? Qt::AscendingOrder : Qt::DescendingOrder); } diff --git a/libs/ui/kis_animation_exporter.cpp b/libs/ui/kis_animation_exporter.cpp index 513a826ec6..1aed326ac1 100644 --- a/libs/ui/kis_animation_exporter.cpp +++ b/libs/ui/kis_animation_exporter.cpp @@ -1,334 +1,385 @@ /* * Copyright (c) 2015 Jouni Pentikäinen * * 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. */ #include "kis_animation_exporter.h" #include #include #include #include +#include #include "KoFileDialog.h" #include "KisDocument.h" #include "kis_image.h" #include "KisImportExportManager.h" #include "kis_image_animation_interface.h" #include "KisPart.h" #include "KisMainWindow.h" #include "kis_paint_layer.h" #include "kis_group_layer.h" #include "kis_time_range.h" #include "kis_painter.h" #include "kis_image_lock_hijacker.h" struct KisAnimationExporterUI::Private { QWidget *parentWidget; KisAnimationExportSaver *exporter; Private(QWidget *parent) : parentWidget(parent), exporter(0) {} }; KisAnimationExporterUI::KisAnimationExporterUI(QWidget *parent) : m_d(new Private(parent)) { } KisAnimationExporterUI::~KisAnimationExporterUI() { if (m_d->exporter) { delete m_d->exporter; } } KisImportExportFilter::ConversionStatus KisAnimationExporterUI::exportSequence(KisDocument *document) { KoFileDialog dialog(m_d->parentWidget, KoFileDialog::SaveFile, "exportsequence"); dialog.setCaption(i18n("Export sequence")); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Export)); QString filename = dialog.filename(); // if the user presses cancel, it returns empty if (filename.isEmpty()) return KisImportExportFilter::UserCancelled; const KisTimeRange fullClipRange = document->image()->animationInterface()->fullClipRange(); int firstFrame = fullClipRange.start(); int lastFrame = fullClipRange.end(); m_d->exporter = new KisAnimationExportSaver(document, filename, firstFrame, lastFrame); return m_d->exporter->exportAnimation(); } struct KisAnimationExporter::Private { Private(KisDocument *document, int fromTime, int toTime) : document(document) , image(document->image()) , firstFrame(fromTime) , lastFrame(toTime) , currentFrame(-1) , batchMode(document->fileBatchMode()) , isCancelled(false) , status(KisImportExportFilter::OK) , tmpDevice(new KisPaintDevice(image->colorSpace())) { } KisDocument *document; KisImageWSP image; int firstFrame; int lastFrame; int currentFrame; bool batchMode; bool isCancelled; KisImportExportFilter::ConversionStatus status; SaveFrameCallback saveFrameCallback; KisPaintDeviceSP tmpDevice; KisPropertiesConfigurationSP exportConfiguration; }; KisAnimationExporter::KisAnimationExporter(KisDocument *document, int fromTime, int toTime) : m_d(new Private(document, fromTime, toTime)) { connect(m_d->image->animationInterface(), SIGNAL(sigFrameReady(int)), this, SLOT(frameReadyToCopy(int)), Qt::DirectConnection); connect(this, SIGNAL(sigFrameReadyToSave()), this, SLOT(frameReadyToSave()), Qt::QueuedConnection); } KisAnimationExporter::~KisAnimationExporter() { } void KisAnimationExporter::setExportConfiguration(KisPropertiesConfigurationSP exportConfiguration) { m_d->exportConfiguration = exportConfiguration; } void KisAnimationExporter::setSaveFrameCallback(SaveFrameCallback func) { m_d->saveFrameCallback = func; } KisImportExportFilter::ConversionStatus KisAnimationExporter::exportAnimation() { QScopedPointer progress; if (!m_d->batchMode) { QString message = i18n("Export frames..."); progress.reset(new QProgressDialog(message, "", 0, 0, KisPart::instance()->currentMainwindow())); progress->setWindowModality(Qt::ApplicationModal); progress->setCancelButton(0); progress->setMinimumDuration(0); progress->setValue(0); emit m_d->document->statusBarMessage(message); emit m_d->document->sigProgress(0); connect(m_d->document, SIGNAL(sigProgressCanceled()), this, SLOT(cancel())); } /** * HACK ALERT: Here we remove the image lock! We do it in a GUI * thread under the barrier lock held, so it is * guaranteed no other stroke will accidentally be * started by this. And showing an app-modal dialog to * the user will prevent him from doing anything * nasty. */ KisImageLockHijacker badGuy(m_d->image); Q_UNUSED(badGuy); KIS_ASSERT_RECOVER(!m_d->image->locked()) { return KisImportExportFilter::InternalError; } m_d->status = KisImportExportFilter::OK; m_d->currentFrame = m_d->firstFrame; m_d->image->animationInterface()->requestFrameRegeneration(m_d->currentFrame, m_d->image->bounds()); QEventLoop loop; loop.connect(this, SIGNAL(sigFinished()), SLOT(quit())); loop.exec(); if (!m_d->batchMode) { disconnect(m_d->document, SIGNAL(sigProgressCanceled()), this, SLOT(cancel())); emit m_d->document->sigProgress(100); emit m_d->document->clearStatusBarMessage(); progress.reset(); } return m_d->status; } void KisAnimationExporter::cancel() { m_d->isCancelled = true; } void KisAnimationExporter::frameReadyToCopy(int time) { if (time != m_d->currentFrame) return; QRect rc = m_d->image->bounds(); KisPainter::copyAreaOptimized(rc.topLeft(), m_d->image->projection(), m_d->tmpDevice, rc); emit sigFrameReadyToSave(); } void KisAnimationExporter::frameReadyToSave() { KIS_ASSERT_RECOVER(m_d->saveFrameCallback) { m_d->status = KisImportExportFilter::InternalError; emit sigFinished(); return; } if (m_d->isCancelled) { m_d->status = KisImportExportFilter::UserCancelled; emit sigFinished(); return; } KisImportExportFilter::ConversionStatus result = KisImportExportFilter::OK; int time = m_d->currentFrame; result = m_d->saveFrameCallback(time, m_d->tmpDevice, m_d->exportConfiguration); if (!m_d->batchMode) { emit m_d->document->sigProgress((time - m_d->firstFrame) * 100 / (m_d->lastFrame - m_d->firstFrame)); } qDebug() << result << time << m_d->lastFrame; if (result == KisImportExportFilter::OK && time < m_d->lastFrame) { m_d->currentFrame = time + 1; m_d->image->animationInterface()->requestFrameRegeneration(m_d->currentFrame, m_d->image->bounds()); } else { emit sigFinished(); } } struct KisAnimationExportSaver::Private { Private(KisDocument *document, int fromTime, int toTime, int _sequenceNumberingOffset) : document(document) , image(document->image()) , firstFrame(fromTime) , lastFrame(toTime) , sequenceNumberingOffset(_sequenceNumberingOffset) , tmpDoc(KisPart::instance()->createDocument()) , exporter(document, fromTime, toTime) { tmpDoc->setAutoSaveDelay(0); tmpImage = new KisImage(tmpDoc->createUndoStore(), image->bounds().width(), image->bounds().height(), image->colorSpace(), QString()); tmpImage->setResolution(image->xRes(), image->yRes()); tmpDoc->setCurrentImage(tmpImage); KisPaintLayer* paintLayer = new KisPaintLayer(tmpImage, "paint device", 255); tmpImage->addNode(paintLayer, tmpImage->rootLayer(), KisLayerSP(0)); tmpDevice = paintLayer->paintDevice(); } KisDocument *document; KisImageWSP image; int firstFrame; int lastFrame; int sequenceNumberingOffset; QScopedPointer tmpDoc; KisImageSP tmpImage; KisPaintDeviceSP tmpDevice; KisAnimationExporter exporter; QString filenamePrefix; QString filenameSuffix; }; KisAnimationExportSaver::KisAnimationExportSaver(KisDocument *document, const QString &baseFilename, int fromTime, int toTime, int sequenceNumberingOffset) : m_d(new Private(document, fromTime, toTime, sequenceNumberingOffset)) { int baseLength = baseFilename.lastIndexOf("."); if (baseLength > -1) { m_d->filenamePrefix = baseFilename.left(baseLength); m_d->filenameSuffix = baseFilename.right(baseFilename.length() - baseLength); } else { m_d->filenamePrefix = baseFilename; } QString mimefilter = KisMimeDatabase::mimeTypeForFile(baseFilename); m_d->tmpDoc->setOutputMimeType(mimefilter.toLatin1()); m_d->tmpDoc->setFileBatchMode(true); using namespace std::placeholders; // For _1 placeholder m_d->exporter.setSaveFrameCallback(std::bind(&KisAnimationExportSaver::saveFrameCallback, this, _1, _2, _3)); } KisAnimationExportSaver::~KisAnimationExportSaver() { } KisImportExportFilter::ConversionStatus KisAnimationExportSaver::exportAnimation(KisPropertiesConfigurationSP cfg) { + QFileInfo info(savedFilesMask()); + + QDir dir(info.absolutePath()); + QStringList filesList = dir.entryList({ info.fileName() }); + + if (!filesList.isEmpty()) { + if (m_d->document->fileBatchMode()) { + return KisImportExportFilter::CreationError; + } + + QStringList truncatedList = filesList; + + while (truncatedList.size() > 3) { + truncatedList.takeLast(); + } + + QString exampleFiles = truncatedList.join(", "); + if (truncatedList.size() != filesList.size()) { + exampleFiles += QString(", ..."); + } + + QMessageBox::StandardButton result = + QMessageBox::warning(0, + i18n("Delete old frames?"), + i18n("Frames with the same naming " + "scheme exist in the destination " + "directory. They are going to be " + "deleted, continue?\n\n" + "Directory: %1\n" + "Files: %2", + info.absolutePath(), exampleFiles), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + + if (result == QMessageBox::Yes) { + Q_FOREACH (const QString &file, filesList) { + if (!dir.remove(file)) { + QMessageBox::critical(0, + i18n("Failed to delete"), + i18n("Failed to delete an old frame file:\n\n" + "%1\n\n" + "Rendering cancelled.", dir.absoluteFilePath(file))); + return KisImportExportFilter::CreationError; + } + } + } else { + return KisImportExportFilter::UserCancelled; + } + } + m_d->exporter.setExportConfiguration(cfg); return m_d->exporter.exportAnimation(); } KisImportExportFilter::ConversionStatus KisAnimationExportSaver::saveFrameCallback(int time, KisPaintDeviceSP frame, KisPropertiesConfigurationSP exportConfiguration) { KisImportExportFilter::ConversionStatus status = KisImportExportFilter::OK; - QString frameNumber = QString("%1").arg(time + m_d->sequenceNumberingOffset, 4, 10, QChar('0')); + QString frameNumber = QString("%1").arg(time - m_d->firstFrame + m_d->sequenceNumberingOffset, 4, 10, QChar('0')); QString filename = m_d->filenamePrefix + frameNumber + m_d->filenameSuffix; QRect rc = m_d->image->bounds(); KisPainter::copyAreaOptimized(rc.topLeft(), frame, m_d->tmpDevice, rc); if (!m_d->tmpDoc->exportDocument(QUrl::fromLocalFile(filename), exportConfiguration)) { status = KisImportExportFilter::InternalError; } return status; } QString KisAnimationExportSaver::savedFilesMask() const { - return m_d->filenamePrefix + "%04d" + m_d->filenameSuffix; + return m_d->filenamePrefix + "????" + m_d->filenameSuffix; } diff --git a/libs/ui/kis_config.cc b/libs/ui/kis_config.cc index 4aa7b7c960..b419827e05 100644 --- a/libs/ui/kis_config.cc +++ b/libs/ui/kis_config.cc @@ -1,1798 +1,1818 @@ /* * Copyright (c) 2002 Patrick Julien * * 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. */ #include "kis_config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_canvas_resource_provider.h" #include "kis_config_notifier.h" #include "kis_snap_config.h" #include #include KisConfig::KisConfig() : m_cfg( KSharedConfig::openConfig()->group("")) { } KisConfig::~KisConfig() { if (qApp->thread() != QThread::currentThread()) { //dbgKrita << "WARNING: KisConfig: requested config synchronization from nonGUI thread! Skipping..."; return; } m_cfg.sync(); } bool KisConfig::disableTouchOnCanvas(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("disableTouchOnCanvas", false)); } void KisConfig::setDisableTouchOnCanvas(bool value) const { m_cfg.writeEntry("disableTouchOnCanvas", value); } bool KisConfig::useProjections(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("useProjections", true)); } void KisConfig::setUseProjections(bool useProj) const { m_cfg.writeEntry("useProjections", useProj); } bool KisConfig::undoEnabled(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("undoEnabled", true)); } void KisConfig::setUndoEnabled(bool undo) const { m_cfg.writeEntry("undoEnabled", undo); } int KisConfig::undoStackLimit(bool defaultValue) const { return (defaultValue ? 30 : m_cfg.readEntry("undoStackLimit", 30)); } void KisConfig::setUndoStackLimit(int limit) const { m_cfg.writeEntry("undoStackLimit", limit); } bool KisConfig::useCumulativeUndoRedo(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useCumulativeUndoRedo",false)); } void KisConfig::setCumulativeUndoRedo(bool value) { m_cfg.writeEntry("useCumulativeUndoRedo", value); } qreal KisConfig::stackT1(bool defaultValue) const { return (defaultValue ? 5 : m_cfg.readEntry("stackT1",5)); } void KisConfig::setStackT1(int T1) { m_cfg.writeEntry("stackT1", T1); } qreal KisConfig::stackT2(bool defaultValue) const { return (defaultValue ? 1 : m_cfg.readEntry("stackT2",1)); } void KisConfig::setStackT2(int T2) { m_cfg.writeEntry("stackT2", T2); } int KisConfig::stackN(bool defaultValue) const { return (defaultValue ? 5 : m_cfg.readEntry("stackN",5)); } void KisConfig::setStackN(int N) { m_cfg.writeEntry("stackN", N); } qint32 KisConfig::defImageWidth(bool defaultValue) const { return (defaultValue ? 1600 : m_cfg.readEntry("imageWidthDef", 1600)); } qint32 KisConfig::defImageHeight(bool defaultValue) const { return (defaultValue ? 1200 : m_cfg.readEntry("imageHeightDef", 1200)); } qreal KisConfig::defImageResolution(bool defaultValue) const { return (defaultValue ? 100.0 : m_cfg.readEntry("imageResolutionDef", 100.0)) / 72.0; } QString KisConfig::defColorModel(bool defaultValue) const { return (defaultValue ? KoColorSpaceRegistry::instance()->rgb8()->colorModelId().id() : m_cfg.readEntry("colorModelDef", KoColorSpaceRegistry::instance()->rgb8()->colorModelId().id())); } void KisConfig::defColorModel(const QString & model) const { m_cfg.writeEntry("colorModelDef", model); } QString KisConfig::defaultColorDepth(bool defaultValue) const { return (defaultValue ? KoColorSpaceRegistry::instance()->rgb8()->colorDepthId().id() : m_cfg.readEntry("colorDepthDef", KoColorSpaceRegistry::instance()->rgb8()->colorDepthId().id())); } void KisConfig::setDefaultColorDepth(const QString & depth) const { m_cfg.writeEntry("colorDepthDef", depth); } QString KisConfig::defColorProfile(bool defaultValue) const { return (defaultValue ? KoColorSpaceRegistry::instance()->rgb8()->profile()->name() : m_cfg.readEntry("colorProfileDef", KoColorSpaceRegistry::instance()->rgb8()->profile()->name())); } void KisConfig::defColorProfile(const QString & profile) const { m_cfg.writeEntry("colorProfileDef", profile); } void KisConfig::defImageWidth(qint32 width) const { m_cfg.writeEntry("imageWidthDef", width); } void KisConfig::defImageHeight(qint32 height) const { m_cfg.writeEntry("imageHeightDef", height); } void KisConfig::defImageResolution(qreal res) const { m_cfg.writeEntry("imageResolutionDef", res*72.0); } void cleanOldCursorStyleKeys(KConfigGroup &cfg) { if (cfg.hasKey("newCursorStyle") && cfg.hasKey("newOutlineStyle")) { cfg.deleteEntry("cursorStyleDef"); } } CursorStyle KisConfig::newCursorStyle(bool defaultValue) const { if (defaultValue) { return CURSOR_STYLE_NO_CURSOR; } int style = m_cfg.readEntry("newCursorStyle", int(-1)); if (style < 0) { // old style format style = m_cfg.readEntry("cursorStyleDef", int(OLD_CURSOR_STYLE_OUTLINE)); switch (style) { case OLD_CURSOR_STYLE_TOOLICON: style = CURSOR_STYLE_TOOLICON; break; case OLD_CURSOR_STYLE_CROSSHAIR: case OLD_CURSOR_STYLE_OUTLINE_CENTER_CROSS: style = CURSOR_STYLE_CROSSHAIR; break; case OLD_CURSOR_STYLE_POINTER: style = CURSOR_STYLE_POINTER; break; case OLD_CURSOR_STYLE_OUTLINE: case OLD_CURSOR_STYLE_NO_CURSOR: style = CURSOR_STYLE_NO_CURSOR; break; case OLD_CURSOR_STYLE_SMALL_ROUND: case OLD_CURSOR_STYLE_OUTLINE_CENTER_DOT: style = CURSOR_STYLE_SMALL_ROUND; break; case OLD_CURSOR_STYLE_TRIANGLE_RIGHTHANDED: case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_RIGHTHANDED: style = CURSOR_STYLE_TRIANGLE_RIGHTHANDED; break; case OLD_CURSOR_STYLE_TRIANGLE_LEFTHANDED: case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_LEFTHANDED: style = CURSOR_STYLE_TRIANGLE_LEFTHANDED; break; default: style = -1; } } cleanOldCursorStyleKeys(m_cfg); // compatibility with future versions if (style < 0 || style >= N_CURSOR_STYLE_SIZE) { style = CURSOR_STYLE_NO_CURSOR; } return (CursorStyle) style; } void KisConfig::setNewCursorStyle(CursorStyle style) { m_cfg.writeEntry("newCursorStyle", (int)style); } OutlineStyle KisConfig::newOutlineStyle(bool defaultValue) const { if (defaultValue) { return OUTLINE_FULL; } int style = m_cfg.readEntry("newOutlineStyle", int(-1)); if (style < 0) { // old style format style = m_cfg.readEntry("cursorStyleDef", int(OLD_CURSOR_STYLE_OUTLINE)); switch (style) { case OLD_CURSOR_STYLE_TOOLICON: case OLD_CURSOR_STYLE_CROSSHAIR: case OLD_CURSOR_STYLE_POINTER: case OLD_CURSOR_STYLE_NO_CURSOR: case OLD_CURSOR_STYLE_SMALL_ROUND: case OLD_CURSOR_STYLE_TRIANGLE_RIGHTHANDED: case OLD_CURSOR_STYLE_TRIANGLE_LEFTHANDED: style = OUTLINE_NONE; break; case OLD_CURSOR_STYLE_OUTLINE: case OLD_CURSOR_STYLE_OUTLINE_CENTER_DOT: case OLD_CURSOR_STYLE_OUTLINE_CENTER_CROSS: case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_RIGHTHANDED: case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_LEFTHANDED: style = OUTLINE_FULL; break; default: style = -1; } } cleanOldCursorStyleKeys(m_cfg); // compatibility with future versions if (style < 0 || style >= N_OUTLINE_STYLE_SIZE) { style = OUTLINE_FULL; } return (OutlineStyle) style; } void KisConfig::setNewOutlineStyle(OutlineStyle style) { m_cfg.writeEntry("newOutlineStyle", (int)style); } QRect KisConfig::colorPreviewRect() const { return m_cfg.readEntry("colorPreviewRect", QVariant(QRect(32, 32, 48, 48))).toRect(); } void KisConfig::setColorPreviewRect(const QRect &rect) { m_cfg.writeEntry("colorPreviewRect", QVariant(rect)); } bool KisConfig::useDirtyPresets(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useDirtyPresets",false)); } void KisConfig::setUseDirtyPresets(bool value) { m_cfg.writeEntry("useDirtyPresets",value); KisConfigNotifier::instance()->notifyConfigChanged(); } bool KisConfig::useEraserBrushSize(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useEraserBrushSize",false)); } void KisConfig::setUseEraserBrushSize(bool value) { m_cfg.writeEntry("useEraserBrushSize",value); KisConfigNotifier::instance()->notifyConfigChanged(); } bool KisConfig::useEraserBrushOpacity(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useEraserBrushOpacity",false)); } void KisConfig::setUseEraserBrushOpacity(bool value) { m_cfg.writeEntry("useEraserBrushOpacity",value); KisConfigNotifier::instance()->notifyConfigChanged(); } QColor KisConfig::getMDIBackgroundColor(bool defaultValue) const { QColor col(77, 77, 77); return (defaultValue ? col : m_cfg.readEntry("mdiBackgroundColor", col)); } void KisConfig::setMDIBackgroundColor(const QColor &v) const { m_cfg.writeEntry("mdiBackgroundColor", v); } QString KisConfig::getMDIBackgroundImage(bool defaultValue) const { return (defaultValue ? "" : m_cfg.readEntry("mdiBackgroundImage", "")); } void KisConfig::setMDIBackgroundImage(const QString &filename) const { m_cfg.writeEntry("mdiBackgroundImage", filename); } QString KisConfig::monitorProfile(int screen) const { // Note: keep this in sync with the default profile for the RGB colorspaces! QString profile = m_cfg.readEntry("monitorProfile" + QString(screen == 0 ? "": QString("_%1").arg(screen)), "sRGB-elle-V2-srgbtrc.icc"); //dbgKrita << "KisConfig::monitorProfile()" << profile; return profile; } QString KisConfig::monitorForScreen(int screen, const QString &defaultMonitor, bool defaultValue) const { return (defaultValue ? defaultMonitor : m_cfg.readEntry(QString("monitor_for_screen_%1").arg(screen), defaultMonitor)); } void KisConfig::setMonitorForScreen(int screen, const QString& monitor) { m_cfg.writeEntry(QString("monitor_for_screen_%1").arg(screen), monitor); } void KisConfig::setMonitorProfile(int screen, const QString & monitorProfile, bool override) const { m_cfg.writeEntry("monitorProfile/OverrideX11", override); m_cfg.writeEntry("monitorProfile" + QString(screen == 0 ? "": QString("_%1").arg(screen)), monitorProfile); } const KoColorProfile *KisConfig::getScreenProfile(int screen) { if (screen < 0) return 0; KisConfig cfg; QString monitorId; if (KisColorManager::instance()->devices().size() > screen) { monitorId = cfg.monitorForScreen(screen, KisColorManager::instance()->devices()[screen]); } //dbgKrita << "getScreenProfile(). Screen" << screen << "monitor id" << monitorId; if (monitorId.isEmpty()) { return 0; } QByteArray bytes = KisColorManager::instance()->displayProfile(monitorId); //dbgKrita << "\tgetScreenProfile()" << bytes.size(); if (bytes.length() > 0) { const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), bytes); //dbgKrita << "\tKisConfig::getScreenProfile for screen" << screen << profile->name(); return profile; } else { //dbgKrita << "\tCould not get a system monitor profile"; return 0; } } const KoColorProfile *KisConfig::displayProfile(int screen) const { if (screen < 0) return 0; // if the user plays with the settings, they can override the display profile, in which case // we don't want the system setting. bool override = useSystemMonitorProfile(); //dbgKrita << "KisConfig::displayProfile(). Override X11:" << override; const KoColorProfile *profile = 0; if (override) { //dbgKrita << "\tGoing to get the screen profile"; profile = KisConfig::getScreenProfile(screen); } // if it fails. check the configuration if (!profile || !profile->isSuitableForDisplay()) { //dbgKrita << "\tGoing to get the monitor profile"; QString monitorProfileName = monitorProfile(screen); //dbgKrita << "\t\tmonitorProfileName:" << monitorProfileName; if (!monitorProfileName.isEmpty()) { profile = KoColorSpaceRegistry::instance()->profileByName(monitorProfileName); } if (profile) { //dbgKrita << "\t\tsuitable for display" << profile->isSuitableForDisplay(); } else { //dbgKrita << "\t\tstill no profile"; } } // if we still don't have a profile, or the profile isn't suitable for display, // we need to get a last-resort profile. the built-in sRGB is a good choice then. if (!profile || !profile->isSuitableForDisplay()) { //dbgKrita << "\tnothing worked, going to get sRGB built-in"; profile = KoColorSpaceRegistry::instance()->profileByName("sRGB Built-in"); } if (profile) { //dbgKrita << "\tKisConfig::displayProfile for screen" << screen << "is" << profile->name(); } else { //dbgKrita << "\tCouldn't get a display profile at all"; } return profile; } QString KisConfig::workingColorSpace(bool defaultValue) const { return (defaultValue ? "RGBA" : m_cfg.readEntry("workingColorSpace", "RGBA")); } void KisConfig::setWorkingColorSpace(const QString & workingColorSpace) const { m_cfg.writeEntry("workingColorSpace", workingColorSpace); } QString KisConfig::printerColorSpace(bool /*defaultValue*/) const { //TODO currently only rgb8 is supported //return (defaultValue ? "RGBA" : m_cfg.readEntry("printerColorSpace", "RGBA")); return QString("RGBA"); } void KisConfig::setPrinterColorSpace(const QString & printerColorSpace) const { m_cfg.writeEntry("printerColorSpace", printerColorSpace); } QString KisConfig::printerProfile(bool defaultValue) const { return (defaultValue ? "" : m_cfg.readEntry("printerProfile", "")); } void KisConfig::setPrinterProfile(const QString & printerProfile) const { m_cfg.writeEntry("printerProfile", printerProfile); } bool KisConfig::useBlackPointCompensation(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("useBlackPointCompensation", true)); } void KisConfig::setUseBlackPointCompensation(bool useBlackPointCompensation) const { m_cfg.writeEntry("useBlackPointCompensation", useBlackPointCompensation); } bool KisConfig::allowLCMSOptimization(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("allowLCMSOptimization", true)); } void KisConfig::setAllowLCMSOptimization(bool allowLCMSOptimization) { m_cfg.writeEntry("allowLCMSOptimization", allowLCMSOptimization); } bool KisConfig::showRulers(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("showrulers", false)); } void KisConfig::setShowRulers(bool rulers) const { m_cfg.writeEntry("showrulers", rulers); } bool KisConfig::rulersTrackMouse(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("rulersTrackMouse", true)); } void KisConfig::setRulersTrackMouse(bool value) const { m_cfg.writeEntry("rulersTrackMouse", value); } qint32 KisConfig::pasteBehaviour(bool defaultValue) const { return (defaultValue ? 2 : m_cfg.readEntry("pasteBehaviour", 2)); } void KisConfig::setPasteBehaviour(qint32 renderIntent) const { m_cfg.writeEntry("pasteBehaviour", renderIntent); } qint32 KisConfig::monitorRenderIntent(bool defaultValue) const { qint32 intent = m_cfg.readEntry("renderIntent", INTENT_PERCEPTUAL); if (intent > 3) intent = 3; if (intent < 0) intent = 0; return (defaultValue ? INTENT_PERCEPTUAL : intent); } void KisConfig::setRenderIntent(qint32 renderIntent) const { if (renderIntent > 3) renderIntent = 3; if (renderIntent < 0) renderIntent = 0; m_cfg.writeEntry("renderIntent", renderIntent); } bool KisConfig::useOpenGL(bool defaultValue) const { if (defaultValue) { return true; } //dbgKrita << "use opengl" << m_cfg.readEntry("useOpenGL", true) << "success" << m_cfg.readEntry("canvasState", "OPENGL_SUCCESS"); QString canvasState = m_cfg.readEntry("canvasState", "OPENGL_SUCCESS"); return (m_cfg.readEntry("useOpenGL", true) && (canvasState == "OPENGL_SUCCESS" || canvasState == "TRY_OPENGL")); } void KisConfig::setUseOpenGL(bool useOpenGL) const { m_cfg.writeEntry("useOpenGL", useOpenGL); } int KisConfig::openGLFilteringMode(bool defaultValue) const { return (defaultValue ? 3 : m_cfg.readEntry("OpenGLFilterMode", 3)); } void KisConfig::setOpenGLFilteringMode(int filteringMode) { m_cfg.writeEntry("OpenGLFilterMode", filteringMode); } bool KisConfig::useOpenGLTextureBuffer(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("useOpenGLTextureBuffer", true)); } void KisConfig::setUseOpenGLTextureBuffer(bool useBuffer) { m_cfg.writeEntry("useOpenGLTextureBuffer", useBuffer); } int KisConfig::openGLTextureSize(bool defaultValue) const { return (defaultValue ? 256 : m_cfg.readEntry("textureSize", 256)); } bool KisConfig::disableVSync(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("disableVSync", true)); } void KisConfig::setDisableVSync(bool disableVSync) { m_cfg.writeEntry("disableVSync", disableVSync); } bool KisConfig::showAdvancedOpenGLSettings(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("showAdvancedOpenGLSettings", false)); } bool KisConfig::forceOpenGLFenceWorkaround(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("forceOpenGLFenceWorkaround", false)); } int KisConfig::numMipmapLevels(bool defaultValue) const { return (defaultValue ? 4 : m_cfg.readEntry("numMipmapLevels", 4)); } int KisConfig::textureOverlapBorder() const { return 1 << qMax(0, numMipmapLevels()); } qint32 KisConfig::maxNumberOfThreads(bool defaultValue) const { return (defaultValue ? QThread::idealThreadCount() : m_cfg.readEntry("maxthreads", QThread::idealThreadCount())); } void KisConfig::setMaxNumberOfThreads(qint32 maxThreads) { m_cfg.writeEntry("maxthreads", maxThreads); } quint32 KisConfig::getGridMainStyle(bool defaultValue) const { int v = m_cfg.readEntry("gridmainstyle", 0); v = qBound(0, v, 2); return (defaultValue ? 0 : v); } void KisConfig::setGridMainStyle(quint32 v) const { m_cfg.writeEntry("gridmainstyle", v); } quint32 KisConfig::getGridSubdivisionStyle(bool defaultValue) const { quint32 v = m_cfg.readEntry("gridsubdivisionstyle", 1); if (v > 2) v = 2; return (defaultValue ? 1 : v); } void KisConfig::setGridSubdivisionStyle(quint32 v) const { m_cfg.writeEntry("gridsubdivisionstyle", v); } QColor KisConfig::getGridMainColor(bool defaultValue) const { QColor col(99, 99, 99); return (defaultValue ? col : m_cfg.readEntry("gridmaincolor", col)); } void KisConfig::setGridMainColor(const QColor & v) const { m_cfg.writeEntry("gridmaincolor", v); } QColor KisConfig::getGridSubdivisionColor(bool defaultValue) const { QColor col(150, 150, 150); return (defaultValue ? col : m_cfg.readEntry("gridsubdivisioncolor", col)); } void KisConfig::setGridSubdivisionColor(const QColor & v) const { m_cfg.writeEntry("gridsubdivisioncolor", v); } quint32 KisConfig::guidesLineStyle(bool defaultValue) const { int v = m_cfg.readEntry("guidesLineStyle", 0); v = qBound(0, v, 2); return (defaultValue ? 0 : v); } void KisConfig::setGuidesLineStyle(quint32 v) const { m_cfg.writeEntry("guidesLineStyle", v); } QColor KisConfig::guidesColor(bool defaultValue) const { QColor col(99, 99, 99); return (defaultValue ? col : m_cfg.readEntry("guidesColor", col)); } void KisConfig::setGuidesColor(const QColor & v) const { m_cfg.writeEntry("guidesColor", v); } void KisConfig::loadSnapConfig(KisSnapConfig *config, bool defaultValue) const { KisSnapConfig defaultConfig(false); if (defaultValue) { *config = defaultConfig; return; } config->setOrthogonal(m_cfg.readEntry("globalSnapOrthogonal", defaultConfig.orthogonal())); config->setNode(m_cfg.readEntry("globalSnapNode", defaultConfig.node())); config->setExtension(m_cfg.readEntry("globalSnapExtension", defaultConfig.extension())); config->setIntersection(m_cfg.readEntry("globalSnapIntersection", defaultConfig.intersection())); config->setBoundingBox(m_cfg.readEntry("globalSnapBoundingBox", defaultConfig.boundingBox())); config->setImageBounds(m_cfg.readEntry("globalSnapImageBounds", defaultConfig.imageBounds())); config->setImageCenter(m_cfg.readEntry("globalSnapImageCenter", defaultConfig.imageCenter())); } void KisConfig::saveSnapConfig(const KisSnapConfig &config) { m_cfg.writeEntry("globalSnapOrthogonal", config.orthogonal()); m_cfg.writeEntry("globalSnapNode", config.node()); m_cfg.writeEntry("globalSnapExtension", config.extension()); m_cfg.writeEntry("globalSnapIntersection", config.intersection()); m_cfg.writeEntry("globalSnapBoundingBox", config.boundingBox()); m_cfg.writeEntry("globalSnapImageBounds", config.imageBounds()); m_cfg.writeEntry("globalSnapImageCenter", config.imageCenter()); } qint32 KisConfig::checkSize(bool defaultValue) const { return (defaultValue ? 32 : m_cfg.readEntry("checksize", 32)); } void KisConfig::setCheckSize(qint32 checksize) const { m_cfg.writeEntry("checksize", checksize); } bool KisConfig::scrollCheckers(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("scrollingcheckers", false)); } void KisConfig::setScrollingCheckers(bool sc) const { m_cfg.writeEntry("scrollingcheckers", sc); } QColor KisConfig::canvasBorderColor(bool defaultValue) const { QColor color(QColor(128,128,128)); return (defaultValue ? color : m_cfg.readEntry("canvasBorderColor", color)); } void KisConfig::setCanvasBorderColor(const QColor& color) const { m_cfg.writeEntry("canvasBorderColor", color); } bool KisConfig::hideScrollbars(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("hideScrollbars", false)); } void KisConfig::setHideScrollbars(bool value) const { m_cfg.writeEntry("hideScrollbars", value); } QColor KisConfig::checkersColor1(bool defaultValue) const { QColor col(220, 220, 220); return (defaultValue ? col : m_cfg.readEntry("checkerscolor", col)); } void KisConfig::setCheckersColor1(const QColor & v) const { m_cfg.writeEntry("checkerscolor", v); } QColor KisConfig::checkersColor2(bool defaultValue) const { return (defaultValue ? QColor(Qt::white) : m_cfg.readEntry("checkerscolor2", QColor(Qt::white))); } void KisConfig::setCheckersColor2(const QColor & v) const { m_cfg.writeEntry("checkerscolor2", v); } bool KisConfig::antialiasCurves(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("antialiascurves", true)); } void KisConfig::setAntialiasCurves(bool v) const { m_cfg.writeEntry("antialiascurves", v); } QColor KisConfig::selectionOverlayMaskColor(bool defaultValue) const { QColor def(255, 0, 0, 220); return (defaultValue ? def : m_cfg.readEntry("selectionOverlayMaskColor", def)); } void KisConfig::setSelectionOverlayMaskColor(const QColor &color) { m_cfg.writeEntry("selectionOverlayMaskColor", color); } bool KisConfig::antialiasSelectionOutline(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("AntialiasSelectionOutline", false)); } void KisConfig::setAntialiasSelectionOutline(bool v) const { m_cfg.writeEntry("AntialiasSelectionOutline", v); } bool KisConfig::showRootLayer(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("ShowRootLayer", false)); } void KisConfig::setShowRootLayer(bool showRootLayer) const { m_cfg.writeEntry("ShowRootLayer", showRootLayer); } bool KisConfig::showGlobalSelection(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("ShowGlobalSelection", false)); } void KisConfig::setShowGlobalSelection(bool showGlobalSelection) const { m_cfg.writeEntry("ShowGlobalSelection", showGlobalSelection); } bool KisConfig::showOutlineWhilePainting(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("ShowOutlineWhilePainting", true)); } void KisConfig::setShowOutlineWhilePainting(bool showOutlineWhilePainting) const { m_cfg.writeEntry("ShowOutlineWhilePainting", showOutlineWhilePainting); } bool KisConfig::hideSplashScreen(bool defaultValue) const { KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen"); return (defaultValue ? true : cfg.readEntry("HideSplashAfterStartup", true)); } void KisConfig::setHideSplashScreen(bool hideSplashScreen) const { KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen"); cfg.writeEntry("HideSplashAfterStartup", hideSplashScreen); } qreal KisConfig::outlineSizeMinimum(bool defaultValue) const { return (defaultValue ? 1.0 : m_cfg.readEntry("OutlineSizeMinimum", 1.0)); } void KisConfig::setOutlineSizeMinimum(qreal outlineSizeMinimum) const { m_cfg.writeEntry("OutlineSizeMinimum", outlineSizeMinimum); } qreal KisConfig::selectionViewSizeMinimum(bool defaultValue) const { return (defaultValue ? 5.0 : m_cfg.readEntry("SelectionViewSizeMinimum", 5.0)); } void KisConfig::setSelectionViewSizeMinimum(qreal outlineSizeMinimum) const { m_cfg.writeEntry("SelectionViewSizeMinimum", outlineSizeMinimum); } int KisConfig::autoSaveInterval(bool defaultValue) const { return (defaultValue ? 15 * 60 : m_cfg.readEntry("AutoSaveInterval", 15 * 60)); } void KisConfig::setAutoSaveInterval(int seconds) const { return m_cfg.writeEntry("AutoSaveInterval", seconds); } bool KisConfig::backupFile(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("CreateBackupFile", true)); } void KisConfig::setBackupFile(bool backupFile) const { m_cfg.writeEntry("CreateBackupFile", backupFile); } bool KisConfig::showFilterGallery(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("showFilterGallery", false)); } void KisConfig::setShowFilterGallery(bool showFilterGallery) const { m_cfg.writeEntry("showFilterGallery", showFilterGallery); } bool KisConfig::showFilterGalleryLayerMaskDialog(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("showFilterGalleryLayerMaskDialog", true)); } void KisConfig::setShowFilterGalleryLayerMaskDialog(bool showFilterGallery) const { m_cfg.writeEntry("setShowFilterGalleryLayerMaskDialog", showFilterGallery); } QString KisConfig::canvasState(bool defaultValue) const { return (defaultValue ? "OPENGL_NOT_TRIED" : m_cfg.readEntry("canvasState", "OPENGL_NOT_TRIED")); } void KisConfig::setCanvasState(const QString& state) const { static QStringList acceptableStates; if (acceptableStates.isEmpty()) { acceptableStates << "OPENGL_SUCCESS" << "TRY_OPENGL" << "OPENGL_NOT_TRIED" << "OPENGL_FAILED"; } if (acceptableStates.contains(state)) { m_cfg.writeEntry("canvasState", state); m_cfg.sync(); } } bool KisConfig::toolOptionsPopupDetached(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("ToolOptionsPopupDetached", false)); } void KisConfig::setToolOptionsPopupDetached(bool detached) const { m_cfg.writeEntry("ToolOptionsPopupDetached", detached); } bool KisConfig::paintopPopupDetached(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("PaintopPopupDetached", false)); } void KisConfig::setPaintopPopupDetached(bool detached) const { m_cfg.writeEntry("PaintopPopupDetached", detached); } QString KisConfig::pressureTabletCurve(bool defaultValue) const { return (defaultValue ? "0,0;1,1" : m_cfg.readEntry("tabletPressureCurve","0,0;1,1;")); } void KisConfig::setPressureTabletCurve(const QString& curveString) const { m_cfg.writeEntry("tabletPressureCurve", curveString); } qreal KisConfig::vastScrolling(bool defaultValue) const { return (defaultValue ? 0.9 : m_cfg.readEntry("vastScrolling", 0.9)); } void KisConfig::setVastScrolling(const qreal factor) const { m_cfg.writeEntry("vastScrolling", factor); } int KisConfig::presetChooserViewMode(bool defaultValue) const { return (defaultValue ? 0 : m_cfg.readEntry("presetChooserViewMode", 0)); } void KisConfig::setPresetChooserViewMode(const int mode) const { m_cfg.writeEntry("presetChooserViewMode", mode); } int KisConfig::presetIconSize(bool defaultValue) const { return (defaultValue ? 30 : m_cfg.readEntry("presetIconSize", 30)); } void KisConfig::setPresetIconSize(const int value) const { m_cfg.writeEntry("presetIconSize", value); } bool KisConfig::firstRun(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("firstRun", true)); } void KisConfig::setFirstRun(const bool first) const { m_cfg.writeEntry("firstRun", first); } int KisConfig::horizontalSplitLines(bool defaultValue) const { return (defaultValue ? 1 : m_cfg.readEntry("horizontalSplitLines", 1)); } void KisConfig::setHorizontalSplitLines(const int numberLines) const { m_cfg.writeEntry("horizontalSplitLines", numberLines); } int KisConfig::verticalSplitLines(bool defaultValue) const { return (defaultValue ? 1 : m_cfg.readEntry("verticalSplitLines", 1)); } void KisConfig::setVerticalSplitLines(const int numberLines) const { m_cfg.writeEntry("verticalSplitLines", numberLines); } bool KisConfig::clicklessSpacePan(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("clicklessSpacePan", true)); } void KisConfig::setClicklessSpacePan(const bool toggle) const { m_cfg.writeEntry("clicklessSpacePan", toggle); } bool KisConfig::hideDockersFullscreen(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("hideDockersFullScreen", true)); } void KisConfig::setHideDockersFullscreen(const bool value) const { m_cfg.writeEntry("hideDockersFullScreen", value); } bool KisConfig::showDockerTitleBars(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("showDockerTitleBars", true)); } void KisConfig::setShowDockerTitleBars(const bool value) const { m_cfg.writeEntry("showDockerTitleBars", value); } bool KisConfig::showDockers(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("showDockers", true)); } void KisConfig::setShowDockers(const bool value) const { m_cfg.writeEntry("showDockers", value); } bool KisConfig::showStatusBar(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("showStatusBar", true)); } void KisConfig::setShowStatusBar(const bool value) const { m_cfg.writeEntry("showStatusBar", value); } bool KisConfig::hideMenuFullscreen(bool defaultValue) const { return (defaultValue ? true: m_cfg.readEntry("hideMenuFullScreen", true)); } void KisConfig::setHideMenuFullscreen(const bool value) const { m_cfg.writeEntry("hideMenuFullScreen", value); } bool KisConfig::hideScrollbarsFullscreen(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("hideScrollbarsFullScreen", true)); } void KisConfig::setHideScrollbarsFullscreen(const bool value) const { m_cfg.writeEntry("hideScrollbarsFullScreen", value); } bool KisConfig::hideStatusbarFullscreen(bool defaultValue) const { return (defaultValue ? true: m_cfg.readEntry("hideStatusbarFullScreen", true)); } void KisConfig::setHideStatusbarFullscreen(const bool value) const { m_cfg.writeEntry("hideStatusbarFullScreen", value); } bool KisConfig::hideTitlebarFullscreen(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("hideTitleBarFullscreen", true)); } void KisConfig::setHideTitlebarFullscreen(const bool value) const { m_cfg.writeEntry("hideTitleBarFullscreen", value); } bool KisConfig::hideToolbarFullscreen(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("hideToolbarFullscreen", true)); } void KisConfig::setHideToolbarFullscreen(const bool value) const { m_cfg.writeEntry("hideToolbarFullscreen", value); } bool KisConfig::fullscreenMode(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("fullscreenMode", true)); } void KisConfig::setFullscreenMode(const bool value) const { m_cfg.writeEntry("fullscreenMode", value); } QStringList KisConfig::favoriteCompositeOps(bool defaultValue) const { return (defaultValue ? QStringList() : m_cfg.readEntry("favoriteCompositeOps", QStringList())); } void KisConfig::setFavoriteCompositeOps(const QStringList& compositeOps) const { m_cfg.writeEntry("favoriteCompositeOps", compositeOps); } QString KisConfig::exportConfiguration(const QString &filterId, bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("ExportConfiguration-" + filterId, QString())); } void KisConfig::setExportConfiguration(const QString &filterId, KisPropertiesConfigurationSP properties) const { QString exportConfig = properties->toXML(); m_cfg.writeEntry("ExportConfiguration-" + filterId, exportConfig); } bool KisConfig::useOcio(bool defaultValue) const { #ifdef HAVE_OCIO return (defaultValue ? false : m_cfg.readEntry("Krita/Ocio/UseOcio", false)); #else Q_UNUSED(defaultValue); return false; #endif } void KisConfig::setUseOcio(bool useOCIO) const { m_cfg.writeEntry("Krita/Ocio/UseOcio", useOCIO); } int KisConfig::favoritePresets(bool defaultValue) const { return (defaultValue ? 10 : m_cfg.readEntry("numFavoritePresets", 10)); } void KisConfig::setFavoritePresets(const int value) { m_cfg.writeEntry("numFavoritePresets", value); } bool KisConfig::levelOfDetailEnabled(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("levelOfDetailEnabled", false)); } void KisConfig::setLevelOfDetailEnabled(bool value) { m_cfg.writeEntry("levelOfDetailEnabled", value); } KisConfig::OcioColorManagementMode KisConfig::ocioColorManagementMode(bool defaultValue) const { return (OcioColorManagementMode)(defaultValue ? INTERNAL : m_cfg.readEntry("Krita/Ocio/OcioColorManagementMode", (int) INTERNAL)); } void KisConfig::setOcioColorManagementMode(OcioColorManagementMode mode) const { m_cfg.writeEntry("Krita/Ocio/OcioColorManagementMode", (int) mode); } QString KisConfig::ocioConfigurationPath(bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("Krita/Ocio/OcioConfigPath", QString())); } void KisConfig::setOcioConfigurationPath(const QString &path) const { m_cfg.writeEntry("Krita/Ocio/OcioConfigPath", path); } QString KisConfig::ocioLutPath(bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("Krita/Ocio/OcioLutPath", QString())); } void KisConfig::setOcioLutPath(const QString &path) const { m_cfg.writeEntry("Krita/Ocio/OcioLutPath", path); } int KisConfig::ocioLutEdgeSize(bool defaultValue) const { return (defaultValue ? 64 : m_cfg.readEntry("Krita/Ocio/LutEdgeSize", 64)); } void KisConfig::setOcioLutEdgeSize(int value) { m_cfg.writeEntry("Krita/Ocio/LutEdgeSize", value); } bool KisConfig::ocioLockColorVisualRepresentation(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("Krita/Ocio/OcioLockColorVisualRepresentation", false)); } void KisConfig::setOcioLockColorVisualRepresentation(bool value) { m_cfg.writeEntry("Krita/Ocio/OcioLockColorVisualRepresentation", value); } QString KisConfig::defaultPalette(bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("defaultPalette", QString())); } void KisConfig::setDefaultPalette(const QString& name) const { m_cfg.writeEntry("defaultPalette", name); } QString KisConfig::toolbarSlider(int sliderNumber, bool defaultValue) const { QString def = "flow"; if (sliderNumber == 1) { def = "opacity"; } if (sliderNumber == 2) { def = "size"; } return (defaultValue ? def : m_cfg.readEntry(QString("toolbarslider_%1").arg(sliderNumber), def)); } void KisConfig::setToolbarSlider(int sliderNumber, const QString &slider) { m_cfg.writeEntry(QString("toolbarslider_%1").arg(sliderNumber), slider); } bool KisConfig::sliderLabels(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("sliderLabels", true)); } void KisConfig::setSliderLabels(bool enabled) { m_cfg.writeEntry("sliderLabels", enabled); } QString KisConfig::currentInputProfile(bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("currentInputProfile", QString())); } void KisConfig::setCurrentInputProfile(const QString& name) { m_cfg.writeEntry("currentInputProfile", name); } bool KisConfig::useSystemMonitorProfile(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("ColorManagement/UseSystemMonitorProfile", false)); } void KisConfig::setUseSystemMonitorProfile(bool _useSystemMonitorProfile) const { m_cfg.writeEntry("ColorManagement/UseSystemMonitorProfile", _useSystemMonitorProfile); } bool KisConfig::presetStripVisible(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("presetStripVisible", true)); } void KisConfig::setPresetStripVisible(bool visible) { m_cfg.writeEntry("presetStripVisible", visible); } bool KisConfig::scratchpadVisible(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("scratchpadVisible", true)); } void KisConfig::setScratchpadVisible(bool visible) { m_cfg.writeEntry("scratchpadVisible", visible); } bool KisConfig::showSingleChannelAsColor(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("showSingleChannelAsColor", false)); } void KisConfig::setShowSingleChannelAsColor(bool asColor) { m_cfg.writeEntry("showSingleChannelAsColor", asColor); } bool KisConfig::hidePopups(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("hidePopups", false)); } void KisConfig::setHidePopups(bool hidepopups) { m_cfg.writeEntry("hidePopups", hidepopups); } int KisConfig::numDefaultLayers(bool defaultValue) const { return (defaultValue ? 2 : m_cfg.readEntry("NumberOfLayersForNewImage", 2)); } void KisConfig::setNumDefaultLayers(int num) { m_cfg.writeEntry("NumberOfLayersForNewImage", num); } quint8 KisConfig::defaultBackgroundOpacity(bool defaultValue) const { return (defaultValue ? (int)OPACITY_OPAQUE_U8 : m_cfg.readEntry("BackgroundOpacityForNewImage", (int)OPACITY_OPAQUE_U8)); } void KisConfig::setDefaultBackgroundOpacity(quint8 value) { m_cfg.writeEntry("BackgroundOpacityForNewImage", (int)value); } QColor KisConfig::defaultBackgroundColor(bool defaultValue) const { return (defaultValue ? QColor(Qt::white) : m_cfg.readEntry("BackgroundColorForNewImage", QColor(Qt::white))); } void KisConfig::setDefaultBackgroundColor(QColor value) { m_cfg.writeEntry("BackgroundColorForNewImage", value); } KisConfig::BackgroundStyle KisConfig::defaultBackgroundStyle(bool defaultValue) const { return (KisConfig::BackgroundStyle)(defaultValue ? LAYER : m_cfg.readEntry("BackgroundStyleForNewImage", (int)LAYER)); } void KisConfig::setDefaultBackgroundStyle(KisConfig::BackgroundStyle value) { m_cfg.writeEntry("BackgroundStyleForNewImage", (int)value); } int KisConfig::lineSmoothingType(bool defaultValue) const { return (defaultValue ? 1 : m_cfg.readEntry("LineSmoothingType", 1)); } void KisConfig::setLineSmoothingType(int value) { m_cfg.writeEntry("LineSmoothingType", value); } qreal KisConfig::lineSmoothingDistance(bool defaultValue) const { return (defaultValue ? 50.0 : m_cfg.readEntry("LineSmoothingDistance", 50.0)); } void KisConfig::setLineSmoothingDistance(qreal value) { m_cfg.writeEntry("LineSmoothingDistance", value); } qreal KisConfig::lineSmoothingTailAggressiveness(bool defaultValue) const { return (defaultValue ? 0.15 : m_cfg.readEntry("LineSmoothingTailAggressiveness", 0.15)); } void KisConfig::setLineSmoothingTailAggressiveness(qreal value) { m_cfg.writeEntry("LineSmoothingTailAggressiveness", value); } bool KisConfig::lineSmoothingSmoothPressure(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("LineSmoothingSmoothPressure", false)); } void KisConfig::setLineSmoothingSmoothPressure(bool value) { m_cfg.writeEntry("LineSmoothingSmoothPressure", value); } bool KisConfig::lineSmoothingScalableDistance(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("LineSmoothingScalableDistance", true)); } void KisConfig::setLineSmoothingScalableDistance(bool value) { m_cfg.writeEntry("LineSmoothingScalableDistance", value); } qreal KisConfig::lineSmoothingDelayDistance(bool defaultValue) const { return (defaultValue ? 50.0 : m_cfg.readEntry("LineSmoothingDelayDistance", 50.0)); } void KisConfig::setLineSmoothingDelayDistance(qreal value) { m_cfg.writeEntry("LineSmoothingDelayDistance", value); } bool KisConfig::lineSmoothingUseDelayDistance(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("LineSmoothingUseDelayDistance", true)); } void KisConfig::setLineSmoothingUseDelayDistance(bool value) { m_cfg.writeEntry("LineSmoothingUseDelayDistance", value); } bool KisConfig::lineSmoothingFinishStabilizedCurve(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("LineSmoothingFinishStabilizedCurve", true)); } void KisConfig::setLineSmoothingFinishStabilizedCurve(bool value) { m_cfg.writeEntry("LineSmoothingFinishStabilizedCurve", value); } bool KisConfig::lineSmoothingStabilizeSensors(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("LineSmoothingStabilizeSensors", true)); } void KisConfig::setLineSmoothingStabilizeSensors(bool value) { m_cfg.writeEntry("LineSmoothingStabilizeSensors", value); } int KisConfig::paletteDockerPaletteViewSectionSize(bool defaultValue) const { return (defaultValue ? 12 : m_cfg.readEntry("paletteDockerPaletteViewSectionSize", 12)); } void KisConfig::setPaletteDockerPaletteViewSectionSize(int value) const { m_cfg.writeEntry("paletteDockerPaletteViewSectionSize", value); } int KisConfig::tabletEventsDelay(bool defaultValue) const { return (defaultValue ? 10 : m_cfg.readEntry("tabletEventsDelay", 10)); } void KisConfig::setTabletEventsDelay(int value) { m_cfg.writeEntry("tabletEventsDelay", value); } bool KisConfig::testingAcceptCompressedTabletEvents(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("testingAcceptCompressedTabletEvents", false)); } void KisConfig::setTestingAcceptCompressedTabletEvents(bool value) { m_cfg.writeEntry("testingAcceptCompressedTabletEvents", value); } bool KisConfig::shouldEatDriverShortcuts(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("shouldEatDriverShortcuts", false)); } bool KisConfig::testingCompressBrushEvents(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("testingCompressBrushEvents", false)); } void KisConfig::setTestingCompressBrushEvents(bool value) { m_cfg.writeEntry("testingCompressBrushEvents", value); } bool KisConfig::useVerboseOpenGLDebugOutput(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useVerboseOpenGLDebugOutput", false)); } int KisConfig::workaroundX11SmoothPressureSteps(bool defaultValue) const { return (defaultValue ? 0 : m_cfg.readEntry("workaroundX11SmoothPressureSteps", 0)); } bool KisConfig::showCanvasMessages(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("showOnCanvasMessages", true)); } void KisConfig::setShowCanvasMessages(bool show) { m_cfg.writeEntry("showOnCanvasMessages", show); } bool KisConfig::compressKra(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("compressLayersInKra", false)); } void KisConfig::setCompressKra(bool compress) { m_cfg.writeEntry("compressLayersInKra", compress); } bool KisConfig::toolOptionsInDocker(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("ToolOptionsInDocker", true)); } void KisConfig::setToolOptionsInDocker(bool inDocker) { m_cfg.writeEntry("ToolOptionsInDocker", inDocker); } const KoColorSpace* KisConfig::customColorSelectorColorSpace(bool defaultValue) const { const KoColorSpace *cs = 0; KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); if (defaultValue || cfg.readEntry("useCustomColorSpace", true)) { KoColorSpaceRegistry* csr = KoColorSpaceRegistry::instance(); QString modelID = cfg.readEntry("customColorSpaceModel", "RGBA"); QString depthID = cfg.readEntry("customColorSpaceDepthID", "U8"); QString profile = cfg.readEntry("customColorSpaceProfile", "sRGB built-in - (lcms internal)"); if (profile == "default") { // qDebug() << "Falling back to default color profile."; profile = "sRGB built-in - (lcms internal)"; } cs = csr->colorSpace(modelID, depthID, profile); } return cs; } void KisConfig::setCustomColorSelectorColorSpace(const KoColorSpace *cs) { KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); cfg.writeEntry("useCustomColorSpace", bool(cs)); if(cs) { cfg.writeEntry("customColorSpaceModel", cs->colorModelId().id()); cfg.writeEntry("customColorSpaceDepthID", cs->colorDepthId().id()); cfg.writeEntry("customColorSpaceProfile", cs->profile()->name()); } KisConfigNotifier::instance()->notifyConfigChanged(); } bool KisConfig::enableOpenGLDebugging(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("enableOpenGLDebugging", false)); } void KisConfig::setEnableOpenGLDebugging(bool value) const { m_cfg.writeEntry("enableOpenGLDebugging", value); } void KisConfig::setEnableAmdVectorizationWorkaround(bool value) { m_cfg.writeEntry("amdDisableVectorWorkaround", value); } bool KisConfig::enableAmdVectorizationWorkaround(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("amdDisableVectorWorkaround", false)); } void KisConfig::setAnimationDropFrames(bool value) { bool oldValue = animationDropFrames(); if (value == oldValue) return; m_cfg.writeEntry("animationDropFrames", value); KisConfigNotifier::instance()->notifyDropFramesModeChanged(); } bool KisConfig::animationDropFrames(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("animationDropFrames", true)); } -int KisConfig::scribbingUpdatesDelay(bool defaultValue) const +int KisConfig::scrubbingUpdatesDelay(bool defaultValue) const { - return (defaultValue ? 30 : m_cfg.readEntry("scribbingUpdatesDelay", 30)); + return (defaultValue ? 30 : m_cfg.readEntry("scrubbingUpdatesDelay", 30)); } -void KisConfig::setScribbingUpdatesDelay(int value) +void KisConfig::setScrubbingUpdatesDelay(int value) { - m_cfg.writeEntry("scribbingUpdatesDelay", value); + m_cfg.writeEntry("scrubbingUpdatesDelay", value); +} + +int KisConfig::scrubbingAudioUpdatesDelay(bool defaultValue) const +{ + return (defaultValue ? -1 : m_cfg.readEntry("scrubbingAudioUpdatesDelay", -1)); +} + +void KisConfig::setScrubbingAudioUpdatesDelay(int value) +{ + m_cfg.writeEntry("scrubbingAudioUpdatesDelay", value); +} + +int KisConfig::audioOffsetTolerance(bool defaultValue) const +{ + return (defaultValue ? -1 : m_cfg.readEntry("audioOffsetTolerance", -1)); +} + +void KisConfig::setAudioOffsetTolerance(int value) +{ + m_cfg.writeEntry("audioOffsetTolerance", value); } bool KisConfig::switchSelectionCtrlAlt(bool defaultValue) const { return defaultValue ? false : m_cfg.readEntry("switchSelectionCtrlAlt", false); } void KisConfig::setSwitchSelectionCtrlAlt(bool value) { m_cfg.writeEntry("switchSelectionCtrlAlt", value); KisConfigNotifier::instance()->notifyConfigChanged(); } bool KisConfig::convertToImageColorspaceOnImport(bool defaultValue) const { return defaultValue ? false : m_cfg.readEntry("ConvertToImageColorSpaceOnImport", false); } void KisConfig::setConvertToImageColorspaceOnImport(bool value) { m_cfg.writeEntry("ConvertToImageColorSpaceOnImport", value); } int KisConfig::stabilizerSampleSize(bool defaultValue) const { #ifdef Q_OS_WIN const int defaultSampleSize = 50; #else const int defaultSampleSize = 15; #endif return defaultValue ? defaultSampleSize : m_cfg.readEntry("stabilizerSampleSize", defaultSampleSize); } void KisConfig::setStabilizerSampleSize(int value) { m_cfg.writeEntry("stabilizerSampleSize", value); } int KisConfig::stabilizerDelayedPaintInterval(bool defaultValue) const { const int defaultInterval = 20; return defaultValue ? defaultInterval : m_cfg.readEntry("stabilizerDelayedPaintInterval", defaultInterval); } void KisConfig::setStabilizerDelayedPaintInterval(int value) { m_cfg.writeEntry("stabilizerDelayedPaintInterval", value); } QString KisConfig::customFFMpegPath(bool defaultValue) const { return defaultValue ? QString() : m_cfg.readEntry("ffmpegExecutablePath", QString()); } void KisConfig::setCustomFFMpegPath(const QString &value) const { m_cfg.writeEntry("ffmpegExecutablePath", value); } bool KisConfig::showBrushHud(bool defaultValue) const { return defaultValue ? false : m_cfg.readEntry("showBrushHud", false); } void KisConfig::setShowBrushHud(bool value) { m_cfg.writeEntry("showBrushHud", value); } QString KisConfig::brushHudSetting(bool defaultValue) const { QString defaultDoc = "\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n"; return defaultValue ? defaultDoc : m_cfg.readEntry("brushHudSettings", defaultDoc); } void KisConfig::setBrushHudSetting(const QString &value) const { m_cfg.writeEntry("brushHudSettings", value); } #include #include void KisConfig::writeKoColor(const QString& name, const KoColor& color) const { QDomDocument doc = QDomDocument(name); QDomElement el = doc.createElement(name); doc.appendChild(el); color.toXML(doc, el); m_cfg.writeEntry(name, doc.toString()); } //ported from kispropertiesconfig. KoColor KisConfig::readKoColor(const QString& name, const KoColor& color) const { QDomDocument doc; if (!m_cfg.readEntry(name).isNull()) { doc.setContent(m_cfg.readEntry(name)); QDomElement e = doc.documentElement().firstChild().toElement(); return KoColor::fromXML(e, Integer16BitsColorDepthID.id(), QHash()); } else { QString blackColor = "\n\n \n\n"; doc.setContent(blackColor); QDomElement e = doc.documentElement().firstChild().toElement(); return KoColor::fromXML(e, Integer16BitsColorDepthID.id(), QHash()); } return color; } diff --git a/libs/ui/kis_config.h b/libs/ui/kis_config.h index 0854cd3d0c..f8dffd6221 100644 --- a/libs/ui/kis_config.h +++ b/libs/ui/kis_config.h @@ -1,540 +1,546 @@ /* * Copyright (c) 2002 Patrick Julien * * 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. */ #ifndef KIS_CONFIG_H_ #define KIS_CONFIG_H_ #include #include #include #include #include #include #include "kis_global.h" #include "kis_properties_configuration.h" #include "kritaui_export.h" class KoColorProfile; class KoColorSpace; class KisSnapConfig; class KRITAUI_EXPORT KisConfig { public: KisConfig(); ~KisConfig(); bool disableTouchOnCanvas(bool defaultValue = false) const; void setDisableTouchOnCanvas(bool value) const; bool useProjections(bool defaultValue = false) const; void setUseProjections(bool useProj) const; bool undoEnabled(bool defaultValue = false) const; void setUndoEnabled(bool undo) const; int undoStackLimit(bool defaultValue = false) const; void setUndoStackLimit(int limit) const; bool useCumulativeUndoRedo(bool defaultValue = false) const; void setCumulativeUndoRedo(bool value); double stackT1(bool defaultValue = false) const; void setStackT1(int T1); double stackT2(bool defaultValue = false) const; void setStackT2(int T2); int stackN(bool defaultValue = false) const; void setStackN(int N); qint32 defImageWidth(bool defaultValue = false) const; void defImageWidth(qint32 width) const; qint32 defImageHeight(bool defaultValue = false) const; void defImageHeight(qint32 height) const; qreal defImageResolution(bool defaultValue = false) const; void defImageResolution(qreal res) const; /** * @return the id of the default color model used for creating new images. */ QString defColorModel(bool defaultValue = false) const; /** * set the id of the default color model used for creating new images. */ void defColorModel(const QString & model) const; /** * @return the id of the default color depth used for creating new images. */ QString defaultColorDepth(bool defaultValue = false) const; /** * set the id of the default color depth used for creating new images. */ void setDefaultColorDepth(const QString & depth) const; /** * @return the id of the default color profile used for creating new images. */ QString defColorProfile(bool defaultValue = false) const; /** * set the id of the default color profile used for creating new images. */ void defColorProfile(const QString & depth) const; CursorStyle newCursorStyle(bool defaultValue = false) const; void setNewCursorStyle(CursorStyle style); OutlineStyle newOutlineStyle(bool defaultValue = false) const; void setNewOutlineStyle(OutlineStyle style); QRect colorPreviewRect() const; void setColorPreviewRect(const QRect &rect); /// get the profile the user has selected for the given screen QString monitorProfile(int screen) const; void setMonitorProfile(int screen, const QString & monitorProfile, bool override) const; QString monitorForScreen(int screen, const QString &defaultMonitor, bool defaultValue = true) const; void setMonitorForScreen(int screen, const QString& monitor); /// Get the actual profile to be used for the given screen, which is /// either the screen profile set by the color management system or /// the custom monitor profile set by the user, depending on the configuration const KoColorProfile *displayProfile(int screen) const; QString workingColorSpace(bool defaultValue = false) const; void setWorkingColorSpace(const QString & workingColorSpace) const; QString importProfile(bool defaultValue = false) const; void setImportProfile(const QString & importProfile) const; QString printerColorSpace(bool defaultValue = false) const; void setPrinterColorSpace(const QString & printerColorSpace) const; QString printerProfile(bool defaultValue = false) const; void setPrinterProfile(const QString & printerProfile) const; bool useBlackPointCompensation(bool defaultValue = false) const; void setUseBlackPointCompensation(bool useBlackPointCompensation) const; bool allowLCMSOptimization(bool defaultValue = false) const; void setAllowLCMSOptimization(bool allowLCMSOptimization); void writeKoColor(const QString& name, const KoColor& color) const; KoColor readKoColor(const QString& name, const KoColor& color = KoColor()) const; bool showRulers(bool defaultValue = false) const; void setShowRulers(bool rulers) const; bool rulersTrackMouse(bool defaultValue = false) const; void setRulersTrackMouse(bool value) const; qint32 pasteBehaviour(bool defaultValue = false) const; void setPasteBehaviour(qint32 behaviour) const; qint32 monitorRenderIntent(bool defaultValue = false) const; void setRenderIntent(qint32 monitorRenderIntent) const; bool useOpenGL(bool defaultValue = false) const; void setUseOpenGL(bool useOpenGL) const; int openGLFilteringMode(bool defaultValue = false) const; void setOpenGLFilteringMode(int filteringMode); bool useOpenGLTextureBuffer(bool defaultValue = false) const; void setUseOpenGLTextureBuffer(bool useBuffer); bool disableVSync(bool defaultValue = false) const; void setDisableVSync(bool disableVSync); bool showAdvancedOpenGLSettings(bool defaultValue = false) const; bool forceOpenGLFenceWorkaround(bool defaultValue = false) const; int numMipmapLevels(bool defaultValue = false) const; int openGLTextureSize(bool defaultValue = false) const; int textureOverlapBorder() const; qint32 maxNumberOfThreads(bool defaultValue = false) const; void setMaxNumberOfThreads(qint32 numberOfThreads); quint32 getGridMainStyle(bool defaultValue = false) const; void setGridMainStyle(quint32 v) const; quint32 getGridSubdivisionStyle(bool defaultValue = false) const; void setGridSubdivisionStyle(quint32 v) const; QColor getGridMainColor(bool defaultValue = false) const; void setGridMainColor(const QColor & v) const; QColor getGridSubdivisionColor(bool defaultValue = false) const; void setGridSubdivisionColor(const QColor & v) const; quint32 guidesLineStyle(bool defaultValue = false) const; void setGuidesLineStyle(quint32 v) const; QColor guidesColor(bool defaultValue = false) const; void setGuidesColor(const QColor & v) const; void loadSnapConfig(KisSnapConfig *config, bool defaultValue = false) const; void saveSnapConfig(const KisSnapConfig &config); qint32 checkSize(bool defaultValue = false) const; void setCheckSize(qint32 checkSize) const; bool scrollCheckers(bool defaultValue = false) const; void setScrollingCheckers(bool scollCheckers) const; QColor checkersColor1(bool defaultValue = false) const; void setCheckersColor1(const QColor & v) const; QColor checkersColor2(bool defaultValue = false) const; void setCheckersColor2(const QColor & v) const; QColor canvasBorderColor(bool defaultValue = false) const; void setCanvasBorderColor(const QColor &color) const; bool hideScrollbars(bool defaultValue = false) const; void setHideScrollbars(bool value) const; bool antialiasCurves(bool defaultValue = false) const; void setAntialiasCurves(bool v) const; QColor selectionOverlayMaskColor(bool defaultValue = false) const; void setSelectionOverlayMaskColor(const QColor &color); bool antialiasSelectionOutline(bool defaultValue = false) const; void setAntialiasSelectionOutline(bool v) const; bool showRootLayer(bool defaultValue = false) const; void setShowRootLayer(bool showRootLayer) const; bool showGlobalSelection(bool defaultValue = false) const; void setShowGlobalSelection(bool showGlobalSelection) const; bool showOutlineWhilePainting(bool defaultValue = false) const; void setShowOutlineWhilePainting(bool showOutlineWhilePainting) const; bool hideSplashScreen(bool defaultValue = false) const; void setHideSplashScreen(bool hideSplashScreen) const; qreal outlineSizeMinimum(bool defaultValue = false) const; void setOutlineSizeMinimum(qreal outlineSizeMinimum) const; qreal selectionViewSizeMinimum(bool defaultValue = false) const; void setSelectionViewSizeMinimum(qreal outlineSizeMinimum) const; int autoSaveInterval(bool defaultValue = false) const; void setAutoSaveInterval(int seconds) const; bool backupFile(bool defaultValue = false) const; void setBackupFile(bool backupFile) const; bool showFilterGallery(bool defaultValue = false) const; void setShowFilterGallery(bool showFilterGallery) const; bool showFilterGalleryLayerMaskDialog(bool defaultValue = false) const; void setShowFilterGalleryLayerMaskDialog(bool showFilterGallery) const; // OPENGL_SUCCESS, TRY_OPENGL, OPENGL_NOT_TRIED, OPENGL_FAILED QString canvasState(bool defaultValue = false) const; void setCanvasState(const QString& state) const; bool toolOptionsPopupDetached(bool defaultValue = false) const; void setToolOptionsPopupDetached(bool detached) const; bool paintopPopupDetached(bool defaultValue = false) const; void setPaintopPopupDetached(bool detached) const; QString pressureTabletCurve(bool defaultValue = false) const; void setPressureTabletCurve(const QString& curveString) const; qreal vastScrolling(bool defaultValue = false) const; void setVastScrolling(const qreal factor) const; int presetChooserViewMode(bool defaultValue = false) const; void setPresetChooserViewMode(const int mode) const; int presetIconSize(bool defaultValue = false) const; void setPresetIconSize(const int value) const; bool firstRun(bool defaultValue = false) const; void setFirstRun(const bool firstRun) const; bool clicklessSpacePan(bool defaultValue = false) const; void setClicklessSpacePan(const bool toggle) const; int horizontalSplitLines(bool defaultValue = false) const; void setHorizontalSplitLines(const int numberLines) const; int verticalSplitLines(bool defaultValue = false) const; void setVerticalSplitLines(const int numberLines) const; bool hideDockersFullscreen(bool defaultValue = false) const; void setHideDockersFullscreen(const bool value) const; bool showDockerTitleBars(bool defaultValue = false) const; void setShowDockerTitleBars(const bool value) const; bool showDockers(bool defaultValue = false) const; void setShowDockers(const bool value) const; bool showStatusBar(bool defaultValue = false) const; void setShowStatusBar(const bool value) const; bool hideMenuFullscreen(bool defaultValue = false) const; void setHideMenuFullscreen(const bool value) const; bool hideScrollbarsFullscreen(bool defaultValue = false) const; void setHideScrollbarsFullscreen(const bool value) const; bool hideStatusbarFullscreen(bool defaultValue = false) const; void setHideStatusbarFullscreen(const bool value) const; bool hideTitlebarFullscreen(bool defaultValue = false) const; void setHideTitlebarFullscreen(const bool value) const; bool hideToolbarFullscreen(bool defaultValue = false) const; void setHideToolbarFullscreen(const bool value) const; bool fullscreenMode(bool defaultValue = false) const; void setFullscreenMode(const bool value) const; QStringList favoriteCompositeOps(bool defaultValue = false) const; void setFavoriteCompositeOps(const QStringList& compositeOps) const; QString exportConfiguration(const QString &filterId, bool defaultValue = false) const; void setExportConfiguration(const QString &filterId, KisPropertiesConfigurationSP properties) const; bool useOcio(bool defaultValue = false) const; void setUseOcio(bool useOCIO) const; int favoritePresets(bool defaultValue = false) const; void setFavoritePresets(const int value); bool levelOfDetailEnabled(bool defaultValue = false) const; void setLevelOfDetailEnabled(bool value); enum OcioColorManagementMode { INTERNAL = 0, OCIO_CONFIG, OCIO_ENVIRONMENT }; OcioColorManagementMode ocioColorManagementMode(bool defaultValue = false) const; void setOcioColorManagementMode(OcioColorManagementMode mode) const; QString ocioConfigurationPath(bool defaultValue = false) const; void setOcioConfigurationPath(const QString &path) const; QString ocioLutPath(bool defaultValue = false) const; void setOcioLutPath(const QString &path) const; int ocioLutEdgeSize(bool defaultValue = false) const; void setOcioLutEdgeSize(int value); bool ocioLockColorVisualRepresentation(bool defaultValue = false) const; void setOcioLockColorVisualRepresentation(bool value); bool useSystemMonitorProfile(bool defaultValue = false) const; void setUseSystemMonitorProfile(bool _useSystemMonitorProfile) const; QString defaultPalette(bool defaultValue = false) const; void setDefaultPalette(const QString& name) const; QString toolbarSlider(int sliderNumber, bool defaultValue = false) const; void setToolbarSlider(int sliderNumber, const QString &slider); bool sliderLabels(bool defaultValue = false) const; void setSliderLabels(bool enabled); QString currentInputProfile(bool defaultValue = false) const; void setCurrentInputProfile(const QString& name); bool presetStripVisible(bool defaultValue = false) const; void setPresetStripVisible(bool visible); bool scratchpadVisible(bool defaultValue = false) const; void setScratchpadVisible(bool visible); bool showSingleChannelAsColor(bool defaultValue = false) const; void setShowSingleChannelAsColor(bool asColor); bool hidePopups(bool defaultValue = false) const; void setHidePopups(bool hidepopups); int numDefaultLayers(bool defaultValue = false) const; void setNumDefaultLayers(int num); quint8 defaultBackgroundOpacity(bool defaultValue = false) const; void setDefaultBackgroundOpacity(quint8 value); QColor defaultBackgroundColor(bool defaultValue = false) const; void setDefaultBackgroundColor(QColor value); enum BackgroundStyle { LAYER = 0, PROJECTION = 1 }; BackgroundStyle defaultBackgroundStyle(bool defaultValue = false) const; void setDefaultBackgroundStyle(BackgroundStyle value); int lineSmoothingType(bool defaultValue = false) const; void setLineSmoothingType(int value); qreal lineSmoothingDistance(bool defaultValue = false) const; void setLineSmoothingDistance(qreal value); qreal lineSmoothingTailAggressiveness(bool defaultValue = false) const; void setLineSmoothingTailAggressiveness(qreal value); bool lineSmoothingSmoothPressure(bool defaultValue = false) const; void setLineSmoothingSmoothPressure(bool value); bool lineSmoothingScalableDistance(bool defaultValue = false) const; void setLineSmoothingScalableDistance(bool value); qreal lineSmoothingDelayDistance(bool defaultValue = false) const; void setLineSmoothingDelayDistance(qreal value); bool lineSmoothingUseDelayDistance(bool defaultValue = false) const; void setLineSmoothingUseDelayDistance(bool value); bool lineSmoothingFinishStabilizedCurve(bool defaultValue = false) const; void setLineSmoothingFinishStabilizedCurve(bool value); bool lineSmoothingStabilizeSensors(bool defaultValue = false) const; void setLineSmoothingStabilizeSensors(bool value); int paletteDockerPaletteViewSectionSize(bool defaultValue = false) const; void setPaletteDockerPaletteViewSectionSize(int value) const; int tabletEventsDelay(bool defaultValue = false) const; void setTabletEventsDelay(int value); bool testingAcceptCompressedTabletEvents(bool defaultValue = false) const; void setTestingAcceptCompressedTabletEvents(bool value); bool shouldEatDriverShortcuts(bool defaultValue = false) const; bool testingCompressBrushEvents(bool defaultValue = false) const; void setTestingCompressBrushEvents(bool value); const KoColorSpace* customColorSelectorColorSpace(bool defaultValue = false) const; void setCustomColorSelectorColorSpace(const KoColorSpace *cs); bool useDirtyPresets(bool defaultValue = false) const; void setUseDirtyPresets(bool value); bool useEraserBrushSize(bool defaultValue = false) const; void setUseEraserBrushSize(bool value); bool useEraserBrushOpacity(bool defaultValue = false) const; void setUseEraserBrushOpacity(bool value); QColor getMDIBackgroundColor(bool defaultValue = false) const; void setMDIBackgroundColor(const QColor & v) const; QString getMDIBackgroundImage(bool defaultValue = false) const; void setMDIBackgroundImage(const QString & fileName) const; bool useVerboseOpenGLDebugOutput(bool defaultValue = false) const; int workaroundX11SmoothPressureSteps(bool defaultValue = false) const; bool showCanvasMessages(bool defaultValue = false) const; void setShowCanvasMessages(bool show); bool compressKra(bool defaultValue = false) const; void setCompressKra(bool compress); bool toolOptionsInDocker(bool defaultValue = false) const; void setToolOptionsInDocker(bool inDocker); void setEnableOpenGLDebugging(bool value) const; bool enableOpenGLDebugging(bool defaultValue = false) const; void setEnableAmdVectorizationWorkaround(bool value); bool enableAmdVectorizationWorkaround(bool defaultValue = false) const; bool animationDropFrames(bool defaultValue = false) const; void setAnimationDropFrames(bool value); - int scribbingUpdatesDelay(bool defaultValue = false) const; - void setScribbingUpdatesDelay(int value); + int scrubbingUpdatesDelay(bool defaultValue = false) const; + void setScrubbingUpdatesDelay(int value); + + int scrubbingAudioUpdatesDelay(bool defaultValue = false) const; + void setScrubbingAudioUpdatesDelay(int value); + + int audioOffsetTolerance(bool defaultValue = false) const; + void setAudioOffsetTolerance(int value); bool switchSelectionCtrlAlt(bool defaultValue = false) const; void setSwitchSelectionCtrlAlt(bool value); bool convertToImageColorspaceOnImport(bool defaultValue = false) const; void setConvertToImageColorspaceOnImport(bool value); int stabilizerSampleSize(bool defaultValue = false) const; void setStabilizerSampleSize(int value); int stabilizerDelayedPaintInterval(bool defaultValue = false) const; void setStabilizerDelayedPaintInterval(int value); QString customFFMpegPath(bool defaultValue = false) const; void setCustomFFMpegPath(const QString &value) const; bool showBrushHud(bool defaultValue = false) const; void setShowBrushHud(bool value); QString brushHudSetting(bool defaultValue = false) const; void setBrushHudSetting(const QString &value) const; template void writeEntry(const QString& name, const T& value) { m_cfg.writeEntry(name, value); } template void writeList(const QString& name, const QList& value) { m_cfg.writeEntry(name, value); } template T readEntry(const QString& name, const T& defaultValue=T()) { return m_cfg.readEntry(name, defaultValue); } template QList readList(const QString& name, const QList& defaultValue=QList()) { return m_cfg.readEntry(name, defaultValue); } /// get the profile the color managment system has stored for the given screen static const KoColorProfile* getScreenProfile(int screen); private: KisConfig(const KisConfig&); KisConfig& operator=(const KisConfig&) const; private: mutable KConfigGroup m_cfg; }; #endif // KIS_CONFIG_H_ diff --git a/libs/widgets/kis_file_name_requester.cpp b/libs/widgets/kis_file_name_requester.cpp index b957c54f70..db21da770e 100644 --- a/libs/widgets/kis_file_name_requester.cpp +++ b/libs/widgets/kis_file_name_requester.cpp @@ -1,99 +1,103 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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. */ #include "kis_file_name_requester.h" #include "ui_wdg_file_name_requester.h" #include #include #include "KoIcon.h" KisFileNameRequester::KisFileNameRequester(QWidget *parent) : QWidget(parent) , m_ui(new Ui::WdgFileNameRequester) , m_mode(KoFileDialog::OpenFile) { m_ui->setupUi(this); m_ui->btnSelectFile->setIcon(kisIcon("folder")); connect(m_ui->btnSelectFile, SIGNAL(clicked()), SLOT(slotSelectFile())); connect(m_ui->txtFileName, SIGNAL(textChanged(const QString&)), SIGNAL(textChanged(const QString&))); } KisFileNameRequester::~KisFileNameRequester() { } void KisFileNameRequester::setStartDir(const QString &path) { m_basePath = path; } void KisFileNameRequester::setFileName(const QString &path) { m_ui->txtFileName->setText(path); emit fileSelected(path); } QString KisFileNameRequester::fileName() const { return m_ui->txtFileName->text(); } void KisFileNameRequester::setMode(KoFileDialog::DialogType mode) { m_mode = mode; } KoFileDialog::DialogType KisFileNameRequester::mode() const { return m_mode; } void KisFileNameRequester::setMimeTypeFilters(const QStringList &filterList, QString defaultFilter) { m_mime_filter_list = filterList; m_mime_default_filter = defaultFilter; } void KisFileNameRequester::slotSelectFile() { KoFileDialog dialog(this, m_mode, "OpenDocument"); if (m_mode == KoFileDialog::OpenFile) { dialog.setCaption(i18n("Select a file to load...")); } else if (m_mode == KoFileDialog::OpenDirectory) { dialog.setCaption(i18n("Select a directory to load...")); } if (m_basePath.isEmpty()) { dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); } else { dialog.setDefaultDir(m_basePath); } Q_ASSERT(!m_mime_filter_list.isEmpty()); dialog.setMimeTypeFilters(m_mime_filter_list, m_mime_default_filter); - setFileName(dialog.filename()); + QString newFileName = dialog.filename(); + + if (!newFileName.isEmpty()) { + setFileName(newFileName); + } } diff --git a/packaging/language_tarball/create-l10n-symlink b/packaging/language_tarball/create-l10n-symlink deleted file mode 100755 index a0b74167a7..0000000000 --- a/packaging/language_tarball/create-l10n-symlink +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh - -if [ -z $1 ]; then - echo "Usage: $0 NEW_I18N_FILENAME" - exit 1 -fi - - -CURRENT_FILE=~/web/build/krita-3.0-l10n-win-current.tar.gz -NEW_FILE=~/web/build/$1 - -if [ ! -f $NEW_FILE ]; then - echo "File not exists: $NEW_FILE" - exit 2 -fi - -if [ -e $CURRENT_FILE ]; then - rm $CURRENT_FILE -fi - -ln -s $NEW_FILE $CURRENT_FILE - diff --git a/packaging/language_tarball/l10n-gen b/packaging/language_tarball/l10n-gen deleted file mode 100755 index 71e98b8cd7..0000000000 --- a/packaging/language_tarball/l10n-gen +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import os -import optparse -import subprocess -import re -import requests -import fnmatch -import shutil -import tarfile -import datetime -import subprocess -from tempfile import TemporaryDirectory - -class OptionParser(optparse.OptionParser): - def __init__(self): - super().__init__(self.usage) - - self.add_option('--ppa-testing', action="store_true", dest='ppa_testing') - self.add_option('--ppa-2.9', action="store_true", dest='ppa_2_9') - self.add_option('--win-2.9', action="store_true", dest='win_2_9') - self.add_option('--win-3.0', action="store_true", dest='win_3_0') - - def parse(self): - (self.options, self.args) = self.parse_args() - - usage = '' - - -def get(filename, target, branch, base = 'svn://anonsvn.kde.org/home/kde'): - print('GET: ' + os.path.join(target, filename), end='') - url = os.path.join(base, branch, filename) - - - with TemporaryDirectory() as temp_dir: - error_code = subprocess.call(['svn', 'export', url, temp_dir], - stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - - if (not error_code): - target_path = os.path.join(target, os.path.dirname(filename)) - target_filename = os.path.join(target, filename) - os.makedirs(target_path, exist_ok=True) - shutil.move(os.path.join(temp_dir, os.path.basename(filename)), target_filename) - print(' [OK]') - else: - print(' [NA]') - #import pdb; pdb.set_trace() - return False - - return True - -def get_translations(target, branch = 'branches/stable/l10n-kde4', build = False): - shutil.rmtree(target) - - if get('subdirs', target, branch): - with open(os.path.join(target, 'subdirs'), 'r') as subdirs_file: - subdirs = subdirs_file.read() - - if target.endswith('win'): - all_languages = 'https://quickgit.kde.org/?o=plain&a=blob&p=kdelibs.git&f=kdecore/localization/all_languages.desktop' - print('GET: ' + all_languages, end='') - req = requests.get(all_languages) - if (req.status_code == 200): - os.makedirs(os.path.join(target, 'locale'), exist_ok=True) - with open(os.path.join(target, 'locale', 'all_languages'), 'wb') as file_all_languages: - file_all_languages.write(req.content) - print(' [OK]') - else: - print(' [NA]') - return False - - languages = [] - if build and target.endswith('win'): - os.makedirs(os.path.join(target, 'locale'), exist_ok=True) - - for lang in subdirs.split('\n'): - if lang in ['', 'x-test']: - continue - - if not get(lang + '/messages/calligra/krita.po', target, branch): - continue - - if build: - if not msgfmt(os.path.join(target, lang, 'messages/calligra/krita.po'), target, lang): - return False - - ## these files are prepared by scripty - # - #po_files = [ 'calligra/desktop_calligra_krita.po', - # 'calligra/krita.appdata.po', - #] - - po_files = [] - - if target.endswith('win'): - ## these files are now installed by the 'ext_*' scripts - # po_files.append('frameworks/desktop_frameworks_kconfig.po') - # po_files.append('frameworks/kconfig5_qt.po') - # po_files.append('frameworks/kwidgetsaddons5_qt.po') - # po_files.append('frameworks/kcompletion5_qt.po') - # po_files.append('frameworks/kcoreaddons5_qt.po') - # po_files.append('frameworks/kitemviews5_qt.po') - # po_files.append('frameworks/kwindowsystem5_qt.po') - # po_files.append('frameworks/kwidgetsaddons5_qt.po') - - if get(os.path.join(lang, 'messages', 'entry.desktop'), target, branch) and build: - os.rename(os.path.join(target, lang, 'messages', 'entry.desktop'), os.path.join(target, 'locale', lang, 'entry.desktop')) - - for po_file in po_files: - if get(os.path.join(lang, 'messages', po_file), target, branch) and build: - if not msgfmt(os.path.join(target, lang, 'messages', po_file), target, lang): - return False - - if build: - shutil.rmtree(os.path.join(target, lang)) - - languages.append(lang) - - if target.endswith('win'): - os.remove(os.path.join(target, 'subdirs')) - if build: - now = datetime.datetime.now() - packagename = '{0}-{1}_{2}_{3}.tar.gz'.format(target, now.year, now.month, now.day) - with tarfile.open(packagename, "w:gz") as tar: - tar.add(os.path.join(target, 'locale'), arcname='locale') - else: - with open(os.path.join(target, 'subdirs'), 'w') as subdirs_file: - subdirs_file.write('\n'.join(languages)) - - else: - print('\nCouldn\'t fetch subdirs. Aborting') - return False - - return True - -def msgfmt(po_filename, target, lang): - mo_filename = os.path.splitext(os.path.basename(po_filename))[0] + '.mo' - mo_dirname = os.path.join(target, 'locale', lang, 'LC_MESSAGES') - print('BLD: ' + os.path.join(mo_dirname, mo_filename), end = '') - os.makedirs(mo_dirname, exist_ok=True) - ret = subprocess.call(['msgfmt','-o', os.path.join(mo_dirname, mo_filename), po_filename]) - if (ret == 0): - os.remove(po_filename) - print(' [OK]') - else: - print(' [FAILED]') - return False - return True - -if __name__ == '__main__': - parser = OptionParser() - parser.parse() - - if len(sys.argv) == 1: - parser.print_help() - sys.exit(0) - - - if parser.options.ppa_testing: - get_translations('krita-testing-l10n') - - elif parser.options.ppa_2_9: - get_translations('krita-2.9-l10n') - - elif parser.options.win_2_9: - get_translations('krita-2.9-l10n-win', build=True) - - elif parser.options.win_3_0: - get_translations('krita-3.0-l10n-win', branch = 'trunk/l10n-kf5', build = True) - - else: - parser.print_help() diff --git a/packaging/windows/build_krita.bat b/packaging/windows/build_krita.bat new file mode 100644 index 0000000000..c7285a5176 --- /dev/null +++ b/packaging/windows/build_krita.bat @@ -0,0 +1,20 @@ +set DLLTOOL_EXE=c:\TDM-GCC-64\x86_64-w64-mingw32\bin\dlltool.exe +set MINGW_GCC_BIN=c:\TDM-GCC-64\bin\ +set BUILDROOT=c:\dev +set BUILDDIR_INSTALL=%BUILDROOT%\i +set PATH=c:\dev\i\bin;c:\dev\i\lib;c:\python35;%MINGW_GCC_BIN%;%PATH% +cd c:\dev\build2 + +set /P pkg_root=Insert krita source location: + +if [%pkg_root%] == [] ( +echo You entered an empty name! +pause +exit /B +) + +cmake ..\%pkg_root% -G "MinGW Makefiles" -DBoost_DEBUG=OFF -DBOOST_INCLUDEDIR=c:\dev\i\include -DBOOST_DEBUG=ON -DBOOST_ROOT=c:\dev\i -DBOOST_LIBRARYDIR=c:\dev\i\lib -DCMAKE_INSTALL_PREFIX=c:\dev\i -DCMAKE_PREFIX_PATH=c:\dev\i -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_TESTING=OFF -DKDE4_BUILD_TESTS=OFF -DHAVE_MEMORY_LEAK_TRACKER=OFF -DPACKAGERS_BUILD=ON -Wno-dev -DDEFINE_NO_DEPRECATED=1 -DFOUNDATION_BUILD=1 + +mingw32-make -j4 install + +cd .. diff --git a/packaging/windows/package_2.cmd b/packaging/windows/package_2.cmd index a1536b5501..5cbf0da2f4 100644 --- a/packaging/windows/package_2.cmd +++ b/packaging/windows/package_2.cmd @@ -1,331 +1,331 @@ @echo off :: This batch script is meant to prepare a Krita package folder to be zipped or :: to be a base for the installer. :: :: Just drop it next to the "i" install folder where the dependencies and Krita :: binaries are. :: :: Also copy filelist_bin_dll.txt and filelist_lib_dll.txt if you want more :: fine-grained DLL dependencies copying. :: :: You may want to review the following parameters. :: :: Configuration parameters: :: :: MINGW_GCC_BIN: Path to the mingw-w64/bin dir :: SEVENZIP_EXE: Path to 7-Zip executable (either 7z.exe or 7zG.exe) :: BUILDDIR_SRC: Path to krita source dir (for package shortcut) :: BUILDDIR_INSTALL: Path to INSTALL prefix of the build :: :: Note that paths should only contain alphanumeric, _ and -, except for the :: path to 7-Zip, which is fine if quoted. :: set MINGW_GCC_BIN=C:\TDM-GCC-64\bin\ set SEVENZIP_EXE="C:\Program Files\7-Zip\7z.exe" rem set BUILDDIR_SRC=%CD%\krita set BUILDDIR_SRC=%CD%\krita set BUILDDIR_INSTALL=%CD%\i :: -------- set PATH=%MINGW_GCC_BIN%;%PATH% echo Krita Windows packaging script echo. echo Configurations: echo MINGW_GCC_BIN: %MINGW_GCC_BIN% echo SEVENZIP_EXE: %SEVENZIP_EXE% echo BUILDDIR_SRC: %BUILDDIR_SRC% echo BUILDDIR_INSTALL: %BUILDDIR_INSTALL% echo. if "%1" == "" ( set PAUSE=pause ) else ( set "PAUSE= " ) :: Simple checking if not exist %BUILDDIR_INSTALL% ( echo ERROR: Cannot find the install folder! %PAUSE% exit /B 1 ) if not "%BUILDDIR_INSTALL: =%" == "%BUILDDIR_INSTALL%" ( echo ERROR: Install path contains space, which will not work properly! %PAUSE% exit /B 1 ) :: Decide package name to use if "%1" == "" ( echo Please input a package name. It will be used for the output directory, package echo file name and as the top-level directory of the package. echo Alternatively, you can pass it as the first argument to this script. echo You should only use alphanumeric, _ and - echo. set /P pkg_name=Package name^> setlocal if "!pkg_name!" == "" ( echo ERROR: You cannot choose an empty name! %PAUSE% exit /B 1 ) endlocal ) else ( set pkg_name=%1 ) echo Package name is "%pkg_name%" set pkg_root=%CD%\%pkg_name% echo Packaging dir is %pkg_root% echo. if exist %pkg_root% ( echo ERROR: Packaging dir already exists! Please remove or rename it first. %PAUSE% exit /B 1 ) echo. echo Trying to guess GCC version... g++ --version > NUL if errorlevel 1 ( echo ERROR: g++ is not working. %PAUSE% exit /B 1 ) for /f "delims=" %%a in ('g++ --version ^| find "g++"') do set GCC_VERSION_LINE=%%a echo -- %GCC_VERSION_LINE% if "%GCC_VERSION_LINE:tdm64=%" == "%GCC_VERSION_LINE%" ( echo Compiler doesn't look like TDM64-GCC, assuming simple mingw-w64 set IS_TDM= ) else ( echo Compiler looks like TDM64-GCC set IS_TDM=1 ) echo. echo Trying to guess target architecture... objdump --version > NUL if errorlevel 1 ( echo ERROR: objdump is not working. %PAUSE% exit /B 1 ) for /f "delims=, tokens=1" %%a in ('objdump -f %BUILDDIR_INSTALL%\bin\krita.exe ^| find "architecture"') do set TARGET_ARCH_LINE=%%a echo -- %TARGET_ARCH_LINE% if "%TARGET_ARCH_LINE:x86-64=%" == "%TARGET_ARCH_LINE%" ( echo Target looks like x86 set IS_X64= ) else ( echo Target looks like x64 set IS_x64=1 ) echo. echo Testing for objcopy... objcopy --version > NUL if errorlevel 1 ( echo ERROR: objcopy is not working. %PAUSE% exit /B 1 ) echo. echo Testing for strip... strip --version > NUL if errorlevel 1 ( echo ERROR: strip is not working. %PAUSE% exit /B 1 ) echo. echo Creating base directories... mkdir %pkg_root% && ^ mkdir %pkg_root%\bin && ^ mkdir %pkg_root%\lib && ^ mkdir %pkg_root%\share if errorlevel 1 ( echo ERROR: Cannot create packaging dir tree! %PAUSE% exit /B 1 ) echo. echo Copying GCC libraries... if x%IS_TDM% == x ( if x%is_x64% == x ( :: mingw-w64 x86 set "STDLIBS=gcc_s_dw2-1 gomp-1 stdc++-6 winpthread-1" ) else ( :: mingw-w64 x64 set "STDLIBS=gcc_s_seh-1 gomp-1 stdc++-6 winpthread-1" ) ) else ( if x%is_x64% == x ( :: TDM-GCC x86 set "STDLIBS=gomp-1" ) else ( :: TDM-GCC x64 set "STDLIBS=gomp_64-1" ) ) for %%L in (%STDLIBS%) do copy "%MINGW_GCC_BIN%\lib%%L.dll" %pkg_root%\bin echo. echo Copying files... :: krita.exe copy %BUILDDIR_INSTALL%\bin\krita.exe %pkg_root%\bin :: DLLs from bin/ if exist filelist_bin_dll.txt ( for /f %%F in (filelist_bin_dll.txt) do copy %BUILDDIR_INSTALL%\bin\%%F %pkg_root%\bin ) else ( echo INFO: filelist_bin_dll.txt not found, copying all DLLs except Qt5 from bin/ setlocal enableextensions enabledelayedexpansion for /f "delims=" %%F in ('dir /b "%BUILDDIR_INSTALL%\bin\*.dll"') do ( set file=%%F set file=!file:~0,3! if not x!file! == xQt5 copy %BUILDDIR_INSTALL%\bin\%%F %pkg_root%\bin ) endlocal ) :: symsrv.yes for Dr. Mingw copy %BUILDDIR_INSTALL%\bin\symsrv.yes %pkg_root%\bin :: DLLs from lib/ if exist filelist_lib_dll.txt ( for /f %%F in (filelist_lib_dll.txt) do copy %BUILDDIR_INSTALL%\lib\%%F %pkg_root%\bin ) else ( echo INFO: filelist_lib_dll.txt not found, copying all DLLs from lib/ copy %BUILDDIR_INSTALL%\lib\*.dll %pkg_root%\bin ) :: Boost, there might be more than one leftover but we can't really do much copy %BUILDDIR_INSTALL%\bin\libboost_system-*.dll %pkg_root%\bin :: KF5 plugins may be placed at different locations depending on how Qt is built xcopy /S /Y /I %BUILDDIR_INSTALL%\lib\plugins\imageformats %pkg_root%\bin\imageformats xcopy /S /Y /I %BUILDDIR_INSTALL%\plugins\imageformats %pkg_root%\bin\imageformats xcopy /S /Y /I %BUILDDIR_INSTALL%\lib\plugins\kf5 %pkg_root%\bin\kf5 xcopy /S /Y /I %BUILDDIR_INSTALL%\plugins\kf5 %pkg_root%\bin\kf5 :: Qt Translations :: it seems that windeployqt does these, but only *some* of these??? mkdir %pkg_root%\bin\translations setlocal enableextensions enabledelayedexpansion for /f "delims=" %%F in ('dir /b "%BUILDDIR_INSTALL%\translations\qt_*.qm"') do ( :: Exclude qt_help_*.qm set temp=%%F set temp2=!temp:_help=! if x!temp2! == x!temp! copy %BUILDDIR_INSTALL%\translations\!temp! %pkg_root%\bin\translations\!temp! ) endlocal :: Krita plugins xcopy /Y %BUILDDIR_INSTALL%\lib\kritaplugins\*.dll %pkg_root%\lib\kritaplugins\ :: Share xcopy /Y /S /I %BUILDDIR_INSTALL%\share\color %pkg_root%\share\color xcopy /Y /S /I %BUILDDIR_INSTALL%\share\color-schemes %pkg_root%\share\color-schemes xcopy /Y /S /I %BUILDDIR_INSTALL%\share\icons %pkg_root%\share\icons xcopy /Y /S /I %BUILDDIR_INSTALL%\share\kf5 %pkg_root%\share\kf5 xcopy /Y /S /I %BUILDDIR_INSTALL%\share\krita %pkg_root%\share\krita xcopy /Y /S /I %BUILDDIR_INSTALL%\share\kritaplugins %pkg_root%\share\kritaplugins xcopy /Y /S /I %BUILDDIR_INSTALL%\share\mime %pkg_root%\share\mime :: Not useful on Windows it seems rem xcopy /Y /S /I %BUILDDIR_INSTALL%\share\appdata %pkg_root%\share\appdata rem xcopy /Y /S /I %BUILDDIR_INSTALL%\share\applications %pkg_root%\share\applications rem xcopy /Y /S /I %BUILDDIR_INSTALL%\share\doc %pkg_root%\share\doc rem xcopy /Y /S /I %BUILDDIR_INSTALL%\share\kservices5 %pkg_root%\share\kservices5 rem xcopy /Y /S /I %BUILDDIR_INSTALL%\share\man %pkg_root%\share\man rem xcopy /Y /S /I %BUILDDIR_INSTALL%\share\ocio %pkg_root%\share\ocio :: Copy locale to bin xcopy /Y /S /I %BUILDDIR_INSTALL%\share\locale %pkg_root%\bin\locale :: Copy shortcut link from source (can't create it dynamically) copy %BUILDDIR_SRC%\packaging\windows\krita.lnk %pkg_root% :: windeployqt -%BUILDDIR_INSTALL%\bin\windeployqt.exe --release -concurrent -network -printsupport -svg -xml %pkg_root%\bin\krita.exe +%BUILDDIR_INSTALL%\bin\windeployqt.exe --release -concurrent -network -printsupport -svg -xml -multimedia %pkg_root%\bin\krita.exe :: For chopping relative path :: 512 should be enough :: n+2 to also account for a trailing backslash setlocal enableextensions enabledelayedexpansion for /L %%n in (1 1 512) do if "!pkg_root:~%%n,1!" neq "" set /a "pkg_root_len_plus_one=%%n+2" endlocal & set pkg_root_len_plus_one=%pkg_root_len_plus_one% echo. echo Splitting debug info from binaries... call :split-debug "%pkg_root%\bin\krita.exe" bin\krita.exe setlocal enableextensions enabledelayedexpansion :: Find all DLLs for /r "%pkg_root%" %%F in (*.dll) do ( set relpath=%%F set relpath=!relpath:~%pkg_root_len_plus_one%! call :split-debug "%%F" !relpath! ) endlocal echo. echo Packaging debug info... :: (note that the top-level package dir is not included) %SEVENZIP_EXE% a -tzip %pkg_name%-dbg.zip -r %pkg_root%\*.debug echo -------- echo. echo Packaging stripped binaries... %SEVENZIP_EXE% a -tzip %pkg_name%.zip %pkg_root%\ -xr!.debug echo -------- echo. echo. echo Krita packaged as %pkg_name%.zip if exist %pkg_name%-dbg.zip echo Debug info packaged as %pkg_name%-dbg.zip echo Packaging dir is %pkg_root% echo NOTE: Do not create installer with packaging dir unless you removed all debug echo info from it! echo. echo Please remember to actually test the package before releasing it. echo. %PAUSE% exit /b :split-debug echo Splitting debug info of %2 objcopy --only-keep-debug %~1 %~1.debug if ERRORLEVEL 1 exit /b %ERRORLEVEL% :: If the debug file is small enough then consider there being no debug info. :: Discard these files since they somehow make gdb crash. call :getfilesize %~1.debug if /i %getfilesize_retval% LEQ 2048 ( echo Discarding %2.debug del %~1.debug exit /b 0 ) if not exist %~dp1.debug mkdir %~dp1.debug move %~1.debug %~dp1.debug\ > NUL strip %~1 :: Add debuglink :: FIXME: There is a problem with gdb that cause it to output this warning :: FIXME: "warning: section .gnu_debuglink not found in xxx.debug" :: FIXME: I tried adding a link to itself but this kills drmingw :( objcopy --add-gnu-debuglink="%~dp1.debug\%~nx1.debug" %~1 exit /b %ERRORLEVEL% :getfilesize set getfilesize_retval=%~z1 goto :eof :relpath_dirpath call :relpath_dirpath_internal "" "%~1" goto :eof :relpath_dirpath_internal for /f "tokens=1* delims=\" %%a in ("%~2") do ( :: If part 2 is empty, it means part 1 is probably the file name if x%%b==x ( set relpath_dirpath_retval=%~1 ) else ( call :relpath_dirpath_internal "%~1%%a\" %%b ) ) goto :eof diff --git a/plugins/dockers/animation/kis_time_based_item_model.cpp b/plugins/dockers/animation/kis_time_based_item_model.cpp index 99ff688ca1..74f133b854 100644 --- a/plugins/dockers/animation/kis_time_based_item_model.cpp +++ b/plugins/dockers/animation/kis_time_based_item_model.cpp @@ -1,435 +1,434 @@ /* * Copyright (c) 2016 Jouni Pentikäinen * * 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. */ #include "kis_time_based_item_model.h" #include #include #include "kis_animation_frame_cache.h" #include "kis_animation_player.h" #include "kis_signal_compressor_with_param.h" #include "kis_image.h" #include "kis_image_animation_interface.h" #include "kis_time_range.h" #include "kis_animation_utils.h" #include "kis_keyframe_channel.h" struct KisTimeBasedItemModel::Private { Private() : animationPlayer(0) , numFramesOverride(0) , activeFrameIndex(0) , scrubInProgress(false) , scrubStartFrame(-1) {} KisImageWSP image; KisAnimationFrameCacheWSP framesCache; QPointer animationPlayer; QVector cachedFrames; int numFramesOverride; int activeFrameIndex; bool scrubInProgress; int scrubStartFrame; QScopedPointer > scrubbingCompressor; - int baseNumFrames() const { if (image.isNull()) return 0; KisImageAnimationInterface *i = image->animationInterface(); if (!i) return 1; return i->totalLength(); } int effectiveNumFrames() const { if (image.isNull()) return 0; return qMax(baseNumFrames(), numFramesOverride); } int framesPerSecond() { return image->animationInterface()->framerate(); } }; KisTimeBasedItemModel::KisTimeBasedItemModel(QObject *parent) : QAbstractTableModel(parent) , m_d(new Private()) { KisConfig cfg; using namespace std::placeholders; std::function callback( std::bind(&KisTimeBasedItemModel::slotInternalScrubPreviewRequested, this, _1)); m_d->scrubbingCompressor.reset( - new KisSignalCompressorWithParam(cfg.scribbingUpdatesDelay(), callback, KisSignalCompressor::FIRST_ACTIVE)); + new KisSignalCompressorWithParam(cfg.scrubbingUpdatesDelay(), callback, KisSignalCompressor::FIRST_ACTIVE)); } KisTimeBasedItemModel::~KisTimeBasedItemModel() {} void KisTimeBasedItemModel::setImage(KisImageWSP image) { KisImageWSP oldImage = m_d->image; m_d->image = image; if (image) { KisImageAnimationInterface *ai = image->animationInterface(); slotCurrentTimeChanged(ai->currentUITime()); connect(ai, SIGNAL(sigFramerateChanged()), SLOT(slotFramerateChanged())); connect(ai, SIGNAL(sigUiTimeChanged(int)), SLOT(slotCurrentTimeChanged(int))); } if (image != oldImage) { reset(); } } void KisTimeBasedItemModel::setFrameCache(KisAnimationFrameCacheSP cache) { if (KisAnimationFrameCacheSP(m_d->framesCache) == cache) return; if (m_d->framesCache) { m_d->framesCache->disconnect(this); } m_d->framesCache = cache; if (m_d->framesCache) { connect(m_d->framesCache, SIGNAL(changed()), SLOT(slotCacheChanged())); } } void KisTimeBasedItemModel::setAnimationPlayer(KisAnimationPlayer *player) { if (m_d->animationPlayer == player) return; if (m_d->animationPlayer) { m_d->animationPlayer->disconnect(this); } m_d->animationPlayer = player; if (m_d->animationPlayer) { connect(m_d->animationPlayer, SIGNAL(sigPlaybackStopped()), SLOT(slotPlaybackStopped())); connect(m_d->animationPlayer, SIGNAL(sigFrameChanged()), SLOT(slotPlaybackFrameChanged())); } } void KisTimeBasedItemModel::setLastVisibleFrame(int time) { const int growThreshold = m_d->effectiveNumFrames() - 3; const int growValue = time + 8; const int shrinkThreshold = m_d->effectiveNumFrames() - 12; const int shrinkValue = qMax(m_d->baseNumFrames(), qMin(growValue, shrinkThreshold)); const bool canShrink = m_d->effectiveNumFrames() > m_d->baseNumFrames(); if (time >= growThreshold) { beginInsertColumns(QModelIndex(), m_d->effectiveNumFrames(), growValue - 1); m_d->numFramesOverride = growValue; endInsertColumns(); } else if (time < shrinkThreshold && canShrink) { beginRemoveColumns(QModelIndex(), shrinkValue, m_d->effectiveNumFrames() - 1); m_d->numFramesOverride = shrinkValue; endRemoveColumns(); } } int KisTimeBasedItemModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_d->effectiveNumFrames(); } QVariant KisTimeBasedItemModel::data(const QModelIndex &index, int role) const { switch (role) { case ActiveFrameRole: { return index.column() == m_d->activeFrameIndex; } } return QVariant(); } bool KisTimeBasedItemModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid()) return false; switch (role) { case ActiveFrameRole: { setHeaderData(index.column(), Qt::Horizontal, value, role); break; } } return false; } QVariant KisTimeBasedItemModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal) { switch (role) { case ActiveFrameRole: return section == m_d->activeFrameIndex; case FrameCachedRole: return m_d->cachedFrames.size() > section ? m_d->cachedFrames[section] : false; case FramesPerSecondRole: return m_d->framesPerSecond(); } } return QVariant(); } bool KisTimeBasedItemModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role) { if (orientation == Qt::Horizontal) { switch (role) { case ActiveFrameRole: if (value.toBool() && section != m_d->activeFrameIndex) { int prevFrame = m_d->activeFrameIndex; m_d->activeFrameIndex = section; scrubTo(m_d->activeFrameIndex, m_d->scrubInProgress); /** * Optimization Hack Alert: * * ideally, we should emit all four signals, but... The * point is this code is used in a tight loop during * playback, so it should run as fast as possible. To tell * the story short, commenting out these three lines makes * playback run 15% faster ;) */ if (m_d->scrubInProgress) { //emit dataChanged(this->index(0, prevFrame), this->index(rowCount() - 1, prevFrame)); emit dataChanged(this->index(0, m_d->activeFrameIndex), this->index(rowCount() - 1, m_d->activeFrameIndex)); //emit headerDataChanged (Qt::Horizontal, prevFrame, prevFrame); //emit headerDataChanged (Qt::Horizontal, m_d->activeFrameIndex, m_d->activeFrameIndex); } else { emit dataChanged(this->index(0, prevFrame), this->index(rowCount() - 1, prevFrame)); emit dataChanged(this->index(0, m_d->activeFrameIndex), this->index(rowCount() - 1, m_d->activeFrameIndex)); emit headerDataChanged (Qt::Horizontal, prevFrame, prevFrame); emit headerDataChanged (Qt::Horizontal, m_d->activeFrameIndex, m_d->activeFrameIndex); } } } } return false; } bool KisTimeBasedItemModel::removeFrames(const QModelIndexList &indexes) { KisAnimationUtils::FrameItemList frameItems; Q_FOREACH (const QModelIndex &index, indexes) { int time = index.column(); QList channels = channelsAt(index); Q_FOREACH(KisKeyframeChannel *channel, channels) { if (channel->keyframeAt(time)) { frameItems << KisAnimationUtils::FrameItem(channel->node(), channel->id(), index.column()); } } } if (frameItems.isEmpty()) return false; KisAnimationUtils::removeKeyframes(m_d->image, frameItems); Q_FOREACH (const QModelIndex &index, indexes) { if (index.isValid()) { emit dataChanged(index, index); } } return true; } bool KisTimeBasedItemModel::offsetFrames(QModelIndexList srcIndexes, const QPoint &offset, bool copyFrames, KUndo2Command *parentCommand) { bool result = false; if (srcIndexes.isEmpty()) return result; if (offset.isNull()) return result; KisAnimationUtils::sortPointsForSafeMove(&srcIndexes, offset); KisAnimationUtils::FrameItemList srcFrameItems; KisAnimationUtils::FrameItemList dstFrameItems; QModelIndexList updateIndexes; Q_FOREACH (const QModelIndex &srcIndex, srcIndexes) { QModelIndex dstIndex = index( srcIndex.row() + offset.y(), srcIndex.column() + offset.x()); KisNodeSP srcNode = nodeAt(srcIndex); KisNodeSP dstNode = nodeAt(dstIndex); if (!srcNode || !dstNode) { return false; } QList channels = channelsAt(srcIndex); Q_FOREACH(KisKeyframeChannel *channel, channels) { if (channel->keyframeAt(srcIndex.column())) { srcFrameItems << KisAnimationUtils::FrameItem(srcNode, channel->id(), srcIndex.column()); dstFrameItems << KisAnimationUtils::FrameItem(dstNode, channel->id(), dstIndex.column()); } } if (!copyFrames) { updateIndexes << srcIndex; } updateIndexes << dstIndex; } result = KisAnimationUtils::moveKeyframes(m_d->image, srcFrameItems, dstFrameItems, copyFrames, parentCommand); Q_FOREACH (const QModelIndex &index, updateIndexes) { emit dataChanged(index, index); } return result; } void KisTimeBasedItemModel::slotInternalScrubPreviewRequested(int time) { if (m_d->animationPlayer && !m_d->animationPlayer->isPlaying()) { m_d->animationPlayer->displayFrame(time); } } void KisTimeBasedItemModel::setScrubState(bool active) { if (!m_d->scrubInProgress && active) { m_d->scrubStartFrame = m_d->activeFrameIndex; m_d->scrubInProgress = true; } if (m_d->scrubInProgress && !active) { m_d->scrubInProgress = false; if (m_d->scrubStartFrame >= 0 && m_d->scrubStartFrame != m_d->activeFrameIndex) { scrubTo(m_d->activeFrameIndex, false); } m_d->scrubStartFrame = -1; } } void KisTimeBasedItemModel::scrubTo(int time, bool preview) { if (m_d->animationPlayer && m_d->animationPlayer->isPlaying()) return; KIS_ASSERT_RECOVER_RETURN(m_d->image); if (preview) { if (m_d->animationPlayer) { m_d->scrubbingCompressor->start(time); } } else { m_d->image->animationInterface()->requestTimeSwitchWithUndo(time); } } void KisTimeBasedItemModel::slotFramerateChanged() { emit headerDataChanged(Qt::Horizontal, 0, columnCount() - 1); } void KisTimeBasedItemModel::slotCurrentTimeChanged(int time) { if (time != m_d->activeFrameIndex) { setHeaderData(time, Qt::Horizontal, true, ActiveFrameRole); } } void KisTimeBasedItemModel::slotCacheChanged() { const int numFrames = columnCount(); m_d->cachedFrames.resize(numFrames); for (int i = 0; i < numFrames; i++) { m_d->cachedFrames[i] = m_d->framesCache->frameStatus(i) == KisAnimationFrameCache::Cached; } emit headerDataChanged(Qt::Horizontal, 0, numFrames); } void KisTimeBasedItemModel::slotPlaybackFrameChanged() { if (!m_d->animationPlayer->isPlaying()) return; setData(index(0, m_d->animationPlayer->currentTime()), true, ActiveFrameRole); } void KisTimeBasedItemModel::slotPlaybackStopped() { setData(index(0, m_d->image->animationInterface()->currentUITime()), true, ActiveFrameRole); } void KisTimeBasedItemModel::setPlaybackRange(const KisTimeRange &range) { if (m_d->image.isNull()) return; KisImageAnimationInterface *i = m_d->image->animationInterface(); i->setPlaybackRange(range); } bool KisTimeBasedItemModel::isPlaybackActive() const { return m_d->animationPlayer && m_d->animationPlayer->isPlaying(); } int KisTimeBasedItemModel::currentTime() const { return m_d->image->animationInterface()->currentUITime(); } KisImageWSP KisTimeBasedItemModel::image() const { return m_d->image; } diff --git a/plugins/dockers/animation/timeline_frames_model.cpp b/plugins/dockers/animation/timeline_frames_model.cpp index dc5d4ef97e..4ba41261fe 100644 --- a/plugins/dockers/animation/timeline_frames_model.cpp +++ b/plugins/dockers/animation/timeline_frames_model.cpp @@ -1,646 +1,685 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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. */ #include "timeline_frames_model.h" #include #include #include #include #include #include "kis_layer.h" #include "kis_config.h" #include "kis_global.h" #include "kis_debug.h" #include "kis_image.h" #include "kis_image_animation_interface.h" #include "kis_undo_adapter.h" #include "kis_node_dummies_graph.h" #include "kis_dummies_facade_base.h" #include "kis_signal_compressor.h" #include "kis_signal_compressor_with_param.h" #include "kis_keyframe_channel.h" #include "kundo2command.h" #include "kis_post_execution_undo_adapter.h" #include #include "kis_animation_utils.h" #include "timeline_color_scheme.h" #include "kis_node_model.h" #include "kis_projection_leaf.h" #include "kis_time_range.h" struct TimelineFramesModel::Private { Private() : activeLayerIndex(0), dummiesFacade(0), needFinishInsertRows(false), needFinishRemoveRows(false), updateTimer(200, KisSignalCompressor::FIRST_INACTIVE), parentOfRemovedNode(0) {} int activeLayerIndex; KisDummiesFacadeBase *dummiesFacade; KisImageWSP image; bool needFinishInsertRows; bool needFinishRemoveRows; QList updateQueue; KisSignalCompressor updateTimer; KisNodeDummy* parentOfRemovedNode; QScopedPointer converter; QScopedPointer nodeInterface; QPersistentModelIndex lastClickedIndex; QVariant layerName(int row) const { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return QVariant(); return dummy->node()->name(); } bool layerEditable(int row) const { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return true; return dummy->node()->visible() && !dummy->node()->userLocked(); } bool frameExists(int row, int column) const { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return false; KisKeyframeChannel *primaryChannel = dummy->node()->getKeyframeChannel(KisKeyframeChannel::Content.id()); return (primaryChannel && primaryChannel->keyframeAt(column)); } bool specialKeyframeExists(int row, int column) { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return false; QList channels = dummy->node()->keyframeChannels(); Q_FOREACH(KisKeyframeChannel *channel, channels) { if (channel->id() != KisKeyframeChannel::Content.id() && channel->keyframeAt(column)) { return true; } } return false; } int frameColorLabel(int row, int column) { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return -1; KisKeyframeChannel *primaryChannel = dummy->node()->getKeyframeChannel(KisKeyframeChannel::Content.id()); if (!primaryChannel) return -1; KisKeyframeSP frame = primaryChannel->keyframeAt(column); if (!frame) return -1; return frame->colorLabel(); } void setFrameColorLabel(int row, int column, int color) { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return; KisKeyframeChannel *primaryChannel = dummy->node()->getKeyframeChannel(KisKeyframeChannel::Content.id()); if (!primaryChannel) return; KisKeyframeSP frame = primaryChannel->keyframeAt(column); if (!frame) return; frame->setColorLabel(color); } QVariant layerProperties(int row) const { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return QVariant(); PropertyList props = dummy->node()->sectionModelProperties(); return QVariant::fromValue(props); } bool setLayerProperties(int row, PropertyList props) { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return false; KisNodePropertyListCommand::setNodePropertiesNoUndo(dummy->node(), image, props); return true; } bool addKeyframe(int row, int column, bool copy) { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return false; KisNodeSP node = dummy->node(); if (!KisAnimationUtils::supportsContentFrames(node)) return false; return KisAnimationUtils::createKeyframeLazy(image, node, KisKeyframeChannel::Content.id(), column, copy); } bool addNewLayer(int row) { Q_UNUSED(row); if (nodeInterface) { KisLayerSP layer = nodeInterface->addPaintLayer(); layer->setUseInTimeline(true); } return true; } bool removeLayer(int row) { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return false; if (nodeInterface) { nodeInterface->removeNode(dummy->node()); } return true; } }; TimelineFramesModel::TimelineFramesModel(QObject *parent) : ModelWithExternalNotifications(parent), m_d(new Private) { connect(&m_d->updateTimer, SIGNAL(timeout()), SLOT(processUpdateQueue())); } TimelineFramesModel::~TimelineFramesModel() { } bool TimelineFramesModel::hasConnectionToCanvas() const { return m_d->dummiesFacade; } void TimelineFramesModel::setNodeManipulationInterface(NodeManipulationInterface *iface) { m_d->nodeInterface.reset(iface); } KisNodeSP TimelineFramesModel::nodeAt(QModelIndex index) const { return m_d->converter->dummyFromRow(index.row())->node(); } QList TimelineFramesModel::channelsAt(QModelIndex index) const { KisNodeDummy *srcDummy = m_d->converter->dummyFromRow(index.row()); return srcDummy->node()->keyframeChannels(); } void TimelineFramesModel::setDummiesFacade(KisDummiesFacadeBase *dummiesFacade, KisImageSP image) { KisDummiesFacadeBase *oldDummiesFacade = m_d->dummiesFacade; if (m_d->dummiesFacade) { + m_d->image->animationInterface()->disconnect(this); m_d->image->disconnect(this); m_d->dummiesFacade->disconnect(this); } m_d->image = image; KisTimeBasedItemModel::setImage(image); m_d->dummiesFacade = dummiesFacade; m_d->converter.reset(); if (m_d->dummiesFacade) { m_d->converter.reset(new TimelineNodeListKeeper(this, m_d->dummiesFacade)); connect(m_d->dummiesFacade, SIGNAL(sigDummyChanged(KisNodeDummy*)), SLOT(slotDummyChanged(KisNodeDummy*))); connect(m_d->image->animationInterface(), SIGNAL(sigFullClipRangeChanged()), SIGNAL(sigInfiniteTimelineUpdateNeeded())); + connect(m_d->image->animationInterface(), + SIGNAL(sigAudioChannelChanged()), SIGNAL(sigAudioChannelChanged())); + connect(m_d->image->animationInterface(), + SIGNAL(sigAudioVolumeChanged()), SIGNAL(sigAudioChannelChanged())); } if (m_d->dummiesFacade != oldDummiesFacade) { reset(); } if (m_d->dummiesFacade) { emit sigInfiniteTimelineUpdateNeeded(); + emit sigAudioChannelChanged(); } } void TimelineFramesModel::slotDummyChanged(KisNodeDummy *dummy) { if (!m_d->updateQueue.contains(dummy)) { m_d->updateQueue.append(dummy); } m_d->updateTimer.start(); } void TimelineFramesModel::processUpdateQueue() { Q_FOREACH (KisNodeDummy *dummy, m_d->updateQueue) { int row = m_d->converter->rowForDummy(dummy); if (row >= 0) { emit headerDataChanged (Qt::Vertical, row, row); emit dataChanged(this->index(row, 0), this->index(row, columnCount() - 1)); } } m_d->updateQueue.clear(); } void TimelineFramesModel::slotCurrentNodeChanged(KisNodeSP node) { if (!node) { m_d->activeLayerIndex = -1; return; } KisNodeDummy *dummy = m_d->dummiesFacade->dummyForNode(node); KIS_ASSERT_RECOVER_RETURN(dummy); m_d->converter->updateActiveDummy(dummy); const int row = m_d->converter->rowForDummy(dummy); if (row < 0) { qWarning() << "WARNING: TimelineFramesModel::slotCurrentNodeChanged: node not found!"; } if (row >= 0 && m_d->activeLayerIndex != row) { setData(index(row, 0), true, ActiveLayerRole); } } int TimelineFramesModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); if(!m_d->dummiesFacade) return 0; return m_d->converter->rowCount(); } QVariant TimelineFramesModel::data(const QModelIndex &index, int role) const { if(!m_d->dummiesFacade) return QVariant(); switch (role) { case ActiveLayerRole: { return index.row() == m_d->activeLayerIndex; } case FrameEditableRole: { return m_d->layerEditable(index.row()); } case FrameExistsRole: { return m_d->frameExists(index.row(), index.column()); } case SpecialKeyframeExists: { return m_d->specialKeyframeExists(index.row(), index.column()); } case ColorLabel: { int label = m_d->frameColorLabel(index.row(), index.column()); return label > 0 ? label : QVariant(); } case Qt::DisplayRole: { return QVariant(); } case Qt::TextAlignmentRole: { return QVariant(Qt::AlignHCenter | Qt::AlignVCenter); } } return ModelWithExternalNotifications::data(index, role); } bool TimelineFramesModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid() || !m_d->dummiesFacade) return false; switch (role) { case ActiveLayerRole: { if (value.toBool() && index.row() != m_d->activeLayerIndex) { int prevLayer = m_d->activeLayerIndex; m_d->activeLayerIndex = index.row(); emit dataChanged(this->index(prevLayer, 0), this->index(prevLayer, columnCount() - 1)); emit dataChanged(this->index(m_d->activeLayerIndex, 0), this->index(m_d->activeLayerIndex, columnCount() - 1)); emit headerDataChanged(Qt::Vertical, prevLayer, prevLayer); emit headerDataChanged(Qt::Vertical, m_d->activeLayerIndex, m_d->activeLayerIndex); KisNodeDummy *dummy = m_d->converter->dummyFromRow(m_d->activeLayerIndex); KIS_ASSERT_RECOVER(dummy) { return true; } emit requestCurrentNodeChanged(dummy->node()); emit sigEnsureRowVisible(m_d->activeLayerIndex); } break; } case ColorLabel: { m_d->setFrameColorLabel(index.row(), index.column(), value.toInt()); } break; } return ModelWithExternalNotifications::setData(index, value, role); } QVariant TimelineFramesModel::headerData(int section, Qt::Orientation orientation, int role) const { if(!m_d->dummiesFacade) return QVariant(); if (orientation == Qt::Vertical) { switch (role) { case ActiveLayerRole: return section == m_d->activeLayerIndex; case Qt::DisplayRole: { QVariant value = headerData(section, orientation, Qt::ToolTipRole); if (!value.isValid()) return value; QString name = value.toString(); const int maxNameSize = 13; if (name.size() > maxNameSize) { name = QString("%1...").arg(name.left(maxNameSize)); } return name; } case Qt::TextColorRole: { // WARNING: this role doesn't work for header views! Use // bold font to show isolated mode instead! return QVariant(); } case Qt::FontRole: { KisNodeDummy *dummy = m_d->converter->dummyFromRow(section); if (!dummy) return QVariant(); KisNodeSP node = dummy->node(); QFont baseFont; if (node->projectionLeaf()->isDroppedMask()) { baseFont.setStrikeOut(true); } else if (m_d->image && m_d->image->isolatedModeRoot() && KisNodeModel::belongsToIsolatedGroup(m_d->image, node, m_d->dummiesFacade)) { baseFont.setBold(true); } return baseFont; } case Qt::ToolTipRole: { return m_d->layerName(section); } case TimelinePropertiesRole: { return QVariant::fromValue(m_d->layerProperties(section)); } case OtherLayersRole: { TimelineNodeListKeeper::OtherLayersList list = m_d->converter->otherLayersList(); return QVariant::fromValue(list); } case LayerUsedInTimelineRole: { KisNodeDummy *dummy = m_d->converter->dummyFromRow(section); if (!dummy) return QVariant(); return dummy->node()->useInTimeline(); } } } return ModelWithExternalNotifications::headerData(section, orientation, role); } bool TimelineFramesModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role) { if (!m_d->dummiesFacade) return false; if (orientation == Qt::Vertical) { switch (role) { case ActiveLayerRole: { setData(index(section, 0), value, role); break; } case TimelinePropertiesRole: { TimelineFramesModel::PropertyList props = value.value(); int result = m_d->setLayerProperties(section, props); emit headerDataChanged (Qt::Vertical, section, section); return result; } case LayerUsedInTimelineRole: { KisNodeDummy *dummy = m_d->converter->dummyFromRow(section); if (!dummy) return false; dummy->node()->setUseInTimeline(value.toBool()); return true; } } } return ModelWithExternalNotifications::setHeaderData(section, orientation, value, role); } Qt::DropActions TimelineFramesModel::supportedDragActions() const { return Qt::MoveAction | Qt::CopyAction; } Qt::DropActions TimelineFramesModel::supportedDropActions() const { return Qt::MoveAction | Qt::CopyAction; } QStringList TimelineFramesModel::mimeTypes() const { QStringList types; types << QLatin1String("application/x-krita-frame"); return types; } void TimelineFramesModel::setLastClickedIndex(const QModelIndex &index) { m_d->lastClickedIndex = index; } QMimeData* TimelineFramesModel::mimeData(const QModelIndexList &indexes) const { QMimeData *data = new QMimeData(); QByteArray encoded; QDataStream stream(&encoded, QIODevice::WriteOnly); const int baseRow = m_d->lastClickedIndex.row(); const int baseColumn = m_d->lastClickedIndex.column(); stream << indexes.size(); stream << baseRow << baseColumn; Q_FOREACH (const QModelIndex &index, indexes) { stream << index.row() - baseRow << index.column() - baseColumn; } data->setData("application/x-krita-frame", encoded); return data; } inline void decodeBaseIndex(QByteArray *encoded, int *row, int *col) { int size_UNUSED = 0; QDataStream stream(encoded, QIODevice::ReadOnly); stream >> size_UNUSED >> *row >> *col; } bool TimelineFramesModel::canDropFrameData(const QMimeData */*data*/, const QModelIndex &index) { if (!index.isValid()) return false; /** * Now we support D&D around any layer, so just return 'true' all * the time. */ return true; } bool TimelineFramesModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { Q_UNUSED(row); Q_UNUSED(column); bool result = false; if ((action != Qt::MoveAction && action != Qt::CopyAction) || !parent.isValid()) return result; const bool copyFrames = action == Qt::CopyAction; QByteArray encoded = data->data("application/x-krita-frame"); QDataStream stream(&encoded, QIODevice::ReadOnly); int size, baseRow, baseColumn; stream >> size >> baseRow >> baseColumn; QModelIndexList srcIndexes; for (int i = 0; i < size; i++) { int relRow, relColumn; stream >> relRow >> relColumn; int srcRow = baseRow + relRow; int srcColumn = baseColumn + relColumn; srcIndexes << index(srcRow, srcColumn); } const QPoint offset(parent.column() - baseColumn, parent.row() - baseRow); return offsetFrames(srcIndexes, offset, copyFrames); } Qt::ItemFlags TimelineFramesModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = ModelWithExternalNotifications::flags(index); if (!index.isValid()) return flags; if (m_d->frameExists(index.row(), index.column()) || m_d->specialKeyframeExists(index.row(), index.column())) { if (data(index, FrameEditableRole).toBool()) { flags |= Qt::ItemIsDragEnabled; } } /** * Basically we should forbid overrides only if we D&D a single frame * and allow it when we D&D multiple frames. But we cannot distinguish * it here... So allow all the time. */ flags |= Qt::ItemIsDropEnabled; return flags; } bool TimelineFramesModel::insertRows(int row, int count, const QModelIndex &parent) { Q_UNUSED(parent); KIS_ASSERT_RECOVER(count == 1) { return false; } if (row < 0 || row > rowCount()) return false; bool result = m_d->addNewLayer(row); return result; } bool TimelineFramesModel::removeRows(int row, int count, const QModelIndex &parent) { Q_UNUSED(parent); KIS_ASSERT_RECOVER(count == 1) { return false; } if (row < 0 || row >= rowCount()) return false; bool result = m_d->removeLayer(row); return result; } bool TimelineFramesModel::insertOtherLayer(int index, int dstRow) { Q_UNUSED(dstRow); TimelineNodeListKeeper::OtherLayersList list = m_d->converter->otherLayersList(); if (index < 0 || index >= list.size()) return false; list[index].dummy->node()->setUseInTimeline(true); dstRow = m_d->converter->rowForDummy(list[index].dummy); setData(this->index(dstRow, 0), true, ActiveLayerRole); return true; } int TimelineFramesModel::activeLayerRow() const { return m_d->activeLayerIndex; } bool TimelineFramesModel::createFrame(const QModelIndex &dstIndex) { if (!dstIndex.isValid()) return false; bool result = m_d->addKeyframe(dstIndex.row(), dstIndex.column(), false); if (result) { emit dataChanged(dstIndex, dstIndex); } return result; } bool TimelineFramesModel::copyFrame(const QModelIndex &dstIndex) { if (!dstIndex.isValid()) return false; bool result = m_d->addKeyframe(dstIndex.row(), dstIndex.column(), true); if (result) { emit dataChanged(dstIndex, dstIndex); } return result; } + +QString TimelineFramesModel::audioChannelFileName() const +{ + return m_d->image ? m_d->image->animationInterface()->audioChannelFileName() : QString(); +} + +void TimelineFramesModel::setAudioChannelFileName(const QString &fileName) +{ + KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->image); + m_d->image->animationInterface()->setAudioChannelFileName(fileName); +} + +bool TimelineFramesModel::isAudioMuted() const +{ + return m_d->image ? m_d->image->animationInterface()->isAudioMuted() : false; +} + +void TimelineFramesModel::setAudioMuted(bool value) +{ + KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->image); + m_d->image->animationInterface()->setAudioMuted(value); +} + +qreal TimelineFramesModel::audioVolume() const +{ + return m_d->image ? m_d->image->animationInterface()->audioVolume() : 0.5; +} + +void TimelineFramesModel::setAudioVolume(qreal value) +{ + KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->image); + m_d->image->animationInterface()->setAudioVolume(value); +} diff --git a/plugins/dockers/animation/timeline_frames_model.h b/plugins/dockers/animation/timeline_frames_model.h index 101326bf3b..59c5c05d49 100644 --- a/plugins/dockers/animation/timeline_frames_model.h +++ b/plugins/dockers/animation/timeline_frames_model.h @@ -1,122 +1,132 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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. */ #ifndef __TIMELINE_FRAMES_MODEL_H #define __TIMELINE_FRAMES_MODEL_H #include #include #include "kritaanimationdocker_export.h" #include "kis_node_model.h" #include "kis_types.h" #include "kis_node.h" #include "timeline_node_list_keeper.h" class KisNodeDummy; class KisDummiesFacadeBase; class KisAnimationPlayer; class KRITAANIMATIONDOCKER_EXPORT TimelineFramesModel : public TimelineNodeListKeeper::ModelWithExternalNotifications { Q_OBJECT public: TimelineFramesModel(QObject *parent); ~TimelineFramesModel(); bool hasConnectionToCanvas() const; void setDummiesFacade(KisDummiesFacadeBase *dummiesFacade, KisImageSP image); bool canDropFrameData(const QMimeData *data, const QModelIndex &index); bool insertOtherLayer(int index, int dstRow); int activeLayerRow() const; bool createFrame(const QModelIndex &dstIndex); bool copyFrame(const QModelIndex &dstIndex); + QString audioChannelFileName() const; + void setAudioChannelFileName(const QString &fileName); + + bool isAudioMuted() const; + void setAudioMuted(bool value); + + qreal audioVolume() const; + void setAudioVolume(qreal value); + void setLastClickedIndex(const QModelIndex &index); int rowCount(const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role) const; bool setData(const QModelIndex &index, const QVariant &value, int role); QVariant headerData(int section, Qt::Orientation orientation, int role) const; bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role); Qt::DropActions supportedDragActions() const; Qt::DropActions supportedDropActions() const; QStringList mimeTypes() const; QMimeData * mimeData(const QModelIndexList &indexes) const; bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent); Qt::ItemFlags flags(const QModelIndex &index) const; bool insertRows(int row, int count, const QModelIndex &parent); bool removeRows(int row, int count, const QModelIndex &parent); enum ItemDataRole { ActiveLayerRole = KisTimeBasedItemModel::UserRole, TimelinePropertiesRole, OtherLayersRole, LayerUsedInTimelineRole, ColorLabel }; // metatype is added by the original implementation typedef KisBaseNode::Property Property; typedef KisBaseNode::PropertyList PropertyList; typedef TimelineNodeListKeeper::OtherLayer OtherLayer; typedef TimelineNodeListKeeper::OtherLayersList OtherLayersList; struct NodeManipulationInterface { virtual ~NodeManipulationInterface() {} virtual KisLayerSP addPaintLayer() const = 0; virtual void removeNode(KisNodeSP node) const = 0; }; /** * NOTE: the model has an ownership over the interface, that is it'll * be deleted automatically later */ void setNodeManipulationInterface(NodeManipulationInterface *iface); protected: KisNodeSP nodeAt(QModelIndex index) const; QList channelsAt(QModelIndex index) const; private Q_SLOTS: void slotDummyChanged(KisNodeDummy *dummy); void processUpdateQueue(); public Q_SLOTS: void slotCurrentNodeChanged(KisNodeSP node); Q_SIGNALS: void requestCurrentNodeChanged(KisNodeSP node); void sigInfiniteTimelineUpdateNeeded(); + void sigAudioChannelChanged(); void sigEnsureRowVisible(int row); - + private: struct Private; const QScopedPointer m_d; }; #endif /* __TIMELINE_FRAMES_MODEL_H */ diff --git a/plugins/dockers/animation/timeline_frames_view.cpp b/plugins/dockers/animation/timeline_frames_view.cpp index 51933cb114..826d805fbe 100644 --- a/plugins/dockers/animation/timeline_frames_view.cpp +++ b/plugins/dockers/animation/timeline_frames_view.cpp @@ -1,916 +1,1062 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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. */ #include "timeline_frames_view.h" #include "timeline_frames_model.h" #include "timeline_ruler_header.h" #include "timeline_layers_header.h" #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include "kis_debug.h" #include "timeline_frames_item_delegate.h" #include "kis_zoom_button.h" #include "kis_icon_utils.h" #include "kis_animation_utils.h" #include "kis_custom_modifiers_catcher.h" #include "kis_action.h" #include "kis_signal_compressor.h" #include "kis_time_range.h" #include "kis_color_label_selector_widget.h" +#include "kis_slider_spin_box.h" +#include + +#include +#include +#include + +#include "config-qtmultimedia.h" typedef QPair QItemViewPaintPair; typedef QList QItemViewPaintPairs; struct TimelineFramesView::Private { Private(TimelineFramesView *_q) : q(_q), fps(1), zoomStillPointIndex(-1), zoomStillPointOriginalOffset(0), dragInProgress(false), dragWasSuccessful(false), modifiersCatcher(0), selectionChangedCompressor(300, KisSignalCompressor::FIRST_INACTIVE) {} TimelineFramesView *q; TimelineFramesModel *model; TimelineRulerHeader *horizontalRuler; TimelineLayersHeader *layersHeader; int fps; int zoomStillPointIndex; int zoomStillPointOriginalOffset; QPoint initialDragPanValue; QPoint initialDragPanPos; QToolButton *addLayersButton; KisAction *showHideLayerAction; + QToolButton *audioOptionsButton; + KisColorLabelSelectorWidget *colorSelector; QWidgetAction *colorSelectorAction; KisColorLabelSelectorWidget *multiframeColorSelector; QWidgetAction *multiframeColorSelectorAction; + QMenu *audioOptionsMenu; + QAction *openAudioAction; + QAction *audioMuteAction; + KisSliderSpinBox *volumeSlider; + + QMenu *layerEditingMenu; QMenu *existingLayersMenu; QMenu *frameCreationMenu; QMenu *frameEditingMenu; QMenu *multipleFrameEditingMenu; QMap globalActions; KisZoomButton *zoomDragButton; bool dragInProgress; bool dragWasSuccessful; KisCustomModifiersCatcher *modifiersCatcher; QPoint lastPressedPosition; KisSignalCompressor selectionChangedCompressor; QStyleOptionViewItem viewOptionsV4() const; QItemViewPaintPairs draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const; QPixmap renderToPixmap(const QModelIndexList &indexes, QRect *r) const; }; TimelineFramesView::TimelineFramesView(QWidget *parent) : QTableView(parent), m_d(new Private(this)) { m_d->modifiersCatcher = new KisCustomModifiersCatcher(this); m_d->modifiersCatcher->addModifier("pan-zoom", Qt::Key_Space); m_d->modifiersCatcher->addModifier("offset-frame", Qt::Key_Alt); setCornerButtonEnabled(false); setSelectionBehavior(QAbstractItemView::SelectItems); setSelectionMode(QAbstractItemView::ExtendedSelection); setItemDelegate(new TimelineFramesItemDelegate(this)); setDragEnabled(true); setDragDropMode(QAbstractItemView::DragDrop); setAcceptDrops(true); setDropIndicatorShown(true); setDefaultDropAction(Qt::MoveAction); m_d->horizontalRuler = new TimelineRulerHeader(this); this->setHorizontalHeader(m_d->horizontalRuler); m_d->layersHeader = new TimelineLayersHeader(this); m_d->layersHeader->setSectionResizeMode(QHeaderView::Fixed); m_d->layersHeader->setDefaultSectionSize(24); m_d->layersHeader->setMinimumWidth(60); m_d->layersHeader->setHighlightSections(true); this->setVerticalHeader(m_d->layersHeader); connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), SLOT(slotUpdateInfiniteFramesCount())); connect(horizontalScrollBar(), SIGNAL(sliderReleased()), SLOT(slotUpdateInfiniteFramesCount())); + + /********** New Layer Menu ***********************************************************/ + m_d->addLayersButton = new QToolButton(this); m_d->addLayersButton->setAutoRaise(true); m_d->addLayersButton->setIcon(KisIconUtils::loadIcon("addlayer")); m_d->addLayersButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); m_d->addLayersButton->setPopupMode(QToolButton::InstantPopup); m_d->layerEditingMenu = new QMenu(this); m_d->layerEditingMenu->addAction(KisAnimationUtils::newLayerActionName, this, SLOT(slotAddNewLayer())); m_d->existingLayersMenu = m_d->layerEditingMenu->addMenu(KisAnimationUtils::addExistingLayerActionName); m_d->layerEditingMenu->addSeparator(); m_d->showHideLayerAction = new KisAction(KisAnimationUtils::showLayerActionName, this); m_d->showHideLayerAction->setActivationFlags(KisAction::ACTIVE_LAYER); connect(m_d->showHideLayerAction, SIGNAL(triggered()), SLOT(slotHideLayerFromTimeline())); m_d->showHideLayerAction->setCheckable(true); m_d->globalActions.insert("show_in_timeline", m_d->showHideLayerAction); m_d->layerEditingMenu->addAction(m_d->showHideLayerAction); m_d->layerEditingMenu->addAction(KisAnimationUtils::removeLayerActionName, this, SLOT(slotRemoveLayer())); connect(m_d->existingLayersMenu, SIGNAL(aboutToShow()), SLOT(slotUpdateLayersMenu())); connect(m_d->existingLayersMenu, SIGNAL(triggered(QAction*)), SLOT(slotAddExistingLayer(QAction*))); connect(m_d->layersHeader, SIGNAL(sigRequestContextMenu(const QPoint&)), SLOT(slotLayerContextMenuRequested(const QPoint&))); m_d->addLayersButton->setMenu(m_d->layerEditingMenu); + /********** Audio Channel Menu *******************************************************/ + + m_d->audioOptionsButton = new QToolButton(this); + m_d->audioOptionsButton->setAutoRaise(true); + m_d->audioOptionsButton->setIcon(KisIconUtils::loadIcon("audio-none")); + m_d->audioOptionsButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + m_d->audioOptionsButton->setPopupMode(QToolButton::InstantPopup); + + m_d->audioOptionsMenu = new QMenu(this); + +#ifndef HAVE_QT_MULTIMEDIA + m_d->audioOptionsMenu->addSection(i18nc("@item:inmenu", "Audio playback is not supported in this build!")); +#endif + + m_d->openAudioAction= new QAction("XXX", this); + connect(m_d->openAudioAction, SIGNAL(triggered()), this, SLOT(slotSelectAudioChannelFile())); + m_d->audioOptionsMenu->addAction(m_d->openAudioAction); + + m_d->audioMuteAction = new QAction(i18nc("@item:inmenu", "Mute"), this); + m_d->audioMuteAction->setCheckable(true); + connect(m_d->audioMuteAction, SIGNAL(triggered(bool)), SLOT(slotAudioChannelMute(bool))); + + m_d->audioOptionsMenu->addAction(m_d->audioMuteAction); + m_d->audioOptionsMenu->addAction(i18nc("@item:inmenu", "Remove audio"), this, SLOT(slotAudioChannelRemove())); + + m_d->audioOptionsMenu->addSeparator(); + + m_d->volumeSlider = new KisSliderSpinBox(this); + m_d->volumeSlider->setRange(0, 100); + m_d->volumeSlider->setSuffix("%"); + m_d->volumeSlider->setPrefix(i18nc("@item:inmenu, slider", "Volume:")); + m_d->volumeSlider->setSingleStep(1); + m_d->volumeSlider->setPageStep(10); + m_d->volumeSlider->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + connect(m_d->volumeSlider, SIGNAL(valueChanged(int)), SLOT(slotAudioVolumeChanged(int))); + + QWidgetAction *volumeAction = new QWidgetAction(m_d->audioOptionsMenu); + volumeAction->setDefaultWidget(m_d->volumeSlider); + m_d->audioOptionsMenu->addAction(volumeAction); + + m_d->audioOptionsButton->setMenu(m_d->audioOptionsMenu); + + /********** Frame Editing Context Menu ***********************************************/ + m_d->frameCreationMenu = new QMenu(this); m_d->frameCreationMenu->addAction(KisAnimationUtils::addFrameActionName, this, SLOT(slotNewFrame())); m_d->frameCreationMenu->addAction(KisAnimationUtils::duplicateFrameActionName, this, SLOT(slotCopyFrame())); m_d->colorSelector = new KisColorLabelSelectorWidget(this); m_d->colorSelectorAction = new QWidgetAction(this); m_d->colorSelectorAction->setDefaultWidget(m_d->colorSelector); connect(m_d->colorSelector, &KisColorLabelSelectorWidget::currentIndexChanged, this, &TimelineFramesView::slotColorLabelChanged); m_d->frameEditingMenu = new QMenu(this); m_d->frameEditingMenu->addAction(KisAnimationUtils::removeFrameActionName, this, SLOT(slotRemoveFrame())); m_d->frameEditingMenu->addAction(m_d->colorSelectorAction); m_d->multiframeColorSelector = new KisColorLabelSelectorWidget(this); m_d->multiframeColorSelectorAction = new QWidgetAction(this); m_d->multiframeColorSelectorAction->setDefaultWidget(m_d->multiframeColorSelector); connect(m_d->multiframeColorSelector, &KisColorLabelSelectorWidget::currentIndexChanged, this, &TimelineFramesView::slotColorLabelChanged); m_d->multipleFrameEditingMenu = new QMenu(this); m_d->multipleFrameEditingMenu->addAction(KisAnimationUtils::removeFramesActionName, this, SLOT(slotRemoveFrame())); m_d->multipleFrameEditingMenu->addAction(m_d->multiframeColorSelectorAction); + /********** Zoom Button **************************************************************/ + m_d->zoomDragButton = new KisZoomButton(this); m_d->zoomDragButton->setAutoRaise(true); m_d->zoomDragButton->setIcon(KisIconUtils::loadIcon("zoom-horizontal")); m_d->zoomDragButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); m_d->zoomDragButton->setToolTip(i18nc("@info:tooltip", "Zoom Timeline. Hold down and drag left or right.")); m_d->zoomDragButton->setPopupMode(QToolButton::InstantPopup); connect(m_d->zoomDragButton, SIGNAL(zoomLevelChanged(qreal)), SLOT(slotZoomButtonChanged(qreal))); connect(m_d->zoomDragButton, SIGNAL(zoomStarted(qreal)), SLOT(slotZoomButtonPressed(qreal))); setFramesPerSecond(12); setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); connect(&m_d->selectionChangedCompressor, SIGNAL(timeout()), SLOT(slotSelectionChanged())); } TimelineFramesView::~TimelineFramesView() { } QMap TimelineFramesView::globalActions() const { return m_d->globalActions; } void resizeToMinimalSize(QAbstractButton *w, int minimalSize) { QSize buttonSize = w->sizeHint(); if (buttonSize.height() > minimalSize) { buttonSize = QSize(minimalSize, minimalSize); } w->resize(buttonSize); } void TimelineFramesView::updateGeometries() { QTableView::updateGeometries(); const int availableHeight = m_d->horizontalRuler->height(); const int margin = 2; const int minimalSize = availableHeight - 2 * margin; resizeToMinimalSize(m_d->addLayersButton, minimalSize); + resizeToMinimalSize(m_d->audioOptionsButton, minimalSize); resizeToMinimalSize(m_d->zoomDragButton, minimalSize); int x = 2 * margin; int y = (availableHeight - minimalSize) / 2; m_d->addLayersButton->move(x, 2 * y); + m_d->audioOptionsButton->move(x + minimalSize + 2 * margin, 2 * y); const int availableWidth = m_d->layersHeader->width(); x = availableWidth - margin - minimalSize; m_d->zoomDragButton->move(x, 2 * y); } void TimelineFramesView::setModel(QAbstractItemModel *model) { TimelineFramesModel *framesModel = qobject_cast(model); m_d->model = framesModel; QTableView::setModel(model); connect(m_d->model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)), this, SLOT(slotHeaderDataChanged(Qt::Orientation, int, int))); connect(m_d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(slotDataChanged(QModelIndex,QModelIndex))); connect(m_d->model, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), this, SLOT(slotReselectCurrentIndex())); connect(m_d->model, SIGNAL(sigInfiniteTimelineUpdateNeeded()), this, SLOT(slotUpdateInfiniteFramesCount())); + connect(m_d->model, SIGNAL(sigAudioChannelChanged()), + this, SLOT(slotUpdateAudioActions())); + connect(selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), &m_d->selectionChangedCompressor, SLOT(start())); connect(m_d->model, SIGNAL(sigEnsureRowVisible(int)), SLOT(slotEnsureRowVisible(int))); + slotUpdateAudioActions(); } void TimelineFramesView::setFramesPerSecond(int fps) { m_d->fps = fps; m_d->horizontalRuler->setFramePerSecond(fps); // For some reason simple update sometimes doesn't work here, so // reset the whole header // // m_d->horizontalRuler->reset(); } void TimelineFramesView::slotZoomButtonPressed(qreal staticPoint) { m_d->zoomStillPointIndex = qIsNaN(staticPoint) ? currentIndex().column() : staticPoint; const int w = m_d->horizontalRuler->defaultSectionSize(); m_d->zoomStillPointOriginalOffset = w * m_d->zoomStillPointIndex - horizontalScrollBar()->value(); } void TimelineFramesView::slotZoomButtonChanged(qreal zoomLevel) { if (m_d->horizontalRuler->setZoom(zoomLevel)) { slotUpdateInfiniteFramesCount(); const int w = m_d->horizontalRuler->defaultSectionSize(); horizontalScrollBar()->setValue(w * m_d->zoomStillPointIndex - m_d->zoomStillPointOriginalOffset); viewport()->update(); } } void TimelineFramesView::slotColorLabelChanged(int label) { Q_FOREACH(QModelIndex index, selectedIndexes()) { m_d->model->setData(index, label, TimelineFramesModel::ColorLabel); } KisImageConfig config; config.setDefaultFrameColorLabel(label); } +void TimelineFramesView::slotSelectAudioChannelFile() +{ + if (!m_d->model) return; + + QString defaultDir = QDesktopServices::storageLocation(QDesktopServices::MusicLocation); + + const QString currentFile = m_d->model->audioChannelFileName(); + QDir baseDir = QFileInfo(currentFile).absoluteDir(); + if (baseDir.exists()) { + defaultDir = baseDir.absolutePath(); + } + + const QString result = KisImportExportManager::askForAudioFileName(defaultDir, this); + const QFileInfo info(result); + + if (info.exists()) { + m_d->model->setAudioChannelFileName(info.absoluteFilePath()); + } +} + +void TimelineFramesView::slotAudioChannelMute(bool value) +{ + if (!m_d->model) return; + + if (value != m_d->model->isAudioMuted()) { + m_d->model->setAudioMuted(value); + } +} + +void TimelineFramesView::slotAudioChannelRemove() +{ + if (!m_d->model) return; + m_d->model->setAudioChannelFileName(QString()); +} + +void TimelineFramesView::slotUpdateAudioActions() +{ + if (!m_d->model) return; + + const QString currentFile = m_d->model->audioChannelFileName(); + + if (currentFile.isEmpty()) { + m_d->openAudioAction->setText(i18nc("@item:inmenu", "Open audio...")); + } else { + QFileInfo info(currentFile); + m_d->openAudioAction->setText(i18nc("@item:inmenu", "Change audio (%1)...", info.fileName())); + } + + m_d->audioMuteAction->setChecked(m_d->model->isAudioMuted()); + + QIcon audioIcon; + if (currentFile.isEmpty()) { + audioIcon = KisIconUtils::loadIcon("audio-none"); + } else { + if (m_d->model->isAudioMuted()) { + audioIcon = KisIconUtils::loadIcon("audio-volume-mute"); + } else { + audioIcon = KisIconUtils::loadIcon("audio-volume-high"); + } + } + + m_d->audioOptionsButton->setIcon(audioIcon); + + m_d->volumeSlider->setEnabled(!m_d->model->isAudioMuted()); + + KisSignalsBlocker b(m_d->volumeSlider); + m_d->volumeSlider->setValue(qRound(m_d->model->audioVolume() * 100.0)); +} + +void TimelineFramesView::slotAudioVolumeChanged(int value) +{ + m_d->model->setAudioVolume(qreal(value) / 100.0); +} + void TimelineFramesView::slotUpdateInfiniteFramesCount() { if (horizontalScrollBar()->isSliderDown()) return; const int sectionWidth = m_d->horizontalRuler->defaultSectionSize(); const int calculatedIndex = (horizontalScrollBar()->value() + m_d->horizontalRuler->width() - 1) / sectionWidth; m_d->model->setLastVisibleFrame(calculatedIndex); } void TimelineFramesView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { QTableView::currentChanged(current, previous); if (previous.column() != current.column()) { m_d->model->setData(previous, false, TimelineFramesModel::ActiveFrameRole); m_d->model->setData(current, true, TimelineFramesModel::ActiveFrameRole); } } QItemSelectionModel::SelectionFlags TimelineFramesView::selectionCommand(const QModelIndex &index, const QEvent *event) const { // WARNING: Copy-pasted from KisNodeView! Please keep in sync! /** * Qt has a bug: when we Ctrl+click on an item, the item's * selections gets toggled on mouse *press*, whereas usually it is * done on mouse *release*. Therefore the user cannot do a * Ctrl+D&D with the default configuration. This code fixes the * problem by manually returning QItemSelectionModel::NoUpdate * flag when the user clicks on an item and returning * QItemSelectionModel::Toggle on release. */ if (event && (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease) && index.isValid()) { const QMouseEvent *mevent = static_cast(event); if (mevent->button() == Qt::RightButton && selectionModel()->selectedIndexes().contains(index)) { // Allow calling context menu for multiple layers return QItemSelectionModel::NoUpdate; } if (event->type() == QEvent::MouseButtonPress && (mevent->modifiers() & Qt::ControlModifier)) { return QItemSelectionModel::NoUpdate; } if (event->type() == QEvent::MouseButtonRelease && (mevent->modifiers() & Qt::ControlModifier)) { return QItemSelectionModel::Toggle; } } return QAbstractItemView::selectionCommand(index, event); } void TimelineFramesView::slotSelectionChanged() { int minColumn = std::numeric_limits::max(); int maxColumn = std::numeric_limits::min(); foreach (const QModelIndex &idx, selectedIndexes()) { if (idx.column() > maxColumn) { maxColumn = idx.column(); } if (idx.column() < minColumn) { minColumn = idx.column(); } } KisTimeRange range; if (maxColumn > minColumn) { range = KisTimeRange(minColumn, maxColumn - minColumn + 1); } m_d->model->setPlaybackRange(range); } void TimelineFramesView::slotReselectCurrentIndex() { QModelIndex index = currentIndex(); currentChanged(index, index); } void TimelineFramesView::slotEnsureRowVisible(int row) { QModelIndex index = currentIndex(); if (!index.isValid() || row < 0) return; index = m_d->model->index(row, index.column()); scrollTo(index); } void TimelineFramesView::slotDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { if (m_d->model->isPlaybackActive()) return; int selectedColumn = -1; for (int j = topLeft.column(); j <= bottomRight.column(); j++) { QVariant value = m_d->model->data( m_d->model->index(topLeft.row(), j), TimelineFramesModel::ActiveFrameRole); if (value.isValid() && value.toBool()) { selectedColumn = j; break; } } QModelIndex index = currentIndex(); if (!index.isValid() && selectedColumn < 0) { return; } if (selectedColumn == -1) { selectedColumn = index.column(); } if (selectedColumn != index.column() && !m_d->dragInProgress) { int row= index.isValid() ? index.row() : 0; setCurrentIndex(m_d->model->index(row, selectedColumn)); } } void TimelineFramesView::slotHeaderDataChanged(Qt::Orientation orientation, int first, int last) { Q_UNUSED(first); Q_UNUSED(last); if (orientation == Qt::Horizontal) { const int newFps = m_d->model->headerData(0, Qt::Horizontal, TimelineFramesModel::FramesPerSecondRole).toInt(); if (newFps != m_d->fps) { setFramesPerSecond(newFps); } } else /* if (orientation == Qt::Vertical) */ { updateShowInTimeline(); } } void TimelineFramesView::rowsInserted(const QModelIndex& parent, int start, int end) { QTableView::rowsInserted(parent, start, end); updateShowInTimeline(); } inline bool isIndexDragEnabled(QAbstractItemModel *model, const QModelIndex &index) { return (model->flags(index) & Qt::ItemIsDragEnabled); } QStyleOptionViewItem TimelineFramesView::Private::viewOptionsV4() const { QStyleOptionViewItem option = q->viewOptions(); option.locale = q->locale(); option.locale.setNumberOptions(QLocale::OmitGroupSeparator); option.widget = q; return option; } QItemViewPaintPairs TimelineFramesView::Private::draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const { Q_ASSERT(r); QRect &rect = *r; const QRect viewportRect = q->viewport()->rect(); QItemViewPaintPairs ret; for (int i = 0; i < indexes.count(); ++i) { const QModelIndex &index = indexes.at(i); const QRect current = q->visualRect(index); if (current.intersects(viewportRect)) { ret += qMakePair(current, index); rect |= current; } } rect &= viewportRect; return ret; } QPixmap TimelineFramesView::Private::renderToPixmap(const QModelIndexList &indexes, QRect *r) const { Q_ASSERT(r); QItemViewPaintPairs paintPairs = draggablePaintPairs(indexes, r); if (paintPairs.isEmpty()) return QPixmap(); QPixmap pixmap(r->size()); pixmap.fill(Qt::transparent); QPainter painter(&pixmap); QStyleOptionViewItem option = viewOptionsV4(); option.state |= QStyle::State_Selected; for (int j = 0; j < paintPairs.count(); ++j) { option.rect = paintPairs.at(j).first.translated(-r->topLeft()); const QModelIndex ¤t = paintPairs.at(j).second; //adjustViewOptionsForIndex(&option, current); q->itemDelegate(current)->paint(&painter, option, current); } return pixmap; } void TimelineFramesView::startDrag(Qt::DropActions supportedActions) { QModelIndexList indexes = selectionModel()->selectedIndexes(); if (!indexes.isEmpty() && m_d->modifiersCatcher->modifierPressed("offset-frame")) { QVector rows; int leftmostColumn = std::numeric_limits::max(); Q_FOREACH (const QModelIndex &index, indexes) { leftmostColumn = qMin(leftmostColumn, index.column()); if (!rows.contains(index.row())) { rows.append(index.row()); } } const int lastColumn = m_d->model->columnCount() - 1; selectionModel()->clear(); Q_FOREACH (const int row, rows) { QItemSelection sel(m_d->model->index(row, leftmostColumn), m_d->model->index(row, lastColumn)); selectionModel()->select(sel, QItemSelectionModel::Select); } supportedActions = Qt::MoveAction; { QModelIndexList indexes = selectedIndexes(); for(int i = indexes.count() - 1 ; i >= 0; --i) { if (!isIndexDragEnabled(m_d->model, indexes.at(i))) indexes.removeAt(i); } selectionModel()->clear(); if (indexes.count() > 0) { QMimeData *data = m_d->model->mimeData(indexes); if (!data) return; QRect rect; QPixmap pixmap = m_d->renderToPixmap(indexes, &rect); rect.adjust(horizontalOffset(), verticalOffset(), 0, 0); QDrag *drag = new QDrag(this); drag->setPixmap(pixmap); drag->setMimeData(data); drag->setHotSpot(m_d->lastPressedPosition - rect.topLeft()); drag->exec(supportedActions, Qt::MoveAction); setCurrentIndex(currentIndex()); } } } else { /** * Workaround for Qt5's bugs: * * 1) Qt doesn't treat selection the selection on D&D * correctly, so we save it in advance and restore * afterwards. * * 2) There is a private variable in QAbstractItemView: * QAbstractItemView::Private::currentSelectionStartIndex. * It is initialized *only* when the setCurrentIndex() is called * explicitly on the view object, not on the selection model. * Therefore we should explicitly call setCurrentIndex() after * D&D, even if it already has *correct* value! * * 2) We should also call selectionModel()->select() * explicitly. There are two reasons for it: 1) Qt doesn't * maintain selection over D&D; 2) when reselecting single * element after D&D, Qt goes crazy, because it tries to * read *global* keyboard modifiers. Therefore if we are * dragging with Shift or Ctrl pressed it'll get crazy. So * just reset it explicitly. */ QModelIndexList selectionBefore = selectionModel()->selectedIndexes(); QModelIndex currentBefore = selectionModel()->currentIndex(); // initialize a global status variable m_d->dragWasSuccessful = false; QAbstractItemView::startDrag(supportedActions); QModelIndex newCurrent; QPoint selectionOffset; if (m_d->dragWasSuccessful) { newCurrent = currentIndex(); selectionOffset = QPoint(newCurrent.column() - currentBefore.column(), newCurrent.row() - currentBefore.row()); } else { newCurrent = currentBefore; selectionOffset = QPoint(); } setCurrentIndex(newCurrent); selectionModel()->clearSelection(); Q_FOREACH (const QModelIndex &idx, selectionBefore) { QModelIndex newIndex = model()->index(idx.row() + selectionOffset.y(), idx.column() + selectionOffset.x()); selectionModel()->select(newIndex, QItemSelectionModel::Select); } } } void TimelineFramesView::dragEnterEvent(QDragEnterEvent *event) { m_d->dragInProgress = true; m_d->model->setScrubState(true); QTableView::dragEnterEvent(event); } void TimelineFramesView::dragMoveEvent(QDragMoveEvent *event) { m_d->dragInProgress = true; m_d->model->setScrubState(true); QTableView::dragMoveEvent(event); if (event->isAccepted()) { QModelIndex index = indexAt(event->pos()); if (!m_d->model->canDropFrameData(event->mimeData(), index)) { event->ignore(); } else { selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); } } } void TimelineFramesView::dropEvent(QDropEvent *event) { m_d->dragInProgress = false; m_d->model->setScrubState(false); QAbstractItemView::dropEvent(event); m_d->dragWasSuccessful = event->isAccepted(); } void TimelineFramesView::dragLeaveEvent(QDragLeaveEvent *event) { m_d->dragInProgress = false; m_d->model->setScrubState(false); QAbstractItemView::dragLeaveEvent(event); } void TimelineFramesView::mousePressEvent(QMouseEvent *event) { QPersistentModelIndex index = indexAt(event->pos()); if (m_d->modifiersCatcher->modifierPressed("pan-zoom")) { if (event->button() == Qt::RightButton) { // TODO: try calculate index under mouse cursor even when // it is outside any visible row qreal staticPoint = index.isValid() ? index.column() : currentIndex().column(); m_d->zoomDragButton->beginZoom(event->pos(), staticPoint); } else if (event->button() == Qt::LeftButton) { m_d->initialDragPanPos = event->pos(); m_d->initialDragPanValue = QPoint(horizontalScrollBar()->value(), verticalScrollBar()->value()); } event->accept(); } else if (event->button() == Qt::RightButton) { int numSelectedItems = selectionModel()->selectedIndexes().size(); if (index.isValid() && numSelectedItems <= 1 && m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) { model()->setData(index, true, TimelineFramesModel::ActiveLayerRole); model()->setData(index, true, TimelineFramesModel::ActiveFrameRole); setCurrentIndex(index); if (model()->data(index, TimelineFramesModel::FrameExistsRole).toBool() || model()->data(index, TimelineFramesModel::SpecialKeyframeExists).toBool()) { { KisSignalsBlocker b(m_d->colorSelector); QVariant colorLabel = index.data(TimelineFramesModel::ColorLabel); int labelIndex = colorLabel.isValid() ? colorLabel.toInt() : 0; m_d->colorSelector->setCurrentIndex(labelIndex); } m_d->frameEditingMenu->exec(event->globalPos()); } else { m_d->frameCreationMenu->exec(event->globalPos()); } } else if (numSelectedItems > 1) { int labelIndex = 0; bool haveFrames = false; Q_FOREACH(QModelIndex index, selectedIndexes()) { haveFrames |= index.data(TimelineFramesModel::FrameExistsRole).toBool(); QVariant colorLabel = index.data(TimelineFramesModel::ColorLabel); if (colorLabel.isValid()) { if (labelIndex == 0) { labelIndex = colorLabel.toInt(); } else { labelIndex = 0; break; } } } if (!haveFrames) { m_d->multiframeColorSelectorAction->setVisible(false); } else { KisSignalsBlocker b(m_d->multiframeColorSelector); m_d->multiframeColorSelector->setCurrentIndex(labelIndex); m_d->multiframeColorSelectorAction->setVisible(true); } m_d->multipleFrameEditingMenu->exec(event->globalPos()); } } else { if (index.isValid()) { m_d->model->setLastClickedIndex(index); } m_d->lastPressedPosition = QPoint(horizontalOffset(), verticalOffset()) + event->pos(); QAbstractItemView::mousePressEvent(event); } } void TimelineFramesView::mouseMoveEvent(QMouseEvent *e) { if (m_d->modifiersCatcher->modifierPressed("pan-zoom")) { if (e->buttons() & Qt::RightButton) { m_d->zoomDragButton->continueZoom(e->pos()); } else if (e->buttons() & Qt::LeftButton) { QPoint diff = e->pos() - m_d->initialDragPanPos; QPoint offset = QPoint(m_d->initialDragPanValue.x() - diff.x(), m_d->initialDragPanValue.y() - diff.y()); const int height = m_d->layersHeader->defaultSectionSize(); horizontalScrollBar()->setValue(offset.x()); verticalScrollBar()->setValue(offset.y() / height); } e->accept(); } else { m_d->model->setScrubState(true); QTableView::mouseMoveEvent(e); } } void TimelineFramesView::mouseReleaseEvent(QMouseEvent *e) { if (m_d->modifiersCatcher->modifierPressed("pan-zoom")) { e->accept(); } else { m_d->model->setScrubState(false); QTableView::mouseReleaseEvent(e); } } void TimelineFramesView::wheelEvent(QWheelEvent *e) { QModelIndex index = currentIndex(); int column= -1; if (index.isValid()) { column= index.column() + ((e->delta() > 0) ? 1 : -1); } if (column >= 0 && !m_d->dragInProgress) { setCurrentIndex(m_d->model->index(index.row(), column)); } } void TimelineFramesView::slotUpdateLayersMenu() { QAction *action = 0; m_d->existingLayersMenu->clear(); QVariant value = model()->headerData(0, Qt::Vertical, TimelineFramesModel::OtherLayersRole); if (value.isValid()) { TimelineFramesModel::OtherLayersList list = value.value(); int i = 0; Q_FOREACH (const TimelineFramesModel::OtherLayer &l, list) { action = m_d->existingLayersMenu->addAction(l.name); action->setData(i++); } } } void TimelineFramesView::slotLayerContextMenuRequested(const QPoint &globalPos) { m_d->layerEditingMenu->exec(globalPos); } void TimelineFramesView::updateShowInTimeline() { const int row = m_d->model->activeLayerRow(); const bool status = m_d->model->headerData(row, Qt::Vertical, TimelineFramesModel::LayerUsedInTimelineRole).toBool(); m_d->showHideLayerAction->setChecked(status); } void TimelineFramesView::slotAddNewLayer() { QModelIndex index = currentIndex(); const int newRow = index.isValid() ? index.row() : 0; model()->insertRow(newRow); } void TimelineFramesView::slotAddExistingLayer(QAction *action) { QVariant value = action->data(); if (value.isValid()) { QModelIndex index = currentIndex(); const int newRow = index.isValid() ? index.row() + 1 : 0; m_d->model->insertOtherLayer(value.toInt(), newRow); } } void TimelineFramesView::slotRemoveLayer() { QModelIndex index = currentIndex(); if (!index.isValid()) return; model()->removeRow(index.row()); } void TimelineFramesView::slotHideLayerFromTimeline() { const int row = m_d->model->activeLayerRow(); const bool status = m_d->model->headerData(row, Qt::Vertical, TimelineFramesModel::LayerUsedInTimelineRole).toBool(); m_d->model->setHeaderData(row, Qt::Vertical, !status, TimelineFramesModel::LayerUsedInTimelineRole); } void TimelineFramesView::slotNewFrame() { QModelIndex index = currentIndex(); if (!index.isValid() || !m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) { return; } m_d->model->createFrame(index); } void TimelineFramesView::slotCopyFrame() { QModelIndex index = currentIndex(); if (!index.isValid() || !m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) { return; } m_d->model->copyFrame(index); } void TimelineFramesView::slotRemoveFrame() { QModelIndexList indexes = selectionModel()->selectedIndexes(); for (auto it = indexes.begin(); it != indexes.end(); /*noop*/) { if (!m_d->model->data(*it, TimelineFramesModel::FrameEditableRole).toBool()) { it = indexes.erase(it); } else { ++it; } } if (!indexes.isEmpty()) { m_d->model->removeFrames(indexes); } } diff --git a/plugins/dockers/animation/timeline_frames_view.h b/plugins/dockers/animation/timeline_frames_view.h index 4a77116edc..0d05dd6172 100644 --- a/plugins/dockers/animation/timeline_frames_view.h +++ b/plugins/dockers/animation/timeline_frames_view.h @@ -1,102 +1,108 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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. */ #ifndef __TIMELINE_FRAMES_VIEW_H #define __TIMELINE_FRAMES_VIEW_H #include #include #include "kritaanimationdocker_export.h" class KisAction; class TimelineWidget; class KRITAANIMATIONDOCKER_EXPORT TimelineFramesView : public QTableView { Q_OBJECT public: TimelineFramesView(QWidget *parent); ~TimelineFramesView(); void setModel(QAbstractItemModel *model); void updateGeometries(); QMap globalActions() const; public Q_SLOTS: void slotSelectionChanged(); private Q_SLOTS: void slotUpdateLayersMenu(); void slotAddNewLayer(); void slotAddExistingLayer(QAction *action); void slotDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); void slotRemoveLayer(); void slotHideLayerFromTimeline(); void slotLayerContextMenuRequested(const QPoint &globalPos); void slotNewFrame(); void slotCopyFrame(); void slotRemoveFrame(); void slotReselectCurrentIndex(); void slotUpdateInfiniteFramesCount(); void slotHeaderDataChanged(Qt::Orientation orientation, int first, int last); void slotZoomButtonPressed(qreal staticPoint); void slotZoomButtonChanged(qreal value); void slotColorLabelChanged(int); void slotEnsureRowVisible(int row); + void slotSelectAudioChannelFile(); + void slotAudioChannelMute(bool value); + void slotAudioChannelRemove(); + void slotUpdateAudioActions(); + void slotAudioVolumeChanged(int value); + private: void setFramesPerSecond(int fps); void updateShowInTimeline(); protected: QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex &index, const QEvent *event) const; void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); void startDrag(Qt::DropActions supportedActions); void dragEnterEvent(QDragEnterEvent *event); void dragMoveEvent(QDragMoveEvent *event); void dropEvent(QDropEvent *event); void dragLeaveEvent(QDragLeaveEvent *event); void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *e); void mouseReleaseEvent(QMouseEvent *e); void wheelEvent(QWheelEvent *e); void rowsInserted(const QModelIndex& parent, int start, int end); private: struct Private; const QScopedPointer m_d; }; #endif /* __TIMELINE_FRAMES_VIEW_H */ diff --git a/plugins/extensions/animationrenderer/AnimationRenderer.cpp b/plugins/extensions/animationrenderer/AnimationRenderer.cpp index 4f49c05819..82137dc745 100644 --- a/plugins/extensions/animationrenderer/AnimationRenderer.cpp +++ b/plugins/extensions/animationrenderer/AnimationRenderer.cpp @@ -1,177 +1,177 @@ /* * Copyright (c) 2016 Boudewijn Rempt * * 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. */ #include "AnimationRenderer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "DlgAnimationRenderer.h" K_PLUGIN_FACTORY_WITH_JSON(AnimaterionRendererFactory, "kritaanimationrenderer.json", registerPlugin();) AnimaterionRenderer::AnimaterionRenderer(QObject *parent, const QVariantList &) : KisViewPlugin(parent) { // Shows the big dialog KisAction *action = createAction("render_animation"); action->setActivationFlags(KisAction::IMAGE_HAS_ANIMATION); connect(action, SIGNAL(triggered()), this, SLOT(slotRenderAnimation())); // Re-renders the image sequence as defined in the last render action = createAction("render_image_sequence_again"); action->setActivationFlags(KisAction::IMAGE_HAS_ANIMATION); connect(action, SIGNAL(triggered()), this, SLOT(slotRenderSequenceAgain())); } AnimaterionRenderer::~AnimaterionRenderer() { } void AnimaterionRenderer::slotRenderAnimation() { KisImageWSP image = m_view->image(); if (!image) return; if (!image->animationInterface()->hasAnimation()) return; KisDocument *doc = m_view->document(); doc->setFileProgressProxy(); doc->setFileProgressUpdater(i18n("Export frames")); DlgAnimationRenderer dlgAnimationRenderer(doc, m_view->mainWindow()); dlgAnimationRenderer.setCaption(i18n("Render Animation")); KisConfig kisConfig; KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration(); cfg->fromXML(kisConfig.exportConfiguration("IMAGESEQUENCE")); - // Override the saved start/end with the ones from the image in case of using gui. - cfg->setProperty("first_frame", image->animationInterface()->playbackRange().start()); - cfg->setProperty("last_frame", image->animationInterface()->playbackRange().end()); dlgAnimationRenderer.setSequenceConfiguration(cfg); cfg->clearProperties(); cfg->fromXML(kisConfig.exportConfiguration("ANIMATION_RENDERER")); dlgAnimationRenderer.setVideoConfiguration(cfg); cfg->clearProperties(); cfg->fromXML(kisConfig.exportConfiguration("FFMPEG_CONFIG")); dlgAnimationRenderer.setEncoderConfiguration(cfg); if (dlgAnimationRenderer.exec() == QDialog::Accepted) { KisPropertiesConfigurationSP sequenceConfig = dlgAnimationRenderer.getSequenceConfiguration(); kisConfig.setExportConfiguration("IMAGESEQUENCE", sequenceConfig); QString mimetype = sequenceConfig->getString("mimetype"); QString extension = KisMimeDatabase::suffixesForMimeType(mimetype).first(); QString baseFileName = QString("%1/%2.%3").arg(sequenceConfig->getString("directory")) .arg(sequenceConfig->getString("basename")) .arg(extension); KisAnimationExportSaver exporter(doc, baseFileName, sequenceConfig->getInt("first_frame"), sequenceConfig->getInt("last_frame"), sequenceConfig->getInt("sequence_start")); - bool success = exporter.exportAnimation(dlgAnimationRenderer.getFrameExportConfiguration()); - Q_ASSERT(success); - QString savedFilesMask = exporter.savedFilesMask(); - - KisPropertiesConfigurationSP videoConfig = dlgAnimationRenderer.getVideoConfiguration(); - if (videoConfig) { - kisConfig.setExportConfiguration("ANIMATION_RENDERER", videoConfig); - - KisPropertiesConfigurationSP encoderConfig = dlgAnimationRenderer.getEncoderConfiguration(); - if (encoderConfig) { - kisConfig.setExportConfiguration("FFMPEG_CONFIG", encoderConfig); - encoderConfig->setProperty("savedFilesMask", savedFilesMask); - } + bool success = exporter.exportAnimation(dlgAnimationRenderer.getFrameExportConfiguration()) == KisImportExportFilter::OK; - QSharedPointer encoder = dlgAnimationRenderer.encoderFilter(); - encoder->setMimeType(mimetype.toLatin1()); - QFile fi(videoConfig->getString("filename")); - KisImportExportFilter::ConversionStatus res; - if (!fi.open(QIODevice::WriteOnly)) { - qWarning() << "Could not open" << fi.fileName() << "for writing!"; - res = KisImportExportFilter::CreationError; - } - else { - encoder->setFilename(fi.fileName()); - res = encoder->convert(doc, &fi, encoderConfig); - fi.close(); - } - if (res != KisImportExportFilter::OK) { - QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not render animation:\n%1", doc->errorMessage())); - } - if (videoConfig->getBool("delete_sequence", false)) { - QDir d(sequenceConfig->getString("directory")); - QStringList sequenceFiles = d.entryList(QStringList() << sequenceConfig->getString("basename") + "*." + extension, QDir::Files); - Q_FOREACH(const QString &f, sequenceFiles) { - d.remove(f); + // the folder could have been read-only or something else could happen + if (success) { + QString savedFilesMask = exporter.savedFilesMask(); + + KisPropertiesConfigurationSP videoConfig = dlgAnimationRenderer.getVideoConfiguration(); + if (videoConfig) { + kisConfig.setExportConfiguration("ANIMATION_RENDERER", videoConfig); + + KisPropertiesConfigurationSP encoderConfig = dlgAnimationRenderer.getEncoderConfiguration(); + if (encoderConfig) { + kisConfig.setExportConfiguration("FFMPEG_CONFIG", encoderConfig); + encoderConfig->setProperty("savedFilesMask", savedFilesMask); + } + + QSharedPointer encoder = dlgAnimationRenderer.encoderFilter(); + encoder->setMimeType(mimetype.toLatin1()); + QFile fi(videoConfig->getString("filename")); + KisImportExportFilter::ConversionStatus res; + if (!fi.open(QIODevice::WriteOnly)) { + qWarning() << "Could not open" << fi.fileName() << "for writing!"; + res = KisImportExportFilter::CreationError; + } + else { + encoder->setFilename(fi.fileName()); + res = encoder->convert(doc, &fi, encoderConfig); + fi.close(); + } + if (res != KisImportExportFilter::OK) { + QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not render animation:\n%1", doc->errorMessage())); + } + if (videoConfig->getBool("delete_sequence", false)) { + QDir d(sequenceConfig->getString("directory")); + QStringList sequenceFiles = d.entryList(QStringList() << sequenceConfig->getString("basename") + "*." + extension, QDir::Files); + Q_FOREACH(const QString &f, sequenceFiles) { + d.remove(f); + } } } } } doc->clearFileProgressUpdater(); doc->clearFileProgressProxy(); } void AnimaterionRenderer::slotRenderSequenceAgain() { KisImageWSP image = m_view->image(); if (!image) return; if (!image->animationInterface()->hasAnimation()) return; KisDocument *doc = m_view->document(); doc->setFileProgressProxy(); doc->setFileProgressUpdater(i18n("Export frames")); KisConfig kisConfig; KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration(); cfg->fromXML(kisConfig.exportConfiguration("IMAGESEQUENCE")); QString mimetype = cfg->getString("mimetype"); QString extension = KisMimeDatabase::suffixesForMimeType(mimetype).first(); QString baseFileName = QString("%1/%2.%3").arg(cfg->getString("directory")) .arg(cfg->getString("basename")) .arg(extension); KisAnimationExportSaver exporter(doc, baseFileName, cfg->getInt("first_frame"), cfg->getInt("last_frame"), cfg->getInt("sequence_start")); bool success = exporter.exportAnimation(); Q_ASSERT(success); doc->clearFileProgressUpdater(); doc->clearFileProgressProxy(); } #include "AnimationRenderer.moc" diff --git a/plugins/extensions/animationrenderer/DlgAnimationRenderer.cpp b/plugins/extensions/animationrenderer/DlgAnimationRenderer.cpp index 4f0b4c1939..4b9cc22526 100644 --- a/plugins/extensions/animationrenderer/DlgAnimationRenderer.cpp +++ b/plugins/extensions/animationrenderer/DlgAnimationRenderer.cpp @@ -1,417 +1,443 @@ /* * Copyright (c) 2016 Boudewijn Rempt * * 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. */ #include "DlgAnimationRenderer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include DlgAnimationRenderer::DlgAnimationRenderer(KisDocument *doc, QWidget *parent) : KoDialog(parent) , m_image(doc->image()) + , m_doc(doc) , m_defaultFileName(QFileInfo(doc->url().toLocalFile()).completeBaseName()) { KisConfig cfg; setCaption(i18n("Render Animation")); setButtons(Ok | Cancel); setDefaultButton(Ok); + if (m_defaultFileName.isEmpty()) { + m_defaultFileName = i18n("Untitled"); + } + m_page = new WdgAnimaterionRenderer(this); m_page->layout()->setMargin(0); m_page->dirRequester->setMode(KoFileDialog::OpenDirectory); QString lastLocation = cfg.readEntry("AnimationRenderer/last_sequence_export_location", QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); m_page->dirRequester->setFileName(lastLocation); m_page->intStart->setMinimum(doc->image()->animationInterface()->fullClipRange().start()); m_page->intStart->setMaximum(doc->image()->animationInterface()->fullClipRange().end()); m_page->intStart->setValue(doc->image()->animationInterface()->playbackRange().start()); m_page->intEnd->setMinimum(doc->image()->animationInterface()->fullClipRange().start()); m_page->intEnd->setMaximum(doc->image()->animationInterface()->fullClipRange().end()); m_page->intEnd->setValue(doc->image()->animationInterface()->playbackRange().end()); + QFileInfo audioFileInfo(doc->image()->animationInterface()->audioChannelFileName()); + const bool hasAudio = audioFileInfo.exists(); + m_page->chkIncludeAudio->setEnabled(hasAudio); + m_page->chkIncludeAudio->setChecked(hasAudio && !doc->image()->animationInterface()->isAudioMuted()); + QStringList mimes = KisImportExportManager::mimeFilter(KisImportExportManager::Export); mimes.sort(); Q_FOREACH(const QString &mime, mimes) { QString description = KisMimeDatabase::descriptionForMimeType(mime); if (description.isEmpty()) { description = mime; } m_page->cmbMimetype->addItem(description, mime); if (mime == "image/png") { m_page->cmbMimetype->setCurrentIndex(m_page->cmbMimetype->count() - 1); } } setMainWidget(m_page); resize(m_page->sizeHint()); KoJsonTrader trader; QListlist = trader.query("Krita/AnimationExporter", ""); Q_FOREACH(QPluginLoader *loader, list) { QJsonObject json = loader->metaData().value("MetaData").toObject(); QStringList mimetypes = json.value("X-KDE-Export").toString().split(","); Q_FOREACH(const QString &mime, mimetypes) { KLibFactory *factory = qobject_cast(loader->instance()); if (!factory) { warnUI << loader->errorString(); continue; } QObject* obj = factory->create(0); if (!obj || !obj->inherits("KisImportExportFilter")) { delete obj; continue; } QSharedPointerfilter(static_cast(obj)); if (!filter) { delete obj; continue; } m_renderFilters.append(filter); QString description = KisMimeDatabase::descriptionForMimeType(mime); if (description.isEmpty()) { description = mime; } m_page->cmbRenderType->addItem(description, mime); } } m_page->videoFilename->setMode(KoFileDialog::SaveFile); m_page->videoFilename->setStartDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); qDeleteAll(list); connect(m_page->grpRender, SIGNAL(toggled(bool)), this, SLOT(toggleSequenceType(bool))); connect(m_page->bnExportOptions, SIGNAL(clicked()), this, SLOT(sequenceMimeTypeSelected())); connect(m_page->bnRenderOptions, SIGNAL(clicked()), this, SLOT(selectRenderOptions())); m_page->ffmpegLocation->setFileName(findFFMpeg()); m_page->ffmpegLocation->setMode(KoFileDialog::OpenFile); connect(m_page->ffmpegLocation, SIGNAL(fileSelected(QString)), this, SLOT(ffmpegLocationChanged(QString))); m_page->grpRender->setChecked(cfg.readEntry("AnimationRenderer/render_animation", false)); m_page->chkDeleteSequence->setChecked(cfg.readEntry("AnimationRenderer/delete_sequence", false)); m_page->cmbRenderType->setCurrentIndex(cfg.readEntry("AnimationRenderer/render_type", 0)); + + // connect and cold init connect(m_page->cmbRenderType, SIGNAL(currentIndexChanged(int)), this, SLOT(selectRenderType(int))); + selectRenderType(m_page->cmbRenderType->currentIndex()); } DlgAnimationRenderer::~DlgAnimationRenderer() { KisConfig cfg; cfg.writeEntry("AnimationRenderer/render_animation", m_page->grpRender->isChecked()); cfg.writeEntry("AnimationRenderer/last_sequence_export_location", m_page->dirRequester->fileName()); cfg.writeEntry("AnimationRenderer/render_type", m_page->cmbRenderType->currentIndex()); cfg.writeEntry("AnimationRenderer/delete_sequence", m_page->chkDeleteSequence->isChecked()); cfg.setCustomFFMpegPath(m_page->ffmpegLocation->fileName()); if (m_encoderConfigWidget) { m_encoderConfigWidget->setParent(0); m_encoderConfigWidget->deleteLater(); } if (m_frameExportConfigWidget) { m_frameExportConfigWidget->setParent(0); m_frameExportConfigWidget->deleteLater(); } delete m_page; } KisPropertiesConfigurationSP DlgAnimationRenderer::getSequenceConfiguration() const { KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration(); cfg->setProperty("basename", m_page->txtBasename->text()); + cfg->setProperty("last_document_path", m_doc->localFilePath()); cfg->setProperty("directory", m_page->dirRequester->fileName()); cfg->setProperty("first_frame", m_page->intStart->value()); cfg->setProperty("last_frame", m_page->intEnd->value()); cfg->setProperty("sequence_start", m_page->sequenceStart->value()); cfg->setProperty("mimetype", m_page->cmbMimetype->currentData().toString()); return cfg; } void DlgAnimationRenderer::setSequenceConfiguration(KisPropertiesConfigurationSP cfg) { m_page->txtBasename->setText(cfg->getString("basename", "frame")); + + if (cfg->getString("last_document_path") != m_doc->localFilePath()) { + cfg->removeProperty("first_frame"); + cfg->removeProperty("last_frame"); + cfg->removeProperty("sequence_start"); + } + m_page->dirRequester->setFileName(cfg->getString("directory", QStandardPaths::writableLocation(QStandardPaths::PicturesLocation))); m_page->intStart->setValue(cfg->getInt("first_frame", m_image->animationInterface()->playbackRange().start())); m_page->intEnd->setValue(cfg->getInt("last_frame", m_image->animationInterface()->playbackRange().end())); m_page->sequenceStart->setValue(cfg->getInt("sequence_start", m_image->animationInterface()->playbackRange().start())); QString mimetype = cfg->getString("mimetype"); for (int i = 0; i < m_page->cmbMimetype->count(); ++i) { if (m_page->cmbMimetype->itemData(i).toString() == mimetype) { m_page->cmbMimetype->setCurrentIndex(i); break; } } } KisPropertiesConfigurationSP DlgAnimationRenderer::getFrameExportConfiguration() const { if (m_frameExportConfigWidget) { KisPropertiesConfigurationSP cfg = m_frameExportConfigWidget->configuration(); cfg->setProperty("basename", m_page->txtBasename->text()); cfg->setProperty("directory", m_page->dirRequester->fileName()); cfg->setProperty("first_frame", m_page->intStart->value()); cfg->setProperty("last_frame", m_page->intEnd->value()); cfg->setProperty("sequence_start", m_page->sequenceStart->value()); cfg->setProperty("ffmpeg_path", m_page->ffmpegLocation->fileName()); return m_frameExportConfigWidget->configuration(); } return 0; } bool DlgAnimationRenderer::renderToVideo() const { return m_page->grpRender->isChecked(); } KisPropertiesConfigurationSP DlgAnimationRenderer::getVideoConfiguration() const { if (!m_page->grpRender->isChecked()) { return 0; } KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration(); QString filename = m_page->videoFilename->fileName(); if (QFileInfo(filename).completeSuffix().isEmpty()) { QString mimetype = m_page->cmbRenderType->itemData(m_page->cmbRenderType->currentIndex()).toString(); filename += "." + KisMimeDatabase::suffixesForMimeType(mimetype).first(); } cfg->setProperty("filename", filename); cfg->setProperty("delete_sequence", m_page->chkDeleteSequence->isChecked()); + cfg->setProperty("first_frame", m_page->intStart->value()); + cfg->setProperty("last_frame", m_page->intEnd->value()); + cfg->setProperty("sequence_start", m_page->sequenceStart->value()); return cfg; } void DlgAnimationRenderer::setVideoConfiguration(KisPropertiesConfigurationSP /*cfg*/) { } KisPropertiesConfigurationSP DlgAnimationRenderer::getEncoderConfiguration() const { if (!m_page->grpRender->isChecked()) { return 0; } KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration(); if (m_encoderConfigWidget) { cfg = m_encoderConfigWidget->configuration(); } cfg->setProperty("mimetype", m_page->cmbRenderType->currentData().toString()); cfg->setProperty("directory", m_page->dirRequester->fileName()); cfg->setProperty("first_frame", m_page->intStart->value()); cfg->setProperty("last_frame", m_page->intEnd->value()); cfg->setProperty("sequence_start", m_page->sequenceStart->value()); + cfg->setProperty("include_audio", m_page->chkIncludeAudio->isChecked()); return cfg; } void DlgAnimationRenderer::setEncoderConfiguration(KisPropertiesConfigurationSP /*cfg*/) { } QSharedPointer DlgAnimationRenderer::encoderFilter() const { if (m_page->cmbRenderType->currentIndex() < m_renderFilters.size()) { return m_renderFilters[m_page->cmbRenderType->currentIndex()]; } return QSharedPointer(0); } void DlgAnimationRenderer::selectRenderType(int index) { if (index >= m_renderFilters.size()) return; QString mimetype = m_page->cmbRenderType->itemData(index).toString(); if (!m_page->videoFilename->fileName().isEmpty() && QFileInfo(m_page->videoFilename->fileName()).completeBaseName() != m_defaultFileName) { m_defaultFileName = QFileInfo(m_page->videoFilename->fileName()).completeBaseName(); } m_page->videoFilename->setMimeTypeFilters(QStringList() << mimetype, mimetype); + m_page->videoFilename->setFileName(m_defaultFileName + "." + KisMimeDatabase::suffixesForMimeType(mimetype).first()); } void DlgAnimationRenderer::selectRenderOptions() { int index = m_page->cmbRenderType->currentIndex(); if (m_encoderConfigWidget) { m_encoderConfigWidget->deleteLater(); m_encoderConfigWidget = 0; } if (index >= m_renderFilters.size()) return; QSharedPointer filter = m_renderFilters[index]; QString mimetype = m_page->cmbRenderType->itemData(index).toString(); if (filter) { m_encoderConfigWidget = filter->createConfigurationWidget(0, KisDocument::nativeFormatMimeType(), mimetype.toLatin1()); if (m_encoderConfigWidget) { m_encoderConfigWidget->setConfiguration(filter->lastSavedConfiguration("", mimetype.toLatin1())); KoDialog dlg(this); dlg.setMainWidget(m_encoderConfigWidget); dlg.setButtons(KoDialog::Ok | KoDialog::Cancel); if (!dlg.exec()) { m_encoderConfigWidget->setConfiguration(filter->lastSavedConfiguration()); } dlg.setMainWidget(0); m_encoderConfigWidget->hide(); m_encoderConfigWidget->setParent(0); } } else { m_encoderConfigWidget = 0; } } void DlgAnimationRenderer::toggleSequenceType(bool toggle) { m_page->cmbMimetype->setEnabled(!toggle); for (int i = 0; i < m_page->cmbMimetype->count(); ++i) { if (m_page->cmbMimetype->itemData(i).toString() == "image/png") { m_page->cmbMimetype->setCurrentIndex(i); break; } } } void DlgAnimationRenderer::sequenceMimeTypeSelected() { int index = m_page->cmbMimetype->currentIndex(); if (m_frameExportConfigWidget) { m_frameExportConfigWidget->deleteLater(); m_frameExportConfigWidget = 0; } QString mimetype = m_page->cmbMimetype->itemData(index).toString(); QSharedPointer filter(KisImportExportManager::filterForMimeType(mimetype, KisImportExportManager::Export)); if (filter) { m_frameExportConfigWidget = filter->createConfigurationWidget(0, KisDocument::nativeFormatMimeType(), mimetype.toLatin1()); if (m_frameExportConfigWidget) { m_frameExportConfigWidget->setConfiguration(filter->lastSavedConfiguration("", mimetype.toLatin1())); KoDialog dlg(this); dlg.setMainWidget(m_frameExportConfigWidget); dlg.setButtons(KoDialog::Ok | KoDialog::Cancel); if (!dlg.exec()) { m_frameExportConfigWidget->setConfiguration(filter->lastSavedConfiguration()); } m_frameExportConfigWidget->hide(); m_frameExportConfigWidget->setParent(0); dlg.setMainWidget(0); } } } void DlgAnimationRenderer::ffmpegLocationChanged(const QString &s) { KisConfig cfg; cfg.setCustomFFMpegPath(s); } void DlgAnimationRenderer::slotButtonClicked(int button) { if (button == KoDialog::Ok && m_page->grpRender->isChecked()) { QString ffmpeg = m_page->ffmpegLocation->fileName(); if (m_page->videoFilename->fileName().isEmpty()) { QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("Please enter a file name to render to.")); return; } else if (ffmpeg.isEmpty()) { QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("The location of FFmpeg is unknown. Please install FFmpeg first: Krita cannot render animations without FFmpeg. (www.ffmpeg.org)")); return; } else { QFileInfo fi(ffmpeg); if (!fi.exists()) { QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("The location of FFmpeg is invalid. Please select the correct location of the FFmpeg executable on your system.")); return; } } } KoDialog::slotButtonClicked(button); } QString DlgAnimationRenderer::findFFMpeg() { QString result; QStringList proposedPaths; QString customPath = KisConfig().customFFMpegPath(); proposedPaths << customPath; proposedPaths << customPath + QDir::separator() + "ffmpeg"; proposedPaths << QDir::homePath() + "/bin/ffmpeg"; proposedPaths << "/usr/bin/ffmpeg"; proposedPaths << "/usr/local/bin/ffmpeg"; proposedPaths << KoResourcePaths::getApplicationRoot() + QDir::separator() + "bin" + QDir::separator() + "ffmpeg"; Q_FOREACH (const QString &path, proposedPaths) { if (path.isEmpty()) continue; QProcess testProcess; testProcess.start(path, QStringList() << "-version"); testProcess.waitForFinished(1000); const bool successfulStart = testProcess.state() == QProcess::NotRunning && testProcess.error() == QProcess::UnknownError; if (successfulStart) { result = path; break; } } return result; } diff --git a/plugins/extensions/animationrenderer/DlgAnimationRenderer.h b/plugins/extensions/animationrenderer/DlgAnimationRenderer.h index 9d86a7a710..6861fb41f3 100644 --- a/plugins/extensions/animationrenderer/DlgAnimationRenderer.h +++ b/plugins/extensions/animationrenderer/DlgAnimationRenderer.h @@ -1,95 +1,96 @@ /* * Copyright (c) 2016 Boudewijn Rempt * * 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. */ #ifndef DLG_ANIMATIONRENDERERIMAGE #define DLG_ANIMATIONRENDERERIMAGE #include #include #include "ui_wdg_animationrenderer.h" #include #include class KisDocument; class KisImportExportFilter; class KisConfigWidget; class QHBoxLayout; class WdgAnimaterionRenderer : public QWidget, public Ui::WdgAnimaterionRenderer { Q_OBJECT public: WdgAnimaterionRenderer(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class DlgAnimationRenderer: public KoDialog { Q_OBJECT public: DlgAnimationRenderer(KisDocument *doc, QWidget *parent = 0); ~DlgAnimationRenderer(); KisPropertiesConfigurationSP getSequenceConfiguration() const; void setSequenceConfiguration(KisPropertiesConfigurationSP cfg); KisPropertiesConfigurationSP getFrameExportConfiguration() const; bool renderToVideo() const; KisPropertiesConfigurationSP getVideoConfiguration() const; void setVideoConfiguration(KisPropertiesConfigurationSP cfg); KisPropertiesConfigurationSP getEncoderConfiguration() const; void setEncoderConfiguration(KisPropertiesConfigurationSP cfg); QSharedPointer encoderFilter() const; private Q_SLOTS: void selectRenderType(int i); void selectRenderOptions(); void toggleSequenceType(bool toggle); void sequenceMimeTypeSelected(); void ffmpegLocationChanged(const QString&); protected Q_SLOTS: void slotButtonClicked(int button); private: static QString findFFMpeg(); - KisImageWSP m_image; + KisImageSP m_image; + KisDocument *m_doc; WdgAnimaterionRenderer *m_page {0}; QList> m_renderFilters; KisConfigWidget *m_encoderConfigWidget {0}; KisConfigWidget *m_frameExportConfigWidget {0}; QString m_defaultFileName; }; #endif // DLG_ANIMATIONRENDERERIMAGE diff --git a/plugins/extensions/animationrenderer/wdg_animationrenderer.ui b/plugins/extensions/animationrenderer/wdg_animationrenderer.ui index f60044edc7..4364bdf2dc 100644 --- a/plugins/extensions/animationrenderer/wdg_animationrenderer.ui +++ b/plugins/extensions/animationrenderer/wdg_animationrenderer.ui @@ -1,282 +1,289 @@ WdgAnimaterionRenderer 0 0 444 443 AnimationRenderer Image Image Sequence Base name: frame Fi&le format: label_8 2 0 Select the file format for the image sequence. If you want to render to video or animated gif, you can only select PNG Select the frame export options ... Render location: 1 0 First frame: 1 0 Last frame: 1 0 Naming sequence starts with: 1 0 Qt::Vertical 20 40 0 1 Render true false Render as: Select the ffmpeg render options. ... File: 1 0 FF&Mpeg: ffmpegLocation - + Delete Sequence After Rendering true - + Qt::Vertical 20 40 + + + + Include Audio + + + KisFileNameRequester QWidget
kis_file_name_requester.h
1
txtBasename cmbMimetype intStart intEnd sequenceStart grpRender cmbRenderType chkDeleteSequence
diff --git a/plugins/impex/libkra/kis_kra_loader.cpp b/plugins/impex/libkra/kis_kra_loader.cpp index b076d719bb..3c5e89a147 100644 --- a/plugins/impex/libkra/kis_kra_loader.cpp +++ b/plugins/impex/libkra/kis_kra_loader.cpp @@ -1,1105 +1,1156 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2007 * * 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; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_kra_loader.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lazybrush/kis_colorize_mask.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_resource_server_provider.h" #include "kis_keyframe_channel.h" #include #include "KisDocument.h" #include "kis_config.h" #include "kis_kra_tags.h" #include "kis_kra_utils.h" #include "kis_kra_load_visitor.h" #include "kis_dom_utils.h" #include "kis_image_animation_interface.h" #include "kis_time_range.h" #include "kis_grid_config.h" #include "kis_guides_config.h" #include "kis_image_config.h" #include "KisProofingConfiguration.h" #include "kis_layer_properties_icons.h" #include "kis_node_view_color_scheme.h" /* Color model id comparison through the ages: 2.4 2.5 2.6 ideal ALPHA ALPHA ALPHA ALPHAU8 CMYK CMYK CMYK CMYKAU8 CMYKAF32 CMYKAF32 CMYKA16 CMYKAU16 CMYKAU16 GRAYA GRAYA GRAYA GRAYAU8 GrayF32 GRAYAF32 GRAYAF32 GRAYA16 GRAYAU16 GRAYAU16 LABA LABA LABA LABAU16 LABAF32 LABAF32 LABAU8 LABAU8 RGBA RGBA RGBA RGBAU8 RGBA16 RGBA16 RGBA16 RGBAU16 RgbAF32 RGBAF32 RGBAF32 RgbAF16 RgbAF16 RGBAF16 XYZA16 XYZA16 XYZA16 XYZAU16 XYZA8 XYZA8 XYZAU8 XyzAF16 XyzAF16 XYZAF16 XyzAF32 XYZAF32 XYZAF32 YCbCrA YCBCRA8 YCBCRA8 YCBCRAU8 YCbCrAU16 YCBCRAU16 YCBCRAU16 YCBCRF32 YCBCRF32 */ using namespace KRA; struct KisKraLoader::Private { public: KisDocument* document; QString imageName; // used to be stored in the image, is now in the documentInfo block QString imageComment; // used to be stored in the image, is now in the documentInfo block QMap layerFilenames; // temp storage during loading int syntaxVersion; // version of the fileformat we are loading vKisNodeSP selectedNodes; // the nodes that were active when saving the document. QMap assistantsFilenames; QList assistants; QMap keyframeFilenames; QStringList errorMessages; }; void convertColorSpaceNames(QString &colorspacename, QString &profileProductName) { if (colorspacename == "Grayscale + Alpha") { colorspacename = "GRAYA"; profileProductName.clear(); } else if (colorspacename == "RgbAF32") { colorspacename = "RGBAF32"; profileProductName.clear(); } else if (colorspacename == "RgbAF16") { colorspacename = "RGBAF16"; profileProductName.clear(); } else if (colorspacename == "CMYKA16") { colorspacename = "CMYKAU16"; } else if (colorspacename == "GrayF32") { colorspacename = "GRAYAF32"; profileProductName.clear(); } else if (colorspacename == "GRAYA16") { colorspacename = "GRAYAU16"; } else if (colorspacename == "XyzAF16") { colorspacename = "XYZAF16"; profileProductName.clear(); } else if (colorspacename == "XyzAF32") { colorspacename = "XYZAF32"; profileProductName.clear(); } else if (colorspacename == "YCbCrA") { colorspacename = "YCBCRA8"; } else if (colorspacename == "YCbCrAU16") { colorspacename = "YCBCRAU16"; } } KisKraLoader::KisKraLoader(KisDocument * document, int syntaxVersion) : m_d(new Private()) { m_d->document = document; m_d->syntaxVersion = syntaxVersion; } KisKraLoader::~KisKraLoader() { delete m_d; } KisImageSP KisKraLoader::loadXML(const KoXmlElement& element) { QString attr; KisImageSP image = 0; QString name; qint32 width; qint32 height; QString profileProductName; double xres; double yres; QString colorspacename; const KoColorSpace * cs; if ((attr = element.attribute(MIME)) == NATIVE_MIMETYPE) { if ((m_d->imageName = element.attribute(NAME)).isNull()) { m_d->errorMessages << i18n("Image does not have a name."); return KisImageSP(0); } if ((attr = element.attribute(WIDTH)).isNull()) { m_d->errorMessages << i18n("Image does not specify a width."); return KisImageSP(0); } width = KisDomUtils::toInt(attr); if ((attr = element.attribute(HEIGHT)).isNull()) { m_d->errorMessages << i18n("Image does not specify a height."); return KisImageSP(0); } height = KisDomUtils::toInt(attr); m_d->imageComment = element.attribute(DESCRIPTION); xres = 100.0 / 72.0; if (!(attr = element.attribute(X_RESOLUTION)).isNull()) { qreal value = KisDomUtils::toDouble(attr); if (value > 1.0) { xres = value / 72.0; } } yres = 100.0 / 72.0; if (!(attr = element.attribute(Y_RESOLUTION)).isNull()) { qreal value = KisDomUtils::toDouble(attr); if (value > 1.0) { yres = value / 72.0; } } if ((colorspacename = element.attribute(COLORSPACE_NAME)).isNull()) { // An old file: take a reasonable default. // Krita didn't support anything else in those // days anyway. colorspacename = "RGBA"; } profileProductName = element.attribute(PROFILE); // A hack for an old colorspacename convertColorSpaceNames(colorspacename, profileProductName); QString colorspaceModel = KoColorSpaceRegistry::instance()->colorSpaceColorModelId(colorspacename).id(); QString colorspaceDepth = KoColorSpaceRegistry::instance()->colorSpaceColorDepthId(colorspacename).id(); if (profileProductName.isNull()) { // no mention of profile so get default profile"; cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, ""); } else { cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, profileProductName); } if (cs == 0) { // try once more without the profile cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, ""); if (cs == 0) { m_d->errorMessages << i18n("Image specifies an unsupported color model: %1.", colorspacename); return KisImageSP(0); } } KisImageConfig cfgImage; KisProofingConfigurationSP proofingConfig = cfgImage.defaultProofingconfiguration(); if (!(attr = element.attribute(PROOFINGPROFILENAME)).isNull()) { proofingConfig->proofingProfile = attr; } if (!(attr = element.attribute(PROOFINGMODEL)).isNull()) { proofingConfig->proofingModel = attr; } if (!(attr = element.attribute(PROOFINGDEPTH)).isNull()) { proofingConfig->proofingDepth = attr; } if (!(attr = element.attribute(PROOFINGINTENT)).isNull()) { proofingConfig->intent = (KoColorConversionTransformation::Intent) KisDomUtils::toInt(attr); } if (!(attr = element.attribute(PROOFINGADAPTATIONSTATE)).isNull()) { proofingConfig->adaptationState = KisDomUtils::toDouble(attr); } if (m_d->document) { image = new KisImage(m_d->document->createUndoStore(), width, height, cs, name); } else { image = new KisImage(0, width, height, cs, name); } image->setResolution(xres, yres); loadNodes(element, image, const_cast(image->rootLayer().data())); KoXmlNode child; for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) { KoXmlElement e = child.toElement(); if(e.tagName() == CANVASPROJECTIONCOLOR) { if (e.hasAttribute(COLORBYTEDATA)) { QByteArray colorData = QByteArray::fromBase64(e.attribute(COLORBYTEDATA).toLatin1()); KoColor color((const quint8*)colorData.data(), image->colorSpace()); image->setDefaultProjectionColor(color); } } if(e.tagName()== PROOFINGWARNINGCOLOR) { QDomDocument dom; KoXml::asQDomElement(dom, e); QDomElement eq = dom.firstChildElement(); proofingConfig->warningColor = KoColor::fromXML(eq.firstChildElement(), Integer8BitsColorDepthID.id(), QHash()); } if (e.tagName().toLower() == "animation") { loadAnimationMetadata(e, image); } } image->setProofingConfiguration(proofingConfig); for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) { KoXmlElement e = child.toElement(); if(e.tagName() == "compositions") { loadCompositions(e, image); } } } KoXmlNode child; for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) { KoXmlElement e = child.toElement(); if (e.tagName() == "grid") { loadGrid(e); } else if (e.tagName() == "guides") { loadGuides(e); } else if (e.tagName() == "assistants") { loadAssistantsList(e); + } else if (e.tagName() == "audio") { + loadAudio(e, image); } } return image; } void KisKraLoader::loadBinaryData(KoStore * store, KisImageSP image, const QString & uri, bool external) { // icc profile: if present, this overrides the profile product name loaded in loadXML. QString location = external ? QString() : uri; location += m_d->imageName + ICC_PATH; if (store->hasFile(location)) { if (store->open(location)) { QByteArray data; data.resize(store->size()); bool res = (store->read(data.data(), store->size()) > -1); store->close(); if (res) { const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(image->colorSpace()->colorModelId().id(), image->colorSpace()->colorDepthId().id(), data); if (profile && profile->valid()) { res = image->assignImageProfile(profile); } if (!res) { profile = KoColorSpaceRegistry::instance()->profileByName(KoColorSpaceRegistry::instance()->colorSpaceFactory(image->colorSpace()->id())->defaultProfile()); Q_ASSERT(profile && profile->valid()); image->assignImageProfile(profile); } } } } //load the embed proofing profile, it only needs to be loaded into Krita, not assigned. location = external ? QString() : uri; location += m_d->imageName + ICC_PROOFING_PATH; if (store->hasFile(location)) { if (store->open(location)) { QByteArray proofingData; proofingData.resize(store->size()); bool proofingProfileRes = (store->read(proofingData.data(), store->size())>-1); store->close(); if (proofingProfileRes) { const KoColorProfile *proofingProfile = KoColorSpaceRegistry::instance()->createColorProfile(image->proofingConfiguration()->proofingModel, image->proofingConfiguration()->proofingDepth, proofingData); if (proofingProfile->valid()){ //if (KoColorSpaceEngineRegistry::instance()->get("icc")) { // KoColorSpaceEngineRegistry::instance()->get("icc")->addProfile(proofingProfile->fileName()); //} KoColorSpaceRegistry::instance()->addProfile(proofingProfile); } } } } // Load the layers data: if there is a profile associated with a layer it will be set now. KisKraLoadVisitor visitor(image, store, m_d->layerFilenames, m_d->keyframeFilenames, m_d->imageName, m_d->syntaxVersion); if (external) { visitor.setExternalUri(uri); } image->rootLayer()->accept(visitor); if (!visitor.errorMessages().isEmpty()) { m_d->errorMessages.append(visitor.errorMessages()); } // annotations // exif location = external ? QString() : uri; location += m_d->imageName + EXIF_PATH; if (store->hasFile(location)) { QByteArray data; store->open(location); data = store->read(store->size()); store->close(); image->addAnnotation(KisAnnotationSP(new KisAnnotation("exif", "", data))); } // layer styles location = external ? QString() : uri; location += m_d->imageName + LAYER_STYLES_PATH; if (store->hasFile(location)) { KisPSDLayerStyleCollectionResource *collection = new KisPSDLayerStyleCollectionResource("Embedded Styles.asl"); collection->setName(i18nc("Auto-generated layer style collection name for embedded styles (collection)", "<%1> (embedded)", m_d->imageName)); KIS_ASSERT_RECOVER_NOOP(!collection->valid()); store->open(location); { KoStoreDevice device(store); device.open(QIODevice::ReadOnly); /** * ASL loading code cannot work with non-sequential IO devices, * so convert the device beforehand! */ QByteArray buf = device.readAll(); QBuffer raDevice(&buf); raDevice.open(QIODevice::ReadOnly); collection->loadFromDevice(&raDevice); } store->close(); if (collection->valid()) { KoResourceServer *server = KisResourceServerProvider::instance()->layerStyleCollectionServer(); server->addResource(collection, false); collection->assignAllLayerStyles(image->root()); } else { warnKrita << "WARNING: Couldn't load layer styles library from .kra!"; delete collection; } } if (m_d->document && m_d->document->documentInfo()->aboutInfo("title").isNull()) m_d->document->documentInfo()->setAboutInfo("title", m_d->imageName); if (m_d->document && m_d->document->documentInfo()->aboutInfo("comment").isNull()) m_d->document->documentInfo()->setAboutInfo("comment", m_d->imageComment); loadAssistants(store, uri, external); } vKisNodeSP KisKraLoader::selectedNodes() const { return m_d->selectedNodes; } QList KisKraLoader::assistants() const { return m_d->assistants; } QStringList KisKraLoader::errorMessages() const { return m_d->errorMessages; } void KisKraLoader::loadAssistants(KoStore *store, const QString &uri, bool external) { QString file_path; QString location; QMap handleMap; KisPaintingAssistant* assistant = 0; QMap::const_iterator loadedAssistant = m_d->assistantsFilenames.constBegin(); while (loadedAssistant != m_d->assistantsFilenames.constEnd()){ const KisPaintingAssistantFactory* factory = KisPaintingAssistantFactoryRegistry::instance()->get(loadedAssistant.value()); if (factory) { assistant = factory->createPaintingAssistant(); location = external ? QString() : uri; location += m_d->imageName + ASSISTANTS_PATH; file_path = location + loadedAssistant.key(); assistant->loadXml(store, handleMap, file_path); //If an assistant has too few handles than it should according to it's own setup, just don't load it// if (assistant->handles().size()==assistant->numHandles()){ m_d->assistants.append(toQShared(assistant)); } } loadedAssistant++; } } void KisKraLoader::loadAnimationMetadata(const KoXmlElement &element, KisImageSP image) { QDomDocument qDom; KoXml::asQDomElement(qDom, element); QDomElement qElement = qDom.firstChildElement(); float framerate; KisTimeRange range; int currentTime; KisImageAnimationInterface *animation = image->animationInterface(); if (KisDomUtils::loadValue(qElement, "framerate", &framerate)) { animation->setFramerate(framerate); } if (KisDomUtils::loadValue(qElement, "range", &range)) { animation->setFullClipRange(range); } if (KisDomUtils::loadValue(qElement, "currentTime", ¤tTime)) { animation->switchCurrentTimeAsync(currentTime); } } KisNodeSP KisKraLoader::loadNodes(const KoXmlElement& element, KisImageSP image, KisNodeSP parent) { KoXmlNode node = element.firstChild(); KoXmlNode child; if (!node.isNull()) { if (node.isElement()) { if (node.nodeName().toUpper() == LAYERS.toUpper() || node.nodeName().toUpper() == MASKS.toUpper()) { for (child = node.lastChild(); !child.isNull(); child = child.previousSibling()) { KisNodeSP node = loadNode(child.toElement(), image, parent); if (node) { image->nextLayerName(); // Make sure the nameserver is current with the number of nodes. image->addNode(node, parent); if (node->inherits("KisLayer") && child.childNodesCount() > 0) { loadNodes(child.toElement(), image, node); } } } } } } return parent; } KisNodeSP KisKraLoader::loadNode(const KoXmlElement& element, KisImageSP image, KisNodeSP parent) { // Nota bene: If you add new properties to layers, you should // ALWAYS define a default value in case the property is not // present in the layer definition: this helps a LOT with backward // compatibility. QString name = element.attribute(NAME, "No Name"); QUuid id = QUuid(element.attribute(UUID, QUuid().toString())); qint32 x = element.attribute(X, "0").toInt(); qint32 y = element.attribute(Y, "0").toInt(); qint32 opacity = element.attribute(OPACITY, QString::number(OPACITY_OPAQUE_U8)).toInt(); if (opacity < OPACITY_TRANSPARENT_U8) opacity = OPACITY_TRANSPARENT_U8; if (opacity > OPACITY_OPAQUE_U8) opacity = OPACITY_OPAQUE_U8; const KoColorSpace* colorSpace = 0; if ((element.attribute(COLORSPACE_NAME)).isNull()) { dbgFile << "No attribute color space for layer: " << name; colorSpace = image->colorSpace(); } else { QString colorspacename = element.attribute(COLORSPACE_NAME); QString profileProductName; convertColorSpaceNames(colorspacename, profileProductName); QString colorspaceModel = KoColorSpaceRegistry::instance()->colorSpaceColorModelId(colorspacename).id(); QString colorspaceDepth = KoColorSpaceRegistry::instance()->colorSpaceColorDepthId(colorspacename).id(); dbgFile << "Searching color space: " << colorspacename << colorspaceModel << colorspaceDepth << " for layer: " << name; // use default profile - it will be replaced later in completeLoading colorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, ""); dbgFile << "found colorspace" << colorSpace; if (!colorSpace) { m_d->errorMessages << i18n("Layer %1 specifies an unsupported color model: %2.", name, colorspacename); return 0; } } const bool visible = element.attribute(VISIBLE, "1") == "0" ? false : true; const bool locked = element.attribute(LOCKED, "0") == "0" ? false : true; const bool collapsed = element.attribute(COLLAPSED, "0") == "0" ? false : true; int colorLabelIndex = element.attribute(COLOR_LABEL, "0").toInt(); QVector labels = KisNodeViewColorScheme::instance()->allColorLabels(); if (colorLabelIndex >= labels.size()) { colorLabelIndex = labels.size() - 1; } // Now find out the layer type and do specific handling QString nodeType; if (m_d->syntaxVersion == 1) { nodeType = element.attribute("layertype"); if (nodeType.isEmpty()) { nodeType = PAINT_LAYER; } } else { nodeType = element.attribute(NODE_TYPE); } if (nodeType.isEmpty()) { m_d->errorMessages << i18n("Layer %1 has an unsupported type.", name); return 0; } KisNodeSP node = 0; if (nodeType == PAINT_LAYER) node = loadPaintLayer(element, image, name, colorSpace, opacity); else if (nodeType == GROUP_LAYER) node = loadGroupLayer(element, image, name, colorSpace, opacity); else if (nodeType == ADJUSTMENT_LAYER) node = loadAdjustmentLayer(element, image, name, colorSpace, opacity); else if (nodeType == SHAPE_LAYER) node = loadShapeLayer(element, image, name, colorSpace, opacity); else if (nodeType == GENERATOR_LAYER) node = loadGeneratorLayer(element, image, name, colorSpace, opacity); else if (nodeType == CLONE_LAYER) node = loadCloneLayer(element, image, name, colorSpace, opacity); else if (nodeType == FILTER_MASK) node = loadFilterMask(element, parent); else if (nodeType == TRANSFORM_MASK) node = loadTransformMask(element, parent); else if (nodeType == TRANSPARENCY_MASK) node = loadTransparencyMask(element, parent); else if (nodeType == SELECTION_MASK) node = loadSelectionMask(image, element, parent); else if (nodeType == COLORIZE_MASK) node = loadColorizeMask(image, element, parent, colorSpace); else if (nodeType == FILE_LAYER) { node = loadFileLayer(element, image, name, opacity); } else { m_d->errorMessages << i18n("Layer %1 has an unsupported type: %2.", name, nodeType); return 0; } // Loading the node went wrong. Return empty node and leave to // upstream to complain to the user if (!node) { m_d->errorMessages << i18n("Failure loading layer %1 of type: %2.", name, nodeType); return 0; } node->setVisible(visible, true); node->setUserLocked(locked); node->setCollapsed(collapsed); node->setColorLabelIndex(colorLabelIndex); node->setX(x); node->setY(y); node->setName(name); if (! id.isNull()) // if no uuid in file, new one has been generated already node->setUuid(id); if (node->inherits("KisLayer") || node->inherits("KisColorizeMask")) { QString compositeOpName = element.attribute(COMPOSITE_OP, "normal"); node->setCompositeOpId(compositeOpName); } if (node->inherits("KisLayer")) { KisLayer* layer = qobject_cast(node.data()); QBitArray channelFlags = stringToFlags(element.attribute(CHANNEL_FLAGS, ""), colorSpace->channelCount()); layer->setChannelFlags(channelFlags); if (element.hasAttribute(LAYER_STYLE_UUID)) { QString uuidString = element.attribute(LAYER_STYLE_UUID); QUuid uuid(uuidString); if (!uuid.isNull()) { KisPSDLayerStyleSP dumbLayerStyle(new KisPSDLayerStyle()); dumbLayerStyle->setUuid(uuid); layer->setLayerStyle(dumbLayerStyle); } else { warnKrita << "WARNING: Layer style for layer" << layer->name() << "contains invalid UUID" << uuidString; } } } if (node->inherits("KisGroupLayer")) { if (element.hasAttribute(PASS_THROUGH_MODE)) { bool value = element.attribute(PASS_THROUGH_MODE, "0") != "0"; KisGroupLayer *group = qobject_cast(node.data()); group->setPassThroughMode(value); } } if (node->inherits("KisPaintLayer")) { KisPaintLayer* layer = qobject_cast(node.data()); QBitArray channelLockFlags = stringToFlags(element.attribute(CHANNEL_LOCK_FLAGS, ""), colorSpace->channelCount()); layer->setChannelLockFlags(channelLockFlags); bool onionEnabled = element.attribute(ONION_SKIN_ENABLED, "0") == "0" ? false : true; layer->setOnionSkinEnabled(onionEnabled); bool timelineEnabled = element.attribute(VISIBLE_IN_TIMELINE, "0") == "0" ? false : true; layer->setUseInTimeline(timelineEnabled); } if (element.attribute(FILE_NAME).isNull()) { m_d->layerFilenames[node.data()] = name; } else { m_d->layerFilenames[node.data()] = element.attribute(FILE_NAME); } if (element.hasAttribute("selected") && element.attribute("selected") == "true") { m_d->selectedNodes.append(node); } if (element.hasAttribute(KEYFRAME_FILE)) { m_d->keyframeFilenames.insert(node.data(), element.attribute(KEYFRAME_FILE)); } return node; } KisNodeSP KisKraLoader::loadPaintLayer(const KoXmlElement& element, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { Q_UNUSED(element); KisPaintLayer* layer; layer = new KisPaintLayer(image, name, opacity, cs); Q_CHECK_PTR(layer); // Load exif info /*TODO: write and use the legacy stuff to load that exif tag for( KoXmlNode node = element.firstChild(); !node.isNull(); node = node.nextSibling() ) { KoXmlElement e = node.toElement(); if ( !e.isNull() && e.tagName() == "ExifInfo" ) { layer->paintDevice()->exifInfo()->load(e); } }*/ // TODO load metadata return layer; } KisNodeSP KisKraLoader::loadFileLayer(const KoXmlElement& element, KisImageSP image, const QString& name, quint32 opacity) { QString filename = element.attribute("source", QString()); if (filename.isNull()) return 0; bool scale = (element.attribute("scale", "true") == "true"); int scalingMethod = element.attribute("scalingmethod", "-1").toInt(); if (scalingMethod < 0) { if (scale) { scalingMethod = KisFileLayer::ToImagePPI; } else { scalingMethod = KisFileLayer::None; } } QString documentPath; if (m_d->document) { documentPath = m_d->document->url().toLocalFile(); } QFileInfo info(documentPath); QString basePath = info.absolutePath(); QString fullPath = basePath + QDir::separator() + filename; // Entering the event loop to show the messagebox will delete the image, so up the ref by one image->ref(); if (!QFileInfo(fullPath).exists()) { qApp->setOverrideCursor(Qt::ArrowCursor); QString msg = i18nc( "@info", - "The file associated to a file layer with the name \"%1\" is not found." - "Expected path:" - "%2" + "The file associated to a file layer with the name \"%1\" is not found.\n\n" + "Expected path:\n" + "%2\n\n" "Do you want to locate it manually?", name, fullPath); int result = QMessageBox::warning(0, i18nc("@title:window", "File not found"), msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if (result == QMessageBox::Yes) { KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument"); dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import)); dialog.setDefaultDir(basePath); QString url = dialog.filename(); if (!QFileInfo(basePath).exists()) { filename = url; } else { QDir d(basePath); filename = d.relativeFilePath(url); } } qApp->restoreOverrideCursor(); } KisLayer *layer = new KisFileLayer(image, basePath, filename, (KisFileLayer::ScalingMethod)scalingMethod, name, opacity); Q_CHECK_PTR(layer); return layer; } KisNodeSP KisKraLoader::loadGroupLayer(const KoXmlElement& element, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { Q_UNUSED(element); Q_UNUSED(cs); QString attr; KisGroupLayer* layer; layer = new KisGroupLayer(image, name, opacity); Q_CHECK_PTR(layer); return layer; } KisNodeSP KisKraLoader::loadAdjustmentLayer(const KoXmlElement& element, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { // XXX: do something with filterversion? Q_UNUSED(cs); QString attr; KisAdjustmentLayer* layer; QString filtername; if ((filtername = element.attribute(FILTER_NAME)).isNull()) { // XXX: Invalid adjustmentlayer! We should warn about it! warnFile << "No filter in adjustment layer"; return 0; } KisFilterSP f = KisFilterRegistry::instance()->value(filtername); if (!f) { warnFile << "No filter for filtername" << filtername << ""; return 0; // XXX: We don't have this filter. We should warn about it! } KisFilterConfigurationSP kfc = f->defaultConfiguration(0); // We'll load the configuration and the selection later. layer = new KisAdjustmentLayer(image, name, kfc, 0); Q_CHECK_PTR(layer); layer->setOpacity(opacity); return layer; } KisNodeSP KisKraLoader::loadShapeLayer(const KoXmlElement& element, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { Q_UNUSED(element); Q_UNUSED(cs); QString attr; KoShapeBasedDocumentBase * shapeController = 0; if (m_d->document) { shapeController = m_d->document->shapeController(); } KisShapeLayer* layer = new KisShapeLayer(shapeController, image, name, opacity); Q_CHECK_PTR(layer); return layer; } KisNodeSP KisKraLoader::loadGeneratorLayer(const KoXmlElement& element, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { Q_UNUSED(cs); // XXX: do something with generator version? KisGeneratorLayer* layer; QString generatorname = element.attribute(GENERATOR_NAME); if (generatorname.isNull()) { // XXX: Invalid generator layer! We should warn about it! warnFile << "No generator in generator layer"; return 0; } KisGeneratorSP generator = KisGeneratorRegistry::instance()->value(generatorname); if (!generator) { warnFile << "No generator for generatorname" << generatorname << ""; return 0; // XXX: We don't have this generator. We should warn about it! } KisFilterConfigurationSP kgc = generator->defaultConfiguration(0); // We'll load the configuration and the selection later. layer = new KisGeneratorLayer(image, name, kgc, 0); Q_CHECK_PTR(layer); layer->setOpacity(opacity); return layer; } KisNodeSP KisKraLoader::loadCloneLayer(const KoXmlElement& element, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { Q_UNUSED(cs); KisCloneLayerSP layer = new KisCloneLayer(0, image, name, opacity); KisCloneInfo info; if (! (element.attribute(CLONE_FROM_UUID)).isNull()) { info = KisCloneInfo(QUuid(element.attribute(CLONE_FROM_UUID))); } else { if ((element.attribute(CLONE_FROM)).isNull()) { return 0; } else { info = KisCloneInfo(element.attribute(CLONE_FROM)); } } layer->setCopyFromInfo(info); if ((element.attribute(CLONE_TYPE)).isNull()) { return 0; } else { layer->setCopyType((CopyLayerType) element.attribute(CLONE_TYPE).toInt()); } return layer; } KisNodeSP KisKraLoader::loadFilterMask(const KoXmlElement& element, KisNodeSP parent) { Q_UNUSED(parent); QString attr; KisFilterMask* mask; QString filtername; // XXX: should we check the version? if ((filtername = element.attribute(FILTER_NAME)).isNull()) { // XXX: Invalid filter layer! We should warn about it! warnFile << "No filter in filter layer"; return 0; } KisFilterSP f = KisFilterRegistry::instance()->value(filtername); if (!f) { warnFile << "No filter for filtername" << filtername << ""; return 0; // XXX: We don't have this filter. We should warn about it! } KisFilterConfigurationSP kfc = f->defaultConfiguration(0); // We'll load the configuration and the selection later. mask = new KisFilterMask(); mask->setFilter(kfc); Q_CHECK_PTR(mask); return mask; } KisNodeSP KisKraLoader::loadTransformMask(const KoXmlElement& element, KisNodeSP parent) { Q_UNUSED(element); Q_UNUSED(parent); KisTransformMask* mask; /** * We'll load the transform configuration later on a stage * of binary data loading */ mask = new KisTransformMask(); Q_CHECK_PTR(mask); return mask; } KisNodeSP KisKraLoader::loadTransparencyMask(const KoXmlElement& element, KisNodeSP parent) { Q_UNUSED(element); Q_UNUSED(parent); KisTransparencyMask* mask = new KisTransparencyMask(); Q_CHECK_PTR(mask); return mask; } KisNodeSP KisKraLoader::loadSelectionMask(KisImageSP image, const KoXmlElement& element, KisNodeSP parent) { Q_UNUSED(parent); KisSelectionMaskSP mask = new KisSelectionMask(image); bool active = element.attribute(ACTIVE, "1") == "0" ? false : true; mask->setActive(active); Q_CHECK_PTR(mask); return mask; } KisNodeSP KisKraLoader::loadColorizeMask(KisImageSP image, const KoXmlElement& element, KisNodeSP parent, const KoColorSpace *colorSpace) { Q_UNUSED(parent); KisColorizeMaskSP mask = new KisColorizeMask(); bool editKeystrokes = element.attribute(COLORIZE_EDIT_KEYSTROKES, "1") == "0" ? false : true; bool showColoring = element.attribute(COLORIZE_SHOW_COLORING, "1") == "0" ? false : true; KisLayerPropertiesIcons::setNodeProperty(mask, KisLayerPropertiesIcons::colorizeEditKeyStrokes, editKeystrokes, image); KisLayerPropertiesIcons::setNodeProperty(mask, KisLayerPropertiesIcons::colorizeShowColoring, showColoring, image); delete mask->setColorSpace(colorSpace); mask->setImage(image); return mask; } void KisKraLoader::loadCompositions(const KoXmlElement& elem, KisImageSP image) { KoXmlNode child; for (child = elem.firstChild(); !child.isNull(); child = child.nextSibling()) { KoXmlElement e = child.toElement(); QString name = e.attribute("name"); bool exportEnabled = e.attribute("exportEnabled", "1") == "0" ? false : true; KisLayerCompositionSP composition(new KisLayerComposition(image, name)); composition->setExportEnabled(exportEnabled); KoXmlNode value; for (value = child.lastChild(); !value.isNull(); value = value.previousSibling()) { KoXmlElement e = value.toElement(); QUuid uuid(e.attribute("uuid")); bool visible = e.attribute("visible", "1") == "0" ? false : true; composition->setVisible(uuid, visible); bool collapsed = e.attribute("collapsed", "1") == "0" ? false : true; composition->setCollapsed(uuid, collapsed); } image->addComposition(composition); } } void KisKraLoader::loadAssistantsList(const KoXmlElement &elem) { KoXmlNode child; int count = 0; for (child = elem.firstChild(); !child.isNull(); child = child.nextSibling()) { KoXmlElement e = child.toElement(); QString type = e.attribute("type"); QString file_name = e.attribute("filename"); m_d->assistantsFilenames.insert(file_name,type); count++; } } void KisKraLoader::loadGrid(const KoXmlElement& elem) { QDomDocument dom; KoXml::asQDomElement(dom, elem); QDomElement domElement = dom.firstChildElement(); KisGridConfig config; config.loadDynamicDataFromXml(domElement); config.loadStaticData(); m_d->document->setGridConfig(config); } void KisKraLoader::loadGuides(const KoXmlElement& elem) { QDomDocument dom; KoXml::asQDomElement(dom, elem); QDomElement domElement = dom.firstChildElement(); KisGuidesConfig guides; guides.loadFromXml(domElement); m_d->document->setGuidesConfig(guides); } + +void KisKraLoader::loadAudio(const KoXmlElement& elem, KisImageSP image) +{ + QDomDocument dom; + KoXml::asQDomElement(dom, elem); + QDomElement qElement = dom.firstChildElement(); + + QString fileName; + if (KisDomUtils::loadValue(qElement, "masterChannelPath", &fileName)) { + fileName = QDir::toNativeSeparators(fileName); + + QDir baseDirectory = QFileInfo(m_d->document->localFilePath()).absoluteDir(); + fileName = baseDirectory.absoluteFilePath(fileName); + + QFileInfo info(fileName); + + if (!info.exists()) { + qApp->setOverrideCursor(Qt::ArrowCursor); + QString msg = i18nc( + "@info", + "Audio channel file \"%1\" doesn't exist!\n\n" + "Expected path:\n" + "%2\n\n" + "Do you want to locate it manually?", info.fileName(), info.absoluteFilePath()); + + int result = QMessageBox::warning(0, i18nc("@title:window", "File not found"), msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (result == QMessageBox::Yes) { + info.setFile(KisImportExportManager::askForAudioFileName(info.absolutePath(), 0)); + } + + qApp->restoreOverrideCursor(); + } + + if (info.exists()) { + image->animationInterface()->setAudioChannelFileName(info.absoluteFilePath()); + } + } + + bool audioMuted = false; + if (KisDomUtils::loadValue(qElement, "audioMuted", &audioMuted)) { + image->animationInterface()->setAudioMuted(audioMuted); + } + + qreal audioVolume = 0.5; + if (KisDomUtils::loadValue(qElement, "audioVolume", &audioVolume)) { + image->animationInterface()->setAudioVolume(audioVolume); + } +} diff --git a/plugins/impex/libkra/kis_kra_loader.h b/plugins/impex/libkra/kis_kra_loader.h index 168dc243ad..789cfdad2d 100644 --- a/plugins/impex/libkra/kis_kra_loader.h +++ b/plugins/impex/libkra/kis_kra_loader.h @@ -1,113 +1,114 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2007 * * 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; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_KRA_LOADER_H #define KIS_KRA_LOADER_H class QString; class QStringList; #include "KoXmlReaderForward.h" class KoStore; class KisDocument; class KoColorSpace; class KisPaintingAssistant; #include #include "kritalibkra_export.h" /** * Load old-style 1.x .kra files. Updated for 2.0, let's try to stay * compatible. But 2.0 won't be able to save 1.x .kra files unless we * implement an export filter. */ class KRITALIBKRA_EXPORT KisKraLoader { public: KisKraLoader(KisDocument * document, int syntaxVersion); ~KisKraLoader(); /** * Loading is done in two steps: first all xml is loaded, then, in finishLoading, * the actual layer data is loaded. */ KisImageSP loadXML(const KoXmlElement& elem); void loadBinaryData(KoStore* store, KisImageSP image, const QString & uri, bool external); vKisNodeSP selectedNodes() const; // it's neater to follow the same design as with selectedNodes, so let's have a getter here QList assistants() const; /// if empty, loading didn't fail... QStringList errorMessages() const; private: // this needs to be private, for neatness sake void loadAssistants(KoStore* store, const QString & uri, bool external); void loadAnimationMetadata(const KoXmlElement& element, KisImageSP image); KisNodeSP loadNodes(const KoXmlElement& element, KisImageSP image, KisNodeSP parent); KisNodeSP loadNode(const KoXmlElement& elem, KisImageSP image, KisNodeSP parent); KisNodeSP loadPaintLayer(const KoXmlElement& elem, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity); KisNodeSP loadGroupLayer(const KoXmlElement& elem, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity); KisNodeSP loadAdjustmentLayer(const KoXmlElement& elem, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity); KisNodeSP loadShapeLayer(const KoXmlElement& elem, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity); KisNodeSP loadGeneratorLayer(const KoXmlElement& elem, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity); KisNodeSP loadCloneLayer(const KoXmlElement& elem, KisImageSP image, const QString& name, const KoColorSpace* cs, quint32 opacity); KisNodeSP loadFilterMask(const KoXmlElement& elem, KisNodeSP parent); KisNodeSP loadTransformMask(const KoXmlElement& elem, KisNodeSP parent); KisNodeSP loadTransparencyMask(const KoXmlElement& elem, KisNodeSP parent); KisNodeSP loadSelectionMask(KisImageSP image, const KoXmlElement& elem, KisNodeSP parent); KisNodeSP loadColorizeMask(KisImageSP image, const KoXmlElement& elem, KisNodeSP parent, const KoColorSpace *colorSpace); KisNodeSP loadFileLayer(const KoXmlElement& elem, KisImageSP image, const QString& name, quint32 opacity); void loadNodeKeyframes(KoStore *store, const QString &location, KisNodeSP node); void loadCompositions(const KoXmlElement& elem, KisImageSP image); void loadAssistantsList(const KoXmlElement& elem); void loadGrid(const KoXmlElement& elem); void loadGuides(const KoXmlElement& elem); + void loadAudio(const KoXmlElement& elem, KisImageSP image); private: struct Private; Private * const m_d; }; #endif diff --git a/plugins/impex/libkra/kis_kra_saver.cpp b/plugins/impex/libkra/kis_kra_saver.cpp index 2484dcadce..bd749712ce 100644 --- a/plugins/impex/libkra/kis_kra_saver.cpp +++ b/plugins/impex/libkra/kis_kra_saver.cpp @@ -1,420 +1,452 @@ /* This file is part of the KDE project * Copyright 2008 (C) Boudewijn Rempt * * 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; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_kra_saver.h" #include "kis_kra_tags.h" #include "kis_kra_save_visitor.h" #include "kis_kra_savexml_visitor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_png_converter.h" #include "kis_keyframe_channel.h" #include #include "KisDocument.h" #include #include "kis_dom_utils.h" #include "kis_grid_config.h" #include "kis_guides_config.h" #include "KisProofingConfiguration.h" +#include +#include + using namespace KRA; struct KisKraSaver::Private { public: KisDocument* doc; QMap nodeFileNames; QMap keyframeFilenames; QString imageName; QStringList errorMessages; }; KisKraSaver::KisKraSaver(KisDocument* document) : m_d(new Private) { m_d->doc = document; m_d->imageName = m_d->doc->documentInfo()->aboutInfo("title"); if (m_d->imageName.isEmpty()) { m_d->imageName = i18n("Unnamed"); } } KisKraSaver::~KisKraSaver() { delete m_d; } QDomElement KisKraSaver::saveXML(QDomDocument& doc, KisImageSP image) { QDomElement imageElement = doc.createElement("IMAGE"); // Legacy! Q_ASSERT(image); imageElement.setAttribute(NAME, m_d->imageName); imageElement.setAttribute(MIME, NATIVE_MIMETYPE); imageElement.setAttribute(WIDTH, KisDomUtils::toString(image->width())); imageElement.setAttribute(HEIGHT, KisDomUtils::toString(image->height())); imageElement.setAttribute(COLORSPACE_NAME, image->colorSpace()->id()); imageElement.setAttribute(DESCRIPTION, m_d->doc->documentInfo()->aboutInfo("comment")); // XXX: Save profile as blob inside the image, instead of the product name. if (image->profile() && image->profile()-> valid()) { imageElement.setAttribute(PROFILE, image->profile()->name()); } imageElement.setAttribute(X_RESOLUTION, KisDomUtils::toString(image->xRes()*72.0)); imageElement.setAttribute(Y_RESOLUTION, KisDomUtils::toString(image->yRes()*72.0)); //now the proofing options: imageElement.setAttribute(PROOFINGPROFILENAME, KisDomUtils::toString(image->proofingConfiguration()->proofingProfile)); imageElement.setAttribute(PROOFINGMODEL, KisDomUtils::toString(image->proofingConfiguration()->proofingModel)); imageElement.setAttribute(PROOFINGDEPTH, KisDomUtils::toString(image->proofingConfiguration()->proofingDepth)); imageElement.setAttribute(PROOFINGINTENT, KisDomUtils::toString(image->proofingConfiguration()->intent)); imageElement.setAttribute(PROOFINGADAPTATIONSTATE, KisDomUtils::toString(image->proofingConfiguration()->adaptationState)); quint32 count = 1; // We don't save the root layer, but it does count KisSaveXmlVisitor visitor(doc, imageElement, count, m_d->doc->url().toLocalFile(), true); visitor.setSelectedNodes(m_d->doc->activeNodes()); image->rootLayer()->accept(visitor); m_d->errorMessages.append(visitor.errorMessages()); m_d->nodeFileNames = visitor.nodeFileNames(); m_d->keyframeFilenames = visitor.keyframeFileNames(); saveBackgroundColor(doc, imageElement, image); saveWarningColor(doc, imageElement, image); saveCompositions(doc, imageElement, image); saveAssistantsList(doc, imageElement); saveGrid(doc,imageElement); saveGuides(doc,imageElement); + saveAudio(doc,imageElement); QDomElement animationElement = doc.createElement("animation"); KisDomUtils::saveValue(&animationElement, "framerate", image->animationInterface()->framerate()); KisDomUtils::saveValue(&animationElement, "range", image->animationInterface()->fullClipRange()); KisDomUtils::saveValue(&animationElement, "currentTime", image->animationInterface()->currentUITime()); imageElement.appendChild(animationElement); return imageElement; } bool KisKraSaver::saveKeyframes(KoStore *store, const QString &uri, bool external) { QMap::iterator it; for (it = m_d->keyframeFilenames.begin(); it != m_d->keyframeFilenames.end(); it++) { const KisNode *node = it.key(); QString filename = it.value(); QString location = (external ? QString() : uri) + m_d->imageName + LAYER_PATH + filename; if (!saveNodeKeyframes(store, location, node)) { return false; } } return true; } bool KisKraSaver::saveNodeKeyframes(KoStore *store, QString location, const KisNode *node) { QDomDocument doc = KisDocument::createDomDocument("krita-keyframes", "keyframes", "1.0"); QDomElement root = doc.documentElement(); KisKeyframeChannel *channel; Q_FOREACH (channel, node->keyframeChannels()) { QDomElement element = channel->toXML(doc, m_d->nodeFileNames[node]); root.appendChild(element); } if (store->open(location)) { QByteArray xml = doc.toByteArray(); store->write(xml); store->close(); } else { m_d->errorMessages << i18n("could not save keyframes"); return false; } return true; } bool KisKraSaver::saveBinaryData(KoStore* store, KisImageSP image, const QString &uri, bool external, bool autosave) { QString location; // Save the layers data KisKraSaveVisitor visitor(store, m_d->imageName, m_d->nodeFileNames); if (external) visitor.setExternalUri(uri); image->rootLayer()->accept(visitor); m_d->errorMessages.append(visitor.errorMessages()); if (!m_d->errorMessages.isEmpty()) { return false; } // saving annotations // XXX this only saves EXIF and ICC info. This would probably need // a redesign of the dtd of the krita file to do this more generally correct // e.g. have tags or so. KisAnnotationSP annotation = image->annotation("exif"); if (annotation) { location = external ? QString() : uri; location += m_d->imageName + EXIF_PATH; if (store->open(location)) { store->write(annotation->annotation()); store->close(); } } if (image->profile()) { const KoColorProfile *profile = image->profile(); KisAnnotationSP annotation; if (profile) { QByteArray profileRawData = profile->rawData(); if (!profileRawData.isEmpty()) { if (profile->type() == "icc") { annotation = new KisAnnotation(ICC, profile->name(), profile->rawData()); } else { annotation = new KisAnnotation(PROFILE, profile->name(), profile->rawData()); } } } if (annotation) { location = external ? QString() : uri; location += m_d->imageName + ICC_PATH; if (store->open(location)) { store->write(annotation->annotation()); store->close(); } } } //This'll embed the profile used for proofing into the kra file. if (image->proofingConfiguration()) { const KoColorProfile *proofingProfile = KoColorSpaceRegistry::instance()->profileByName(image->proofingConfiguration()->proofingProfile); if (proofingProfile && proofingProfile->valid()) { QByteArray proofingProfileRaw = proofingProfile->rawData(); if (!proofingProfileRaw.isEmpty()) { annotation = new KisAnnotation(ICCPROOFINGPROFILE, proofingProfile->name(), proofingProfile->rawData()); } } if (annotation) { location = external ? QString() : uri; location += m_d->imageName + ICC_PROOFING_PATH; if (store->open(location)) { store->write(annotation->annotation()); store->close(); } } } { KisPSDLayerStyleCollectionResource collection("not-nexists.asl"); KIS_ASSERT_RECOVER_NOOP(!collection.valid()); collection.collectAllLayerStyles(image->root()); if (collection.valid()) { location = external ? QString() : uri; location += m_d->imageName + LAYER_STYLES_PATH; if (store->open(location)) { QBuffer aslBuffer; aslBuffer.open(QIODevice::WriteOnly); collection.saveToDevice(&aslBuffer); aslBuffer.close(); store->write(aslBuffer.buffer()); store->close(); } } } if (!autosave) { KisPaintDeviceSP dev = image->projection(); KisPNGConverter::saveDeviceToStore("mergedimage.png", image->bounds(), image->xRes(), image->yRes(), dev, store); } saveAssistants(store, uri,external); return true; } QStringList KisKraSaver::errorMessages() const { return m_d->errorMessages; } void KisKraSaver::saveBackgroundColor(QDomDocument& doc, QDomElement& element, KisImageSP image) { QDomElement e = doc.createElement(CANVASPROJECTIONCOLOR); KoColor color = image->defaultProjectionColor(); QByteArray colorData = QByteArray::fromRawData((const char*)color.data(), color.colorSpace()->pixelSize()); e.setAttribute(COLORBYTEDATA, QString(colorData.toBase64())); element.appendChild(e); } void KisKraSaver::saveWarningColor(QDomDocument& doc, QDomElement& element, KisImageSP image) { if (image->proofingConfiguration()) { QDomElement e = doc.createElement(PROOFINGWARNINGCOLOR); KoColor color = image->proofingConfiguration()->warningColor; color.toXML(doc, e); //QByteArray colorData = QByteArray::fromRawData((const char*)color.data(), color.colorSpace()->pixelSize()); //e.setAttribute("ColorData", QString(colorData.toBase64())); element.appendChild(e); } } void KisKraSaver::saveCompositions(QDomDocument& doc, QDomElement& element, KisImageSP image) { if (!image->compositions().isEmpty()) { QDomElement e = doc.createElement("compositions"); Q_FOREACH (KisLayerCompositionSP composition, image->compositions()) { composition->save(doc, e); } element.appendChild(e); } } bool KisKraSaver::saveAssistants(KoStore* store, QString uri, bool external) { QString location; QMap assistantcounters; QByteArray data; QList assistants = m_d->doc->assistants(); QMap handlemap; if (!assistants.isEmpty()) { Q_FOREACH (KisPaintingAssistantSP assist, assistants){ if (!assistantcounters.contains(assist->id())){ assistantcounters.insert(assist->id(),0); } location = external ? QString() : uri; location += m_d->imageName + ASSISTANTS_PATH; location += QString(assist->id()+"%1.assistant").arg(assistantcounters[assist->id()]); data = assist->saveXml(handlemap); store->open(location); store->write(data); store->close(); assistantcounters[assist->id()]++; } } return true; } bool KisKraSaver::saveAssistantsList(QDomDocument& doc, QDomElement& element) { int count_ellipse = 0, count_perspective = 0, count_ruler = 0, count_vanishingpoint = 0,count_infiniteruler = 0, count_parallelruler = 0, count_concentricellipse = 0, count_fisheyepoint = 0, count_spline = 0; QList assistants = m_d->doc->assistants(); if (!assistants.isEmpty()) { QDomElement assistantsElement = doc.createElement("assistants"); Q_FOREACH (KisPaintingAssistantSP assist, assistants){ if (assist->id() == "ellipse"){ assist->saveXmlList(doc, assistantsElement, count_ellipse); count_ellipse++; } else if (assist->id() == "spline"){ assist->saveXmlList(doc, assistantsElement, count_spline); count_spline++; } else if (assist->id() == "perspective"){ assist->saveXmlList(doc, assistantsElement, count_perspective); count_perspective++; } else if (assist->id() == "vanishing point"){ assist->saveXmlList(doc, assistantsElement, count_vanishingpoint); count_vanishingpoint++; } else if (assist->id() == "infinite ruler"){ assist->saveXmlList(doc, assistantsElement, count_infiniteruler); count_infiniteruler++; } else if (assist->id() == "parallel ruler"){ assist->saveXmlList(doc, assistantsElement, count_parallelruler); count_parallelruler++; } else if (assist->id() == "concentric ellipse"){ assist->saveXmlList(doc, assistantsElement, count_concentricellipse); count_concentricellipse++; } else if (assist->id() == "fisheye-point"){ assist->saveXmlList(doc, assistantsElement, count_fisheyepoint); count_fisheyepoint++; } else if (assist->id() == "ruler"){ assist->saveXmlList(doc, assistantsElement, count_ruler); count_ruler++; } } element.appendChild(assistantsElement); } return true; } bool KisKraSaver::saveGrid(QDomDocument& doc, QDomElement& element) { KisGridConfig config = m_d->doc->gridConfig(); if (!config.isDefault()) { QDomElement gridElement = config.saveDynamicDataToXml(doc, "grid"); element.appendChild(gridElement); } return true; } bool KisKraSaver::saveGuides(QDomDocument& doc, QDomElement& element) { KisGuidesConfig guides = m_d->doc->guidesConfig(); if (guides.hasGuides()) { QDomElement guidesElement = guides.saveToXml(doc, "guides"); element.appendChild(guidesElement); } return true; } +bool KisKraSaver::saveAudio(QDomDocument& doc, QDomElement& element) +{ + const KisImageAnimationInterface *interface = m_d->doc->image()->animationInterface(); + QString fileName = interface->audioChannelFileName(); + if (fileName.isEmpty()) return true; + + if (!QFileInfo::exists(fileName)) { + m_d->errorMessages << i18n("Audio channel file %1 doesn't exist!", fileName); + return false; + } + + const QDir documentDir = QFileInfo(m_d->doc->localFilePath()).absoluteDir(); + KIS_ASSERT_RECOVER_RETURN_VALUE(documentDir.exists(), false); + + fileName = documentDir.relativeFilePath(fileName); + fileName = QDir::fromNativeSeparators(fileName); + + KIS_ASSERT_RECOVER_RETURN_VALUE(!fileName.isEmpty(), false); + + QDomElement audioElement = doc.createElement("audio"); + KisDomUtils::saveValue(&audioElement, "masterChannelPath", fileName); + KisDomUtils::saveValue(&audioElement, "audioMuted", interface->isAudioMuted()); + KisDomUtils::saveValue(&audioElement, "audioVolume", interface->audioVolume()); + element.appendChild(audioElement); + + return true; +} + diff --git a/plugins/impex/libkra/kis_kra_saver.h b/plugins/impex/libkra/kis_kra_saver.h index 810abcfd7c..ae8368b957 100644 --- a/plugins/impex/libkra/kis_kra_saver.h +++ b/plugins/impex/libkra/kis_kra_saver.h @@ -1,63 +1,64 @@ /* This file is part of the KDE project * Copyright 2008 (C) Boudewijn Rempt * * 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; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_KRA_SAVER #define KIS_KRA_SAVER #include class KisDocument; class QDomElement; class QDomDocument; class KoStore; class QString; class QStringList; #include "kritalibkra_export.h" class KRITALIBKRA_EXPORT KisKraSaver { public: KisKraSaver(KisDocument* document); ~KisKraSaver(); QDomElement saveXML(QDomDocument& doc, KisImageSP image); bool saveKeyframes(KoStore *store, const QString &uri, bool external); bool saveBinaryData(KoStore* store, KisImageSP image, const QString & uri, bool external, bool includeMerge); /// @return a list with everthing that went wrong while saving QStringList errorMessages() const; private: void saveBackgroundColor(QDomDocument& doc, QDomElement& element, KisImageSP image); void saveWarningColor(QDomDocument& doc, QDomElement& element, KisImageSP image); void saveCompositions(QDomDocument& doc, QDomElement& element, KisImageSP image); bool saveAssistants(KoStore *store,QString uri, bool external); bool saveAssistantsList(QDomDocument& doc, QDomElement& element); bool saveGrid(QDomDocument& doc, QDomElement& element); bool saveGuides(QDomDocument& doc, QDomElement& element); + bool saveAudio(QDomDocument& doc, QDomElement& element); bool saveNodeKeyframes(KoStore *store, QString location, const KisNode *node); struct Private; Private * const m_d; }; #endif diff --git a/plugins/impex/video/video_saver.cpp b/plugins/impex/video/video_saver.cpp index f04d662a66..1ee24db8e6 100644 --- a/plugins/impex/video/video_saver.cpp +++ b/plugins/impex/video/video_saver.cpp @@ -1,303 +1,330 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * 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. */ #include "video_saver.h" #include #include #include #include #include #include #include #include #include #include #include "kis_config.h" #include "kis_animation_exporter.h" #include #include #include #include #include #include +#include #include "KisPart.h" class KisFFMpegProgressWatcher : public QObject { Q_OBJECT public: KisFFMpegProgressWatcher(QFile &progressFile, int totalFrames) : m_progressFile(progressFile), m_totalFrames(totalFrames) { connect(&m_progressWatcher, SIGNAL(fileChanged(QString)), SLOT(slotFileChanged())); m_progressWatcher.addPath(m_progressFile.fileName()); } private Q_SLOTS: void slotFileChanged() { int currentFrame = -1; bool isEnded = false; while(!m_progressFile.atEnd()) { QString line = QString(m_progressFile.readLine()).remove(QChar('\n')); QStringList var = line.split("="); if (var[0] == "frame") { currentFrame = var[1].toInt(); } else if (var[0] == "progress") { isEnded = var[1] == "end"; } } if (isEnded) { emit sigProgressChanged(100); emit sigProcessingFinished(); } else { emit sigProgressChanged(100 * currentFrame / m_totalFrames); } } Q_SIGNALS: void sigProgressChanged(int percent); void sigProcessingFinished(); private: QFileSystemWatcher m_progressWatcher; QFile &m_progressFile; int m_totalFrames; }; class KisFFMpegRunner { public: KisFFMpegRunner(const QString &ffmpegPath) : m_cancelled(false), m_ffmpegPath(ffmpegPath) {} public: KisImageBuilder_Result runFFMpeg(const QStringList &specialArgs, const QString &actionName, const QString &logPath, int totalFrames) { dbgFile << "runFFMpeg: specialArgs" << specialArgs << "actionName" << actionName << "logPath" << logPath << "totalFrames" << totalFrames; QTemporaryFile progressFile(QDir::tempPath() + QDir::separator() + "KritaFFmpegProgress.XXXXXX"); progressFile.open(); m_process.setStandardOutputFile(logPath); m_process.setProcessChannelMode(QProcess::MergedChannels); QStringList args; args << "-v" << "debug" << "-nostdin" << "-progress" << progressFile.fileName() << specialArgs; qDebug() << "\t" << m_ffmpegPath << args.join(" "); m_cancelled = false; m_process.start(m_ffmpegPath, args); return waitForFFMpegProcess(actionName, progressFile, m_process, totalFrames); } void cancel() { m_cancelled = true; m_process.kill(); } private: KisImageBuilder_Result waitForFFMpegProcess(const QString &message, QFile &progressFile, QProcess &ffmpegProcess, int totalFrames) { KisFFMpegProgressWatcher watcher(progressFile, totalFrames); QProgressDialog progress(message, "", 0, 0, KisPart::instance()->currentMainwindow()); progress.setWindowModality(Qt::ApplicationModal); progress.setCancelButton(0); progress.setMinimumDuration(0); progress.setValue(0); progress.setRange(0, 100); QEventLoop loop; loop.connect(&watcher, SIGNAL(sigProcessingFinished()), SLOT(quit())); loop.connect(&ffmpegProcess, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(quit())); loop.connect(&watcher, SIGNAL(sigProgressChanged(int)), &progress, SLOT(setValue(int))); loop.exec(); // wait for some errorneous case ffmpegProcess.waitForFinished(5000); KisImageBuilder_Result retval = KisImageBuilder_RESULT_OK; if (ffmpegProcess.state() != QProcess::NotRunning) { // sorry... ffmpegProcess.kill(); retval = KisImageBuilder_RESULT_FAILURE; } else if (m_cancelled) { retval = KisImageBuilder_RESULT_CANCEL; } else if (ffmpegProcess.exitCode()) { retval = KisImageBuilder_RESULT_FAILURE; } return retval; } private: QProcess m_process; bool m_cancelled; QString m_ffmpegPath; }; VideoSaver::VideoSaver(KisDocument *doc, const QString &ffmpegPath, bool batchMode) : m_image(doc->image()) , m_doc(doc) , m_batchMode(batchMode) , m_ffmpegPath(ffmpegPath) , m_runner(new KisFFMpegRunner(ffmpegPath)) { } VideoSaver::~VideoSaver() { } KisImageSP VideoSaver::image() { return m_image; } bool VideoSaver::hasFFMpeg() const { return !m_ffmpegPath.isEmpty(); } KisImageBuilder_Result VideoSaver::encode(const QString &filename, KisPropertiesConfigurationSP configuration) { qDebug() << "ffmpeg" << m_ffmpegPath << "filename" << filename << "configuration" << configuration->toXML(); if (m_ffmpegPath.isEmpty()) { m_ffmpegPath = configuration->getString("ffmpeg_path"); if (!QFileInfo(m_ffmpegPath).exists()) { return KisImageBuilder_RESULT_FAILURE; } } KisImageBuilder_Result result = KisImageBuilder_RESULT_OK; KisImageAnimationInterface *animation = m_image->animationInterface(); const KisTimeRange fullRange = animation->fullClipRange(); - const KisTimeRange clipRange(configuration->getInt("firstframe", fullRange.start()), configuration->getInt("lastFrame"), fullRange.end()); const int frameRate = animation->framerate(); + KIS_SAFE_ASSERT_RECOVER_NOOP(configuration->hasProperty("first_frame")); + KIS_SAFE_ASSERT_RECOVER_NOOP(configuration->hasProperty("last_frame")); + KIS_SAFE_ASSERT_RECOVER_NOOP(configuration->hasProperty("include_audio")); + + const KisTimeRange clipRange(configuration->getInt("first_frame", fullRange.start()), configuration->getInt("last_frame", fullRange.end())); + const bool includeAudio = configuration->getBool("include_audio", true); + const QDir framesDir(configuration->getString("directory")); QString resultFile; if (QFileInfo(filename).isAbsolute()) { resultFile = filename; } else { resultFile = framesDir.absolutePath() + "/" + filename; } const QFileInfo info(resultFile); const QString suffix = info.suffix().toLower(); const QString palettePath = framesDir.filePath("palette.png"); const QString savedFilesMask = configuration->getString("savedFilesMask"); const QStringList additionalOptionsList = configuration->getString("customUserOptions").split(' ', QString::SkipEmptyParts); if (suffix == "gif") { { QStringList args; args << "-r" << QString::number(frameRate) + << "-pattern_type" << "glob" << "-i" << savedFilesMask << "-vf" << "palettegen" << "-y" << palettePath; KisImageBuilder_Result result = m_runner->runFFMpeg(args, i18n("Fetching palette..."), framesDir.filePath("log_generate_palette_gif.log"), clipRange.duration() + clipRange.start()); if (result != KisImageBuilder_RESULT_OK) { return result; } } { QStringList args; args << "-r" << QString::number(frameRate) + << "-pattern_type" << "glob" << "-i" << savedFilesMask << "-i" << palettePath << "-lavfi" << "[0:v][1:v] paletteuse" << additionalOptionsList << "-y" << resultFile; dbgFile << "savedFilesMask" << savedFilesMask << "start" << clipRange.start() << "duration" << clipRange.duration(); KisImageBuilder_Result result = m_runner->runFFMpeg(args, i18n("Encoding frames..."), framesDir.filePath("log_encode_gif.log"), clipRange.duration() + clipRange.start()); if (result != KisImageBuilder_RESULT_OK) { return result; } } } else { QStringList args; - args << "-r" << QString::number(frameRate) - << "-i" << savedFilesMask - << additionalOptionsList + args << "-r" << QString::number(frameRate); + args << "-pattern_type" << "glob"; + args << "-i" << savedFilesMask; + + + QFileInfo audioFileInfo = animation->audioChannelFileName(); + if (includeAudio && audioFileInfo.exists()) { + const int msecStart = clipRange.start() * 1000 / animation->framerate(); + const int msecDuration = clipRange.duration() * 1000 / animation->framerate(); + + const QTime startTime = QTime::fromMSecsSinceStartOfDay(msecStart); + const QTime durationTime = QTime::fromMSecsSinceStartOfDay(msecDuration); + const QString ffmpegTimeFormat("H:m:s.zzz"); + + args << "-ss" << startTime.toString(ffmpegTimeFormat); + args << "-t" << durationTime.toString(ffmpegTimeFormat); + + args << "-i" << audioFileInfo.absoluteFilePath(); + } + + args << additionalOptionsList << "-y" << resultFile; result = m_runner->runFFMpeg(args, i18n("Encoding frames..."), framesDir.filePath("log_encode.log"), clipRange.duration() + clipRange.start()); } return result; } void VideoSaver::cancel() { m_runner->cancel(); } #include "video_saver.moc"