diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 7b0525d989..568f805628 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -1,212 +1,217 @@ 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(NOT SUBMAKE_JOBS) set(SUBMAKE_JOBS 1) endif(NOT SUBMAKE_JOBS) 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 + 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 (MINGW) option(QT_ENABLE_DEBUG_INFO "Build Qt with debug info included" OFF) option(QT_ENABLE_DYNAMIC_OPENGL "Build Qt with dynamic ANGLE support '-opengl dynamic -angle' (needs env var 'WindowsSdkDir' set to path of Windows 10 SDK)" ON) if (QT_ENABLE_DYNAMIC_OPENGL) if (DEFINED ENV{WindowsSdkDir}) message(STATUS "WindowsSdkDir is set to '$ENV{WindowsSdkDir}'") else (DEFINED ENV{WindowsSdkDir}) message(FATAL_ERROR "Environment variable 'WindowsSdkDir' not set! Please set it to path of Windows 10 SDK or disable QT_ENABLE_DYNAMIC_OPENGL") endif (DEFINED ENV{WindowsSdkDir}) endif (QT_ENABLE_DYNAMIC_OPENGL) endif (MINGW) set(SECURITY_EXE_LINKER_FLAGS "") set(SECURITY_SHARED_LINKER_FLAGS "") set(SECURITY_MODULE_LINKER_FLAGS "") if (MINGW) option(USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags (mingw-w64)" ON) if (USE_MINGW_HARDENING_LINKER) set(SECURITY_EXE_LINKER_FLAGS "-Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(SECURITY_SHARED_LINKER_FLAGS "-Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(SECURITY_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(SECURITY_EXE_LINKER_FLAGS "${SECURITY_EXE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x140000000") set(SECURITY_SHARED_LINKER_FLAGS "${SECURITY_SHARED_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") set(SECURITY_MODULE_LINKER_FLAGS "${SECURITY_MODULE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") set(GLOBAL_PROFILE ${GLOBAL_PROFILE} -DCMAKE_EXE_LINKER_FLAGS=${SECURITY_EXE_LINKER_FLAGS} -DCMAKE_SHARED_LINKER_FLAGS=${SECURITY_SHARED_LINKER_FLAGS} -DCMAKE_MODULE_LINKER_FLAGS=${SECURITY_MODULE_LINKER_FLAGS} ) 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) 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} + 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_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() +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 () function(TestCompileLinkPythonLibs OUTPUT_VARNAME) include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_INCLUDES ${PYTHON_INCLUDE_PATH}) set(CMAKE_REQUIRED_LIBRARIES ${PYTHON_LIBRARIES}) if (MINGW) set(CMAKE_REQUIRED_DEFINITIONS -D_hypot=hypot) endif (MINGW) unset(${OUTPUT_VARNAME} CACHE) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { Py_InitializeEx(0); }" ${OUTPUT_VARNAME}) endfunction() if (MINGW) option(ENABLE_PYTHON_DEPS "Enable Python deps (sip, pyqt)" ON) if (ENABLE_PYTHON_DEPS) - find_package(PythonInterp 3.6 EXACT) - find_package(PythonLibs 3.6 EXACT) + if (ENABLE_PYTHON_2) + message(FATAL_ERROR "Python 2.7 is not supported on Windows at the moment.") + else(ENABLE_PYTHON_2) + find_package(PythonInterp 3.6 EXACT) + find_package(PythonLibs 3.6 EXACT) + endif(ENABLE_PYTHON_2) if (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) message(STATUS "Python requirements met.") TestCompileLinkPythonLibs(CAN_USE_PYTHON_LIBS) if (NOT CAN_USE_PYTHON_LIBS) message(FATAL_ERROR "Compiling with Python library failed, please check whether the architecture is correct!") endif (NOT CAN_USE_PYTHON_LIBS) else (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) message(FATAL_ERROR "Python requirements not met. To disable Python deps, set ENABLE_PYTHON_DEPS to OFF.") endif (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) endif (ENABLE_PYTHON_DEPS) endif (MINGW) # this list must be dependency-ordered if (ENABLE_PYTHON_DEPS OR NOT MINGW) add_subdirectory( ext_python ) endif (ENABLE_PYTHON_DEPS OR NOT MINGW) 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_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 ) if (ENABLE_PYTHON_DEPS OR NOT MINGW) add_subdirectory( ext_sip ) add_subdirectory( ext_pyqt ) endif (ENABLE_PYTHON_DEPS OR NOT MINGW) if (MSVC OR MINGW) add_subdirectory( ext_drmingw ) +# add_subdirectory( ext_ffmpeg ) endif (MSVC OR MINGW) if (NOT APPLE) add_subdirectory( ext_gmic ) endif (NOT APPLE) add_subdirectory(ext_giflib) diff --git a/3rdparty/ext_exiv2/CMakeLists.txt b/3rdparty/ext_exiv2/CMakeLists.txt index 262789f0c5..29c51c7ed6 100644 --- a/3rdparty/ext_exiv2/CMakeLists.txt +++ b/3rdparty/ext_exiv2/CMakeLists.txt @@ -1,16 +1,16 @@ SET(PREFIX_ext_exiv2 "${EXTPREFIX}" ) ExternalProject_Add( ext_exiv2 DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://www.exiv2.org/builds/exiv2-0.26-trunk.tar.gz + URL http://files.kde.org/krita/build/dependencies/exiv2-0.26-trunk.tar.gz URL_MD5 5399e3b570d7f9205f0e76d47582da4c PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/tzname.patch COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/patch_mingw.patch INSTALL_DIR ${PREFIX_ext_exiv2} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${PREFIX_ext_exiv2} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DEXIV2_ENABLE_BUILD_SAMPLES=OFF -DEXIV2_ENABLE_BUILD_PO=OFF -DEXIV2_ENABLE_NLS=OFF -DICONV_INCLUDE_DIR=${PREFIX_ext_exiv2}/include UPDATE_COMMAND "" DEPENDS ext_iconv ext_expat ) diff --git a/3rdparty/ext_ffmpeg/CMakeLists.txt b/3rdparty/ext_ffmpeg/CMakeLists.txt new file mode 100644 index 0000000000..2e60c26203 --- /dev/null +++ b/3rdparty/ext_ffmpeg/CMakeLists.txt @@ -0,0 +1,34 @@ +SET(PREFIX_ext_ffmpeg "${EXTPREFIX}" ) + +if(MSVC OR MINGW) + if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + ExternalProject_Add( ext_ffmpeg + DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} + URL https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-4.0-win64-static.zip + URL_MD5 bf496481c6991c529e2e94a8e0fa3113 + + INSTALL_DIR ${PREFIX_ext_ffmpeg} + CONFIGURE_COMMAND "" + BUILD_COMMAND ${CMAKE_COMMAND} -E echo deploying ffmpeg3 64-bit binary + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy /bin/ffmpeg.exe ${PREFIX_ext_ffmpeg}/bin/ffmpeg.exe + COMMAND ${CMAKE_COMMAND} -E copy /LICENSE.txt ${PREFIX_ext_ffmpeg}/bin/ffmpeg_LICENSE.txt + COMMAND ${CMAKE_COMMAND} -E copy /README.txt ${PREFIX_ext_ffmpeg}/bin/ffmpeg_README.txt + UPDATE_COMMAND "" + ) + else("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + ExternalProject_Add( ext_ffmpeg + DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} + URL https://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-4.0-win32-static.zip + URL_MD5 a969a969e3404fe35100e85a37186e5f + + INSTALL_DIR ${PREFIX_ext_ffmpeg} + CONFIGURE_COMMAND "" + BUILD_COMMAND ${CMAKE_COMMAND} -E echo deploying ffmpeg3 32-bit binary + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy /bin/ffmpeg.exe ${PREFIX_ext_ffmpeg}/bin/ffmpeg.exe + COMMAND ${CMAKE_COMMAND} -E copy /LICENSE.txt ${PREFIX_ext_ffmpeg}/bin/ffmpeg_LICENSE.txt + COMMAND ${CMAKE_COMMAND} -E copy /README.txt ${PREFIX_ext_ffmpeg}/bin/ffmpeg_README.txt + UPDATE_COMMAND "" + ) + endif("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + +endif() diff --git a/3rdparty/ext_frameworks/CMakeLists.txt b/3rdparty/ext_frameworks/CMakeLists.txt index ee7097a35a..db29086e91 100644 --- a/3rdparty/ext_frameworks/CMakeLists.txt +++ b/3rdparty/ext_frameworks/CMakeLists.txt @@ -1,243 +1,241 @@ SET(EXTPREFIX_frameworks "${EXTPREFIX}" ) # # All needed frameworks: # # Archive # Config # WidgetsAddons # Completion # CoreAddons # GuiAddons # I18n # ItemModels # ItemViews # WindowSystem # On Linux: # KCrash ExternalProject_Add( ext_extra_cmake_modules DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://download.kde.org/Attic/frameworks/5.24/extra-cmake-modules-5.24.0.zip - URL_MD5 e0c19ba97ebd964f9bdc9110c64ce96a + URL http://download.kde.org/stable/frameworks/5.44/extra-cmake-modules-5.44.0.zip + URL_MD5 74aa8fc501e27024390b01c81f2925eb PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/ecm_install_to_share.diff INSTALL_DIR ${EXTPREFIX_frameworks} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" ) ExternalProject_Add( ext_karchive DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://download.kde.org/Attic/frameworks/5.24/karchive-5.24.0.zip - URL_MD5 739843accfe9bd85ab2f1582722cf01e + URL http://download.kde.org/stable/frameworks/5.44/karchive-5.44.0.zip + URL_MD5 c60a8e22b88cc7328610041638459689 INSTALL_DIR ${EXTPREFIX_frameworks} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_extra_cmake_modules ) ExternalProject_Add( ext_kconfig DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://download.kde.org/Attic/frameworks/5.24/kconfig-5.24.0.zip - URL_MD5 f87ecff795eb76e4ec6561758a5baf87 + URL http://download.kde.org/stable/frameworks/5.44/kconfig-5.44.0.zip + URL_MD5 d0223ea471bbf463ec42c2a2355a5183 PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/kconfig.diff INSTALL_DIR ${EXTPREFIX_frameworks} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_karchive ) ExternalProject_Add( ext_kwidgetsaddons DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://download.kde.org/Attic/frameworks/5.24/kwidgetsaddons-5.24.0.zip - URL_MD5 0e399b427814a4814c65a3cf407f9d79 + URL http://download.kde.org/stable/frameworks/5.44/kwidgetsaddons-5.44.0.zip + URL_MD5 a9911d8d0f8aaf7a7afd84c41c8f80a1 INSTALL_DIR ${EXTPREFIX_frameworks} PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/kwidgetsaddons.diff CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_kconfig ) ExternalProject_Add( ext_kcompletion DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://download.kde.org/Attic/frameworks/5.24/kcompletion-5.24.0.zip - URL_MD5 e8764251ab45005aa81dba242852300c + URL http://download.kde.org/stable/frameworks/5.44/kcompletion-5.44.0.zip + URL_MD5 0647885a702c338a1b656eb4f311ad16 INSTALL_DIR ${EXTPREFIX_frameworks} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_kwidgetsaddons ) ExternalProject_Add( ext_kcoreaddons DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://download.kde.org/Attic/frameworks/5.24/kcoreaddons-5.24.0.zip - URL_MD5 2885878625b19ad0300ef3770b897112 + URL http://download.kde.org/stable/frameworks/5.44/kcoreaddons-5.44.0.zip + URL_MD5 16a7379f3e2941d1c19d6f80939f15e8 INSTALL_DIR ${EXTPREFIX_frameworks} PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/desktoptojson.diff - COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/kcoreaddons-compile-fix.diff CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_kcompletion ) ExternalProject_Add( ext_kguiaddons DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://download.kde.org/Attic/frameworks/5.24/kguiaddons-5.24.0.zip - URL_MD5 9bdadbc57d0634816ef80ee9798c3d6c + URL http://download.kde.org/stable/frameworks/5.44/kguiaddons-5.44.0.zip + URL_MD5 440eefbf5abcafc492dcf857f7e4eaf5 INSTALL_DIR ${EXTPREFIX_frameworks} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_kcoreaddons ) if(APPLE) ExternalProject_Add( ext_ki18n DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://download.kde.org/Attic/frameworks/5.17/ki18n-5.17.0.zip - URL_MD5 7d60380d09f98defbf878ea9daba0fbb + URL http://download.kde.org/stable/frameworks/5.44/ki18n-5.44.0.zip + URL_MD5 333ab0a3f65a298e928d746144d4dc8e INSTALL_DIR ${EXTPREFIX_frameworks} - PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/ki18n.diff - COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/ki18n-appdatalocation.diff + PATCH_COMMAND COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/ki18n-appdatalocation.diff CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_kguiaddons ext_gettext ) else() ExternalProject_Add( ext_ki18n DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://download.kde.org/Attic/frameworks/5.17/ki18n-5.17.0.zip - URL_MD5 7d60380d09f98defbf878ea9daba0fbb + URL http://download.kde.org/stable/frameworks/5.44/ki18n-5.44.0.zip + URL_MD5 333ab0a3f65a298e928d746144d4dc8e INSTALL_DIR ${EXTPREFIX_frameworks} - PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/ki18n.diff - COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/ki18n-appdatalocation.diff + PATCH_COMMAND COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/ki18n-appdatalocation.diff CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_kguiaddons ) endif() + ExternalProject_Add( ext_kitemmodels DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://download.kde.org/Attic/frameworks/5.24/kitemmodels-5.24.0.zip - URL_MD5 ff41589f48395fc01d5fc7887593779d + URL http://download.kde.org/stable/frameworks/5.44/kitemmodels-5.44.0.zip + URL_MD5 ea43a5e2cc7033eb672796b108d7403b INSTALL_DIR ${EXTPREFIX_frameworks} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_ki18n ) ExternalProject_Add( ext_kitemviews DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://download.kde.org/Attic/frameworks/5.24/kitemviews-5.24.0.zip - URL_MD5 33f638d027a3011a6a69f7484eee3287 + URL http://download.kde.org/stable/frameworks/5.44/kitemviews-5.44.0.zip + URL_MD5 8b15c703313c7a790c7db897ef17de7d INSTALL_DIR ${EXTPREFIX_frameworks} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_kitemmodels ) ExternalProject_Add( ext_kimageformats DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://download.kde.org/Attic/frameworks/5.24/kimageformats-5.24.0.zip - URL_MD5 c1964516bcb2bfe882858f0c0913deb5 + URL http://download.kde.org/stable/frameworks/5.44/kimageformats-5.44.0.zip + URL_MD5 02a98b682f9cb655592148d7ebcc05e7 INSTALL_DIR ${EXTPREFIX_frameworks} PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/kimageformats.diff CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_kitemviews ) ExternalProject_Add( ext_kwindowsystem DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://download.kde.org/Attic/frameworks/5.24/kwindowsystem-5.24.0.zip - URL_MD5 5915e4f63ded983af6db7db3a6cbae1a + URL http://download.kde.org/stable/frameworks/5.44/kwindowsystem-5.44.0.zip + URL_MD5 75329f47cf8cd413fa1d15a57c298563 INSTALL_DIR ${EXTPREFIX_frameworks} PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/kwindowsystem-x11.diff CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_kimageformats ) ExternalProject_Add( ext_kcrash DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://download.kde.org/Attic/frameworks/5.24/kcrash-5.24.0.zip - URL_MD5 a2e41e6650105fc3ac8fbd44afbae4fe + URL http://download.kde.org/stable/frameworks/5.44/kcrash-5.44.0.zip + URL_MD5 61adc0e125c65288968d958acf25f4aa INSTALL_DIR ${EXTPREFIX_frameworks} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_kwindowsystem ) diff --git a/3rdparty/ext_frameworks/kconfig.diff b/3rdparty/ext_frameworks/kconfig.diff index 75df9fd9d3..df589aa57b 100644 --- a/3rdparty/ext_frameworks/kconfig.diff +++ b/3rdparty/ext_frameworks/kconfig.diff @@ -1,133 +1,144 @@ diff --git a/autotests/kconfigtest.cpp b/autotests/kconfigtest.cpp -index 2b905b5..8c72d58 100644 +index 3e0578f..6c4408d 100644 --- a/autotests/kconfigtest.cpp +++ b/autotests/kconfigtest.cpp -@@ -570,7 +570,7 @@ void KConfigTest::testPathQtHome() +@@ -587,7 +587,7 @@ void KConfigTest::testPathQtHome() qunsetenv("QT_CACHE_HOME"); qunsetenv("QT_CONFIG_HOME"); QVERIFY(group.hasKey("dataDir")); - QCOMPARE(group.readEntry("dataDir", QString()), QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation).append(QStringLiteral("/kconfigtest"))); + QCOMPARE(group.readEntry("dataDir", QString()), QStandardPaths::writableLocation(QStandardPaths::AppDataLocation).append(QStringLiteral("/kconfigtest"))); QVERIFY(group.hasKey("cacheDir")); QCOMPARE(group.readEntry("cacheDir", QString()), QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation).append(QStringLiteral("/kconfigtest"))); QVERIFY(group.hasKey("configDir")); diff --git a/autotests/kdesktopfiletest.cpp b/autotests/kdesktopfiletest.cpp -index 393a6a0..083626d 100644 +index fd4a5c9..db08f22 100644 --- a/autotests/kdesktopfiletest.cpp +++ b/autotests/kdesktopfiletest.cpp -@@ -201,7 +201,7 @@ void KDesktopFileTest::testIsAuthorizedDesktopFile() +@@ -222,7 +222,7 @@ void KDesktopFileTest::testIsAuthorizedDesktopFile() QVERIFY(QFile::exists(fileName)); QVERIFY(!KDesktopFile::isAuthorizedDesktopFile(fileName)); - const QString installedFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("/kservices5/http_cache_cleaner.desktop")); + const QString installedFile = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("/kservices5/http_cache_cleaner.desktop")); if (!installedFile.isEmpty()) { QVERIFY(KDesktopFile::isAuthorizedDesktopFile(installedFile)); } else { +@@ -281,8 +281,8 @@ void KDesktopFileTest::testLocateLocal_data() + { + QString systemConfigLocation = QStandardPaths::standardLocations(QStandardPaths::GenericConfigLocation).last(); + QString writableConfigLocation = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); +- QString systemDataLocation = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation).last(); +- QString writableDataLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); ++ QString systemDataLocation = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).last(); ++ QString writableDataLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + + QTest::addColumn("path"); + QTest::addColumn("result"); diff --git a/autotests/test_kconf_update.cpp b/autotests/test_kconf_update.cpp -index 4180619..32cd88a 100644 +index 3353061..a80ae1e 100644 --- a/autotests/test_kconf_update.cpp +++ b/autotests/test_kconf_update.cpp @@ -625,7 +625,7 @@ void TestKConfUpdate::testScript() QSharedPointer updFile(writeUpdFile(updContent)); - const QString scriptDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kconf_update"; + const QString scriptDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/kconf_update"; QVERIFY(QDir().mkpath(scriptDir)); QString scriptPath = scriptDir + "/test.sh"; writeFile(scriptPath, updScript); diff --git a/docs/options.md b/docs/options.md -index ba7e2d8..a960a7d 100644 +index fab22e1..8823818 100644 --- a/docs/options.md +++ b/docs/options.md @@ -96,4 +96,4 @@ They are: - `$QT_CACHE_HOME` - QStandardPaths::GenericConfigLocation - `$QT_CONFIG_HOME` - QStandardPaths::GenericConfigLocation -- `$QT_DATA_HOME` - QStandardPaths::GenericDataLocation -+ `$QT_DATA_HOME` - QStandardPaths::AppDataLocation + * `$QT_CACHE_HOME` - QStandardPaths::GenericCacheLocation + * `$QT_CONFIG_HOME` - QStandardPaths::GenericConfigLocation +-* `$QT_DATA_HOME` - QStandardPaths::GenericDataLocation ++* `$QT_DATA_HOME` - QStandardPaths::AppDataLocation diff --git a/src/core/kconfig.cpp b/src/core/kconfig.cpp -index ad52da9..0401ced 100644 +index c8eb90a..18d1b69 100644 --- a/src/core/kconfig.cpp +++ b/src/core/kconfig.cpp -@@ -228,7 +228,7 @@ QString KConfigPrivate::expandString(const QString &value) +@@ -229,7 +229,7 @@ QString KConfigPrivate::expandString(const QString &value) env = QString::fromLocal8Bit(pEnv.constData()); } else { if (aVarName == QStringLiteral("QT_DATA_HOME")) { - env = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); + env = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); } else if (aVarName == QStringLiteral("QT_CONFIG_HOME")) { env = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); } else if (aVarName == QStringLiteral("QT_CACHE_HOME")) { diff --git a/src/core/kdesktopfile.cpp b/src/core/kdesktopfile.cpp -index 4a55030..e28e5df 100644 +index b0b6a87..daddd54 100644 --- a/src/core/kdesktopfile.cpp +++ b/src/core/kdesktopfile.cpp @@ -88,7 +88,7 @@ QString KDesktopFile::locateLocal(const QString &path) } } // Relative to xdg data dir? (much more common) - Q_FOREACH (const QString &dir, QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation)) { + Q_FOREACH (const QString &dir, QStandardPaths::standardLocations(QStandardPaths::AppDataLocation)) { if (path.startsWith(dir + plus)) { - relativePath = dir.mid(path.length() + 1); + relativePath = path.mid(dir.length() + 1); } @@ -97,7 +97,7 @@ QString KDesktopFile::locateLocal(const QString &path) // What now? The desktop file doesn't come from XDG_DATA_DIRS. Use filename only and hope for the best. relativePath = path.mid(path.lastIndexOf(QLatin1Char('/')) + 1); } - return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + relativePath; + return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1Char('/') + relativePath; } bool KDesktopFile::isDesktopFile(const QString &path) @@ -134,7 +134,7 @@ bool KDesktopFile::isAuthorizedDesktopFile(const QString &path) } } const QString servicesDir = QStringLiteral("kservices5/"); // KGlobal::dirs()->xdgDataRelativePath("services") - Q_FOREACH (const QString &xdgDataPrefix, QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation)) { + Q_FOREACH (const QString &xdgDataPrefix, QStandardPaths::standardLocations(QStandardPaths::AppDataLocation)) { if (QDir(xdgDataPrefix).exists()) { const QString prefix = QFileInfo(xdgDataPrefix).canonicalFilePath(); if (realPath.startsWith(prefix + QLatin1Char('/') + servicesDir, sensitivity)) { diff --git a/src/kconf_update/kconf_update.cpp b/src/kconf_update/kconf_update.cpp -index 2acd720..d89ae3d 100644 +index ab7d946..eeaf6ca 100644 --- a/src/kconf_update/kconf_update.cpp +++ b/src/kconf_update/kconf_update.cpp @@ -125,7 +125,7 @@ KonfUpdate::KonfUpdate(QCommandLineParser *parser) m_bUseConfigInfo = false; if (parser->isSet(QStringLiteral("check"))) { m_bUseConfigInfo = true; - const QString file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kconf_update/" + parser->value(QStringLiteral("check"))); + const QString file = QStandardPaths::locate(QStandardPaths::AppDataLocation, "kconf_update/" + parser->value(QStringLiteral("check"))); if (file.isEmpty()) { qWarning("File '%s' not found.", parser->value(QStringLiteral("check")).toLocal8Bit().data()); log() << "File '" << parser->value(QStringLiteral("check")) << "' passed on command line not found" << endl; @@ -177,7 +177,7 @@ KonfUpdate::log() { if (!m_textStream) { #if 0 - QString dir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "kconf_update/log"; + QString dir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1Char('/') + "kconf_update/log"; QDir().mkpath(dir); QString file = dir + "/update.log"; m_file = new QFile(file); @@ -206,7 +206,7 @@ QStringList KonfUpdate::findUpdateFiles(bool dirtyOnly) { QStringList result; - const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kconf_update"), QStandardPaths::LocateDirectory); + const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("kconf_update"), QStandardPaths::LocateDirectory); Q_FOREACH (const QString &d, dirs) { const QDir dir(d); @@ -760,7 +760,7 @@ void KonfUpdate::gotScript(const QString &_script) return; } - QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kconf_update/") + script); + QString path = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("kconf_update/") + script); if (path.isEmpty()) { if (interpreter.isEmpty()) { path = CMAKE_INSTALL_PREFIX "/" LIB_INSTALL_DIR "/kconf_update_bin/" + script; diff --git a/3rdparty/ext_frameworks/kcoreaddons-compile-fix.diff b/3rdparty/ext_frameworks/kcoreaddons-compile-fix.diff deleted file mode 100644 index 08fc26605c..0000000000 --- a/3rdparty/ext_frameworks/kcoreaddons-compile-fix.diff +++ /dev/null @@ -1,19 +0,0 @@ -commit e0ea4199bc4da359eb91ab51274785d17a4f2909 -Author: Ralf Habacker -Date: Wed Sep 21 13:38:03 2016 +0200 - - Windows compile fix. - -diff --git a/src/lib/util/kuser_win.cpp b/src/lib/util/kuser_win.cpp -index 1d77f89..5c3fa45 100644 ---- a/src/lib/util/kuser_win.cpp -+++ b/src/lib/util/kuser_win.cpp -@@ -853,7 +853,7 @@ static std::unique_ptr queryProcessInformation(TOKEN_INFORMATION_CLASS t - HANDLE _token; - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &_token)) { - qWarning("Failed to get the token for the current process: %d", (int)GetLastError()); -- return false; -+ return nullptr; - } - ScopedHANDLE token(_token, handleCloser); - // query required size diff --git a/3rdparty/ext_frameworks/ki18n-appdatalocation.diff b/3rdparty/ext_frameworks/ki18n-appdatalocation.diff index 098a38d991..7b548ef1da 100644 --- a/3rdparty/ext_frameworks/ki18n-appdatalocation.diff +++ b/3rdparty/ext_frameworks/ki18n-appdatalocation.diff @@ -1,22 +1,22 @@ diff --git a/src/kcatalog.cpp b/src/kcatalog.cpp -index 7711e9b..b6280ed 100644 +index c18d40f..b0ed09d 100644 --- a/src/kcatalog.cpp +++ b/src/kcatalog.cpp -@@ -112,7 +112,7 @@ QString KCatalog::catalogLocaleDir(const QByteArray &domain, - { - QString relpath = QString::fromLatin1("%1/LC_MESSAGES/%2.mo") - .arg(language, QFile::decodeName(domain)); +@@ -128,7 +128,7 @@ QString KCatalog::catalogLocaleDir(const QByteArray &domain, + } + } + - QString file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, + QString file = QStandardPaths::locate(QStandardPaths::AppDataLocation, - QString::fromLatin1("locale/") + relpath); + QStringLiteral("locale/") + relpath); QString localeDir; if (file.isEmpty()) { -@@ -127,7 +127,7 @@ QString KCatalog::catalogLocaleDir(const QByteArray &domain, +@@ -143,7 +143,7 @@ QString KCatalog::catalogLocaleDir(const QByteArray &domain, QSet KCatalog::availableCatalogLanguages(const QByteArray &domain_) { QString domain = QFile::decodeName(domain_); - QStringList localeDirPaths = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, + QStringList localeDirPaths = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, - QString::fromLatin1("locale"), + QStringLiteral("locale"), QStandardPaths::LocateDirectory); - QSet availableLanguages; + diff --git a/3rdparty/ext_frameworks/kwidgetsaddons.diff b/3rdparty/ext_frameworks/kwidgetsaddons.diff index bbd5d1eb84..0c4fdb01f2 100644 --- a/3rdparty/ext_frameworks/kwidgetsaddons.diff +++ b/3rdparty/ext_frameworks/kwidgetsaddons.diff @@ -1,56 +1,15 @@ -diff --git a/src/kcollapsiblegroupbox.cpp b/src/kcollapsiblegroupbox.cpp -index edda7af..3ae7d86 100644 ---- a/src/kcollapsiblegroupbox.cpp -+++ b/src/kcollapsiblegroupbox.cpp -@@ -94,9 +94,9 @@ void KCollapsibleGroupBox::setTitle(const QString& title) - } - - d->shortcutId = grabShortcut(QKeySequence::mnemonic(title)); -- -+#ifndef QT_NO_ACCESSIBILITY - setAccessibleName(title); -- -+#endif - emit titleChanged(); - } - -diff --git a/src/kled.cpp b/src/kled.cpp -index 1e46929..63a817b 100644 ---- a/src/kled.cpp -+++ b/src/kled.cpp -@@ -57,7 +57,9 @@ KLed::KLed(const QColor &color, QWidget *parent) - d(new Private) - { - setColor(color); -+#ifndef QT_NO_ACCESSIBILITY - updateAccessibleName(); -+#endif - } - - KLed::KLed(const QColor &color, State state, Look look, Shape shape, -@@ -70,7 +72,9 @@ KLed::KLed(const QColor &color, State state, Look look, Shape shape, - d->shape = shape; - - setColor(color); -+#ifndef QT_NO_ACCESSIBILITY - updateAccessibleName(); -+#endif - } - - KLed::~KLed() diff --git a/CMakeLists.txt b/CMakeLists.txt -index 8baeda3..bd5491e 100755 +index f7eb931..3f0f6d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -36,8 +36,8 @@ if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") +@@ -39,8 +39,8 @@ if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") endif() add_subdirectory(src) -add_subdirectory(autotests) -add_subdirectory(tests) +#add_subdirectory(autotests) +#add_subdirectory(tests) # create a Config.cmake and a ConfigVersion.cmake file and install them set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5WidgetsAddons") - diff --git a/3rdparty/ext_gmic/CMakeLists.txt b/3rdparty/ext_gmic/CMakeLists.txt index d565a8110f..53f58dd1dc 100644 --- a/3rdparty/ext_gmic/CMakeLists.txt +++ b/3rdparty/ext_gmic/CMakeLists.txt @@ -1,37 +1,37 @@ SET(PREFIX_ext_gmic "${EXTPREFIX}" ) # Download the gmic sources ExternalProject_Add( ext_gmic_base DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL https://gmic.eu/files/source/gmic_2.2.0.tar.gz - URL_HASH SHA1=caf72b80da8bd3e31111cff8407b005e7115d2c2 + URL https://gmic.eu/files/source/gmic_2.2.3.tar.gz + URL_HASH SHA1=43f975a462a842be5eeff70d8d372476547163b0 SOURCE_DIR gmic CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" BUILD_IN_SOURCE 1 ) # Download and build gmic-qt # FIXME: Forcing CMAKE_BUILD_TYPE to Release ExternalProject_Add( ext_gmic_qt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL https://github.com/c-koi/gmic-qt/archive/v.220.tar.gz - URL_HASH SHA1=2b3731e7c90c79d298758b7702a68c1468035429 + URL https://github.com/c-koi/gmic-qt/archive/v.2.2.3.tar.gz + URL_HASH SHA1=a3c889de09031d34754c9bb3bd1004a2f67713c6 PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/desktop_icon.diff SOURCE_DIR gmic-qt INSTALL_DIR ${PREFIX_ext_gmic} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${PREFIX_ext_gmic} -DGMIC_QT_HOST=krita -DCMAKE_BUILD_TYPE=Release ${GLOBAL_PROFILE} UPDATE_COMMAND "" DEPENDS ext_gmic_base ) add_custom_target(ext_gmic) add_dependencies(ext_gmic ext_gmic_qt) diff --git a/CMakeLists.txt b/CMakeLists.txt index f8658a11d1..999ec152ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,732 +1,753 @@ 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) +set(MIN_FRAMEWORKS_VERSION 5.18.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.11 -Wno-macro-redefined -Wno-deprecated-register) endif() -if (LINUX) - if (CMAKE_COMPILER_IS_GNUCXX AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9 AND NOT WINDOWS) - add_definitions(-Werror=delete-incomplete) - endif() +if (CMAKE_COMPILER_IS_GNUCXX AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9 AND NOT WIN32) + add_definitions(-Werror=delete-incomplete) endif() ###################### ####################### ## Constants defines ## ####################### ###################### # define common versions of Krita applications, used to generate kritaversion.h # update these version for every release: set(KRITA_VERSION_STRING "4.1.0-pre-alpha") # Major version: 3 for 3.x, 4 for 4.x, etc. set(KRITA_STABLE_VERSION_MAJOR 4) # Minor version: 0 for 4.0, 1 for 4.1, etc. set(KRITA_STABLE_VERSION_MINOR 1) # Bugfix release version, or 0 for before the first stable release set(KRITA_VERSION_RELEASE 0) # the 4th digit, really only used for the Windows installer: # - [Pre-]Alpha: Starts from 0, increment 1 per release # - Beta: Starts from 50, increment 1 per release # - Stable: Set to 100, bump to 101 if emergency update is needed set(KRITA_VERSION_REVISION 0) set(KRITA_ALPHA 1) # uncomment only for Alpha #set(KRITA_BETA 1) # uncomment only for Beta #set(KRITA_RC 1) # uncomment only for RC set(KRITA_YEAR 2018) # update every year if(NOT DEFINED KRITA_ALPHA AND NOT DEFINED KRITA_BETA AND NOT DEFINED KRITA_RC) set(KRITA_STABLE 1) # do not edit endif() message(STATUS "Krita version: ${KRITA_VERSION_STRING}") # Define the generic version of the Krita libraries here # This makes it easy to advance it when the next Krita release comes. # 14 was the last GENERIC_KRITA_LIB_VERSION_MAJOR of the previous Krita series # (2.x) so we're starting with 15 in 3.x series, 16 in 4.x series if(KRITA_STABLE_VERSION_MAJOR EQUAL 4) math(EXPR GENERIC_KRITA_LIB_VERSION_MAJOR "${KRITA_STABLE_VERSION_MINOR} + 16") else() # let's make sure we won't forget to update the "16" message(FATAL_ERROR "Reminder: please update offset == 16 used to compute GENERIC_KRITA_LIB_VERSION_MAJOR to something bigger") endif() set(GENERIC_KRITA_LIB_VERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}.0.0") set(GENERIC_KRITA_LIB_SOVERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}") LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/kde_macro") # fetch git revision for the current build set(KRITA_GIT_SHA1_STRING "") set(KRITA_GIT_BRANCH_STRING "") include(GetGitRevisionDescription) get_git_head_hash(GIT_SHA1) get_git_branch(GIT_BRANCH) if(GIT_SHA1) string(SUBSTRING ${GIT_SHA1} 0 7 GIT_SHA1) set(KRITA_GIT_SHA1_STRING ${GIT_SHA1}) if(GIT_BRANCH) set(KRITA_GIT_BRANCH_STRING ${GIT_BRANCH}) else() set(KRITA_GIT_BRANCH_STRING "(detached HEAD)") endif() endif() 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) 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).") +option(ENABLE_PYTHON_2 "Enables the compiler to look for Python 2.7 instead of Python 3. Some packaged scripts are not compatible with Python 2 and this should only be used if you really have to use 2.7." OFF) include(MacroJPEG) ######################################################### ## Look for Python3 It is also searched by KF5, ## ## so we should request the correct version in advance ## ######################################################### function(TestCompileLinkPythonLibs OUTPUT_VARNAME) include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_INCLUDES ${PYTHON_INCLUDE_PATH}) set(CMAKE_REQUIRED_LIBRARIES ${PYTHON_LIBRARIES}) if (MINGW) set(CMAKE_REQUIRED_DEFINITIONS -D_hypot=hypot) endif (MINGW) unset(${OUTPUT_VARNAME} CACHE) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { Py_InitializeEx(0); }" ${OUTPUT_VARNAME}) endfunction() if(MINGW) - find_package(PythonInterp 3.6 EXACT) - find_package(PythonLibs 3.6 EXACT) + if(ENABLE_PYTHON_2) + message(FATAL_ERROR "Python 2.7 is not supported on Windows at the moment.") + else(ENABLE_PYTHON_2) + find_package(PythonInterp 3.6 EXACT) + find_package(PythonLibs 3.6 EXACT) + endif(ENABLE_PYTHON_2) if (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) - find_package(PythonLibrary 3.6) + if(ENABLE_PYTHON_2) + find_package(PythonLibrary 2.7) + else(ENABLE_PYTHON_2) + find_package(PythonLibrary 3.6) + endif(ENABLE_PYTHON_2) TestCompileLinkPythonLibs(CAN_USE_PYTHON_LIBS) if (NOT CAN_USE_PYTHON_LIBS) message(FATAL_ERROR "Compiling with Python library failed, please check whether the architecture is correct. Python will be disabled.") endif (NOT CAN_USE_PYTHON_LIBS) endif (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) else(MINGW) - find_package(PythonInterp 3.0) - find_package(PythonLibrary 3.0) + if(ENABLE_PYTHON_2) + find_package(PythonInterp 2.7) + find_package(PythonLibrary 2.7) + else(ENABLE_PYTHON_2) + find_package(PythonInterp 3.0) + find_package(PythonLibrary 3.0) + endif(ENABLE_PYTHON_2) endif(MINGW) ######################## ######################### ## Look for KDE and Qt ## ######################### ######################## -find_package(ECM 5.19 REQUIRED NOMODULE) +find_package(ECM 5.22 REQUIRED NOMODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(ECMOptionalAddSubdirectory) include(ECMAddAppIcon) include(ECMSetupVersion) include(ECMMarkNonGuiExecutable) include(ECMGenerateHeaders) include(GenerateExportHeader) include(ECMMarkAsTest) include(ECMInstallIcons) include(CMakePackageConfigHelpers) include(WriteBasicConfigVersionFile) include(CheckFunctionExists) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings) # do not reorder to be alphabetical: this is the order in which the frameworks # depend on each other. find_package(KF5 ${MIN_FRAMEWORKS_VERSION} REQUIRED COMPONENTS 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 APPLE) find_package(Qt5Quick ${MIN_QT_VERSION}) set_package_properties(Qt5Quick PROPERTIES DESCRIPTION "QtQuick" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used for the touch gui for Krita") macro_bool_to_01(Qt5Quick_FOUND HAVE_QT_QUICK) find_package(Qt5QuickWidgets ${MIN_QT_VERSION}) set_package_properties(Qt5QuickWidgets PROPERTIES DESCRIPTION "QtQuickWidgets" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used for the touch gui for Krita") - +endif() if (NOT WIN32 AND NOT APPLE) find_package(Qt5 ${MIN_QT_VERSION} REQUIRED X11Extras) find_package(Qt5DBus ${MIN_QT_VERSION}) set(HAVE_DBUS ${Qt5DBus_FOUND}) set_package_properties(Qt5DBus PROPERTIES DESCRIPTION "Qt DBUS integration" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used to provide a dbus api on Linux") find_package(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}) 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 ) if (${Qt5_VERSION} VERSION_GREATER "5.8.0" ) add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50900) elseif(${Qt5_VERSION} VERSION_GREATER "5.7.0" ) add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50800) elseif(${Qt5_VERSION} VERSION_GREATER "5.6.0" ) add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50700) else() add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50600) endif() 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() # KDECompilerSettings adds the `--export-all-symbols` linker flag. # We don't really need it. if(MINGW) string(REPLACE "-Wl,--export-all-symbols" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") string(REPLACE "-Wl,--export-all-symbols" "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}") endif(MINGW) # enable exceptions globally kde_enable_exceptions() set(KRITA_DEFAULT_TEST_DATA_DIR ${CMAKE_SOURCE_DIR}/sdk/tests/data/) macro(macro_add_unittest_definitions) add_definitions(-DFILES_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data/") add_definitions(-DFILES_OUTPUT_DIR="${CMAKE_CURRENT_BINARY_DIR}") add_definitions(-DFILES_DEFAULT_DATA_DIR="${KRITA_DEFAULT_TEST_DATA_DIR}") add_definitions(-DSYSTEM_RESOURCES_DATA_DIR="${CMAKE_SOURCE_DIR}/krita/data/") endmacro() # overcome some platform incompatibilities if(WIN32) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/winquirks) add_definitions(-D_USE_MATH_DEFINES) add_definitions(-DNOMINMAX) set(WIN32_PLATFORM_NET_LIBS ws2_32.lib netapi32.lib) endif() # set custom krita plugin installdir 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 1.55 REQUIRED COMPONENTS system) +find_package(Boost 1.55 REQUIRED COMPONENTS system) include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) ## ## Test for GNU Scientific Library ## find_package(GSL) set_package_properties(GSL PROPERTIES URL "http://www.gnu.org/software/gsl" TYPE RECOMMENDED PURPOSE "Required by Krita's Transform tool.") macro_bool_to_01(GSL_FOUND HAVE_GSL) configure_file(config-gsl.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-gsl.h ) ########################### ############################ ## Optional dependencies ## ############################ ########################### find_package(ZLIB) set_package_properties(ZLIB PROPERTIES DESCRIPTION "Compression library" URL "http://www.zlib.net/" TYPE OPTIONAL PURPOSE "Optionally used by the G'Mic and the PSD plugins") macro_bool_to_01(ZLIB_FOUND HAVE_ZLIB) find_package(OpenEXR) set_package_properties(OpenEXR PROPERTIES DESCRIPTION "High dynamic-range (HDR) image file format" URL "http://www.openexr.com" TYPE OPTIONAL PURPOSE "Required by the Krita OpenEXR filter") macro_bool_to_01(OPENEXR_FOUND HAVE_OPENEXR) set(LINK_OPENEXR_LIB) if(OPENEXR_FOUND) include_directories(SYSTEM ${OPENEXR_INCLUDE_DIR}) set(LINK_OPENEXR_LIB ${OPENEXR_LIBRARIES}) add_definitions(${OPENEXR_DEFINITIONS}) endif() find_package(TIFF) set_package_properties(TIFF PROPERTIES DESCRIPTION "TIFF Library and Utilities" URL "http://www.remotesensing.org/libtiff" TYPE OPTIONAL PURPOSE "Required by the Krita TIFF filter") 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") find_package(GIF) set_package_properties(GIF PROPERTIES DESCRIPTION "Library for loading and saving gif files." URL "http://giflib.sourceforge.net/" TYPE OPTIONAL PURPOSE "Required by the Krita GIF filter") +find_package(HEIF "1.2.0") +set_package_properties(HEIF PROPERTIES + DESCRIPTION "Library for loading and saving heif files." + URL "https://github.com/strukturag/libheif" + TYPE OPTIONAL + PURPOSE "Required by the Krita HEIF filter") + 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) set_package_properties(PythonLibrary PROPERTIES DESCRIPTION "Python Library" URL "http://www.python.org" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(PYTHONLIBS_FOUND HAVE_PYTHONLIBS) find_package(SIP "4.18.0") set_package_properties(SIP PROPERTIES DESCRIPTION "Support for generating SIP Python bindings" URL "https://www.riverbankcomputing.com/software/sip/download" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(SIP_FOUND HAVE_SIP) find_package(PyQt5 "5.6.0") set_package_properties(PyQt5 PROPERTIES DESCRIPTION "Python bindings for Qt5." URL "https://www.riverbankcomputing.com/software/pyqt/download5" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(PYQT5_FOUND HAVE_PYQT5) ## ## Look for OpenGL ## # TODO: see if there is a better check for QtGui being built with opengl support (and thus the QOpenGL* classes) if(Qt5Gui_OPENGL_IMPLEMENTATION) message(STATUS "Found QtGui OpenGL support") else() message(FATAL_ERROR "Did NOT find QtGui OpenGL support. Check your Qt configuration. You cannot build Krita without Qt OpenGL support.") endif() ## ## Test for eigen3 ## find_package(Eigen3 3.0 REQUIRED) set_package_properties(Eigen3 PROPERTIES DESCRIPTION "C++ template library for linear algebra" URL "http://eigen.tuxfamily.org" TYPE REQUIRED) ## ## Test for exiv2 ## find_package(Exiv2 0.16 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 2.4 REQUIRED) set_package_properties(LCMS2 PROPERTIES DESCRIPTION "LittleCMS Color management engine" URL "http://www.littlecms.com" TYPE REQUIRED PURPOSE "Will be used for color management and is necessary for Krita") if(LCMS2_FOUND) if(NOT ${LCMS2_VERSION} VERSION_LESS 2040 ) set(HAVE_LCMS24 TRUE) endif() set(HAVE_REQUIRED_LCMS_VERSION TRUE) set(HAVE_LCMS2 TRUE) endif() ## ## Test for Vc ## set(OLD_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ) set(HAVE_VC FALSE) -if (NOT ${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") +if (NOT ${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") if(NOT MSVC) find_package(Vc 1.1.0) set_package_properties(Vc PROPERTIES DESCRIPTION "Portable, zero-overhead SIMD library for C++" URL "https://github.com/VcDevel/Vc" TYPE OPTIONAL PURPOSE "Required by the Krita for vectorization") macro_bool_to_01(Vc_FOUND HAVE_VC) endif() endif() configure_file(config-vc.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-vc.h ) if(HAVE_VC) message(STATUS "Vc found!") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/vc") include (VcMacros) if(Vc_COMPILER_IS_CLANG) set(ADDITIONAL_VC_FLAGS "-Wabi -ffp-contract=fast") if(NOT WIN32) set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC") endif() elseif (NOT MSVC) set(ADDITIONAL_VC_FLAGS "-Wabi -fabi-version=0 -ffp-contract=fast") if(NOT WIN32) set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC") endif() 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) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) endmacro() macro(ko_compile_for_all_implementations _objs _src) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY Scalar SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) endmacro() endif() set(CMAKE_MODULE_PATH ${OLD_CMAKE_MODULE_PATH} ) add_definitions(${QT_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS}) ## ## Test endianness ## include (TestBigEndian) test_big_endian(CMAKE_WORDS_BIGENDIAN) ## ## Test for qt-poppler ## find_package(Poppler COMPONENTS Qt5) set_package_properties(Poppler PROPERTIES DESCRIPTION "A PDF rendering library" URL "http://poppler.freedesktop.org" TYPE OPTIONAL PURPOSE "Required by the Krita PDF filter.") ############################ ############################# ## 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) if(WIN32) - include(${CMAKE_CURRENT_LIST_DIR}/packaging/windows/ConfigureInstallerNsis.cmake) + include(${CMAKE_CURRENT_LIST_DIR}/packaging/windows/installer/ConfigureInstallerNsis.cmake) endif() message("\nBroken tests:") foreach(tst ${KRITA_BROKEN_TESTS}) message(" * ${tst}") endforeach() feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) + +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/po OR EXISTS ${CMAKE_CURRENT_BINARY_DIR}/po ) + find_package(KF5I18n CONFIG REQUIRED) + ki18n_install(po) +endif() diff --git a/build-tools/docker/default-home/.bash_aliases b/build-tools/docker/default-home/.bash_aliases index 716865784d..766e667f0e 100644 --- a/build-tools/docker/default-home/.bash_aliases +++ b/build-tools/docker/default-home/.bash_aliases @@ -1,8 +1,38 @@ alias ll='ls -alF' alias la='ls -A' alias l='ls -CF' alias ..='cd ..' alias ...='cd ../..' alias ....='cd ../../..' +alias gdba='gdb --args' +alias gdbr='gdb --eval-command=r --args' +alias gdbrk='gdb --eval-command=r --args krita' +alias gdbatt='gdb --eval-command=cont --pid=$(pidof krita)' + +function tst { + make -j8 $1 && ./$1 $2 $3 $4 $5 $6 $7 $8 +} + +function tsti { + make -j8 -C .. && make -j1 -C .. install/fast && ./$1 $2 $3 $4 $5 $6 $7 $8 +} + +function tstfast { + make -j8 $1/fast && ./$1 $2 $3 $4 $5 $6 $7 $8 +} + +function mi { + make -j8 && make -j1 install/fast +} + +function mik { + make -j8 && make -j1 install/fast && krita $* +} + +function nmik { + nice make -j8 && nice make -j1 install/fast && krita $* +} + + diff --git a/build-tools/docker/default-home/devenv.inc b/build-tools/docker/default-home/devenv.inc index 5c7103df62..4030c1f28f 100644 --- a/build-tools/docker/default-home/devenv.inc +++ b/build-tools/docker/default-home/devenv.inc @@ -1,26 +1,26 @@ prepend() { [ -d "$2" ] && eval $1=\"$2\$\{$1:+':'\$$1\}\" && export $1 ; } export KRITADIR=/home/appimage/appimage-workspace/krita-inst export DEPSDIR=/home/appimage/appimage-workspace/deps/usr prepend PATH $KRITADIR/bin prepend LD_LIBRARY_PATH $KRITADIR/lib64 prepend LD_LIBRARY_PATH $KRITADIR/lib -prepend XDG_DATA_DIRS $KRITADIR/share +#prepend XDG_DATA_DIRS $KRITADIR/share prepend PKG_CONFIG_PATH $KRITADIR/lib64/pkgconfig prepend PKG_CONFIG_PATH $KRITADIR/lib/pkgconfig prepend PATH $DEPSDIR/bin prepend LD_LIBRARY_PATH $DEPSDIR/lib64 prepend LD_LIBRARY_PATH $DEPSDIR/lib -prepend XDG_DATA_DIRS $DEPSDIR/share +#prepend XDG_DATA_DIRS $DEPSDIR/share prepend PKG_CONFIG_PATH $DEPSDIR/lib64/pkgconfig prepend PKG_CONFIG_PATH $DEPSDIR/lib/pkgconfig prepend CMAKE_PREFIX_PATH $DEPSDIR -prepend PYTHONPATH $DEPSDIR/lib/python3.5/ -prepend PYTHONPATH $DEPSDIR/sip +#prepend PYTHONPATH $DEPSDIR/lib/python3.5/ +#prepend PYTHONPATH $DEPSDIR/sip -prepend PYQT_SIP_DIR_OVERRIDE $DEPSDIR/share/sip \ No newline at end of file +#prepend PYQT_SIP_DIR_OVERRIDE $DEPSDIR/share/sip diff --git a/build-tools/windows/build.cmd b/build-tools/windows/build.cmd index 2302b6e31c..ee818591af 100644 --- a/build-tools/windows/build.cmd +++ b/build-tools/windows/build.cmd @@ -1,767 +1,767 @@ @echo off setlocal enabledelayedexpansion goto begin :: Subroutines :find_on_path out_variable file_name set %1=%~f$PATH:2 goto :EOF :get_dir_path out_variable file_path set %1=%~dp2 goto :EOF :get_full_path out_variable file_path setlocal set FULL_PATH=%~f2 if not exist "%FULL_PATH%" ( set FULL_PATH= ) else ( if exist "%FULL_PATH%\" ( set FULL_PATH= ) ) endlocal & set "%1=%FULL_PATH%" goto :EOF :get_full_path_dir out_variable file_path setlocal set FULL_PATH=%~dp2 if not exist "%FULL_PATH%" ( set FULL_PATH= ) endlocal & set "%1=%FULL_PATH%" goto :EOF :prompt_for_string out_variable prompt set /p %1=%~2^> goto :EOF :prompt_for_positive_integer out_variable prompt setlocal call :prompt_for_string USER_INPUT "%~2" if "%USER_INPUT%" == "" set USER_INPUT=0 set /a RESULT=%USER_INPUT% if not %RESULT% GTR 0 ( set RESULT= ) endlocal & set "%1=%RESULT%" goto :EOF :prompt_for_file out_variable prompt setlocal :prompt_for_file__retry call :prompt_for_string USER_INPUT "%~2" if "%USER_INPUT%" == "" ( endlocal set %1= goto :EOF ) call :get_full_path RESULT "%USER_INPUT%" if "%RESULT%" == "" ( echo Input does not point to valid file! set USER_INPUT= goto prompt_for_file__retry ) endlocal & set "%1=%RESULT%" goto :EOF :prompt_for_dir out_variable prompt setlocal :prompt_for_dir__retry call :prompt_for_string USER_INPUT "%~2" if "%USER_INPUT%" == "" ( endlocal set %1= goto :EOF ) call :get_full_path_dir RESULT "%USER_INPUT%\" if "%RESULT%" == "" ( echo Input does not point to valid dir! set USER_INPUT= goto prompt_for_dir__retry ) endlocal & set "%1=%RESULT%" goto :EOF :usage echo Usage: echo %~n0 [--no-interactive] [ OPTIONS ... ] echo. echo Basic options: echo --no-interactive Run without interactive prompts echo When not specified, the script will prompt echo for some of the parameters. echo --jobs ^ Set parallel jobs count when building echo Defaults to no. of logical cores echo --skip-deps Skips (re)building of deps echo --skip-krita Skips (re)building of Krita echo. echo Path options: echo --src-dir ^ Specify Krita source dir echo If unspecified, this will be determined from echo the script location. echo --download-dir ^ Specify deps download dir echo Can be omitted if --skip-deps is used echo --deps-build-dir ^ Specify deps build dir echo Can be omitted if --skip-deps is used echo --deps-install-dir ^ Specify deps install dir echo --krita-build-dir ^ Specify Krita build dir echo Can be omitted if --skip-krita is used echo --krita-install-dir ^ Specify Krita install dir echo Can be omitted if --skip-krita is used echo. goto :EOF :usage_and_exit call :usage exit /b :usage_and_fail call :usage exit /b 100 :: ---------------------------- :begin echo Krita build script for Windows echo. :: command-line args parsing set ARG_NO_INTERACTIVE= set ARG_JOBS= set ARG_SKIP_DEPS= set ARG_SKIP_KRITA= set ARG_SRC_DIR= set ARG_DOWNLOAD_DIR= set ARG_DEPS_BUILD_DIR= set ARG_DEPS_INSTALL_DIR= set ARG_KRITA_BUILD_DIR= set ARG_KRITA_INSTALL_DIR= :args_parsing_loop set CURRENT_MATCHED= if not "%1" == "" ( if "%1" == "--no-interactive" ( set ARG_NO_INTERACTIVE=1 set CURRENT_MATCHED=1 ) if "%1" == "--jobs" ( if not "%ARG_JOBS%" == "" ( echo ERROR: Arg --jobs specified more than once 1>&2 echo. goto usage_and_fail ) set /a "ARG_JOBS=%2" if not !ARG_JOBS! GTR 0 ( echo ERROR: Arg --jobs is not a positive integer 1>&2 echo. goto usage_and_fail ) shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--skip-deps" ( set ARG_SKIP_DEPS=1 set CURRENT_MATCHED=1 ) if "%1" == "--skip-krita" ( set ARG_SKIP_KRITA=1 set CURRENT_MATCHED=1 ) if "%1" == "--src-dir" ( if not "%ARG_SRC_DIR%" == "" ( echo ERROR: Arg --src-dir specified more than once 1>&2 echo. goto usage_and_fail ) if not exist "%~f2\" ( echo ERROR: Arg --src-dir does not point to a directory 1>&2 echo. goto usage_and_fail ) call :get_dir_path ARG_SRC_DIR "%~f2\" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--download-dir" ( if not "%ARG_DOWNLOAD_DIR%" == "" ( echo ERROR: Arg --download-dir specified more than once 1>&2 echo. goto usage_and_fail ) if "%~f2" == "" ( echo ERROR: Arg --download-dir does not point to a valid path 1>&2 echo. goto usage_and_fail ) call :get_dir_path ARG_DOWNLOAD_DIR "%~f2\" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--deps-build-dir" ( if not "%ARG_DEPS_BUILD_DIR%" == "" ( echo ERROR: Arg --deps-build-dir specified more than once 1>&2 echo. goto usage_and_fail ) if "%~f2" == "" ( echo ERROR: Arg --deps-build-dir does not point to a valid path 1>&2 echo. goto usage_and_fail ) call :get_dir_path ARG_DEPS_BUILD_DIR "%~f2\" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--deps-install-dir" ( if not "%ARG_DEPS_INSTALL_DIR%" == "" ( echo ERROR: Arg --deps-install-dir specified more than once 1>&2 echo. goto usage_and_fail ) if "%~f2" == "" ( echo ERROR: Arg --deps-install-dir does not point to a valid path 1>&2 echo. goto usage_and_fail ) call :get_dir_path ARG_DEPS_INSTALL_DIR "%~f2\" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--krita-build-dir" ( if not "%ARG_KRITA_BUILD_DIR%" == "" ( echo ERROR: Arg --krita-build-dir specified more than once 1>&2 echo. goto usage_and_fail ) if "%~f2" == "" ( echo ERROR: Arg --krita-build-dir does not point to a valid path 1>&2 echo. goto usage_and_fail ) call :get_dir_path ARG_KRITA_BUILD_DIR "%~f2\" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--krita-install-dir" ( if not "%ARG_KRITA_INSTALL_DIR%" == "" ( echo ERROR: Arg --krita-install-dir specified more than once 1>&2 echo. goto usage_and_fail ) if "%~f2" == "" ( echo ERROR: Arg --krita-install-dir does not point to a valid path 1>&2 echo. goto usage_and_fail ) call :get_dir_path ARG_KRITA_INSTALL_DIR "%~f2\" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--help" ( goto usage_and_exit ) if not "!CURRENT_MATCHED!" == "1" ( echo ERROR: Unknown option %1 1>&2 echo. goto usage_and_fail ) shift /1 goto args_parsing_loop ) if "%ARG_NO_INTERACTIVE%" == "1" ( echo Non-interactive mode ) else ( echo Interactive mode :: Trick to pause on exit call :real_begin pause exit /b !ERRORLEVEL! ) :real_begin echo. if "%ARG_SKIP_DEPS%" == "1" ( if "%ARG_SKIP_KRITA%" == "1" ( echo ERROR: You cannot skip both deps and Krita 1>&2 echo. exit /b 102 ) echo Building of deps will be skipped. ) else ( if "%ARG_SKIP_KRITA%" == "1" ( echo Building of Krita will be skipped. ) else ( echo Both deps and Krita will be built. ) ) :: Check environment config if "%CMAKE_EXE%" == "" ( call :find_on_path CMAKE_EXE cmake.exe if "!CMAKE_EXE!" == "" ( if not "%ARG_NO_INTERACTIVE%" == "1" ( call :prompt_for_file CMAKE_EXE "Provide path to cmake.exe" ) if "!CMAKE_EXE!" == "" ( echo ERROR: CMake not found! 1>&2 exit /b 102 ) ) else ( echo Found CMake on PATH: !CMAKE_EXE! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this correct? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_file CMAKE_EXE "Provide path to cmake.exe" if "!CMAKE_EXE!" == "" ( echo ERROR: CMake not found! 1>&2 exit /b 102 ) ) ) ) ) echo CMake: %CMAKE_EXE% if "%SEVENZIP_EXE%" == "" ( call :find_on_path SEVENZIP_EXE 7z.exe if "!SEVENZIP_EXE!" == "" ( set "SEVENZIP_EXE=%ProgramFiles%\7-Zip\7z.exe" if "!SEVENZIP_EXE!" == "" ( set "SEVENZIP_EXE=%ProgramFiles(x86)%\7-Zip\7z.exe" ) if "!SEVENZIP_EXE!" == "" ( echo 7-Zip not found ) ) ) if "%SEVENZIP_EXE%" == "" ( echo 7-Zip: %SEVENZIP_EXE% ) if "%MINGW_BIN_DIR%" == "" ( call :find_on_path MINGW_BIN_DIR_MAKE_EXE mingw32-make.exe if "!MINGW_BIN_DIR_MAKE_EXE!" == "" ( if not "%ARG_NO_INTERACTIVE%" == "1" ( call :prompt_for_file MINGW_BIN_DIR_MAKE_EXE "Provide path to mingw32-make.exe of mingw-w64" ) if "!MINGW_BIN_DIR_MAKE_EXE!" == "" ( echo ERROR: mingw-w64 not found! 1>&2 exit /b 102 ) call :get_dir_path MINGW_BIN_DIR "!MINGW_BIN_DIR_MAKE_EXE!" ) else ( call :get_dir_path MINGW_BIN_DIR "!MINGW_BIN_DIR_MAKE_EXE!" echo Found mingw on PATH: !MINGW_BIN_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this correct? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_file MINGW_BIN_DIR_MAKE_EXE "Provide path to mingw32-make.exe of mingw-w64" if "!MINGW_BIN_DIR_MAKE_EXE!" == "" ( echo ERROR: mingw-w64 not found! 1>&2 exit /b 102 ) call :get_dir_path MINGW_BIN_DIR "!MINGW_BIN_DIR_MAKE_EXE!" ) ) ) ) echo mingw-w64: %MINGW_BIN_DIR% if "%PYTHON_BIN_DIR%" == "" ( call :find_on_path PYTHON_BIN_DIR_PYTHON_EXE python.exe if "!PYTHON_BIN_DIR_PYTHON_EXE!" == "" ( if not "%ARG_NO_INTERACTIVE%" == "1" ( call :prompt_for_file PYTHON_BIN_DIR_PYTHON_EXE "Provide path to python.exe of Python 3.6.2" ) if "!PYTHON_BIN_DIR_PYTHON_EXE!" == "" ( echo ERROR: Python not found! 1>&2 exit /b 102 ) call :get_dir_path PYTHON_BIN_DIR "!PYTHON_BIN_DIR_PYTHON_EXE!" ) else ( call :get_dir_path PYTHON_BIN_DIR "!PYTHON_BIN_DIR_PYTHON_EXE!" echo Found Python on PATH: !PYTHON_BIN_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this correct? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_file PYTHON_BIN_DIR_PYTHON_EXE "Provide path to python.exe of Python 3.6.2" if "!PYTHON_BIN_DIR_PYTHON_EXE!" == "" ( echo ERROR: Python not found! 1>&2 exit /b 102 ) call :get_dir_path PYTHON_BIN_DIR "!PYTHON_BIN_DIR_PYTHON_EXE!" ) ) ) ) echo Python: %PYTHON_BIN_DIR% if "%ARG_SKIP_DEPS%" == "1" goto skip_windows_sdk_dir_check if "%WindowsSdkDir%" == "" if not "%ProgramFiles(x86)%" == "" set "WindowsSdkDir=%ProgramFiles(x86)%\Windows Kits\10" if "%WindowsSdkDir%" == "" set "WindowsSdkDir=%ProgramFiles(x86)%\Windows Kits\10" if exist "%WindowsSdkDir%\" ( pushd "%WindowsSdkDir%" if exist "bin\x64\fxc.exe" ( set HAVE_FXC_EXE=1 ) else ( for /f "delims=" %%a in ('dir /a:d /b "bin\10.*"') do ( if exist "bin\%%a\x64\fxc.exe" ( set HAVE_FXC_EXE=1 ) ) ) popd ) set QT_ENABLE_DYNAMIC_OPENGL=ON if not "%HAVE_FXC_EXE%" == "1" ( set WindowsSdkDir= echo Windows SDK 10 with fxc.exe not found echo Qt will *not* be built with ANGLE ^(dynamic OpenGL^) support. if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( exit /b 102 ) ) set QT_ENABLE_DYNAMIC_OPENGL=OFF ) else echo Windows SDK 10 with fxc.exe found on %WindowsSdkDir% :skip_windows_sdk_dir_check if not "%ARG_JOBS%" == "" ( set "PARALLEL_JOBS=%ARG_JOBS%" ) if "%PARALLEL_JOBS%" == "" ( echo Number of logical CPU cores detected: %NUMBER_OF_PROCESSORS% echo Enabling %NUMBER_OF_PROCESSORS% parallel jobs set PARALLEL_JOBS=%NUMBER_OF_PROCESSORS% if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this correct? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_positive_integer PARALLEL_JOBS "Provide no. of parallel jobs" if "!PARALLEL_JOBS!" == "" ( echo ERROR: Invalid job count! 1>&2 exit /b 102 ) ) ) ) echo Parallel jobs count: %PARALLEL_JOBS% if not "%ARG_SRC_DIR%" == "" ( set "KRITA_SRC_DIR=%ARG_SRC_DIR%" ) if "%KRITA_SRC_DIR%" == "" ( :: Check whether this looks like to be in the source tree set "_temp=%~dp0" if "!_temp:~-21!" == "\build-tools\windows\" ( if exist "!_temp:~0,-21!\CMakeLists.txt" ( if exist "!_temp:~0,-21!\3rdparty\CMakeLists.txt" ( set "KRITA_SRC_DIR=!_temp:~0,-21!\" echo Script is running inside Krita src dir ) ) ) ) if "%KRITA_SRC_DIR%" == "" ( if not "%ARG_NO_INTERACTIVE%" == "1" ( call :prompt_for_dir KRITA_SRC_DIR "Provide path of Krita src dir" ) if "!KRITA_SRC_DIR!" == "" ( echo ERROR: Krita src dir not found! 1>&2 exit /b 102 ) ) echo Krita src: %KRITA_SRC_DIR% if "%ARG_SKIP_DEPS%" == "1" goto skip_deps_args_check if not "%ARG_DOWNLOAD_DIR%" == "" ( set "DEPS_DOWNLOAD_DIR=%ARG_DOWNLOAD_DIR%" ) if "%DEPS_DOWNLOAD_DIR%" == "" ( set DEPS_DOWNLOAD_DIR=%CD%\d\ echo Using default deps download dir: !DEPS_DOWNLOAD_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_dir DEPS_DOWNLOAD_DIR "Provide path of depps download dir" ) ) if "!DEPS_DOWNLOAD_DIR!" == "" ( echo ERROR: Deps download dir not set! 1>&2 exit /b 102 ) ) echo Deps download dir: %DEPS_DOWNLOAD_DIR% if not "%ARG_DEPS_BUILD_DIR%" == "" ( set "DEPS_BUILD_DIR=%ARG_DEPS_BUILD_DIR%" ) if "%DEPS_BUILD_DIR%" == "" ( set DEPS_BUILD_DIR=%CD%\b_deps\ echo Using default deps build dir: !DEPS_BUILD_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_dir DEPS_BUILD_DIR "Provide path of deps build dir" ) ) if "!DEPS_BUILD_DIR!" == "" ( echo ERROR: Deps build dir not set! 1>&2 exit /b 102 ) ) echo Deps build dir: %DEPS_BUILD_DIR% :skip_deps_args_check if not "%ARG_DEPS_INSTALL_DIR%" == "" ( set "DEPS_INSTALL_DIR=%ARG_DEPS_INSTALL_DIR%" ) if "%DEPS_INSTALL_DIR%" == "" ( set DEPS_INSTALL_DIR=%CD%\i_deps\ echo Using default deps install dir: !DEPS_INSTALL_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_dir DEPS_INSTALL_DIR "Provide path of deps install dir" ) ) if "!DEPS_INSTALL_DIR!" == "" ( echo ERROR: Deps install dir not set! 1>&2 exit /b 102 ) ) echo Deps install dir: %DEPS_INSTALL_DIR% if "%ARG_SKIP_KRITA%" == "1" goto skip_krita_args_check if not "%ARG_KRITA_BUILD_DIR%" == "" ( set "KRITA_BUILD_DIR=%ARG_KRITA_BUILD_DIR%" ) if "%KRITA_BUILD_DIR%" == "" ( set KRITA_BUILD_DIR=%CD%\b\ echo Using default Krita build dir: !KRITA_BUILD_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_dir KRITA_BUILD_DIR "Provide path of Krita build dir" ) ) if "!KRITA_BUILD_DIR!" == "" ( echo ERROR: Krita build dir not set! 1>&2 exit /b 102 ) ) echo Krita build dir: %KRITA_BUILD_DIR% if not "%ARG_KRITA_INSTALL_DIR%" == "" ( set "KRITA_INSTALL_DIR=%ARG_KRITA_INSTALL_DIR%" ) if "%KRITA_INSTALL_DIR%" == "" ( set KRITA_INSTALL_DIR=%CD%\i\ echo Using default Krita install dir: !KRITA_INSTALL_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_dir KRITA_INSTALL_DIR "Provide path of Krita install dir" ) ) if "!KRITA_INSTALL_DIR!" == "" ( echo ERROR: Krita install dir not set! 1>&2 exit /b 102 ) ) echo Krita install dir: %KRITA_INSTALL_DIR% :skip_krita_args_check echo. if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is the above ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( exit /b 1 ) echo. ) :: Initialize clean PATH set PATH=%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\ set PATH=%MINGW_BIN_DIR%;%PYTHON_BIN_DIR%;%PATH% echo Creating dirs... if NOT "%ARG_SKIP_DEPS%" == "1" ( mkdir %DEPS_DOWNLOAD_DIR% if errorlevel 1 ( if not exist "%DEPS_DOWNLOAD_DIR%\" ( echo ERROR: Cannot create deps download dir! 1>&2 exit /b 103 ) ) mkdir %DEPS_BUILD_DIR% if errorlevel 1 ( if not exist "%DEPS_BUILD_DIR%\" ( echo ERROR: Cannot create deps build dir! 1>&2 exit /b 103 ) ) mkdir %DEPS_INSTALL_DIR% if errorlevel 1 ( if not exist "%DEPS_INSTALL_DIR%\" ( echo ERROR: Cannot create deps install dir! 1>&2 exit /b 103 ) ) ) if NOT "%ARG_SKIP_KRITA%" == "1" ( mkdir %KRITA_BUILD_DIR% if errorlevel 1 ( if not exist "%KRITA_BUILD_DIR%\" ( echo ERROR: Cannot create Krita build dir! 1>&2 exit /b 103 ) ) mkdir %KRITA_INSTALL_DIR% if errorlevel 1 ( if not exist "%KRITA_INSTALL_DIR%\" ( echo ERROR: Cannot create Krita install dir! 1>&2 exit /b 103 ) ) ) echo. set CMAKE_BUILD_TYPE=RelWithDebInfo set QT_ENABLE_DEBUG_INFO=OFF :: Paths for CMake set "BUILDDIR_DOWNLOAD_CMAKE=%DEPS_DOWNLOAD_DIR:\=/%" set "BUILDDIR_DOWNLOAD_CMAKE=%BUILDDIR_DOWNLOAD_CMAKE: =\ %" set "BUILDDIR_DEPS_INSTALL_CMAKE=%DEPS_INSTALL_DIR:\=/%" set "BUILDDIR_DEPS_INSTALL_CMAKE=%BUILDDIR_DEPS_INSTALL_CMAKE: =\ %" set "BUILDDIR_KRITA_INSTALL_CMAKE=%KRITA_INSTALL_DIR:\=/%" set "BUILDDIR_KRITA_INSTALL_CMAKE=%BUILDDIR_KRITA_INSTALL_CMAKE: =\ %" set PATH=%DEPS_INSTALL_DIR%\bin;%PATH% if not "%GETTEXT_SEARCH_PATH%" == "" ( set PATH=!PATH!;!GETTEXT_SEARCH_PATH! ) if "%ARG_SKIP_DEPS%" == "1" goto skip_build_deps pushd %DEPS_BUILD_DIR% if errorlevel 1 ( echo ERROR: Cannot enter deps build dir! 1>&2 exit /b 104 ) echo Running CMake for deps... "%CMAKE_EXE%" "%KRITA_SRC_DIR%\3rdparty" ^ -DSUBMAKE_JOBS=%PARALLEL_JOBS% ^ -DQT_ENABLE_DEBUG_INFO=%QT_ENABLE_DEBUG_INFO% ^ -DQT_ENABLE_DYNAMIC_OPENGL=%QT_ENABLE_DYNAMIC_OPENGL% ^ -DEXTERNALS_DOWNLOAD_DIR=%BUILDDIR_DOWNLOAD_CMAKE% ^ -DINSTALL_ROOT=%BUILDDIR_DEPS_INSTALL_CMAKE% ^ -G "MinGW Makefiles" ^ -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% if errorlevel 1 ( echo ERROR: CMake configure failed! 1>&2 exit /b 104 ) echo. set EXT_TARGETS=patch png2ico gettext qt zlib boost eigen3 exiv2 fftw3 ilmbase set EXT_TARGETS=%EXT_TARGETS% jpeg lcms2 ocio openexr png tiff gsl vc libraw set EXT_TARGETS=%EXT_TARGETS% giflib freetype poppler kwindowsystem drmingw gmic -set EXT_TARGETS=%EXT_TARGETS% python sip pyqt +set EXT_TARGETS=%EXT_TARGETS% python sip pyqt ffmpeg for %%a in (%EXT_TARGETS%) do ( echo Building ext_%%a... "%CMAKE_EXE%" --build . --config %CMAKE_BUILD_TYPE% --target ext_%%a if errorlevel 1 ( echo ERROR: Building of ext_%%a failed! 1>&2 exit /b 105 ) ) echo. echo ******** Built deps ******** popd :skip_build_deps if "%ARG_SKIP_KRITA%" == "1" goto skip_build_krita pushd %KRITA_BUILD_DIR% if errorlevel 1 ( echo ERROR: Cannot enter Krita build dir! 1>&2 exit /b 104 ) echo Running CMake for Krita... "%CMAKE_EXE%" "%KRITA_SRC_DIR%\." ^ -DBoost_DEBUG=OFF ^ -DBOOST_INCLUDEDIR=%BUILDDIR_DEPS_INSTALL_CMAKE%/include ^ -DBOOST_ROOT=%BUILDDIR_DEPS_INSTALL_CMAKE% ^ -DBOOST_LIBRARYDIR=%BUILDDIR_DEPS_INSTALL_CMAKE%/lib ^ -DCMAKE_PREFIX_PATH=%BUILDDIR_DEPS_INSTALL_CMAKE% ^ -DCMAKE_INSTALL_PREFIX=%BUILDDIR_KRITA_INSTALL_CMAKE% ^ -DBUILD_TESTING=OFF ^ -DHAVE_MEMORY_LEAK_TRACKER=OFF ^ -Wno-dev ^ -G "MinGW Makefiles" ^ -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% if errorlevel 1 ( echo ERROR: CMake configure failed! 1>&2 exit /b 104 ) echo. echo Building Krita... "%CMAKE_EXE%" --build . --config %CMAKE_BUILD_TYPE% --target install -- -j%PARALLEL_JOBS% if errorlevel 1 ( echo ERROR: Building of Krita failed! 1>&2 exit /b 105 ) echo. echo ******** Built Krita ******** popd :skip_build_krita echo Krita build completed! diff --git a/cmake/modules/FindFFTW3.cmake b/cmake/modules/FindFFTW3.cmake index 7578ef1887..8d30a1f6da 100644 --- a/cmake/modules/FindFFTW3.cmake +++ b/cmake/modules/FindFFTW3.cmake @@ -1,59 +1,59 @@ # - Try to find the Fftw3 Library # Once done this will define # # FFTW3_FOUND - system has fftw3 # FFTW3_INCLUDE_DIRS - the fftw3 include directories # FFTW3_LIBRARIES - the libraries needed to use fftw3 # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # if (NOT WIN32) -include(LibFindMacros) -libfind_pkg_check_modules(FFTW3_PKGCONF fftw3>=3.2) - -find_path(FFTW3_INCLUDE_DIR - NAMES fftw3.h - HINTS ${FFTW3_PKGCONF_INCLUDE_DIRS} ${FFTW3_PKGCONF_INCLUDEDIR} - PATH_SUFFIXES fftw3 -) - -find_library(FFTW3_LIBRARY - NAMES fftw3 - HINTS ${FFTW3_PKGCONF_LIBRARY_DIRS} ${FFTW3_PKGCONF_LIBDIR} -) - -set(FFTW3_PROCESS_LIBS FFTW3_LIBRARY) -set(FFTW3_PROCESS_INCLUDES FFTW3_INCLUDE_DIR) -libfind_process(FFTW3) - -if(FFTW3_FOUND) - message(STATUS "FFTW Found Version: " ${FFTW_VERSION}) -endif() + include(LibFindMacros) + libfind_pkg_check_modules(FFTW3_PKGCONF fftw3>=3.2) + + find_path(FFTW3_INCLUDE_DIR + NAMES fftw3.h + HINTS ${FFTW3_PKGCONF_INCLUDE_DIRS} ${FFTW3_PKGCONF_INCLUDEDIR} + PATH_SUFFIXES fftw3 + ) + + find_library(FFTW3_LIBRARY + NAMES fftw3 + HINTS ${FFTW3_PKGCONF_LIBRARY_DIRS} ${FFTW3_PKGCONF_LIBDIR} + ) + + set(FFTW3_PROCESS_LIBS FFTW3_LIBRARY) + set(FFTW3_PROCESS_INCLUDES FFTW3_INCLUDE_DIR) + libfind_process(FFTW3) + + if(FFTW3_FOUND) + message(STATUS "FFTW Found Version: " ${FFTW_VERSION}) + endif() else() -# TODO: Maybe use fftw3/FFTW3Config.cmake? + # TODO: Maybe use fftw3/FFTW3Config.cmake? -find_path(FFTW3_INCLUDE_DIR - NAMES fftw3.h -) + find_path(FFTW3_INCLUDE_DIR + NAMES fftw3.h + ) -find_library( - FFTW3_LIBRARY - NAMES libfftw3 libfftw3-3 libfftw3f-3 libfftw3l-3 - DOC "Libraries to link against for FFT Support") + find_library( + FFTW3_LIBRARY + NAMES libfftw3 libfftw3-3 libfftw3f-3 libfftw3l-3 + DOC "Libraries to link against for FFT Support") -if (FFTW3_LIBRARY) - set(FFTW3_LIBRARY_DIR ${FFTW3_LIBRARY}) -endif() + if (FFTW3_LIBRARY) + set(FFTW3_LIBRARY_DIR ${FFTW3_LIBRARY}) + endif() -set (FFTW3_LIBRARIES ${FFTW3_LIBRARY}) + set (FFTW3_LIBRARIES ${FFTW3_LIBRARY}) -if(FFTW3_INCLUDE_DIR AND FFTW3_LIBRARY_DIR) - set (FFTW3_FOUND true) - message(STATUS "Correctly found FFTW3") -else() - message(STATUS "Could not find FFTW3") -endif() + if(FFTW3_INCLUDE_DIR AND FFTW3_LIBRARY_DIR) + set (FFTW3_FOUND true) + message(STATUS "Correctly found FFTW3") + else() + message(STATUS "Could not find FFTW3") + endif() endif() diff --git a/cmake/modules/FindHEIF.cmake b/cmake/modules/FindHEIF.cmake new file mode 100644 index 0000000000..2f7fa9befa --- /dev/null +++ b/cmake/modules/FindHEIF.cmake @@ -0,0 +1,50 @@ +# - Try to find the libheif library +# Once done this will define +# +# HEIF_FOUND - system has heif +# HEIF_INCLUDE_DIRS - the heif include directories +# HEIF_LIBRARIES - the libraries needed to use heif +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# +if (NOT WIN32) + include(LibFindMacros) + libfind_pkg_check_modules(HEIF_PKGCONF libheif) + + find_path(HEIF_INCLUDE_DIR + NAMES libheif/heif.h + HINTS ${HEIF_PKGCONF_INCLUDE_DIRS} ${HEIF_PKGCONF_INCLUDEDIR} + PATH_SUFFIXES heif + ) + + find_library(HEIF_LIBRARY + NAMES heif + HINTS ${HEIF_PKGCONF_LIBRARY_DIRS} ${HEIF_PKGCONF_LIBDIR} + ) + + set(HEIF_PROCESS_LIBS HEIF_LIBRARY) + set(HEIF_PROCESS_INCLUDES HEIF_INCLUDE_DIR) + libfind_process(HEIF) + +else() + find_path(HEIF_INCLUDE_DIR + NAMES heif.h + ) + + find_library ( + HEIF_LIBRARY + NAMES libheif libheif + DOC "Libraries to link against for HEIF Support" + ) + + if (HEIF_LIBRARY) + set(HEIF_LIBRARY_DIR ${HEIF_LIBRARY}) + endif() + + set (HEIF_LIBRARIES ${HEIF_LIBRARY}) + + if(HEIF_INCLUDE_DIR AND HEIF_LIBRARY_DIR) + set (HEIF_FOUND true) + endif() +endif() + diff --git a/cmake/modules/FindLibWpd.cmake b/cmake/modules/FindLibWpd.cmake deleted file mode 100644 index ed9bc26067..0000000000 --- a/cmake/modules/FindLibWpd.cmake +++ /dev/null @@ -1,26 +0,0 @@ -# - Try to find the libwpd (WordPerfect library) -# Once done this will define -# -# LIBWPD_FOUND - system has LIBWPD -# LIBWPD_INCLUDE_DIRS - the LIBWPD include directory -# LIBWPD_LIBRARIES - Link these to use LIBWPD -# LIBWPD_DEFINITIONS - Compiler switches required for using LIBWPD -# - -include(LibFindMacros) -libfind_pkg_check_modules(WPD_PKGCONF libwpd-0.10) - -find_path(WPD_INCLUDE_DIR - NAMES libwpd/libwpd.h - HINTS ${WPD_PKGCONF_INCLUDE_DIRS} ${WPD_PKGCONF_INCLUDEDIR} - PATH_SUFFIXES libwpd-0.10 -) - -find_library(WPD_LIBRARY - NAMES wpd libwpd wpd-0.10 libwpd-0.10 - HINTS ${WPD_PKGCONF_LIBRARY_DIRS} ${WPD_PKGCONF_LIBDIR} -) - -set(LIBWPD_PROCESS_LIBS WPD_LIBRARY) -set(LIBWPD_PROCESS_INCLUDES WPD_INCLUDE_DIR) -libfind_process(LIBWPD) diff --git a/cmake/modules/FindLibWpg.cmake b/cmake/modules/FindLibWpg.cmake deleted file mode 100644 index 16c2a1cde8..0000000000 --- a/cmake/modules/FindLibWpg.cmake +++ /dev/null @@ -1,29 +0,0 @@ -# - Try to find LibWpg -# Once done this will define -# -# LIBWPG_FOUND - libwpg is available -# LIBWPG_INCLUDE_DIRS - include directory, e.g. /usr/include -# LIBWPG_LIBRARIES - the libraries needed to use LibWpg -# LIBWPG_DEFINITIONS - Compiler switches required for using LibWpg -# -# Copyright (C) 2007 Ariya Hidayat -# Redistribution and use is allowed according to the terms of the BSD license. - -include(LibFindMacros) -libfind_package(LIBWPG LibWpd) -libfind_pkg_check_modules(LIBWPG_PKGCONF libwpg-0.3) - -find_path(LIBWPG_INCLUDE_DIR - NAMES libwpg/libwpg.h - HINTS ${LIBWPG_PKGCONF_INCLUDE_DIRS} ${LIBWPG_PKGCONF_INCLUDEDIR} - PATH_SUFFIXES libwpg-0.3 -) - -find_library(LIBWPG_LIBRARY - NAMES wpg wpg-0.3 - HINTS ${LIBWPG_PKGCONF_LIBRARY_DIRS} ${LIBWPG_PKGCONF_LIBDIR} -) - -set(LIBWPG_PROCESS_LIBS LIBWPG_LIBRARY LIBWPD_LIBRARIES) -set(LIBWPG_PROCESS_INCLUDES LIBWPG_INCLUDE_DIR LIBWPD_INCLUDE_DIRS) -libfind_process(LIBWPG) diff --git a/cmake/modules/FindLibWps.cmake b/cmake/modules/FindLibWps.cmake deleted file mode 100644 index f8c82256fd..0000000000 --- a/cmake/modules/FindLibWps.cmake +++ /dev/null @@ -1,28 +0,0 @@ -# - Try to find LibWps -# Once done this will define -# -# LIBWPS_FOUND - libwps is available -# LIBWPS_INCLUDE_DIRS - include directory, e.g. /usr/include -# LIBWPS_LIBRARIES - the libraries needed to use LibWps -# -# Copyright (C) 2013 Yue Liu -# Redistribution and use is allowed according to the terms of the BSD license. - -include(LibFindMacros) -libfind_package(LIBWPS LibWpd) -libfind_pkg_check_modules(LIBWPS_PKGCONF libwps-0.3) - -find_path(LIBWPS_INCLUDE_DIR - NAMES libwps/libwps.h - HINTS ${LIBWPS_PKGCONF_INCLUDE_DIRS} ${LIBWPS_PKGCONF_INCLUDEDIR} - PATH_SUFFIXES libwps-0.3 -) - -find_library(LIBWPS_LIBRARY - NAMES wps wps-0.3 - HINTS ${LIBWPS_PKGCONF_LIBRARY_DIRS} ${LIBWPS_PKGCONF_LIBDIR} -) - -set(LIBWPS_PROCESS_LIBS LIBWPS_LIBRARY LIBWPD_LIBRARIES) -set(LIBWPS_PROCESS_INCLUDES LIBWPS_INCLUDE_DIR LIBWPD_INCLUDE_DIRS) -libfind_process(LIBWPS) diff --git a/cmake/modules/FindPythonLibrary.cmake b/cmake/modules/FindPythonLibrary.cmake index f9b34aa78d..849d42a92a 100644 --- a/cmake/modules/FindPythonLibrary.cmake +++ b/cmake/modules/FindPythonLibrary.cmake @@ -1,54 +1,58 @@ # Find Python # ~~~~~~~~~~~ # Find the Python interpreter and related Python directories. # # This file defines the following variables: # # PYTHON_EXECUTABLE - The path and filename of the Python interpreter. # # PYTHON_SHORT_VERSION - The version of the Python interpreter found, # excluding the patch version number. (e.g. 2.5 and not 2.5.1)) # # PYTHON_LONG_VERSION - The version of the Python interpreter found as a human # readable string. # # PYTHON_SITE_PACKAGES_DIR - Location of the Python site-packages directory. # # PYTHON_INCLUDE_PATH - Directory holding the python.h include file. # # PYTHON_LIBRARY, PYTHON_LIBRARIES- Location of the Python library. # Copyright (c) 2007, Simon Edwards # Copyright (c) 2012, Luca Beltrame # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. include(FindPackageHandleStandardArgs) -find_package(PythonInterp) +if (ENABLE_PYTHON_2) + find_package(PythonInterp 2.7 REQUIRED) +else(ENABLE_PYTHON_2) + find_package(PythonInterp 3.0 REQUIRED) +endif(ENABLE_PYTHON_2) if (PYTHONINTERP_FOUND) # Set the Python libraries to what we actually found for interpreters set(Python_ADDITIONAL_VERSIONS "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}") # These are kept for compatibility set(PYTHON_SHORT_VERSION "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}") set(PYTHON_LONG_VERSION ${PYTHON_VERSION_STRING}) find_package(PythonLibs QUIET) if(PYTHONLIBS_FOUND) set(PYTHON_LIBRARY ${PYTHON_LIBRARIES}) endif(PYTHONLIBS_FOUND) # Auto detect Python site-packages directory execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(True))" OUTPUT_VARIABLE PYTHON_SITE_PACKAGES_DIR OUTPUT_STRIP_TRAILING_WHITESPACE ) message(STATUS "Python system site-packages directory: ${PYTHON_SITE_PACKAGES_DIR}") endif(PYTHONINTERP_FOUND) find_package_handle_standard_args(PythonLibrary DEFAULT_MSG PYTHON_LIBRARY) diff --git a/krita/data/aboutdata/aboutdata.qrc b/krita/data/aboutdata/aboutdata.qrc index 9eb9f37255..9dd1b78735 100644 --- a/krita/data/aboutdata/aboutdata.qrc +++ b/krita/data/aboutdata/aboutdata.qrc @@ -1,9 +1,10 @@ backers.txt credits.txt developers.txt LICENSE + libraries.txt diff --git a/krita/data/aboutdata/libraries.txt b/krita/data/aboutdata/libraries.txt new file mode 100644 index 0000000000..09a21f7a7c --- /dev/null +++ b/krita/data/aboutdata/libraries.txt @@ -0,0 +1,32 @@ +Boost,https://www.boost.org,Boost Software License - Version 1.0 +DrMingw,https://github.com/jrfonseca/drmingw,LGPLv2.1 +Eigen3,http://eigen.tuxfamily.org,MPL2 +Exiv2,http://www.exiv2.org,GPLv2 +Expat,https://github.com/libexpat/libexpat,MIT +#ffmpeg,https://www.ffmpeg.org,LGPL2.1+ +fftw3,http://www.fftw.org/,GPLv2+ +fontconfig,https://www.freedesktop.org/wiki/Software/fontconfig/,BSD +freetype,https://www.freetype.org/,FTL or GPLv2 +gettext,https://www.gnu.org/software/gettext/,GPLv3 +giflib,http://giflib.sourceforge.net/,BSD +gmic,http://gmic.eu/,CeCILLv2.1 +gmic-qt,http://gmic.eu/,GPLv3 +GNU Scientific Library,http://www.gnu.org/software/gsl,GPLv3 +libheif,https://github.com/strukturag/libheif,LGPLv3 +iconv,https://www.gnu.org/software/libiconv/,LGPLv2 or GPLv3 +ilmbase,http://www.openexr.com,Modified BSD +libjpeg-turbo,http://www.libjpeg-turbo.org,BSD +littlecms2,http://www.littlecms.com,MIT +libraw,http://www.libraw.org,LGPLv2.1 or CDDL 1.0 +OpenColorIO,http://www.opencolorio.org,BSD +OpenEXR,http://www.openexr.com,Modified BSD +libpng,http://www.libpng.org,libpng license +poppler,http://poppler.freedesktop.org,GPLv2 and GPLv3 +PyQt,https://www.riverbankcomputing.com/software/pyqt/download5,GPLv3 +Python,http://www.python.org,Python Software Foundation License v2 +Qt,https://www.qt.io,GPLv2 + GPLv3 + LGPLv2.1 + LGPLv3 +SIP,https://www.riverbankcomputing.com/software/sip/download,GPLv3 +libiff,http://www.remotesensing.org/libtiff,BSD +Vc,https://github.com/VcDevel/Vc,BSD +zlib,http://www.zlib.net/,BSD +KDE Frameworks 5,https://www.kde.org,LGPLv+ diff --git a/krita/data/templates/animation/Anim-Jp-EN.desktop b/krita/data/templates/animation/Anim-Jp-EN.desktop index b2e1294130..99bf400a65 100644 --- a/krita/data/templates/animation/Anim-Jp-EN.desktop +++ b/krita/data/templates/animation/Anim-Jp-EN.desktop @@ -1,29 +1,29 @@ [Desktop Entry] Type=Link URL=.source/Anim-Jp-EN.kra Icon=template_animation Name=Animation-Japanese-En Name[ar]=حركة يابانيّة (إنجليزيّ) Name[ca]=Animació-Japonès-EN Name[ca@valencia]=Animació-Japonés-EN Name[de]=Animation-Japanisch-En Name[el]=Εφέ-κίνησης-Ιαπωνικό-En Name[en_GB]=Animation-Japanese-En Name[es]=Animación-Japonés-En Name[et]=Animation-Japanese-En Name[gl]=Animación-xaponesa-en-inglés Name[is]=Hreyfimynd-Japanska-En Name[it]=Animazione-Giapponese-EN Name[ja]=日本式アニメ(英語版) Name[nl]=Animatie-Japans-En Name[pl]=Animacja-Japońska-En Name[pt]=Animação-Japonês-EN Name[pt_BR]=Animation-Japanese-En Name[ru]=Анимация-японская-англ Name[sk]=Animation-Japanese-En Name[sv]=Animering-japanska-en Name[tr]=Canlandırma-Japonca-İngilizce Name[uk]=Японська анімація (англійською) Name[x-test]=xxAnimation-Japanese-Enxx -Name[zh_CN]=日本动画 (英式) +Name[zh_CN]=日式动画 (英文图层名) Name[zh_TW]=動畫-Japanese-En diff --git a/krita/data/templates/comics/.directory b/krita/data/templates/comics/.directory index cee998feb6..b9f581ae43 100644 --- a/krita/data/templates/comics/.directory +++ b/krita/data/templates/comics/.directory @@ -1,42 +1,42 @@ [Desktop Entry] Name=Comic Templates Name[ar]=قوالب الهزليّات Name[bs]=Predlošci stripova Name[ca]=Plantilles per a còmics Name[ca@valencia]=Plantilles per a còmics Name[cs]=Šablony komixů Name[da]=Tegneserieskabeloner Name[de]=Comic-Vorlagen Name[el]=Πρότυπα κόμικ Name[en_GB]=Comic Templates Name[es]=Plantillas de cómic Name[et]=Koomiksimallid Name[eu]=Komiki-txantiloiak Name[fi]=Sarjakuvapohjat Name[fr]=Modèles de bandes dessinées Name[gl]=Modelos de banda deseñada Name[hu]=Képregénysablonok Name[ia]=Patronos de Comic Name[is]=Comic sniðmát Name[it]=Modelli di fumetti Name[ja]=コミックテンプレート Name[kk]=Комикс үлгілері Name[ko]=만화 서식 Name[lt]=Komiksų šablonai Name[nb]=Tegneseriemaler Name[nds]=Comic-Vörlagen Name[nl]=Stripverhaalsjabloon Name[pl]=Szablony komiksów Name[pt]=Modelos de Banda Desenhada Name[pt_BR]=Modelos de quadrinhos Name[ru]=Шаблоны комиксов Name[sk]=Komixové šablóny Name[sl]=Predloge za stripe Name[sv]=Seriemallar Name[tr]=Çizgi Roman Şablonu Name[uk]=Шаблони коміксів Name[wa]=Modeles di bindes d' imådjes Name[x-test]=xxComic Templatesxx -Name[zh_CN]=漫画模板 +Name[zh_CN]=欧美漫画模板 Name[zh_TW]=漫畫範本 X-KDE-DefaultTab=true diff --git a/krita/data/templates/comics/BD-EuroTemplate.desktop b/krita/data/templates/comics/BD-EuroTemplate.desktop index a11167d7b3..449d33d571 100644 --- a/krita/data/templates/comics/BD-EuroTemplate.desktop +++ b/krita/data/templates/comics/BD-EuroTemplate.desktop @@ -1,76 +1,76 @@ [Desktop Entry] Type=Link URL=.source/BD-EuroTemplate.kra Icon=template_comics_empty Name=European BD template Name[bs]=Evropski BD predložak Name[ca]=Plantilla europea BD Name[ca@valencia]=Plantilla europea BD Name[da]=Europæisk BD-skabelon Name[de]=Europäische „Bande Dessinée (BD)“-Vorlage Name[el]=Ευρωπαϊκό BD πρότυπο Name[en_GB]=European BD template Name[es]=plantilla de cómic europeo Name[et]=Euroopa BD mall Name[eu]=Europako BD-txantiloia Name[fi]=Eurooppalainen BD-pohja Name[fr]=Modèle européen de bandes dessinées Name[gl]=Formato europeo (2×4 viñetas) Name[hu]=Európai BD sablon Name[is]=Evrópskt BD teiknimyndasögusniðmát Name[it]=Modello MD europeo Name[ja]=バンドデシネテンプレート Name[kk]=Еуропалық BD үлгісі Name[lt]=Europos DB šablonas Name[nb]=Europeisk BD-mal Name[nds]=Europääsch BD-Vörlaag Name[nl]=Europees BD-sjabloon Name[pl]=Europejski szablon BD Name[pt]=Modelo de BD Europeia Name[pt_BR]=Modelo Europeu BD Name[ru]=Шаблон в европейском стиле (BD) Name[sk]=Európska BD šablóna Name[sl]=Evropska predloga BD Name[sv]=Europeisk BD-mall Name[tr]=Avrupa BD Şablonu Name[uk]=Європейський шаблон BD Name[wa]=Modele di binde d' imådje a l' uropeyinne Name[x-test]=xxEuropean BD templatexx -Name[zh_CN]=欧洲 BD 模板 +Name[zh_CN]=欧式 BD (比利时-法国) 风格漫画模板 Name[zh_TW]=歐洲漫畫範本 Comment=template for European BD-style comics Comment[bs]=predložak za evropske BD stripove Comment[ca]=plantilla per a còmics d'estil BD europeu Comment[ca@valencia]=plantilla per a còmics d'estil BD europeu Comment[da]=Skabelon til tegneserier i europæisk BD-stil Comment[de]=Vorlage für Comics im europäischen „Bande Dessinée“-Stil Comment[el]=πρότυπο για Ευρωπαϊκά BD-style κόμικς Comment[en_GB]=template for European BD-style comics Comment[es]=plantilla para cómics de estilo europeo Comment[et]=Euroopa BD-stiilis koomiksi mall Comment[eu]=Europako BD estiloko komikietarako txantiloia Comment[fi]=Eurooppalaisen BD-tyylin sarjakuvan pohja Comment[fr]=Modèle européen de bandes dessinées Comment[gl]=Páxina de banda deseñada de formato europeo, con 2×4 viñetas regulares. Comment[hu]=sablon az európai BD-stílusú képregényekhez Comment[is]=Sniðmát fyrir evrópskar BD-teiknimyndir Comment[it]=modello per fumetti in stile BD europeo Comment[ja]=バンドデシネ式コミック用テンプレート Comment[kk]=Еуропалық BD-стильдегі комикс үлгісі Comment[nb]=mal for europeiske tegneserier i BD-stil Comment[nds]=BD-Vörlaag för europääsche Comics Comment[nl]=sjabloon voor Europese strips in BD-stijl Comment[pl]=szablon dla Europejskiego stylu komików BD Comment[pt]=modelo de banda desenhada do estilo Europeu Comment[pt_BR]=Modelo de quadrinhos no estilo Europeu BD Comment[ru]=Шаблон комиксов в европейском стиле (BD) Comment[sk]=šablóna pre európske BD komixy Comment[sl]=predloga za stripe v evropskem slogu BD Comment[sv]=seriemall med europeisk BD-stil Comment[tr]=Avrupa BD tarzı çizgi romanlar için şablon Comment[uk]=шаблон для європейських коміксів у стилі BD Comment[wa]=Modele po les bindes d' imådje al môde uropeyinne Comment[x-test]=xxtemplate for European BD-style comicsxx -Comment[zh_CN]=欧洲 BD 式漫画模板 +Comment[zh_CN]=欧式 BD (比利时-法国) 风格漫画模板 Comment[zh_TW]=歐洲 BD-風格的連環漫畫範本 X-Krita-Version=28 diff --git a/krita/data/templates/comics/Manga-JpTemplate.desktop b/krita/data/templates/comics/Manga-JpTemplate.desktop index ea19a697f5..07d58ace64 100644 --- a/krita/data/templates/comics/Manga-JpTemplate.desktop +++ b/krita/data/templates/comics/Manga-JpTemplate.desktop @@ -1,83 +1,83 @@ [Desktop Entry] Type=Link URL=.source/Manga-JpTemplate.kra Icon=template_comics_empty Name=Manga template Name[ar]=قالب مانغا Name[bs]=Manga predložak Name[ca]=Plantilla per a manga Name[ca@valencia]=Plantilla per a manga Name[cs]=Šablona Mangy Name[da]=Manga-skabelon Name[de]=Manga-Vorlage Name[el]=Πρότυπο μάνγκα Name[en_GB]=Manga template Name[es]=Plantilla manga Name[et]=Manga mall Name[eu]=Manga-txantiloia Name[fi]=Mangapohja Name[fr]=Modèle de Manga Name[gl]=Formato Manga Name[hu]=Manga sablon Name[ia]=Patrono de Manga Name[is]=Manga sniðmát Name[it]=Modello manga Name[ja]=漫画テンプレート Name[kk]=Үлгіні басқару Name[ko]=일본식 만화 서식 Name[lt]=Manga šablonas Name[nb]=Manga-mal Name[nds]=Manga-Vörlaag Name[nl]=Manga-sjabloon Name[pl]=Szablon Mangi Name[pt]=Modelo Manga Name[pt_BR]=Modelo de mangá Name[ru]=Шаблон манги Name[sk]=Manga šablóna Name[sl]=Predloga Manga Name[sv]=Manga-mall Name[tr]=Manga şablonu Name[uk]=Шаблон манґи Name[wa]=Modele di manga Name[x-test]=xxManga templatexx -Name[zh_CN]=漫画模板 +Name[zh_CN]=日式漫画模板 Name[zh_TW]=日本漫畫範本 Comment=template for Japanese Manga-style comics Comment[ar]=قالب للهزليّات بنمط المانغا اليابانيّة Comment[bs]=predložak za japanske Manga stripove Comment[ca]=plantilla per a còmics d'estil manga japonès Comment[ca@valencia]=plantilla per a còmics d'estil manga japonés Comment[cs]=šablona pro japonské komiksy ve stylu Manga Comment[da]=skabelon til tegneserier i japansk Manga-stil Comment[de]=Vorlage für Comics im Stil japanischer Mangas Comment[el]=Πρότυπο για Ιαπωνικά μάνγκα κόμικς Comment[en_GB]=template for Japanese Manga-style comics Comment[es]=plantilla para cómics de estilo manga japonés Comment[et]=Jaapani manga-stiilis koomiksi mall Comment[eu]=Japoniako Manga estiloko komikietarako txantiloia Comment[fi]=japanilaisen mangatyylisen sarjakuvan pohja Comment[fr]=Modèle de mangas japonais Comment[gl]=Páxina de banda deseñada de formato Manga, con 2×3 viñetas non regulares. Comment[hu]=sablon a japán Manga-stílusú képregényekhez Comment[is]=Sniðmát fyrir japanskar Manga-teiknimyndir Comment[it]=modello per fumetti in stile manga giapponese Comment[ja]=日本式漫画用テンプレート Comment[kk]=Жапондық манга-стильдегі комикс үлгісі Comment[ko]=일본식 만화 서식 Comment[nb]=mal for japanske tegneserier i Manga-stil Comment[nds]=Vörlaag för japaansche Manga-Comics Comment[nl]=sjabloon voor strips in Japanse Manga-stijl Comment[pl]=szablon dla Japońskiego stylu komiksów Mangi Comment[pt]=modelo de banda desenhada Manga do estilo Japonês Comment[pt_BR]=Modelo de quadrinhos no estilo mangá japonês Comment[ru]=Шаблон комиксов в японском стиле манга Comment[sk]=šablóna pre japonské manga komixy Comment[sl]=predloge za stripe v japonskem slogu Manga Comment[sv]=seriemall med japansk Manga-stil Comment[tr]=Japon Manga çizgi romanları için şablon Comment[uk]=шаблон японських коміксів у стилі манґа Comment[wa]=Modele di bindes d' imådje al môde des mangas djaponès Comment[x-test]=xxtemplate for Japanese Manga-style comicsxx Comment[zh_CN]=日式漫画模板 Comment[zh_TW]=日本 Manga-風格的連環漫畫範本 X-Krita-Version=28 diff --git a/krita/data/templates/comics/a4_waffle_grid.desktop b/krita/data/templates/comics/a4_waffle_grid.desktop index d66f063a1c..97aaee3762 100644 --- a/krita/data/templates/comics/a4_waffle_grid.desktop +++ b/krita/data/templates/comics/a4_waffle_grid.desktop @@ -1,69 +1,69 @@ [Desktop Entry] Type=Link URL=.source/a4_waffle_grid.kra Icon=template_comics_empty Name=waffle-iron grid Name[bs]=mreža sječenog željeza Name[ca]=graella de ferro Name[ca@valencia]=graella de ferro Name[da]=vaffeljernsgitter Name[de]=Waffeleisengitter Name[el]=waffle-iron κάνναβος Name[en_GB]=waffle-iron grid Name[es]=rejilla de hierro para gofres Name[et]=Vahvlimasina ruudustik Name[eu]=gofreetarako burdinazko sareta Name[fr]=Grille en métal-gaufré Name[gl]=Grade de 3×5 viñetas Name[is]=vöfflujárnshnit Name[it]=Griglia a wafer Name[ja]=格子状コマ Name[kk]=торлы көзді Name[nb]=vaffeljern-rutenett Name[nds]=Wafeliesengadder Name[nl]=wafelijzer-raster Name[pl]=siatka gofrownicy Name[pt]=grelha de ferro para 'waffles' Name[pt_BR]=Grade de ferro vazia Name[ru]=Страница с ячейками Name[sk]=vaflovo-železná mriežka Name[sv]=våffelmönster Name[tr]=waffle-çelik ızgara Name[uk]=сітка з комірками Name[wa]=grile di fier a wåfes Name[x-test]=xxwaffle-iron gridxx -Name[zh_CN]=华夫铁网格 +Name[zh_CN]=十五宫格 Name[zh_TW]=鬆餅機格線 Comment=300 dpi, A4 waffle-iron grid comic page with ink and color layers Comment[bs]=300 dpi, A4 mreža sječenog željeza stranica stripa s slojevima za tintu i bojemreža sječenog željeza Comment[ca]=300 ppp, pàgina de còmic amb graella de ferro amb capes de tinta i color Comment[ca@valencia]=300 ppp, pàgina de còmic amb graella de ferro amb capes de tinta i color Comment[da]=300 dpi, A4 tegneserieside i vaffeljernsgitter med blæk og farvelag Comment[de]=Comicseite mit Waffeleisengitter-Muster, Tinten- und Farbebenen. Format A4, Auflösung 300 dpi. Comment[el]=300 dpi, σελίδα κόμικ A4 με waffle-iron κάνναβο και στρώματα μελάνης και χρώματος Comment[en_GB]=300 dpi, A4 waffle-iron grid comic page with ink and colour layers Comment[es]=página de cómic con rejilla de hierro para gofres de tamaño A4, a 300 ppp, con tinta y capas de colores Comment[et]=300 DPI A4 vahvlimasina ruudustikuga koomiksilehekülg tindi- ja värvikihiga Comment[eu]=Gofreetarako burdinazko sareta duen 300 dpi-ko A4 komiki-orria, tinta- eta kolore-geruzaduna Comment[fr]=Page de bande dessinée avec Grille en métal-gaufré de 300 dpi, A4 avec encre et calques colorés Comment[gl]=Páxina de banda deseñada de grade en A4 a 300 dpi con 3×5 viñetas regulares e capas de tinta e cor. Comment[is]=300 pát, A4 vöfflujárnshnit teiknimyndasíða með lögum fyrir blek og liti Comment[it]=Pagina di fumetti con griglia a wafer a 300 dpi, A4, con livelli per inchiostro e colore Comment[ja]=300 dpi A4 サイズの、ペン入れレイヤーと彩色レイヤーを備えた格子状コマテンプレート Comment[kk]=300 н/д A4 торлы көзді парақтағы комикс Comment[nb]=300 dpi, A4 tegneserieside med vaffeljern-rutenett, med tusj- og fargelag Comment[nds]=300 dpi, A4 Wafeliesengadder-Comicsiet mit Dint un Klöörlagen. Comment[nl]=300 dpi, A4 wafelijzer-raster strippagina met inkt en kleurlagen Comment[pl]=300 dpi, strona A4 siatki gofrownicy z warstwami tuszu i koloru Comment[pt]=banda desenhada A4, em grelha de 'waffle' a 300 ppp, com camadas de cores e de pinturas Comment[pt_BR]=Página de quadrinhos A4, em grade de ferro a 300 ppp, com camadas de cores e de pinturas Comment[ru]=300 dpi, страница комикса в формате A4 с ячейками и слоями контуров и цветов Comment[sk]=300 dpi, A4 vaflovo železná mriežka komiksovej strany s atramentom a farebnými vrstvami Comment[sv]=300 punkter/tum, A4 våffelmönstrad seriesida med bläck- och färglager Comment[tr]=300 dpi, A4 waffle-çelik ızgara mürekkep ve renk katmanlı çizgi roman sayfası Comment[uk]=300 т/д, сторінка коміксу у форматі A4 з комірками та шарами контурів та кольорів Comment[wa]=Pådje A4 di binde d' imådjes avou on discôpaedje come ene grile di fier a wåfes avou des coûtches d' intche eyet d' coleurs. Comment[x-test]=xx300 dpi, A4 waffle-iron grid comic page with ink and color layersxx -Comment[zh_CN]=300 DPI,A4 尺寸华夫铁网格漫画页,带有墨水和色彩图层 +Comment[zh_CN]=300 DPI,A4 尺寸的十五宫格网格漫画页,内置线稿和着色图层 Comment[zh_TW]=300 dpi,A4 大小的烘餅鐵模狀的格線,有墨水與顏色圖層 X-Krita-Version=28 diff --git a/krita/data/templates/design/Designcinema16_10_2484x1200_96dpiRGB_8bit_.desktop b/krita/data/templates/design/Designcinema16_10_2484x1200_96dpiRGB_8bit_.desktop index aa23211122..44b1ec0f8b 100644 --- a/krita/data/templates/design/Designcinema16_10_2484x1200_96dpiRGB_8bit_.desktop +++ b/krita/data/templates/design/Designcinema16_10_2484x1200_96dpiRGB_8bit_.desktop @@ -1,38 +1,38 @@ [Desktop Entry] Icon=template_ratio_1610 Name=Design cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[ar]=تصميم سينمائيّ ١٦:١٠ [ ٢٤٨٤×١٢٠٠ ، ٩٦ نقطة/بوصة ، ٨ بتّ ] Name[bs]=Design cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[ca]=Disseny de cine 16:10 [2484x1200 / 96ppp RGB / 8bit] Name[ca@valencia]=Disseny de cine 16:10 [2484x1200 / 96ppp RGB / 8bit] Name[cs]=Návrh kino 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[da]=Design-cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[de]=Design-Kino 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[el]=Design cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[en_GB]=Design cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[es]=Diseño de cine 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[et]=Disainkino 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[eu]=Zinema-diseinua 16:10 [2484 x 1200, 96 dpi GBU, 8 bit] Name[fr]=style cinéma 16:10 [ 2484x1200, 96dpi RGB, 8bit ] Name[gl]=Deseño de cine 16:10 (2484×1200, 96 dpi RGB, 8 bits) Name[hu]=Tervező mozi 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[is]=Hanna kvikmynd 16:10 [ 2484x1200 , 96pát RGB , 8bita ] Name[it]=Stile cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[ja]=映画 16:10 [ 2484x1200、96dpi RGB、8 ビット ] Name[kk]=Кино пішімі 106:1 [ 2484x1200 , 96 н/д RGB , 8бит ] Name[nb]=Designkino 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[nl]=Design cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[pl]=Kino projekcyjne 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[pt]=Desenho de cinema 16:10 [ 2484x1200 , 96ppp RGB , 8-bits ] Name[pt_BR]=Design de cinema 16:10 [ 2484x1200, 96dpi RGB, 8bits ] Name[ru]=Дизайн кино 16:10 [ 2484x1200 , 96dpi RGB , 8 бит ] Name[sk]=Design cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[sv]=Design film 16:10 [ 2484x1200, 96 punkter/tum RGB, 8 bitar ] Name[tr]=Sineme tasarla 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[uk]=Компонування кіноекрана 16:10 [2484⨯1200, 96 т./д., RGB, 8 бітів] Name[x-test]=xxDesign cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ]xx -Name[zh_CN]=设计影院 16:10 [ 2484x1200 像素, 96dpi RGB , 8 位 ] +Name[zh_CN]=16:10 电影荧幕设计模板 [ 2484x1200 像素, 96dpi RGB , 8 位 ] Name[zh_TW]=設計電影院 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Type=Link URL[$e]=.source/Designcinema16_10_2484x1200_96dpiRGB_8bit_.kra X-KDE-Hidden=false diff --git a/krita/data/templates/design/Designcinema2.39_1_2484x1040_96dpiRGB_8bit_.desktop b/krita/data/templates/design/Designcinema2.39_1_2484x1040_96dpiRGB_8bit_.desktop index 2b8e41a138..f028942b5d 100644 --- a/krita/data/templates/design/Designcinema2.39_1_2484x1040_96dpiRGB_8bit_.desktop +++ b/krita/data/templates/design/Designcinema2.39_1_2484x1040_96dpiRGB_8bit_.desktop @@ -1,38 +1,38 @@ [Desktop Entry] Icon=template_ratio_2391 Name=Design cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[ar]=تصميم سينمائيّ ٢٫٣٩:١ [ ٢٤٨٤×١٠٤٠ ، ٩٦ نقطة/بوصة ، ٨ بتّ ] Name[bs]=Design cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[ca]=Disseny de cine 2,39:1 [2484x1040 / 96ppp RGB / 8bit] Name[ca@valencia]=Disseny de cine 2,39:1 [2484x1040 / 96ppp RGB / 8bit] Name[cs]=Návrh kino 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[da]=Design-cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[de]=Design-Kino 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[el]=Design cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[en_GB]=Design cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[es]=Diseño de cine 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[et]=Disainkino 2,39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[eu]=Zinema-diseinua 2.39:1 [2484 x 1040, 96 dpi GBU, 8 bit] Name[fr]=style cinéma 2.39:1 [ 2484x1040, 96dpi RGB, 8bit ] Name[gl]=Deseño de cine 2.39:1 (2484×1040, 96 dpi RGB, 8 bits) Name[hu]=Tervező mozi 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[is]=Hanna kvikmynd 2.39:1 [ 2484x1040 , 96pát RGB , 8bita ] Name[it]=Stile cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[ja]=映画 2.39:1 [ 2484x1040、96dpi RGB、8 ビット ] Name[kk]=Кино пішімі 2.39:1 [ 2484x1040 , 96 н/д RGB , 8бит ] Name[nb]=Designkino 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[nl]=Design cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[pl]=Kino projekcyjne 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[pt]=Desenho de cinema 2,39:1 [ 2484x1040 , 96ppp RGB , 8-bits ] Name[pt_BR]=Design de cinema 2.39:1 [ 2484x1040, 96dpi RGB, 8bits ] Name[ru]=Дизайн кино 2.39:1 [ 2484x1040 , 96dpi RGB , 8 бит ] Name[sk]=Design cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[sv]=Design film 2,39:1 [ 2484x1040, 96 punkter/tum RGB, 8 bitar ] Name[tr]=Sineme tasarla 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[uk]=Компонування кіноекрана 2,39:1 [2484⨯1040, 96 т./д., RGB, 8 бітів] Name[x-test]=xxDesign cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ]xx -Name[zh_CN]=设计影院 2.39:1 [ 2484x1040 像素, 96dpi RGB , 8 位] +Name[zh_CN]=2.39:1 电影荧幕设计模板 [ 2484x1040 像素, 96dpi RGB , 8 位] Name[zh_TW]=設計電影院 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Type=Link URL[$e]=.source/Designcinema2.39_1_2484x1040_96dpiRGB_8bit_.kra X-KDE-Hidden=false diff --git a/krita/data/templates/design/DesignpresentationA3Landscape_4960x3508_300dpiRGB_8bit_.desktop b/krita/data/templates/design/DesignpresentationA3Landscape_4960x3508_300dpiRGB_8bit_.desktop index ffdbf1b8fb..13b5228e55 100644 --- a/krita/data/templates/design/DesignpresentationA3Landscape_4960x3508_300dpiRGB_8bit_.desktop +++ b/krita/data/templates/design/DesignpresentationA3Landscape_4960x3508_300dpiRGB_8bit_.desktop @@ -1,37 +1,37 @@ [Desktop Entry] Icon=template_DIN_A3_landscape Name=Design presentation A3 Landscape [ 4960x3508 , 300dpi RGB , 8bit ] Name[bs]=Design prezentacija A3 položeno [ 4960x3508 , 300dpi RGB , 8bit ] Name[ca]=Disseny de presentació A3 apaïsada [4960x3508 / 300ppp RGB / 8bit] Name[ca@valencia]=Disseny de presentació A3 apaïsada [4960x3508 / 300ppp RGB / 8bit] Name[cs]=Návrh prezentace A3 vodorovně [ 4960x3508 , 300dpi RGB , 8bit ] Name[da]=Design-præsentation A3 liggende [ 4960x3508 , 300dpi RGB , 8bit ] Name[de]=Design-Präsentation A3 Querformat [ 4960x3508 , 300dpi RGB , 8bit ] Name[el]=Design presentation A3 Landscape [ 4960x3508 , 300dpi RGB , 8bit ] Name[en_GB]=Design presentation A3 Landscape [ 4960x3508 , 300dpi RGB , 8bit ] Name[es]=Diseño de presentación A3 apaisado [ 4960x3508 , 300dpi RGB , 8bit ] Name[et]=Disainesitlus A3 rõhtpaigutusega [ 4960x3508 , 300dpi RGB , 8bit ] Name[eu]=Aurkezpen-diseinua A3 horizontala [4960 x 3508, 300 dpi GBU, 8 bit] Name[fr]=Style présentation A3 paysage [ 4960x3508, 300dpi RGB, 8bit ] Name[gl]=Deseño de presentación A3 apaisada (4960×3508, 300 dpi RGB, 8 bits) Name[hu]=Tervező bemutató A3 fekvő [ 4960x3508 , 300dpi RGB , 8bit ] Name[is]=Hanna kynningu A3 lárétt : [ 4960x3508 , 300pát RGB , 8bita ] Name[it]=Stile di presentazione A3 orizzontale [ 4960x3508 , 300dpi RGB , 8bit ] Name[ja]=プレゼンテーション A3 横向き [ 4960x3508、300dpi RGB、8 ビット ] Name[kk]=Презентация пішімі A3 жатық [ 4960x3508 , 300 н/д RGB , 8бит ] Name[nb]=Design presentasjon A3 liggende [ 4960x3508 , 300dpi RGB , 8bit ] Name[nl]=Design presentatie A3 Landschap [ 4960x3508 , 300dpi RGB , 8bit ] Name[pl]=Prezentacja projekcyjna A3 poziomo [ 4960x3508 , 300dpi RGB , 8bit ] Name[pt]=Desenho de apresentação A3 em Paisagem [ 4960x3508 , 300ppp RGB , 8-bits ] Name[pt_BR]=Design de apresentação A3 paisagem [ 4960x3508, 300dpi RGB, 8bits ] Name[ru]=Дизайн презентации A3 Ландшафтный [ 4960x3508 , 300dpi RGB , 8bit ] Name[sk]=Dizajn prezentácia A3 krajinka [ 4960x3508 , 300dpi RGB , 8bit ] Name[sv]=Design presentation A3 landskap [ 4960x3508, 300 punkter/tum RGB, 8 bitar ] Name[tr]=A3 Yatay sunum tasarla [ 4960x3508 , 300dpi RGB , 8bit ] Name[uk]=Компонування презентації, A3, альбомна [4960⨯3508, 300 т./д., RGB, 8 бітів] Name[x-test]=xxDesign presentation A3 Landscape [ 4960x3508 , 300dpi RGB , 8bit ]xx -Name[zh_CN]=设计展示 A3 横版 [ 4960x3508 像素, 300dpi RGB , 8 位] +Name[zh_CN]=A3 横版设计模板 [ 4960x3508 像素, 300dpi RGB , 8 位] Name[zh_TW]=設計圖像 A3 風景畫 [ 4960x3508 , 300dpi RGB , 8bit ] Type=Link URL[$e]=.source/DesignpresentationA3Landscape_4960x3508_300dpiRGB_8bit_.kra X-KDE-Hidden=false diff --git a/krita/data/templates/design/DesignpresentationA4portrait_2480x3508,300dpiRGB_8bit_.desktop b/krita/data/templates/design/DesignpresentationA4portrait_2480x3508,300dpiRGB_8bit_.desktop index d1d949de93..53168f21c0 100644 --- a/krita/data/templates/design/DesignpresentationA4portrait_2480x3508,300dpiRGB_8bit_.desktop +++ b/krita/data/templates/design/DesignpresentationA4portrait_2480x3508,300dpiRGB_8bit_.desktop @@ -1,37 +1,37 @@ [Desktop Entry] Icon=template_DIN_A4_portrait Name=Design presentation A4 portrait [ 2480x3508 , 300dpi RGB , 8bit ] Name[bs]=Design prezentacija A4 uspravno [ 2480x3508 , 300dpi RGB , 8bit ] Name[ca]=Disseny de presentació A4 vertical [2480x3508 / 300ppp RGB / 8bit] Name[ca@valencia]=Disseny de presentació A4 vertical [2480x3508 / 300ppp RGB / 8bit] Name[cs]=Návrh prezentace A4 svisle [ x3508 , 300dpi RGB , 8bit ] Name[da]=Design-præsentation A4 stående [ x3508 , 300dpi RGB , 8bit ] Name[de]=Design-Präsentation A4 Hochformat [ 2480x3508 , 300dpi RGB , 8bit ] Name[el]=Design presentation A4 portrait [ 2480x3508 , 300dpi RGB , 8bit ] Name[en_GB]=Design presentation A4 portrait [ 2480x3508 , 300dpi RGB , 8bit ] Name[es]=Diseño de presentación A4 retrato [ 2480x3508 , 300dpi RGB , 8bit ] Name[et]=Disainesitlus A4 püstpaigutusega [ 2480x3508 , 300dpi RGB , 8bit ] Name[eu]=Aurkezpen-diseinua A4 bertikala [2480 x 3508, 300 dpi GBU, 8 bit] Name[fr]=Style présentation A4 portrait [ 2480x3508, 300dpi RGB, 8bit ] Name[gl]=Deseño de presentación A4 vertical (2480×3508, 300 dpi RGB, 8 bits) Name[hu]=Tervező bemutató A4 álló [ 2480x3508 , 300dpi RGB , 8bit ] Name[is]=Hanna kynningu A4 lóðrétt : [ 2480x3508 , 300pát RGB , 8bita ] Name[it]=Stile di presentazione A4 verticale [ 2480x3508 , 300dpi RGB , 8bit ] Name[ja]=プレゼンテーション A4 縦向き [ 2480x3508、300dpi RGB、8 ビット ] Name[kk]=Презентация пішімі A4 жатық [ 2460x3508 , 300 н/д RGB , 8бит ] Name[nb]=Design presentasjon A4 stående [ x3508 , 300dpi RGB , 8bit ] Name[nl]=Design presentatie A4 portret [ 2480x3508 , 300dpi RGB , 8bit ] Name[pl]=Prezentacja projekcyjna A4 pionowo [ 2480x3508 , 300dpi RGB , 8bit ] Name[pt]=Desenho de apresentação A4 em Paisagem [ 2480x3508 , 300ppp RGB , 8-bits ] Name[pt_BR]=Design de apresentação A4 retrato [ 2480x3508, 300dpi RGB, 8bits ] Name[ru]=Дизайн презентации A4 Портретный [ 2480x3508 , 300dpi RGB , 8bit ] Name[sk]=Dizajn prezentácia A4 portrét [ 2480x3508 , 300dpi RGB , 8bit ] Name[sv]=Design presentation A4 porträtt [ 2480x3508, 300 punkter/tum RGB, 8 bitar ] Name[tr]=A4 dikey sunum tasarla [ 2480x3508 , 300dpi RGB , 8bit ] Name[uk]=Компонування презентації, A4, книжкова [2480x3508, 300 т./д., RGB, 8 бітів] Name[x-test]=xxDesign presentation A4 portrait [ 2480x3508 , 300dpi RGB , 8bit ]xx -Name[zh_CN]=设计展示 A4 竖版 [ 2480x3508 像素, 300dpi RGB , 8 位 ] +Name[zh_CN]=A4 竖版设计模板 [ 2480x3508 像素, 300dpi RGB , 8 位 ] Name[zh_TW]=設計圖像 A4 肖像 [ 2480x3508 , 300dpi RGB , 8bit ] Type=Link URL[$e]=.source/DesignpresentationA4portrait_2480x3508_300dpiRGB_8bit_.kra X-KDE-Hidden=false diff --git a/krita/data/templates/design/DesignpresentationA4portrait_2480x3508_300dpiRGB_8bit_.desktop b/krita/data/templates/design/DesignpresentationA4portrait_2480x3508_300dpiRGB_8bit_.desktop index d1d949de93..53168f21c0 100644 --- a/krita/data/templates/design/DesignpresentationA4portrait_2480x3508_300dpiRGB_8bit_.desktop +++ b/krita/data/templates/design/DesignpresentationA4portrait_2480x3508_300dpiRGB_8bit_.desktop @@ -1,37 +1,37 @@ [Desktop Entry] Icon=template_DIN_A4_portrait Name=Design presentation A4 portrait [ 2480x3508 , 300dpi RGB , 8bit ] Name[bs]=Design prezentacija A4 uspravno [ 2480x3508 , 300dpi RGB , 8bit ] Name[ca]=Disseny de presentació A4 vertical [2480x3508 / 300ppp RGB / 8bit] Name[ca@valencia]=Disseny de presentació A4 vertical [2480x3508 / 300ppp RGB / 8bit] Name[cs]=Návrh prezentace A4 svisle [ x3508 , 300dpi RGB , 8bit ] Name[da]=Design-præsentation A4 stående [ x3508 , 300dpi RGB , 8bit ] Name[de]=Design-Präsentation A4 Hochformat [ 2480x3508 , 300dpi RGB , 8bit ] Name[el]=Design presentation A4 portrait [ 2480x3508 , 300dpi RGB , 8bit ] Name[en_GB]=Design presentation A4 portrait [ 2480x3508 , 300dpi RGB , 8bit ] Name[es]=Diseño de presentación A4 retrato [ 2480x3508 , 300dpi RGB , 8bit ] Name[et]=Disainesitlus A4 püstpaigutusega [ 2480x3508 , 300dpi RGB , 8bit ] Name[eu]=Aurkezpen-diseinua A4 bertikala [2480 x 3508, 300 dpi GBU, 8 bit] Name[fr]=Style présentation A4 portrait [ 2480x3508, 300dpi RGB, 8bit ] Name[gl]=Deseño de presentación A4 vertical (2480×3508, 300 dpi RGB, 8 bits) Name[hu]=Tervező bemutató A4 álló [ 2480x3508 , 300dpi RGB , 8bit ] Name[is]=Hanna kynningu A4 lóðrétt : [ 2480x3508 , 300pát RGB , 8bita ] Name[it]=Stile di presentazione A4 verticale [ 2480x3508 , 300dpi RGB , 8bit ] Name[ja]=プレゼンテーション A4 縦向き [ 2480x3508、300dpi RGB、8 ビット ] Name[kk]=Презентация пішімі A4 жатық [ 2460x3508 , 300 н/д RGB , 8бит ] Name[nb]=Design presentasjon A4 stående [ x3508 , 300dpi RGB , 8bit ] Name[nl]=Design presentatie A4 portret [ 2480x3508 , 300dpi RGB , 8bit ] Name[pl]=Prezentacja projekcyjna A4 pionowo [ 2480x3508 , 300dpi RGB , 8bit ] Name[pt]=Desenho de apresentação A4 em Paisagem [ 2480x3508 , 300ppp RGB , 8-bits ] Name[pt_BR]=Design de apresentação A4 retrato [ 2480x3508, 300dpi RGB, 8bits ] Name[ru]=Дизайн презентации A4 Портретный [ 2480x3508 , 300dpi RGB , 8bit ] Name[sk]=Dizajn prezentácia A4 portrét [ 2480x3508 , 300dpi RGB , 8bit ] Name[sv]=Design presentation A4 porträtt [ 2480x3508, 300 punkter/tum RGB, 8 bitar ] Name[tr]=A4 dikey sunum tasarla [ 2480x3508 , 300dpi RGB , 8bit ] Name[uk]=Компонування презентації, A4, книжкова [2480x3508, 300 т./д., RGB, 8 бітів] Name[x-test]=xxDesign presentation A4 portrait [ 2480x3508 , 300dpi RGB , 8bit ]xx -Name[zh_CN]=设计展示 A4 竖版 [ 2480x3508 像素, 300dpi RGB , 8 位 ] +Name[zh_CN]=A4 竖版设计模板 [ 2480x3508 像素, 300dpi RGB , 8 位 ] Name[zh_TW]=設計圖像 A4 肖像 [ 2480x3508 , 300dpi RGB , 8bit ] Type=Link URL[$e]=.source/DesignpresentationA4portrait_2480x3508_300dpiRGB_8bit_.kra X-KDE-Hidden=false diff --git a/krita/data/templates/design/Designscreen4_3_2250x1680_96dpiRGB_8bit_.desktop b/krita/data/templates/design/Designscreen4_3_2250x1680_96dpiRGB_8bit_.desktop index 501bdec3bb..bc79ac93e2 100644 --- a/krita/data/templates/design/Designscreen4_3_2250x1680_96dpiRGB_8bit_.desktop +++ b/krita/data/templates/design/Designscreen4_3_2250x1680_96dpiRGB_8bit_.desktop @@ -1,37 +1,37 @@ [Desktop Entry] Icon=template_ratio_43 Name=Design screen 4:3 [ 2250x1680 , 96dpi RGB , 8bit ] Name[bs]=Design ekran 4:3 [ 2250x1680 , 96dpi RGB , 8bit ] Name[ca]=Disseny de pantalla 4:3 [2250x1680 / 96ppp RGB / 8bit] Name[ca@valencia]=Disseny de pantalla 4:3 [2250x1680 / 96ppp RGB / 8bit] Name[cs]=Návrh obrazovka 4:3 [ 2250x1680 , 96dpi RGB , 8bit ] Name[da]=Design-skærm 4:3 [ 2250x1680 , 96dpi RGB , 8bit ] Name[de]=Design-Bildschirm 4:3 [ 2250x1680 , 96dpi RGB , 8bit ] Name[el]=Design screen 4:3 [ 2250x1680 , 96dpi RGB , 8bit ] Name[en_GB]=Design screen 4:3 [ 2250x1680 , 96dpi RGB , 8bit ] Name[es]=Diseño de pantalla 4:3 [ 2250x1680 , 96dpi RGB , 8bit ] Name[et]=Disainekraan 4:3 [ 2250x1680 , 96dpi RGB , 8bit ] Name[eu]=Diseinu-pantaila 4:3 [2250 x 1680, 96 dpi GBU, 8 bit] Name[fr]=Style écran 4:3 [ 2250x1680, 96dpi RGB, 8bit ] Name[gl]=Deseño de pantalla 4:3 (2250×1680, 96 dpi RGB, 8 bits) Name[hu]=Tervező kijelző 4:3 [ 2250x1680 , 96dpi RGB , 8bit ] Name[is]=Hanna skjá 4:3 [ 2250x1680 , 96pát RGB , 8bita ] Name[it]=Stile di disegno 4:3 [ 2250x1680 , 96dpi RGB , 8bit ] Name[ja]=スクリーン 4:3 [ 2250x1680、96dpi RGB、8ビット ] Name[kk]=Экран пішімі 4:3 [ 2250x1680 , 96 н/д RGB , 8бит ] Name[nb]=Design skjerm 4:3 [ 2250x1680 , 96dpi RGB , 8bit ] Name[nl]=Design screen 4:3 [ 2250x1680 , 96dpi RGB , 8bit ] Name[pl]=Ekran projekcyjny 4:3 [ 2250x1680 , 96dpi RGB , 8bit ] Name[pt]=Desenho de ecrã 4:3 [ 2250x1680 , 96ppp RGB , 8-bits ] Name[pt_BR]=Design de tela 4:3 [ 2250x1680, 96dpi RGB, 8bits ] Name[ru]=Дизайн экрана 4:3 [ 2250x1680 , 96dpi RGB , 8bit ] Name[sk]=Dizajn obrazovka 4:3 [ 2250x1680 , 96dpi RGB , 8bit ] Name[sv]=Design skärm 4:3 [ 2250x1680, 96 punkter/tum RGB, 8 bitar ] Name[tr]=Ekran tasarla 4:3 [ 2250x1680 , 96dpi RGB , 8bit ] Name[uk]=Компонування екрана 4:3 [2250⨯1680, 96 т./д., RGB, 8 бітів] Name[x-test]=xxDesign screen 4:3 [ 2250x1680 , 96dpi RGB , 8bit ]xx -Name[zh_CN]=设计屏幕 4:3 [ 2250x1680 像素, 96dpi RGB , 8 位] +Name[zh_CN]=4:3 屏幕设计模板 [ 2250x1680 像素, 96dpi RGB , 8 位] Name[zh_TW]=設計螢幕 4:3 [ 2250x1680 , 96dpi RGB , 8bit ] Type=Link URL[$e]=.source/Designscreen4_3_2250x1680_96dpiRGB_8bit_.kra X-KDE-Hidden=false diff --git a/krita/data/templates/design/web_design.desktop b/krita/data/templates/design/web_design.desktop index 8cdd01ef3b..0315644351 100644 --- a/krita/data/templates/design/web_design.desktop +++ b/krita/data/templates/design/web_design.desktop @@ -1,35 +1,35 @@ [Desktop Entry] Icon=template_web_design Name=Web Design [ 2160x1440 , 72ppi RGB , 8bit ] Name[ar]=تصميم وبّ [ ٢١٦٠×١٤٤٠ ، ٧٢ بكسل/بوصة ، ٨ بتّ ] Name[bs]=Web dizajn [ 2160x1440 , 72ppi RGB , 8bit ] Name[ca]=Disseny web [2160x1440 / 72ppi RGB / 8bit] Name[ca@valencia]=Disseny web [2160x1440 / 72ppi RGB / 8bit] Name[cs]=Návrh webu [ 2160x1440 , 72ppi RGB , 8bit ] Name[da]=Webdesign [ 2160x1440 , 72ppi RGB , 8bit ] Name[de]=Web-Design [ 2160x1440 , 72ppi RGB , 8bit ] Name[el]=Σχεδίαση διαδικτυακών τόπων [ 2160x1440 , 72ppi RGB , 8bit ] Name[en_GB]=Web Design [ 2160x1440 , 72ppi RGB , 8bit ] Name[es]=Diseño de web 4:3 [ 2160x1440 , 72ppi RGB , 8bit ] Name[et]=Veebidisain [ 2160x1440, 72ppi RGB, 8-bitine ] Name[fr]=Style écran [ 2160x1440, 72ppi RGB , 8bit ] Name[gl]=Deseño web (2160×1440, 72 ppi RGB, 8 bits) Name[is]=Vefhönnun [ 2160x1440 , 72pát RGB , 8bita ] Name[it]=Progettazione web [ 2160x1440 , 72ppi RGB , 8bit ] Name[ja]=ウェブデザイン [ 2160x1440、72ppi RGB、8 ビット ] Name[nb]=Web Design [ 2160x1440 , 72ppi RGB , 8bit ] Name[nl]=Webontwerp [ 2160x1440 , 72ppi RGB , 8bit ] Name[pl]=Projekt sieciowy [ 2160x1440 , 72ppi RGB , 8bit ] Name[pt]=Desenho na Web [ 2160x1440 , 72ppp RGB , 8-bits ] Name[pt_BR]=Web Design [ 2160x1440 , 72ppi RGB , 8bits ] Name[ru]=Веб-дизайн [ 2160x1440 , 72ppi RGB , 8 бит ] Name[sk]=Webový dizajn [ 2160x1440 , 72ppi RGB , 8bit ] Name[sv]=Webbdesign [ 2160x1440, 72 punkter/tum RGB, 8 bitar ] Name[tr]=Web Tasarımı [ 2160x1440 , 72ppi RGB , 8bit ] Name[uk]=Вебдизайн [2160⨯1440, 72 т./д., RGB, 8 бітів] Name[x-test]=xxWeb Design [ 2160x1440 , 72ppi RGB , 8bit ]xx -Name[zh_CN]=网页设计 [ 2160x1440 像素, 72ppi RGB , 8 位 ] +Name[zh_CN]=网页设计模板 [ 2160x1440 像素, 72ppi RGB , 8 位 ] Name[zh_TW]=網頁設計 [ 2160x1440 , 72ppi RGB , 8bit ] Type=Link URL[$e]=.source/web_design.kra X-KDE-Hidden=false diff --git a/krita/data/templates/dslr/.directory b/krita/data/templates/dslr/.directory index a59afd77ea..45a429f165 100644 --- a/krita/data/templates/dslr/.directory +++ b/krita/data/templates/dslr/.directory @@ -1,40 +1,40 @@ [Desktop Entry] Name=DSLR Templates Name[ar]=قوالب DSLR Name[bs]=DSLR Predlošci Name[ca]=Plantilles DSLR Name[ca@valencia]=Plantilles DSLR Name[cs]=Šablony DSLR Name[da]=DSLR-skabeloner Name[de]=DSLR-Vorlagen Name[el]=Πρότυπα DSLR Name[en_GB]=DSLR Templates Name[es]=Plantillas DSLR Name[et]=Digitaalpeegelkaamera (DSLR) mallid Name[eu]=DSLR txantiloiak Name[fi]=DSLR-pohjat Name[fr]=Modèles DSLR Name[gl]=Modelos DSLR Name[hu]=DSLR sablonok Name[ia]=Patronos de DSLR Name[is]=DSLR sniðmát Name[it]=Modelli DSLR Name[ja]=デジタル一眼レフテンプレート Name[kk]=DSLR үлгілері Name[ko]=DSLR 서식 Name[lt]=DSLR šablonai Name[nb]=DSLR-maler Name[nl]=DSLR-sjablonen Name[pl]=Szablony DSLR Name[pt]=Modelos de DSLR Name[pt_BR]=Modelos DSLR Name[ru]=Шаблоны для фотоаппаратов Name[sk]=Šablóny DSLR Name[sl]=Predloge DSLR Name[sv]=Mallar för digitala spegelreflexkameror Name[tr]=DSLR Şablonları Name[uk]=Шаблони DSLR Name[x-test]=xxDSLR Templatesxx -Name[zh_CN]=DSLR 模板 +Name[zh_CN]=单反相机模板 Name[zh_TW]=數位單眼相機範本 X-KDE-DefaultTab=true diff --git a/krita/data/templates/dslr/Canon_550D_5184x3456.desktop b/krita/data/templates/dslr/Canon_550D_5184x3456.desktop index 0634f88cf3..b3a199f067 100755 --- a/krita/data/templates/dslr/Canon_550D_5184x3456.desktop +++ b/krita/data/templates/dslr/Canon_550D_5184x3456.desktop @@ -1,40 +1,40 @@ [Desktop Entry] Icon=template_dslr Name=Canon_550D_5184x3456 Name[bs]=Canon_550D_5184x3456 Name[ca]=Canon_550D_5184x3456 Name[ca@valencia]=Canon_550D_5184x3456 Name[cs]=Canon_550D_5184x3456 Name[da]=Canon_550D_5184x3456 Name[de]=Canon 550D 5184x3456 Name[el]=Canon_550D_5184x3456 Name[en_GB]=Canon_550D_5184x3456 Name[es]=Canon_550D_5184x3456 Name[et]=Canon_550D_5184x3456 Name[eu]=Canon_550D_5184x3456 Name[fi]=Canon 550D 5184 × 3456 Name[fr]=Canon_550D_5184x3456 Name[gl]=Canon 550D (5184×3456) Name[hu]=Canon_550D_5184x3456 Name[is]=Canon_550D_5184x3456 Name[it]=Canon_550D_5184x3456 Name[ja]=キヤノンEOS Kiss X4(5184x3456) Name[kk]=Canon_550D_5184x3456 Name[ko]=Canon_550D_5184x3456 Name[nb]=Canon_550D_5184x3456 Name[nl]=Canon_550D_5184x3456 Name[pl]=Canon_550D_5184x3456 Name[pt]=Canon_550D_5184x3456 Name[pt_BR]=Canon 550D 5184x3456 Name[ru]=Canon_550D_5184x3456 Name[sk]=Canon_550D_5184x3456 Name[sl]=Canon_550D_5184x3456 Name[sv]=Canon_550D_5184x3456 Name[tr]=Canon_550D_5184x3456 Name[uk]=Canon 550D 5184⨯3456 Name[x-test]=xxCanon_550D_5184x3456xx -Name[zh_CN]=佳能 550D 相机 5184x3456 像素 +Name[zh_CN]=佳能 550D 相机模板 5184x3456 像素 Name[zh_TW]=Canon_550D_5184x3456 Type=Link URL[$e]=.source/Canon_550D_5184x3456.kra X-KDE-Hidden=false diff --git a/krita/data/templates/dslr/Canon_5Dmk3_5760x3840.desktop b/krita/data/templates/dslr/Canon_5Dmk3_5760x3840.desktop index 1018f0ae41..d1739ec86e 100755 --- a/krita/data/templates/dslr/Canon_5Dmk3_5760x3840.desktop +++ b/krita/data/templates/dslr/Canon_5Dmk3_5760x3840.desktop @@ -1,40 +1,40 @@ [Desktop Entry] Icon=template_dslr Name=Canon_5Dmk3_5760x3840 Name[bs]=Canon_5Dmk3_5760x3840 Name[ca]=Canon_5Dmk3_5760x3840 Name[ca@valencia]=Canon_5Dmk3_5760x3840 Name[cs]=Canon_5Dmk3_5760x3840 Name[da]=Canon_5Dmk3_5760x3840 Name[de]=Canon 5Dmk3 5760x3840 Name[el]=Canon_5Dmk3_5760x3840 Name[en_GB]=Canon_5Dmk3_5760x3840 Name[es]=Canon_5Dmk3_5760x3840 Name[et]=Canon_5Dmk3_5760x3840 Name[eu]=Canon_5Dmk3_5760x3840 Name[fi]=Canon 5Dmk3 5760 × 3840 Name[fr]=Canon_5Dmk3_5760x3840 Name[gl]=Canon 5Dmk3 (5760×3840) Name[hu]=Canon_5Dmk3_5760x3840 Name[is]=Canon_5Dmk3_5760x3840 Name[it]=Canon_5Dmk3_5760x3840 Name[ja]=キヤノンEOS 5D Mark III(5760x3840) Name[kk]=Canon_5Dmk3_5760x3840 Name[ko]=Canon_5Dmk3_5760x3840 Name[nb]=Canon_5Dmk3_5760x3840 Name[nl]=Canon_5Dmk3_5760x3840 Name[pl]=Canon_5Dmk3_5760x3840 Name[pt]=Canon_5Dmk3_5760x3840 Name[pt_BR]=Canon 5D Mark III 5760x3840 Name[ru]=Canon_5Dmk3_5760x3840 Name[sk]=Canon_5Dmk3_5760x3840 Name[sl]=Canon_5Dmk3_5760x3840 Name[sv]=Canon_5Dmk3_5760x3840 Name[tr]=Canon_5Dmk3_5760x3840 Name[uk]=Canon 5Dmk3 5760⨯3840 Name[x-test]=xxCanon_5Dmk3_5760x3840xx -Name[zh_CN]=佳能 5Dmk3 相机 5760x3840 像素 +Name[zh_CN]=佳能 5Dmk3 相机模板 5760x3840 像素 Name[zh_TW]=Canon_5Dmk3_5760x3840 Type=Link URL[$e]=.source/Canon_5Dmk3_5760x3840.kra X-KDE-Hidden=false diff --git a/krita/data/templates/dslr/Nikon_D3000_3872x2592.desktop b/krita/data/templates/dslr/Nikon_D3000_3872x2592.desktop index 880eff1ebc..48055de450 100755 --- a/krita/data/templates/dslr/Nikon_D3000_3872x2592.desktop +++ b/krita/data/templates/dslr/Nikon_D3000_3872x2592.desktop @@ -1,40 +1,40 @@ [Desktop Entry] Icon=template_dslr Name=Nikon_D3000_3872x2592 Name[bs]=Nikon_D3000_3872x2592 Name[ca]=Nikon_D3000_3872x2592 Name[ca@valencia]=Nikon_D3000_3872x2592 Name[cs]=Nikon_D3000_3872x2592 Name[da]=Nikon_D3000_3872x2592 Name[de]=Nikon D3000 3872x2592 Name[el]=Nikon_D3000_3872x2592 Name[en_GB]=Nikon_D3000_3872x2592 Name[es]=Nikon_D3000_3872x2592 Name[et]=Nikon_D3000_3872x2592 Name[eu]=Nikon_D3000_3872x2592 Name[fi]=Nikon D3000 3872 × 2592 Name[fr]=Nikon_D3000_3872x2592 Name[gl]=Nikon D3000 (3872×2592) Name[hu]=Nikon_D3000_3872x2592 Name[is]=Nikon_D3000_3872x2592 Name[it]=Nikon_D3000_3872x2592 Name[ja]=ニコンD3000(3872x2592) Name[kk]=Nikon_D3000_3872x2592 Name[ko]=Nikon_D3000_3872x2592 Name[nb]=Nikon_D3000_3872x2592 Name[nl]=Nikon_D3000_3872x2592 Name[pl]=Nikon_D3000_3872x2592 Name[pt]=Nikon_D3000_3872x2592 Name[pt_BR]=Nikon D3000 3872x2592 Name[ru]=Nikon_D3000_3872x2592 Name[sk]=Nikon_D3000_3872x2592 Name[sl]=Nikon_D3000_3872x2592 Name[sv]=Nikon_D3000_3872x2592 Name[tr]=Nikon_D3000_3872x2592 Name[uk]=Nikon D3000 3872⨯2592 Name[x-test]=xxNikon_D3000_3872x2592xx -Name[zh_CN]=尼康 D3000 相机 3872x2592 像素 +Name[zh_CN]=尼康 D3000 相机模板 3872x2592 像素 Name[zh_TW]=Nikon_D3000_3872x2592 Type=Link URL[$e]=.source/Nikon_D3000_3872x2592.kra X-KDE-Hidden=false diff --git a/krita/data/templates/dslr/Nikon_D5000_4288x2848.desktop b/krita/data/templates/dslr/Nikon_D5000_4288x2848.desktop index 4c44420551..ca330180bb 100755 --- a/krita/data/templates/dslr/Nikon_D5000_4288x2848.desktop +++ b/krita/data/templates/dslr/Nikon_D5000_4288x2848.desktop @@ -1,41 +1,41 @@ [Desktop Entry] Icon=template_dslr Name=Nikon_D5000_4288x2848 Name[ast]=Nikon_D5000_4288x2848 Name[bs]=Nikon_D5000_4288x2848 Name[ca]=Nikon_D5000_4288x2848 Name[ca@valencia]=Nikon_D5000_4288x2848 Name[cs]=Nikon_D5000_4288x2848 Name[da]=Nikon_D5000_4288x2848 Name[de]=Nikon D5000 4288x2848 Name[el]=Nikon_D5000_4288x2848 Name[en_GB]=Nikon_D5000_4288x2848 Name[es]=Nikon_D5000_4288x2848 Name[et]=Nikon_D5000_4288x2848 Name[eu]=Nikon_D5000_4288x2848 Name[fi]=Nikon D5000 4288 × 2848 Name[fr]=Nikon_D5000_4288x2848 Name[gl]=Nikon D5000 (4288×2848) Name[hu]=Nikon_D5000_4288x2848 Name[is]=Nikon_D5000_4288x2848 Name[it]=Nikon_D5000_4288x2848 Name[ja]=ニコンD5000(4288x2848) Name[kk]=Nikon_D5000_4288x2848 Name[ko]=Nikon_D5000_4288x2848 Name[nb]=Nikon_D5000_4288x2848 Name[nl]=Nikon_D5000_4288x2848 Name[pl]=Nikon_D5000_4288x2848 Name[pt]=Nikon_D5000_4288x2848 Name[pt_BR]=Nikon D5000 4288x2848 Name[ru]=Nikon_D5000_4288x2848 Name[sk]=Nikon_D5000_4288x2848 Name[sl]=Nikon_D5000_4288x2848 Name[sv]=Nikon_D5000_4288x2848 Name[tr]=Nikon_D5000_4288x2848 Name[uk]=Nikon D5000 4288⨯2848 Name[x-test]=xxNikon_D5000_4288x2848xx -Name[zh_CN]=尼康 D5000 相机 4288x2848 像素 +Name[zh_CN]=尼康 D5000 相机模板 4288x2848 像素 Name[zh_TW]=Nikon_D5000_4288x2848 Type=Link URL[$e]=.source/Nikon_D5000_4288x2848.kra X-KDE-Hidden=false diff --git a/krita/data/templates/dslr/Nikon_D7000_4928x3264.desktop b/krita/data/templates/dslr/Nikon_D7000_4928x3264.desktop index d5e78ec76e..aba2175444 100755 --- a/krita/data/templates/dslr/Nikon_D7000_4928x3264.desktop +++ b/krita/data/templates/dslr/Nikon_D7000_4928x3264.desktop @@ -1,40 +1,40 @@ [Desktop Entry] Icon=template_dslr Name=Nikon_D7000_4928x3264 Name[bs]=Nikon_D7000_4928x3264 Name[ca]=Nikon_D7000_4928x3264 Name[ca@valencia]=Nikon_D7000_4928x3264 Name[cs]=Nikon_D7000_4928x3264 Name[da]=Nikon_D7000_4928x3264 Name[de]=Nikon D7000 4928x3264 Name[el]=Nikon_D7000_4928x3264 Name[en_GB]=Nikon_D7000_4928x3264 Name[es]=Nikon_D7000_4928x3264 Name[et]=Nikon_D7000_4928x3264 Name[eu]=Nikon_D7000_4928x3264 Name[fi]=Nikon D7000 4928 × 3264 Name[fr]=Nikon_D7000_4928x3264 Name[gl]=Nikon D7000 (4928×3264) Name[hu]=Nikon_D7000_4928x3264 Name[is]=Nikon_D7000_4928x3264 Name[it]=Nikon_D7000_4928x3264 Name[ja]=ニコンD7000(4928x3264) Name[kk]=Nikon_D7000_4928x3264 Name[ko]=Nikon_D7000_4928x3264 Name[nb]=Nikon_D7000_4928x3264 Name[nl]=Nikon_D7000_4928x3264 Name[pl]=Nikon_D7000_4928x3264 Name[pt]=Nikon_D7000_4928x3264 Name[pt_BR]=Nikon D7000 4928x3264 Name[ru]=Nikon_D7000_4928x3264 Name[sk]=Nikon_D7000_4928x3264 Name[sl]=Nikon_D7000_4928x3264 Name[sv]=Nikon_D7000_4928x3264 Name[tr]=Nikon_D7000_4928x3264 Name[uk]=Nikon D7000 4928⨯3264 Name[x-test]=xxNikon_D7000_4928x3264xx -Name[zh_CN]=尼康 D7000 相机 4928x3264 像素 +Name[zh_CN]=尼康 D7000 相机模板 4928x3264 像素 Name[zh_TW]=Nikon_D7000_4928x3264 Type=Link URL[$e]=.source/Nikon_D7000_4928x3264.kra X-KDE-Hidden=false diff --git a/krita/data/templates/texture/Texture1024x10248bitsrgb.desktop b/krita/data/templates/texture/Texture1024x10248bitsrgb.desktop index f0ddfc5ad4..a55724ccfd 100644 --- a/krita/data/templates/texture/Texture1024x10248bitsrgb.desktop +++ b/krita/data/templates/texture/Texture1024x10248bitsrgb.desktop @@ -1,34 +1,34 @@ [Desktop Entry] Icon=template_texture Name=Texture 1024x1024 8bit srgb Name[bs]=Tekstura 1024x1024 8bit srgb Name[ca]=Textura 1024x1024 8bit SRGB Name[ca@valencia]=Textura 1024x1024 8bit SRGB Name[cs]=Textura 1024x1024 8bit srgb Name[da]=Tekstur 1024x1024 8bit srgb Name[de]=Textur 1024x1024 8bit srgb Name[el]=Υφή 1024x1024 8bit srgb Name[en_GB]=Texture 1024x1024 8bit srgb Name[es]=Textura 1024x1024 8bits srgb Name[et]=Tekstuur 1024x1024 8bit srgb Name[fr]=Texture 1024x1024 8bit srgb Name[gl]=Textura de 1024×1024 e 8 bits SRGB Name[is]=Efnisáferð 1024x1024 8bita srgb Name[it]=Trama 1024x1024 8bit srgb Name[ja]=テクスチャ 1024x1024 8 ビット sRGB Name[nb]=Tekstur 1024x1024 8bit srgb Name[nl]=Textuur 1024x1024 8bit srgb Name[pl]=Tekstura 1024x1024 8bit srgb Name[pt]=Textura 1024x1024 8-bits sRGB Name[pt_BR]=Textura 1024x1024 8-bits sRGB Name[ru]=Текстура 1024x1024 8 бит srgb Name[sk]=Textúra 1024x1024 8bit srgb Name[sv]=Struktur 1024 x 1024 8-bitar SRGB Name[tr]=Doku 1024x1024 8bit srgb Name[uk]=Текстура 1024⨯1024, 8-бітова, srgb Name[x-test]=xxTexture 1024x1024 8bit srgbxx -Name[zh_CN]=纹理 1024x1024 像素 8位 srgb 色彩空间 +Name[zh_CN]=1024x1024 纹理模板 8位 sRGB 色彩空间 Name[zh_TW]=紋理 1024x1024 8位元 srgb Type=Link URL[$e]=.source/Texture1024x10248bitsrgb.kra X-KDE-Hidden=false diff --git a/krita/data/templates/texture/Texture1k32bitscalar.desktop b/krita/data/templates/texture/Texture1k32bitscalar.desktop index 358f8224aa..db4e5e1d77 100755 --- a/krita/data/templates/texture/Texture1k32bitscalar.desktop +++ b/krita/data/templates/texture/Texture1k32bitscalar.desktop @@ -1,37 +1,37 @@ [Desktop Entry] Icon=template_texture Name=Texture 1k 32bit scalar Name[bs]=Tekstura 1k 32bit scalar Name[ca]=Textura 1k 32bit escalar Name[ca@valencia]=Textura 1k 32bit escalar Name[cs]=Textura 1k 32bit skalární Name[da]=Tekstur 1k 32bit scalar Name[de]=Textur 1k 32bit scalar Name[el]=Υφή 1k 32bit βαθμωτό Name[en_GB]=Texture 1k 32bit scalar Name[es]=Textura 1k 32 bit escalar Name[et]=Tekstuur 1k 32bit skalaar Name[eu]=Testura 1k 16bit eskalarra Name[fr]=Texture 1k 32bit scalaire Name[gl]=Textura de 1k e 32 bits escalar Name[hu]=Textúra 1k 32bit skalár Name[is]=Efnisáferð 1k 32bita scalar Name[it]=Trama 1k 32bit scalare Name[ja]=テクスチャ 1k 32 ビットスカラー Name[kk]=Текстура 1k 32 бит скаляр Name[nb]=Tekstur 1k 32bit skalar Name[nl]=Textuur 1k 32bit scalar Name[pl]=Tekstura 1k 32bit skalar Name[pt]=Textura 1k 32-bits escalar Name[pt_BR]=Textura 1k 32bits escalar Name[ru]=Текстура 1k 32 бит scalar Name[sk]=Textúra 1k 32bit skalár Name[sv]=Struktur 1k 32-bitar skalär Name[tr]=Doku 1k 32bit sayısal Name[uk]=Текстура 1k, 32-бітова, скалярна Name[x-test]=xxTexture 1k 32bit scalarxx -Name[zh_CN]=纹理 1K 像素 32 位 scalar 色彩空间 +Name[zh_CN]=1K 纹理模板 32 位 Scalar 色彩空间 Name[zh_TW]=紋理 1k 32位元 scalar Type=Link URL[$e]=.source/Texture1k32bitscalar.kra X-KDE-Hidden=false diff --git a/krita/data/templates/texture/Texture1k8bitsrgb.desktop b/krita/data/templates/texture/Texture1k8bitsrgb.desktop index 5bb6c68fcf..9f1e734caa 100755 --- a/krita/data/templates/texture/Texture1k8bitsrgb.desktop +++ b/krita/data/templates/texture/Texture1k8bitsrgb.desktop @@ -1,37 +1,37 @@ [Desktop Entry] Icon=template_texture Name=Texture 1k 8bit srgb Name[bs]=Tekstura 1k 8bit srgb Name[ca]=Textura 1k 8bit SRGB Name[ca@valencia]=Textura 1k 8bit SRGB Name[cs]=Textura 1k 8bit srgb Name[da]=Tekstur 1k 8bit srgb Name[de]=Textur 1k 8bit srgb Name[el]=Υφή 1k 8bit srgb Name[en_GB]=Texture 1k 8bit srgb Name[es]=Textura 1k 8bit srgb Name[et]=Tekstuur 1k 8bit srgb Name[eu]=Testura 1k 8bit sGBU Name[fr]=Texture 1k 8bit srgb Name[gl]=Textura de 1k e 8 bits SRGB Name[hu]=Textúra 1k 8bit srgb Name[is]=Efnisáferð 1k 8bita srgb Name[it]=Trama 1k 8bit srgb Name[ja]=テクスチャ 1k 8 ビット sRGB Name[kk]=Текстура 1k 8 бит srgb Name[nb]=Tekstur 1k 8bit srgb Name[nl]=Textuur 1k 8bit srgb Name[pl]=Tekstura 1k 8bit srgb Name[pt]=Textura 1k 8-bits sRGB Name[pt_BR]=Textura 1k 8bits sRGB Name[ru]=Текстура 1k 8 бит srgb Name[sk]=Textúra 1k 8bit srgb Name[sv]=Struktur 1k 8-bitar SRGB Name[tr]=Doku 1k 8bit srgb Name[uk]=Текстура 1k, 8-бітова, srgb Name[x-test]=xxTexture 1k 8bit srgbxx -Name[zh_CN]=纹理 1K 像素 8 位 sRGB 色彩空间 +Name[zh_CN]=1K 纹理模板 8 位 sRGB 色彩空间 Name[zh_TW]=紋理 1k 8位元 srgb Type=Link URL[$e]=.source/Texture1k8bitsrgb.kra X-KDE-Hidden=false diff --git a/krita/data/templates/texture/Texture2048x20488bitsrgb.desktop b/krita/data/templates/texture/Texture2048x20488bitsrgb.desktop index 6f63ff535e..caa7bf6571 100644 --- a/krita/data/templates/texture/Texture2048x20488bitsrgb.desktop +++ b/krita/data/templates/texture/Texture2048x20488bitsrgb.desktop @@ -1,34 +1,34 @@ [Desktop Entry] Icon=template_texture Name=Texture 2048x2048 8bit srgb Name[bs]=Tekstura 2048x2048 8bit srgb Name[ca]=Textura 2048x2048 8bit SRGB Name[ca@valencia]=Textura 2048x2048 8bit SRGB Name[cs]=Textura 2048x2048 8bit srgb Name[da]=Tekstur 2048x2048 8bit srgb Name[de]=Textur 2048x2048 8bit srgb Name[el]=Υφή 2048x2048 8bit srgb Name[en_GB]=Texture 2048x2048 8bit srgb Name[es]=Textura 2048x2048 8bits srgb Name[et]=Tekstuur 2048x2048 8bit srgb Name[fr]=Texture 2048x2048 8bit srgb Name[gl]=Textura de 2048×2048 e 8 bits SRGB Name[is]=Efnisáferð 2048x2048 8bita srgb Name[it]=Trama 2048x2048 8bit srgb Name[ja]=テクスチャ 2048x2048 8 ビット sRGB Name[nb]=Tekstur 2048x2048 8bit srgb Name[nl]=Textuur 2048x2048 8bit srgb Name[pl]=Tekstura 2048x2048 8bit srgb Name[pt]=Textura 2048x2048 8-bits sRGB Name[pt_BR]=Textura 2048x2048 8bits sRGB Name[ru]=Текстура 2048x2048 8 бит srgb Name[sk]=Textúra 2048x2048 8bit srgb Name[sv]=Struktur 2048 x 2048 8-bitar SRGB Name[tr]=Doku 2048x2048 8bit srgb Name[uk]=Текстура 2048⨯2048, 8-бітова, srgb Name[x-test]=xxTexture 2048x2048 8bit srgbxx -Name[zh_CN]=纹理 2048x2048 像素 8 位 sRGB 色彩空间 +Name[zh_CN]=2048x2048 纹理模板 8 位 sRGB 色彩空间 Name[zh_TW]=紋理 2048x2048 8位元 srgb Type=Link URL[$e]=.source/Texture2048x20488bitsrgb.kra X-KDE-Hidden=false diff --git a/krita/data/templates/texture/Texture256x2568bitsrgb.desktop b/krita/data/templates/texture/Texture256x2568bitsrgb.desktop index 46b9098194..9201971eb3 100644 --- a/krita/data/templates/texture/Texture256x2568bitsrgb.desktop +++ b/krita/data/templates/texture/Texture256x2568bitsrgb.desktop @@ -1,34 +1,34 @@ [Desktop Entry] Icon=template_texture Name=Texture 256x256 8bit srgb Name[bs]=Tekstura 256x256 8bit srgb Name[ca]=Textura 256x256 8bit SRGB Name[ca@valencia]=Textura 256x256 8bit SRGB Name[cs]=Textura 256x256 8bit srgb Name[da]=Tekstur 256x256 8bit srgb Name[de]=Textur 256x256 8bit srgb Name[el]=Υφή 256x256 8bit srgb Name[en_GB]=Texture 256x256 8bit srgb Name[es]=Textura 256x256 8bits srgb Name[et]=Tekstuur 256x256 8bit srgb Name[fr]=Texture 256x256 8bit srgb Name[gl]=Textura de 256×256 e 8 bits SRGB Name[is]=Efnisáferð 256x256 8bita srgb Name[it]=Trama 256x256 8bit srgb Name[ja]=テクスチャ 256x256 8 ビット sRGB Name[nb]=Tekstur 256x256 8bit srgb Name[nl]=Textuur 256x256 8bit srgb Name[pl]=Tekstura 256x256 8bit srgb Name[pt]=Textura 256x256 8-bits sRGB Name[pt_BR]=Textura 256x256 8bits sRGB Name[ru]=Текстура 256x256 8 бит srgb Name[sk]=Textúra 256x256 8bit srgb Name[sv]=Struktur 256 x 256 8-bitar SRGB Name[tr]=Doku 256x256 8bit srgb Name[uk]=Текстура 256⨯256, 8-бітова, srgb Name[x-test]=xxTexture 256x256 8bit srgbxx -Name[zh_CN]=纹理 256x256 像素 8 位 srgb 色彩空间 +Name[zh_CN]=256x256 纹理模板 8 位 sRGB 色彩空间 Name[zh_TW]=紋理 256x256 8位元 srgb Type=Link URL[$e]=.source/Texture256x2568bitsrgb.kra X-KDE-Hidden=false diff --git a/krita/data/templates/texture/Texture2k32bitscalar.desktop b/krita/data/templates/texture/Texture2k32bitscalar.desktop index 6bc8af023f..925de1b1c6 100755 --- a/krita/data/templates/texture/Texture2k32bitscalar.desktop +++ b/krita/data/templates/texture/Texture2k32bitscalar.desktop @@ -1,37 +1,37 @@ [Desktop Entry] Icon=template_texture Name=Texture 2k 32bit scalar Name[bs]=Tekstura 2k 32bit scalar Name[ca]=Textura 2k 32bit escalar Name[ca@valencia]=Textura 2k 32bit escalar Name[cs]=Textura 2k 32bit skalární Name[da]=Tekstur 2k 32bit scalar Name[de]=Textur 2k 32bit scalar Name[el]=Υφή 2k 32bit βαθμωτό Name[en_GB]=Texture 2k 32bit scalar Name[es]=Textura 2k 32bit escalar Name[et]=Tekstuur 2k 32bit skalaar Name[eu]=Testura 2k 32bit eskalarra Name[fr]=Texture 2k 32bit scalaire Name[gl]=Textura de 2k e 32 bits escalar Name[hu]=Textúra 2k 32bit skalár Name[is]=Efnisáferð 2k 32bita scalar Name[it]=Trama 2k 32bit scalare Name[ja]=テクスチャ 2k 32 ビットスカラー Name[kk]=Текстура 2k 32 бит скаляр Name[nb]=Tekstur 2k 32bit skalar Name[nl]=Textuur 2k 32bit scalar Name[pl]=Tekstura 2k 32bit skalar Name[pt]=Textura 2k 32-bits escalar Name[pt_BR]=Textura 2k 32bits escalar Name[ru]=Текстура 2k 32 бит scalar Name[sk]=Textúra 2k 32bit skalár Name[sv]=Struktur 2k 32-bitar skalär Name[tr]=Doku 2k 32bit sayısal Name[uk]=Текстура 2k, 32-бітова, скалярна Name[x-test]=xxTexture 2k 32bit scalarxx -Name[zh_CN]=纹理 2K 像素 32 位 scalar 色彩空间 +Name[zh_CN]=2K 纹理模板 32 位 Scalar 色彩空间 Name[zh_TW]=紋理 2k 32位元 scalar Type=Link URL[$e]=.source/Texture2k32bitscalar.kra X-KDE-Hidden=false diff --git a/krita/data/templates/texture/Texture2k8bitsrgb.desktop b/krita/data/templates/texture/Texture2k8bitsrgb.desktop index 85b0f91b29..b0ec1a0ce6 100755 --- a/krita/data/templates/texture/Texture2k8bitsrgb.desktop +++ b/krita/data/templates/texture/Texture2k8bitsrgb.desktop @@ -1,37 +1,37 @@ [Desktop Entry] Icon=template_texture Name=Texture 2k 8bit srgb Name[bs]=Tekstura 2k 8bit srgb Name[ca]=Textura 2k 8bit SRGB Name[ca@valencia]=Textura 2k 8bit SRGB Name[cs]=Textura 2k 8bit srgb Name[da]=Tekstur 2k 8bit srgb Name[de]=Textur 2k 8bit srgb Name[el]=Υφή 2k 8bit srgb Name[en_GB]=Texture 2k 8bit srgb Name[es]=Textura 2k 8bit srgb Name[et]=Tekstuur 2k 8bit srgb Name[eu]=Testura 2k 8bit sGBU Name[fr]=Texture 2k 8bit srgb Name[gl]=Textura de 2k e 8 bits SRGB Name[hu]=Textúra 2k 8bit srgb Name[is]=Efnisáferð 2k 8bita srgb Name[it]=Trama 2k 8bit srgb Name[ja]=テクスチャ 2k 8 ビット sRGB Name[kk]=Текстура 2k 8 бит srgb Name[nb]=Tekstur 2k 8bit srgb Name[nl]=Textuur 2k 8bit srgb Name[pl]=Tekstura 2k 8bit srgb Name[pt]=Textura 2k 8-bits sRGB Name[pt_BR]=Textura 2k 8bits sRGB Name[ru]=Текстура 2k 8 бит srgb Name[sk]=Textúra 2k 8bit srgb Name[sv]=Struktur 2k 8-bitar SRGB Name[tr]=Doku 2k 8bit srgb Name[uk]=Текстура 2k, 8-бітова, srgb Name[x-test]=xxTexture 2k 8bit srgbxx -Name[zh_CN]=纹理 2K 像素 8 位 sRGB 色彩空间 +Name[zh_CN]=2K 纹理模板 8 位 sRGB 色彩空间 Name[zh_TW]=紋理 2k 8位元 srgb Type=Link URL[$e]=.source/Texture2k8bitsrgb.kra X-KDE-Hidden=false diff --git a/krita/data/templates/texture/Texture4096x40968bitsrgb.desktop b/krita/data/templates/texture/Texture4096x40968bitsrgb.desktop index ad77961faf..02ba02633b 100644 --- a/krita/data/templates/texture/Texture4096x40968bitsrgb.desktop +++ b/krita/data/templates/texture/Texture4096x40968bitsrgb.desktop @@ -1,34 +1,34 @@ [Desktop Entry] Icon=template_texture Name=Texture 4096x4096 8bit srgb Name[bs]=Tekstura 4096x4096 8bit srgb Name[ca]=Textura 4096x4096 8bit SRGB Name[ca@valencia]=Textura 4096x4096 8bit SRGB Name[cs]=Textura 4096x4096 8bit srgb Name[da]=Tekstur 4096x4096 8bit srgb Name[de]=Textur 4096x4096 8bit srgb Name[el]=Υφή 4096x4096 8bit srgb Name[en_GB]=Texture 4096x4096 8bit srgb Name[es]=Textura 4096x4096 8bits srgb Name[et]=Tekstuur 4096x4096 8bit srgb Name[fr]=Texture 4096x4096 8bit srgb Name[gl]=Textura de 4096×4096 e 8 bits SRGB Name[is]=Efnisáferð 4096x4096 8bita srgb Name[it]=Trama 4096x4096 8bit srgb Name[ja]=テクスチャ 4096x4096 8 ビット sRGB Name[nb]=Tekstur 4096x4096 8bit srgb Name[nl]=Textuur 4096x4096 8bit srgb Name[pl]=Tekstura 4096x4096 8bit srgb Name[pt]=Textura 4096x4096 8-bits sRGB Name[pt_BR]=Textura 4096x4096 8bits sRGB Name[ru]=Текстура 4096x4096 8 бит srgb Name[sk]=Textúra 4096x4096 8bit srgb Name[sv]=Struktur 4096 x 4096 8-bitar SRGB Name[tr]=Doku 4096x4096 8bit srgb Name[uk]=Текстура 4096⨯4096, 8-бітова, srgb Name[x-test]=xxTexture 4096x4096 8bit srgbxx -Name[zh_CN]=纹理 4096x4096 像素 8 位 sRGB 色彩空间 +Name[zh_CN]=4096x4096 纹理模板 8 位 sRGB 色彩空间 Name[zh_TW]=紋理 4096x4096 8位元 srgb Type=Link URL[$e]=.source/Texture4096x40968bitsrgb.kra X-KDE-Hidden=false diff --git a/krita/data/templates/texture/Texture4k32bitscalar.desktop b/krita/data/templates/texture/Texture4k32bitscalar.desktop index 4cc5067cdd..3a11919d0e 100755 --- a/krita/data/templates/texture/Texture4k32bitscalar.desktop +++ b/krita/data/templates/texture/Texture4k32bitscalar.desktop @@ -1,37 +1,37 @@ [Desktop Entry] Icon=template_texture Name=Texture 4k 32bit scalar Name[bs]=Tekstura 4k 32bit scalar Name[ca]=Textura 4k 32bit escalar Name[ca@valencia]=Textura 4k 32bit escalar Name[cs]=Textura 4k 32bit skalární Name[da]=Tekstur 4k 32bit scalar Name[de]=Textur 4k 32bit scalar Name[el]=Υφή 4k 32bit βαθμωτό Name[en_GB]=Texture 4k 32bit scalar Name[es]=Textura 4k 32bit escalar Name[et]=Tekstuur 4k 32bit skalaar Name[eu]=Testura 4k 32bit eskalarra Name[fr]=Texture 4k 32bit scalaire Name[gl]=Textura de 4k e 32 bits escalar Name[hu]=Textúra 4k 32bit skalár Name[is]=Efnisáferð 4k 32bita scalar Name[it]=Trama 4k 32bit scalare Name[ja]=テクスチャ 4k 32 ビットスカラー Name[kk]=Текстура 4k 32 бит скаляр Name[nb]=Tekstur 4k 32bit skalar Name[nl]=Textuur 4k 32bit scalar Name[pl]=Tekstura 4k 32bit skalar Name[pt]=Textura 4k 32-bits escalar Name[pt_BR]=Textura 4k 32bits escalar Name[ru]=Текстура 4k 32 бит scalar Name[sk]=Textúra 4k 32bit skalár Name[sv]=Struktur 4k 32-bitar skalär Name[tr]=Doku 4k 32bit sayısal Name[uk]=Текстура 4k, 32-бітова, скалярна Name[x-test]=xxTexture 4k 32bit scalarxx -Name[zh_CN]=纹理 4K 像素 32 位 scalar 色彩空间 +Name[zh_CN]=4K 纹理模板 32 位 Scalar 色彩空间 Name[zh_TW]=紋理 4k 32位元 scalar Type=Link URL[$e]=.source/Texture4k32bitscalar.kra X-KDE-Hidden=false diff --git a/krita/data/templates/texture/Texture4k8bitsrgb.desktop b/krita/data/templates/texture/Texture4k8bitsrgb.desktop index aa70b9aec5..f89aac9dd3 100755 --- a/krita/data/templates/texture/Texture4k8bitsrgb.desktop +++ b/krita/data/templates/texture/Texture4k8bitsrgb.desktop @@ -1,37 +1,37 @@ [Desktop Entry] Icon=template_texture Name=Texture 4k 8bit srgb Name[bs]=Tekstura 4k 8bit srgb Name[ca]=Textura 4k 8bit SRGB Name[ca@valencia]=Textura 4k 8bit SRGB Name[cs]=Textura 4k 8bit srgb Name[da]=Tekstur 4k 8bit srgb Name[de]=Textur 4k 8bit srgb Name[el]=Υφή 4k 8bit srgb Name[en_GB]=Texture 4k 8bit srgb Name[es]=Textura 4k 8bit srgb Name[et]=Tekstuur 4k 8bit srgb Name[eu]=Testura 4k 8bit sGBU Name[fr]=Texture 4k 8bit srgb Name[gl]=Textura de 4k e 8 bits SRGB Name[hu]=Textúra 4k 8bit srgb Name[is]=Efnisáferð 4k 8bita srgb Name[it]=Trama 4k 8bit srgb Name[ja]=テクスチャ 4k 8 ビット sRGB Name[kk]=Текстура 4k 8 бит srgb Name[nb]=Tekstur 4k 8bit srgb Name[nl]=Textuur 4k 8bit srgb Name[pl]=Tekstura 4k 8bit srgb Name[pt]=Textura 4k 8-bits sRGB Name[pt_BR]=Textura 4k 8bits sRGB Name[ru]=Текстура 4k 8 бит srgb Name[sk]=Textúra 4k 8bit srgb Name[sv]=Struktur 4k 8-bitar SRGB Name[tr]=Doku 4k 8bit srgb Name[uk]=Текстура 4k, 8-бітова, srgb Name[x-test]=xxTexture 4k 8bit srgbxx -Name[zh_CN]=纹理 4K 像素 8 位 srgb 色彩空间 +Name[zh_CN]=4K 纹理模板 8 位 sRGB 色彩空间 Name[zh_TW]=紋理 4k 8位元 srgb Type=Link URL[$e]=.source/Texture4k8bitsrgb.kra X-KDE-Hidden=false diff --git a/krita/data/templates/texture/Texture512x5128bitsrgb.desktop b/krita/data/templates/texture/Texture512x5128bitsrgb.desktop index bf8729bd17..571c77ca22 100644 --- a/krita/data/templates/texture/Texture512x5128bitsrgb.desktop +++ b/krita/data/templates/texture/Texture512x5128bitsrgb.desktop @@ -1,34 +1,34 @@ [Desktop Entry] Icon=template_texture Name=Texture 512x512 8bit srgb Name[bs]=Tekstura 512x512 8bit srgb Name[ca]=Textura 512x512 8bit SRGB Name[ca@valencia]=Textura 512x512 8bit SRGB Name[cs]=Textura 512x512 8bit srgb Name[da]=Tekstur 512x512 8bit srgb Name[de]=Textur 512x512 8bit srgb Name[el]=Υφή 512x512 8bit srgb Name[en_GB]=Texture 512x512 8bit srgb Name[es]=Textura 512x512 8bits srgb Name[et]=Tekstuur 512x512 8bit srgb Name[fr]=Texture 512x512 8bit srgb Name[gl]=Textura de 512×512 e 8 bits SRGB Name[is]=Efnisáferð 512x512 8bita srgb Name[it]=Trama 512x512 8bit srgb Name[ja]=テクスチャ 512x512 8 ビット sRGB Name[nb]=Tekstur 512x512 8bit srgb Name[nl]=Textuur 512x512 8bit srgb Name[pl]=Tekstura 512x512 8bit srgb Name[pt]=Textura 512x512 8-bits sRGB Name[pt_BR]=Textura 512x512 8bits sRGB Name[ru]=Текстура 512x512 8 бит srgb Name[sk]=Textúra 512x512 8bit srgb Name[sv]=Struktur 512 x 512 8-bitar SRGB Name[tr]=Doku 512x512 8bit srgb Name[uk]=Текстура 512⨯512, 8-бітова, srgb Name[x-test]=xxTexture 512x512 8bit srgbxx -Name[zh_CN]=纹理 512x512 像素 8 位 sRGB 色彩空间 +Name[zh_CN]=512x512 纹理模板 8 位 sRGB 色彩空间 Name[zh_TW]=紋理 512x512 8位元 srgb Type=Link URL[$e]=.source/Texture512x5128bitsrgb.kra X-KDE-Hidden=false diff --git a/krita/data/templates/texture/Texture8k32bitscalar.desktop b/krita/data/templates/texture/Texture8k32bitscalar.desktop index 12d86b64a9..3b6f443ebc 100755 --- a/krita/data/templates/texture/Texture8k32bitscalar.desktop +++ b/krita/data/templates/texture/Texture8k32bitscalar.desktop @@ -1,37 +1,37 @@ [Desktop Entry] Icon=template_texture Name=Texture 8k 32bit scalar Name[bs]=Tekstura 8k 32bit scalar Name[ca]=Textura 8k 32bit escalar Name[ca@valencia]=Textura 8k 32bit escalar Name[cs]=Textura 8k 32bit skalární Name[da]=Tekstur 8k 32bit scalar Name[de]=Textur 8k 32bit scalar Name[el]=Υφή 8k 32bit βαθμωτό Name[en_GB]=Texture 8k 32bit scalar Name[es]=Textura 8k 32 bit escalar Name[et]=Tekstuur 8k 32bit skalaar Name[eu]=Testura 8k 32bit eskalarra Name[fr]=Texture 8k 32bit scalaire Name[gl]=Textura de 8k e 32 bits escalar Name[hu]=Textúra 8k 32bit skalár Name[is]=Efnisáferð 8k 32bita scalar Name[it]=Trama 8k 32bit scalare Name[ja]=テクスチャ 8k 32 ビットスカラー Name[kk]=Текстура 8k 32 бит скаляр Name[nb]=Tekstur 8k 32bit skalar Name[nl]=Textuur 8k 32bit scalar Name[pl]=Tekstura 8k 32bit skalar Name[pt]=Textura 8k 32-bits escalar Name[pt_BR]=Textura 8k 32bits escalar Name[ru]=Текстура 8k 32 бит scalar Name[sk]=Textúra 8k 32bit skalár Name[sv]=Struktur 8k 32-bitar skalär Name[tr]=Doku 8k 32bit sayısal Name[uk]=Текстура 8k, 32-бітова, скалярна Name[x-test]=xxTexture 8k 32bit scalarxx -Name[zh_CN]=纹理 8K 像素 32 位 scalar 色彩空间 +Name[zh_CN]=8K 纹理模板 32 位 Scalar 色彩空间 Name[zh_TW]=紋理 8k 32位元 scalar Type=Link URL[$e]=.source/Texture8k32bitscalar.kra X-KDE-Hidden=false diff --git a/krita/data/templates/texture/Texture8k8bitsrgb.desktop b/krita/data/templates/texture/Texture8k8bitsrgb.desktop index 196b742f2c..23b7a5e969 100755 --- a/krita/data/templates/texture/Texture8k8bitsrgb.desktop +++ b/krita/data/templates/texture/Texture8k8bitsrgb.desktop @@ -1,38 +1,38 @@ [Desktop Entry] Icon=template_texture Name=Texture 8k 8bit srgb Name[bs]=Tekstura 8k 8bit srgb Name[ca]=Textura 8k 8bit SRGB Name[ca@valencia]=Textura 8k 8bit SRGB Name[cs]=Textura 8k 8bit srgb Name[da]=Tekstur 8k 8bit srgb Name[de]=Textur 8k 8bit srgb Name[el]=Υφή 8k 8bit srgb Name[en_GB]=Texture 8k 8bit srgb Name[es]=Textura 8k 8bit srgb Name[et]=Tekstuur 8k 8bit srgb Name[eu]=Testura 8k 8bit sGBU Name[fr]=Texture 8k 8bit srgb Name[gl]=Textura de 8k e 8 bits SRGB Name[hu]=Textúra 8k 8bit srgb Name[is]=Efnisáferð 8k 8bita srgb Name[it]=Trama 8k 8bit srgb Name[ja]=テクスチャ 8k 8 ビット sRGB Name[kk]=Текстура 8k 8 бит srgb Name[nb]=Tekstur 8k 8bit srgb Name[nl]=Textuur 8k 8bit srgb Name[pl]=Tekstura 8k 8bit srgb Name[pt]=Textura 8k 8-bits sRGB Name[pt_BR]=Textura 8k 8bits sRGB Name[ru]=Текстура 8k 8 бит srgb Name[sk]=Textúra 8k 8bit srgb Name[sl]=Tekstura 8k 8 bitov srgb Name[sv]=Struktur 8k 8-bitar SRGB Name[tr]=Doku 8k 8bit srgb Name[uk]=Текстура 8k, 8-бітова, srgb Name[x-test]=xxTexture 8k 8bit srgbxx -Name[zh_CN]=纹理 8K 像素 8 位 srgb 色彩空间 +Name[zh_CN]=8K 纹理模板 8 位 sRGB 色彩空间 Name[zh_TW]=紋理 8k 8位元 srgb Type=Link URL[$e]=.source/Texture8k8bitsrgb.kra X-KDE-Hidden=false diff --git a/krita/krita.action b/krita/krita.action index 701eca9471..efccab0f4e 100644 --- a/krita/krita.action +++ b/krita/krita.action @@ -1,3501 +1,3476 @@ General Open Resources Folder Opens a file browser at the location Krita saves resources such as brushes to. Opens a file browser at the location Krita saves resources such as brushes to. Open Resources Folder 0 0 false Cleanup removed files... Cleanup removed files Cleanup removed files 0 0 false C&ascade Cascade Cascade 10 0 false &Tile Tile Tile 10 0 false Create Resource Bundle... Create Resource Bundle Create Resource Bundle 0 0 false Show File Toolbar Show File Toolbar Show File Toolbar false Show color selector Show color selector Show color selector Shift+I false Show MyPaint shade selector Show MyPaint shade selector Show MyPaint shade selector Shift+M false Show minimal shade selector Show minimal shade selector Show minimal shade selector Shift+N false Show color history Show color history Show color history H false Show common colors Show common colors Show common colors U false Show Tool Options Show Tool Options Show Tool Options \ false Show Brush Editor Show Brush Editor Show Brush Editor F5 false Show Brush Presets Show Brush Presets Show Brush Presets F6 false Toggle Tablet Debugger Toggle Tablet Debugger Toggle Tablet Debugger 0 0 Ctrl+Shift+T false Show system information for bug reports. Show system information for bug reports. Show system information for bug reports. false Rename Composition... Rename Composition Rename Composition 0 0 false Update Composition Update Composition Update Composition 0 0 false Use multiple of 2 for pixel scale Use multiple of 2 for pixel scale Use multiple of 2 for pixel scale Use multiple of 2 for pixel scale 1 0 true + + + &Invert Selection + + Invert current selection + Invert Selection + 10000000000 + 100 + Ctrl+Shift+I + false + + + Painting lightness-increase Make brush color lighter Make brush color lighter Make brush color lighter 0 0 L false lightness-decrease Make brush color darker Make brush color darker Make brush color darker 0 0 K false Make brush color more saturated Make brush color more saturated Make brush color more saturated false Make brush color more desaturated Make brush color more desaturated Make brush color more desaturated false Shift brush color hue clockwise Shift brush color hue clockwise Shift brush color hue clockwise false Shift brush color hue counter-clockwise Shift brush color hue counter-clockwise Shift brush color hue counter-clockwise false Make brush color more red Make brush color more red Make brush color more red false Make brush color more green Make brush color more green Make brush color more green false Make brush color more blue Make brush color more blue Make brush color more blue false Make brush color more yellow Make brush color more yellow Make brush color more yellow false opacity-increase Increase opacity Increase opacity Increase opacity 0 0 O false opacity-decrease Decrease opacity Decrease opacity Decrease opacity 0 0 I false draw-eraser Set eraser mode Set eraser mode Set eraser mode 10000 0 E true view-refresh Reload Original Preset Reload Original Preset Reload Original Preset 10000 false transparency-unlocked Preserve Alpha Preserve Alpha Preserve Alpha 10000 true transform_icons_penPressure Use Pen Pressure Use Pen Pressure Use Pen Pressure 10000 true symmetry-horizontal Horizontal Mirror Tool Horizontal Mirror Tool Horizontal Mirror Tool 0 true symmetry-vertical Vertical Mirror Tool Vertical Mirror Tool Vertical Mirror Tool 0 true Hide Mirror X Line Hide Mirror X Line Hide Mirror X Line 10000 true Hide Mirror Y Line Hide Mirror Y Line Hide Mirror Y Line 10000 true Lock Lock X Line Lock X Line 10000 true Lock Y Line Lock Y Line Lock Y Line 10000 true Move to Canvas Center Move to Canvas Center X Move to Canvas Center X 10000 false Move to Canvas Center Y Move to Canvas Center Y Move to Canvas Center Y 10000 false - - - &Invert Selection - - Invert current selection - Invert Selection - 10000000000 - 100 - Ctrl+Shift+I - false - - &Toggle Selection Display Mode Toggle Selection Display Mode Toggle Selection Display Mode 0 0 false Next Favourite Preset Next Favourite Preset Next Favourite Preset , false Previous Favourite Preset Previous Favourite Preset Previous Favourite Preset . false preset-switcher Switch to Previous Preset Switch to Previous Preset Switch to Previous Preset / false Hide Brushes and Stuff Toolbar Hide Brushes and Stuff Toolbar Hide Brushes and Stuff Toolbar true Reset Foreground and Background Color Reset Foreground and Background Color Reset Foreground and Background Color D false Swap Foreground and Background Color Swap Foreground and Background Color Swap Foreground and Background Color X false smoothing-weighted Brush Smoothing: Weighted Brush Smoothing: Weighted Brush Smoothing: Weighted false smoothing-no Brush Smoothing: Disabled Brush Smoothing: Disabled Brush Smoothing: Disabled false smoothing-stabilizer Brush Smoothing: Stabilizer Brush Smoothing: Stabilizer Brush Smoothing: Stabilizer false brushsize-decrease Decrease Brush Size Decrease Brush Size Decrease Brush Size 0 0 [ false smoothing-basic Brush Smoothing: Basic Brush Smoothing: Basic Brush Smoothing: Basic false brushsize-increase Increase Brush Size Increase Brush Size Increase Brush Size 0 0 ] false Toggle Assistant Toggle Assistant ToggleAssistant Ctrl+Shift+L true Undo Polygon Selection Points Undo Polygon Selection Points Undo Polygon Selection Points Shift+Z false Fill with Foreground Color (Opacity) Fill with Foreground Color (Opacity) Fill with Foreground Color (Opacity) 10000 1 Ctrl+Shift+Backspace false Fill with Background Color (Opacity) Fill with Background Color (Opacity) Fill with Background Color (Opacity) 10000 1 Ctrl+Backspace false Fill with Pattern (Opacity) Fill with Pattern (Opacity) Fill with Pattern (Opacity) 10000 1 false Convert &to Shape Convert to Shape Convert to Shape 10000000000 0 false &Select Opaque Select Opaque Select Opaque 100000 100 false &Show Global Selection Mask Shows global selection as a usual selection mask in <interface>Layers</interface> docker Show Global Selection Mask 100000 100 true Filters color-to-alpha &Color to Alpha... Color to Alpha Color to Alpha 10000 0 false &Top Edge Detection Top Edge Detection Top Edge Detection 10000 0 false &Index Colors... Index Colors Index Colors 10000 0 false Emboss Horizontal &Only Emboss Horizontal Only Emboss Horizontal Only 10000 0 false D&odge Dodge Dodge 10000 0 false &Sharpen Sharpen Sharpen 10000 0 false B&urn Burn Burn 10000 0 false &Mean Removal Mean Removal Mean Removal 10000 0 false &Gaussian Blur... Gaussian Blur Gaussian Blur 10000 0 false Emboss &in All Directions Emboss in All Directions Emboss in All Directions 10000 0 false &Small Tiles... Small Tiles Small Tiles 10000 0 false &Levels... Levels Levels 10000 0 Ctrl+L false &Sobel... Sobel Sobel 10000 0 false &Wave... Wave Wave 10000 0 false &Motion Blur... Motion Blur Motion Blur 10000 0 false &Color Adjustment curves... Color Adjustment curves Color Adjustment curves 10000 0 Ctrl+M false Pi&xelize... Pixelize Pixelize 10000 0 false Emboss (&Laplacian) Emboss (Laplacian) Emboss (Laplacian) 10000 0 false &Left Edge Detection Left Edge Detection Left Edge Detection 10000 0 false &Blur... Blur Blur 10000 0 false &Raindrops... Raindrops Raindrops 10000 0 false &Bottom Edge Detection Bottom Edge Detection Bottom Edge Detection 10000 0 false &Random Noise... Random Noise Random Noise 10000 0 false &Brightness/Contrast curve... Brightness/Contrast curve Brightness/Contrast curve 10000 0 false - Colo&r Balance.. + Colo&r Balance... - Color Balance.. - Color Balance.. + Color Balance + Color Balance 10000 0 Ctrl+B false &Phong Bumpmap... Phong Bumpmap Phong Bumpmap 10000 0 false &Desaturate Desaturate Desaturate 10000 0 Ctrl+Shift+U false Color &Transfer... Color Transfer Color Transfer 10000 0 false Emboss &Vertical Only Emboss Vertical Only Emboss Vertical Only 10000 0 false &Lens Blur... Lens Blur Lens Blur 10000 0 false M&inimize Channel Minimize Channel Minimize Channel 10000 0 false M&aximize Channel Maximize Channel Maximize Channel 10000 0 false &Oilpaint... Oilpaint Oilpaint 10000 0 false &Right Edge Detection Right Edge Detection Right Edge Detection 10000 0 false &Auto Contrast Auto Contrast Auto Contrast 10000 0 false &Round Corners... Round Corners Round Corners 10000 0 false &Unsharp Mask... Unsharp Mask Unsharp Mask 10000 0 false &Emboss with Variable Depth... Emboss with Variable Depth Emboss with Variable Depth 10000 0 false Emboss &Horizontal && Vertical Emboss Horizontal & Vertical Emboss Horizontal & Vertical 10000 0 false Random &Pick... Random Pick Random Pick 10000 0 false &Gaussian Noise Reduction... Gaussian Noise Reduction Gaussian Noise Reduction 10000 0 false &Posterize... Posterize Posterize 10000 0 false &Wavelet Noise Reducer... Wavelet Noise Reducer Wavelet Noise Reducer 10000 0 false &HSV Adjustment... HSV Adjustment HSV Adjustment 10000 0 Ctrl+U false Tool Shortcuts Dynamic Brush Tool Dynamic Brush Tool Dynamic Brush Tool false Crop Tool Crop the image to an area Crop the image to an area C false Polygon Tool Polygon Tool. Shift-mouseclick ends the polygon. Polygon Tool. Shift-mouseclick ends the polygon. false Rectangle Tool Rectangle Tool Rectangle Tool false Multibrush Tool Multibrush Tool Multibrush Tool Q false Lazy Brush Tool Lazy Brush Tool Lazy Brush Tool Smart Patch Tool Smart Patch Tool Smart Patch Tool Pan Tool Pan Tool Pan Tool Select Shapes Tool Select Shapes Tool Select Shapes Tool false Color Picker Select a color from the image or current layer Select a color from the image or current layer P false Text Editing Tool Text editing Text editing false Outline Selection Tool Outline Selection Tool Outline Selection Tool false Artistic Text Tool Artistic text editing Artistic text editing false Bezier Curve Selection Tool Select a Bezier Curve Selection Tool false Similar Color Selection Tool Select a Similar Color Selection Tool false Fill Tool Fill a contiguous area of color with a color, or fill a selection. Fill a contiguous area of color with a color, or fill a selection. F false Line Tool Line Tool Line Tool false Freehand Path Tool Freehand Path Tool Freehand Path Tool false Bezier Curve Tool - Bezier Curve Tool. Shift-mouseclick ends the curve. - Bezier Curve Tool. Shift-mouseclick ends the curve. + Bezier Curve Tool. Shift-mouseclick or double-click ends the curve. + Bezier Curve Tool. Shift-mouseclick or double-click ends the curve. false Ellipse Tool Ellipse Tool Ellipse Tool false Freehand Brush Tool Freehand Brush Tool Freehand Brush Tool B false Create object Create object Create object false Elliptical Selection Tool Elliptical Selection Tool Elliptical Selection Tool J false Contiguous Selection Tool Contiguous Selection Tool Contiguous Selection Tool false Pattern editing Pattern editing Pattern editing false Review Review Review false Draw a gradient. Draw a gradient. Draw a gradient. G false Polygonal Selection Tool Polygonal Selection Tool Polygonal Selection Tool false Measurement Tool Measure the distance between two points Measure the distance between two points false Rectangular Selection Tool Rectangular Selection Tool Rectangular Selection Tool Ctrl+R false Move Tool Move a layer Move a layer T false Vector Image Tool Vector Image (EMF/WMF/SVM/SVG) tool Vector Image (EMF/WMF/SVM/SVG) tool false Calligraphy Calligraphy Calligraphy false Path editing Path editing Path editing false Zoom Tool Zoom Tool Zoom Tool false Polyline Tool Polyline Tool. Shift-mouseclick ends the polyline. Polyline Tool. Shift-mouseclick ends the polyline. false Transform Tool Transform a layer or a selection Transform a layer or a selection Ctrl+T false Assistant Tool Assistant Tool Assistant Tool false Text tool Text tool Text tool false Gradient Editing Tool Gradient editing Gradient editing false Blending Modes Select Normal Blending Mode Select Normal Blending Mode Select Normal Blending Mode 0 0 Alt+Shift+N false Select Dissolve Blending Mode Select Dissolve Blending Mode Select Dissolve Blending Mode 0 0 Alt+Shift+I false Select Behind Blending Mode Select Behind Blending Mode Select Behind Blending Mode 0 0 Alt+Shift+Q false Select Clear Blending Mode Select Clear Blending Mode Select Clear Blending Mode 0 0 Alt+Shift+R false Select Darken Blending Mode Select Darken Blending Mode Select Darken Blending Mode 0 0 Alt+Shift+K false Select Multiply Blending Mode Select Multiply Blending Mode Select Multiply Blending Mode 0 0 Alt+Shift+M false Select Color Burn Blending Mode Select Color Burn Blending Mode Select Color Burn Blending Mode 0 0 Alt+Shift+B false Select Linear Burn Blending Mode Select Linear Burn Blending Mode Select Linear Burn Blending Mode 0 0 Alt+Shift+A false Select Lighten Blending Mode Select Lighten Blending Mode Select Lighten Blending Mode 0 0 Alt+Shift+G false Select Screen Blending Mode Select Screen Blending Mode Select Screen Blending Mode 0 0 Alt+Shift+S false Select Color Dodge Blending Mode Select Color Dodge Blending Mode Select Color Dodge Blending Mode 0 0 Alt+Shift+D false Select Linear Dodge Blending Mode Select Linear Dodge Blending Mode Select Linear Dodge Blending Mode 0 0 Alt+Shift+W false Select Overlay Blending Mode Select Overlay Blending Mode Select Overlay Blending Mode 0 0 Alt+Shift+O false Select Hard Overlay Blending Mode Select Hard Overlay Blending Mode Select Hard Overlay Blending Mode 0 0 Alt+Shift+P false Select Soft Light Blending Mode Select Soft Light Blending Mode Select Soft Light Blending Mode 0 0 Alt+Shift+F false Select Hard Light Blending Mode Select Hard Light Blending Mode Select Hard Light Blending Mode 0 0 Alt+Shift+H false Select Vivid Light Blending Mode Select Vivid Light Blending Mode Select Vivid Light Blending Mode 0 0 Alt+Shift+V false Select Linear Light Blending Mode Select Linear Light Blending Mode Select Linear Light Blending Mode 0 0 Alt+Shift+J false Select Pin Light Blending Mode Select Pin Light Blending Mode Select Pin Light Blending Mode 0 0 Alt+Shift+Z false Select Hard Mix Blending Mode Select Hard Mix Blending Mode Select Hard Mix Blending Mode 0 0 Alt+Shift+L false Select Difference Blending Mode Select Difference Blending Mode Select Difference Blending Mode 0 0 Alt+Shift+E false Select Exclusion Blending Mode Select Exclusion Blending Mode Select Exclusion Blending Mode 0 0 Alt+Shift+X false Select Hue Blending Mode Select Hue Blending Mode Select Hue Blending Mode 0 0 Alt+Shift+U false Select Saturation Blending Mode Select Saturation Blending Mode Select Saturation Blending Mode 0 0 Alt+Shift+T false Select Color Blending Mode Select Color Blending Mode Select Color Blending Mode 0 0 Alt+Shift+C false Select Luminosity Blending Mode Select Luminosity Blending Mode Select Luminosity Blending Mode 0 0 Alt+Shift+Y false Animation Previous frame Move to previous frame Move to previous frame 1 0 false Next frame Move to next frame Move to next frame 1 0 false Play / pause animation Play / pause animation Play / pause animation 1 0 false addblankframe Create Blank Frame Add blank frame Add blank frame 100000 0 false addduplicateframe Create Duplicate Frame Add duplicate frame Add duplicate frame 100000 0 false Toggle onion skin Toggle onion skin Toggle onion skin 100000 0 false Previous Keyframe false Next Keyframe false First Frame false Last Frame false Auto Frame Mode true true Show in Timeline true - - - Insert Keyframe Right - - Insert keyframes to the right of selection moving the tail of animation to the right - - 100000 - 0 - - false - - - - + Insert Keyframe Left - Insert keyframes to the left of selection moving the tail of animation to the right + Insert keyframes to the left of selection, moving the tail of animation to the right. 100000 0 false - + - Insert N Keyframes Right + Insert Keyframe Right - Insert several keyframes to the right of selection moving the tail of animation to the right + Insert keyframes to the right of selection, moving the tail of animation to the right. 100000 0 false - + - Insert N Keyframes Left + Insert Multiple Keyframes - Insert several keyframes to the left of selection moving the tail of animation to the right + Insert several keyframes based on user parameters. 100000 0 false Remove Frame and Pull Remove keyframes moving the tail of animation to the left 100000 0 false deletekeyframe Remove Keyframe Remove keyframes without moving anything around 100000 0 false - - - Insert Column Right - - Insert column to the right of selection moving the tail of animation to the right - - 100000 - 0 - - false - - - - + Insert Column Left - Insert column to the left of selection moving the tail of animation to the right + Insert column to the left of selection, moving the tail of animation to the right 100000 0 false - + - Insert N Columns Right + Insert Column Right - Insert several columns to the right of selection moving the tail of animation to the right + Insert column to the right of selection, moving the tail of animation to the right 100000 0 false - + - Insert N Columns Left + Insert Multiple Columns - Insert several columns to the left of selection moving the tail of animation to the right + Insert several columns based on user parameters. 100000 0 false Remove Column and Pull Remove columns moving the tail of animation to the left 100000 0 false Remove Column Remove columns without moving anything around 100000 0 false Insert Hold Frame Insert a hold frame after every keyframe 100000 0 false - + - Insert N Hold Frames + Insert Multiple Hold Frames Insert N hold frames after every keyframe 100000 0 false Remove Hold Frame Remove a hold frame after every keyframe 100000 0 false - + - Remove N Hold Frames + Remove Multiple Hold Frames Remove N hold frames after every keyframe 100000 0 false Insert Hold Column Insert a hold column into the frame at the current position 100000 0 false - + - Insert N Hold Columns + Insert Multiple Hold Columns Insert N hold columns into the frame at the current position 100000 0 false Remove Hold Column Remove a hold column from the frame at the current position 100000 0 false - + - Remove N Hold Columns + Remove Multiple Hold Columns Remove N hold columns from the frame at the current position 100000 0 false Mirror Frames Mirror frames' position 100000 0 false Mirror Columns Mirror columns' position 100000 0 false Copy to Clipboard Copy frames to clipboard 100000 0 false Cut to Clipboard Cut frames to clipboard 100000 0 false Paste from Clipboard Paste frames from clipboard 100000 0 false Copy Columns to Clipboard Copy columns to clipboard 100000 0 false Cut Columns to Clipboard Cut columns to clipboard 100000 0 false Paste Columns from Clipboard Paste columns from clipboard 100000 0 false Set Start Time 100000 0 false Set End Time 100000 0 false Update Playback Range 100000 0 false Layers Activate next layer Activate next layer Activate next layer 1000 0 PgUp false Activate previous layer Activate previous layer Activate previous layer 1000 0 PgDown false Activate previously selected layer Activate previously selected layer Activate previously selected layer 1000 0 ; false groupLayer &Group Layer Group Layer Group Layer 1000 0 false cloneLayer &Clone Layer Clone Layer Clone Layer 1000 0 false vectorLayer &Vector Layer Vector Layer Vector Layer 1000 0 false filterLayer &Filter Layer... Filter Layer Filter Layer 1000 0 false fillLayer &Fill Layer... Fill Layer Fill Layer 1000 0 false fileLayer &File Layer... File Layer File Layer 1000 0 false transparencyMask &Transparency Mask Transparency Mask Transparency Mask 100000 0 false filterMask &Filter Mask... Filter Mask Filter Mask 100000 0 false filterMask &Colorize Mask Colorize Mask Colorize Mask 100000 0 false transformMask &Transform Mask... Transform Mask Transform Mask 100000 0 false selectionMask &Local Selection Local Selection Local Selection 100000 0 false view-filter &Isolate Layer Isolate Layer Isolate Layer 1000 0 true layer-locked &Toggle layer lock Toggle layer lock Toggle layer lock 1000 0 false visible Toggle layer &visibility Toggle layer visibility Toggle layer visibility 1000 0 false transparency-locked Toggle layer &alpha Toggle layer alpha Toggle layer alpha 1000 0 false transparency-enabled Toggle layer alpha &inheritance Toggle layer alpha inheritance Toggle layer alpha inheritance 1000 0 false paintLayer &Paint Layer Paint Layer Paint Layer 1000 0 Insert false &New Layer From Visible New layer from visible New layer from visible 1000 0 false duplicatelayer &Duplicate Layer or Mask Duplicate Layer or Mask Duplicate Layer or Mask 1000 0 Ctrl+J false &Cut Selection to New Layer Cut Selection to New Layer Cut Selection to New Layer 100000000 1 Ctrl+Shift+J false Copy &Selection to New Layer Copy Selection to New Layer Copy Selection to New Layer 100000000 0 Ctrl+Alt+J false Copy Layer Copy layer to clipboard Copy layer to clipboard 1000 0 false Cut Layer Cut layer to clipboard Cut layer to clipboard 1000 0 false Paste Layer Paste layer from clipboard Paste layer from clipboard 1000 0 false Quick Group Create a group layer containing selected layers Quick Group 100000 0 Ctrl+G false Quick Ungroup Remove grouping of the layers or remove one layer out of the group Quick Ungroup 100000 0 Ctrl+Alt+G false Quick Clipping Group Group selected layers and add a layer with clipped alpha channel Quick Clipping Group 100000 0 Ctrl+Shift+G false All Layers Select all layers Select all layers 10000 0 false Visible Layers Select all visible layers Select all visible layers 10000 0 false Locked Layers Select all locked layers Select all locked layers 10000 0 false Invisible Layers Select all invisible layers Select all invisible layers 10000 0 false Unlocked Layers Select all unlocked layers Select all unlocked layers 10000 0 false document-save &Save Layer/Mask... Save Layer/Mask Save Layer/Mask 1000 0 false document-save Save Vector Layer as SVG... Save Vector Layer as SVG Save Vector Layer as SVG 1000 0 false document-save Save &Group Layers... Save Group Layers Save Group Layers 100000 0 false Convert group to &animated layer Convert child layers into animation frames Convert child layers into animation frames 100000 0 false fileLayer to &File Layer Saves out the layers into a new image and then references that image. Convert to File Layer 100000 0 false I&mport Layer... Import Layer Import Layer 100000 0 false paintLayer &as Paint Layer... as Paint Layer as Paint Layer 1000 0 false transparencyMask as &Transparency Mask... as Transparency Mask as Transparency Mask 1000 0 false filterMask as &Filter Mask... as Filter Mask as Filter Mask 1000 0 false selectionMask as &Selection Mask... as Selection Mask as Selection Mask 1000 0 false paintLayer to &Paint Layer to Paint Layer to Paint Layer 1000 0 false transparencyMask to &Transparency Mask to Transparency Mask to Transparency Mask 1000 0 false filterMask to &Filter Mask... to Filter Mask to Filter Mask 1000 0 false selectionMask to &Selection Mask to Selection Mask to Selection Mask 1000 0 false transparencyMask &Alpha into Mask Alpha into Mask Alpha into Mask 100000 10 false transparency-enabled &Write as Alpha Write as Alpha Write as Alpha 1000000 1 false document-save &Save Merged... Save Merged Save Merged 1000000 0 false split-layer Split Layer... Split Layer Split Layer 1000 0 false Wavelet Decompose ... Wavelet Decompose Wavelet Decompose 1000 1 false symmetry-horizontal Mirror Layer Hori&zontally Mirror Layer Horizontally Mirror Layer Horizontally 1000 1 false symmetry-vertical Mirror Layer &Vertically Mirror Layer Vertically Mirror Layer Vertically 1000 1 false &Rotate Layer... Rotate Layer Rotate Layer 1000 1 false object-rotate-right Rotate &Layer 90° to the Right Rotate Layer 90° to the Right Rotate Layer 90° to the Right 1000 1 false object-rotate-left Rotate Layer &90° to the Left Rotate Layer 90° to the Left Rotate Layer 90° to the Left 1000 1 false Rotate Layer &180° Rotate Layer 180° Rotate Layer 180° 1000 1 false Scale &Layer to new Size... Scale Layer to new Size Scale Layer to new Size 100000 1 false &Shear Layer... Shear Layer Shear Layer 1000 1 false &Offset Layer... Offset Layer Offset Layer 100000 1 false Clones &Array... Clones Array Clones Array 100000 0 false &Edit metadata... Edit metadata Edit metadata 100000 1 false &Histogram... Histogram Histogram 100000 0 false &Convert Layer Color Space... Convert Layer Color Space Convert Layer Color Space 100000 1 false merge-layer-below &Merge with Layer Below Merge with Layer Below Merge with Layer Below 100000 0 Ctrl+E false &Flatten Layer Flatten Layer Flatten Layer 100000 0 false Ras&terize Layer Rasterize Layer Rasterize Layer 10000000 1 false Flatten ima&ge Flatten image Flatten image 100000 0 Ctrl+Shift+E false La&yer Style... Layer Style Layer Style 100000 1 false Move into previous group Move into previous group Move into previous group 0 0 false Move into next group Move into next group Move into next group 0 0 false Rename current layer Rename current layer Rename current layer 100000 0 F2 false deletelayer &Remove Layer Remove Layer Remove Layer 1000 1 Shift+Delete false arrowupblr Move Layer or Mask Up Move Layer or Mask Up Ctrl+PgUp false arrowdown Move Layer or Mask Down Move Layer or Mask Down Ctrl+PgDown false properties &Properties... Properties Properties 1000 1 F3 false diff --git a/krita/krita4.xmlgui b/krita/krita4.xmlgui index f55ff7f18b..907ebfe326 100644 --- a/krita/krita4.xmlgui +++ b/krita/krita4.xmlgui @@ -1,389 +1,386 @@ &File &Edit Fill Special &View &Canvas &Snap To &Image &Rotate &Layer New &Import/Export Import &Convert &Select &Group &Transform &Rotate S&plit S&plit Alpha &Select Filte&r &Tools - Scripting - - Recording + Scripts + + Macros - - - Macros Setti&ngs &Help File Brushes and Stuff diff --git a/krita/kritamenu.action b/krita/kritamenu.action index 5c5bcb41a1..151a10df76 100644 --- a/krita/kritamenu.action +++ b/krita/kritamenu.action @@ -1,1822 +1,1822 @@ File document-new &New Create new document New 0 0 Ctrl+N false document-open &Open... Open an existing document Open 0 0 Ctrl+O false document-open-recent Open &Recent Open a document which was recently opened Open Recent 1 0 false document-save &Save Save Save 1 0 Ctrl+S false document-save-as Save &As... Save document under a new name Save As 1 0 Ctrl+Shift+S false Sessions... Open session manager Sessions 0 0 false document-import Open ex&isting Document as Untitled Document... Open existing Document as Untitled Document Open existing Document as Untitled Document 0 0 false document-export E&xport... Export Export 1 0 false application-pdf &Export as PDF... Export as PDF Export as PDF 1 0 false Import animation frames... Import animation frames Import animation frames 1 0 false &Render Animation... Render Animation to GIF, Image Sequence or Video Render Animation 1000 0 false &Render Image Sequence Again Render Animation to Image Sequence Again Render Animation 1000 0 false Save Incremental &Version Save Incremental Version Save Incremental Version 1 0 Ctrl+Alt+S false Save Incremental &Backup Save Incremental Backup Save Incremental Backup 1 0 F4 false &Create Template From Image... Create Template From Image Create Template From Image 1 0 false Create Copy &From Current Image Create Copy From Current Image Create Copy From Current Image 1 0 false document-print &Print... Print document Print 1 0 Ctrl+P false document-print-preview Print Previe&w Show a print preview of document Print Preview 1 0 false configure &Document Information Document Information Document Information 1 0 false &Close All Close All Close All 1 0 Ctrl+Shift+W false C&lose Close Close 1 0 false &Quit Quit application Quit 0 0 Ctrl+Q false Edit edit-undo Undo Undo last action Undo 1 0 Ctrl+Z false edit-redo Redo Redo last undone action Redo 1 0 Ctrl+Shift+Z false edit-cut Cu&t Cut selection to clipboard Cut 0 0 Ctrl+X false edit-copy &Copy Copy selection to clipboard Copy 0 0 Ctrl+C false C&opy (sharp) Copy (sharp) Copy (sharp) 100000000 0 false Cut (&sharp) Cut (sharp) Cut (sharp) 100000000 0 false Copy &merged Copy merged Copy merged 100000000 0 Ctrl+Shift+C false edit-paste &Paste Paste clipboard content Paste 0 0 Ctrl+V false Paste at Cursor Paste at cursor Paste at cursor 0 0 Ctrl+Alt+V false Paste into &New Image Paste into New Image Paste into New Image 0 0 Ctrl+Shift+N false edit-clear C&lear Clear Clear 1 0 Del false &Fill with Foreground Color Fill with Foreground Color Fill with Foreground Color 10000 1 Shift+Backspace false Fill &with Background Color Fill with Background Color Fill with Background Color 10000 1 Backspace false F&ill with Pattern Fill with Pattern Fill with Pattern 10000 1 false Fill Special Fill with Foreground Color (Opacity) Fill with Foreground Color (Opacity) Fill with Foreground Color (Opacity) 10000 1 Ctrl+Shift+Backspace false Fill with Background Color (Opacity) Fill with Background Color (Opacity) Fill with Background Color (Opacity) 10000 1 Ctrl+Backspace false Fill with Pattern (Opacity) Fill with Pattern (Opacity) Fill with Pattern (Opacity) 10000 1 false Stro&ke selected shapes Stroke selected shapes Stroke selected shapes 1000000000 0 false Stroke Selec&tion... Stroke selection Stroke selection 10000000000 0 false Delete keyframe Delete keyframe Delete keyframe 100000 0 false Window window-new &New Window New Window New Window 0 0 false N&ext Next Next 10 0 false Previous Previous Previous false View &Show Canvas Only Show just the canvas or the whole window Show Canvas Only 0 0 Tab true view-fullscreen F&ull Screen Mode Display the window in full screen Full Screen Mode 0 0 Ctrl+Shift+F true &Wrap Around Mode Wrap Around Mode Wrap Around Mode 1 0 W true &Instant Preview Mode Instant Preview Mode Instant Preview Mode 1 0 Shift+L true Soft Proofing Turns on Soft Proofing Turns on Soft Proofing Ctrl+Y true Out of Gamut Warnings Turns on warnings for colors out of proofed gamut, needs soft proofing to be turned on. Turns on warnings for colors out of proofed gamut, needs soft proofing to be turned on. Ctrl+Shift+Y true mirror-view Mirror View Mirror View Mirror View M false zoom-original &Reset zoom Reset zoom Reset zoom 1 0 Ctrl+0 false zoom-in Zoom &In Zoom In 0 0 Ctrl++ false zoom-out Zoom &Out Zoom Out 0 0 Ctrl+- false rotate-canvas-right Rotate &Canvas Right Rotate Canvas Right Rotate Canvas Right 1 0 Ctrl+] false rotate-canvas-left Rotate Canvas &Left Rotate Canvas Left Rotate Canvas Left 1 0 Ctrl+[ false rotation-reset Reset Canvas Rotation Reset Canvas Rotation Reset Canvas Rotation 1 0 false Show &Rulers The rulers show the horizontal and vertical positions of the mouse on the image and can be used to position your mouse at the right place on the canvas. <p>Uncheck this to hide the rulers.</p> Show Rulers Show Rulers 1 0 true Rulers Track Pointer The rulers will track current mouse position and show it on screen. It can cause suptle performance slowdown Rulers Track Pointer Rulers Track Pointer 1 0 true Show Guides Show or hide guides Show Guides 1 0 true Lock Guides Lock or unlock guides Lock Guides 1 0 true Snap to Guides Snap cursor to guides position Snap to Guides 1 0 true Show Status &Bar Show or hide the status bar Show Status Bar 0 0 true Show Pixel Grid Show Pixel Grid Show Pixel Grid 1000 0 true view-grid Show &Grid Show Grid Show Grid 1000 0 Ctrl+Shift+' true Snap To Grid Snap To Grid Snap To Grid 1000 Ctrl+Shift+; true Show Snap Options Popup Show Snap Options Popup Show Snap Options Popup 1000 Shift+s false Snap Orthogonal Snap Orthogonal Snap Orthogonal 1000 true Snap Node Snap Node Snap Node 1000 true Snap Extension Snap Extension Snap Extension 1000 true Snap Intersection Snap Intersection Snap Intersection 1000 true Snap Bounding Box Snap Bounding Box Snap Bounding Box 1000 true Snap Image Bounds Snap Image Bounds Snap Image Bounds 1000 true Snap Image Center Snap Image Center Snap Image Center 1000 true S&how Painting Assistants Show Painting Assistants Show Painting Assistants 1000 0 true Show &Assistant Previews Show Assistant Previews Show Assistant Previews 1000 0 true S&how Reference Images Show Reference Images Show Reference Images 1000 0 true Image document-properties &Properties... Properties Properties 1000 0 false format-stroke-color &Image Background Color and Transparency... Change the background color of the image Image Background Color and Transparency 1000 0 false &Convert Image Color Space... Convert Image Color Space Convert Image Color Space 1000 0 false trim-to-image &Trim to Image Size Trim to Image Size Trim to Image Size 1 0 false Trim to Current &Layer Trim to Current Layer Trim to Current Layer 100000 0 false Trim to S&election Trim to Selection Trim to Selection 100000000 0 false &Rotate Image... Rotate Image Rotate Image 1000 0 false object-rotate-right Rotate &Image 90° to the Right Rotate Image 90° to the Right Rotate Image 90° to the Right 1000 0 false object-rotate-left Rotate Image &90° to the Left Rotate Image 90° to the Left Rotate Image 90° to the Left 1000 0 false Rotate Image &180° Rotate Image 180° Rotate Image 180° 1000 0 false &Shear Image... Shear Image Shear Image 1000 0 false symmetry-horizontal &Mirror Image Horizontally Mirror Image Horizontally Mirror Image Horizontally 1000 0 false symmetry-vertical Mirror Image &Vertically Mirror Image Vertically Mirror Image Vertically 1000 0 false Scale Image To &New Size... Scale Image To New Size Scale Image To New Size 1000 0 Ctrl+Alt+I false &Offset Image... Offset Image Offset Image 1000 0 false R&esize Canvas... Resize Canvas Resize Canvas 1000 0 Ctrl+Alt+C false Im&age Split Image Split Image Split 1000 0 false Separate Ima&ge... Separate Image Separate Image 1000 0 false Select edit-select-all Select &All Select All Select All 0 0 Ctrl+A false edit-select-all &Deselect Deselect Deselect 1100000000 0 Ctrl+Shift+A false &Reselect Reselect Reselect 0 0 Ctrl+Shift+D false - + - &Invert + &Invert Selection - Invert - Invert + Invert Selection + Invert Selection 10000 0 Ctrl+I false &Convert to Vector Selection Convert to Vector Selection Convert to Vector Selection 10000000000 0 false Convert Shapes to &Vector Selection Convert Shapes to Vector Selection Convert Shapes to Vector Selection 1000000000 0 false &Feather Selection... Feather Selection Feather Selection 10000000000 100 Shift+F6 false Dis&play Selection Display Selection Display Selection 1000 0 Ctrl+H true Sca&le... Scale Scale 100000000 100 false S&elect from Color Range... Select from Color Range Select from Color Range 10000 100 false Select &Opaque Select Opaque Select Opaque 10000 100 false &Grow Selection... Grow Selection Grow Selection 10000000000 100 false S&hrink Selection... Shrink Selection Shrink Selection 10000000000 100 false &Border Selection... Border Selection Border Selection 10000000000 100 false S&mooth Smooth Smooth 10000000000 100 false Filter &Apply Filter Again Apply Filter Again Apply Filter Again 0 0 Ctrl+F false Adjust Adjust Adjust false Artistic Artistic Artistic false Blur Blur Blur false Colors Colors Colors false Edge Detection Edge Detection Edge Detection false Enhance Enhance Enhance false Emboss Emboss Emboss false Map Map Map false Other Other Other false gmic Start G'MIC-Qt Start G'Mic-Qt Start G'Mic-Qt false gmic Re-apply the last G'MIC filter Apply the last G'Mic-Qt action again Apply the last G'Mic-Qt action again false Tools media-record &Start recording macro Start recording macro Start recording macro 1000 0 false media-playback-stop - Stop &recording actions + Stop &recording macro - Stop recording actions - Stop recording actions + Stop recording macro + Stop recording macro 1000 0 false media-playback-start &Open and play... Open and play Open and play 0 0 false document-edit Open &and edit... Open and edit Open and edit 0 0 false Settings configure &Configure Krita... Configure Krita Configure Krita 0 0 false &Manage Resources... Manage Resources Manage Resources 0 0 false preferences-desktop-locale Switch Application &Language... Switch Application Language Switch Application Language false &Show Dockers Show Dockers Show Dockers 0 0 true configure Configure Tool&bars... Configure Toolbars Configure Toolbars 0 0 false Dockers Dockers Dockers false &Themes Themes Themes false im-user Active Author Profile Active Author Profile Active Author Profile configure-shortcuts Configure S&hortcuts... Configure Shortcuts Configure Shortcuts 0 0 false &Window Window Window false Help help-contents Krita &Handbook Krita Handbook Krita Handbook F1 false tools-report-bug &Report Bug... Report Bug Report Bug false calligrakrita &About Krita About Krita About Krita false kde About &KDE About KDE About KDE false Brushes and Stuff &Gradients Gradients Gradients false &Patterns Patterns Patterns false &Color Color Color false &Painter's Tools Painter's Tools Painter's Tools false Brush composite Brush composite Brush composite false Brush option slider 1 Brush option slider 1 Brush option slider 1 false Brush option slider 2 Brush option slider 2 Brush option slider 2 false Brush option slider 3 Brush option slider 3 Brush option slider 3 false Mirror Mirror Mirror false Layouts Select layout false Workspaces Workspaces Workspaces false diff --git a/krita/main.cc b/krita/main.cc index 7f5ef3306d..0ddd93a601 100644 --- a/krita/main.cc +++ b/krita/main.cc @@ -1,389 +1,403 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2002 Patrick Julien * Copyright (c) 2015 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "data/splash/splash_screen.xpm" #include "data/splash/splash_holidays.xpm" #include "data/splash/splash_screen_x2.xpm" #include "data/splash/splash_holidays_x2.xpm" #include "KisDocument.h" #include "kis_splash_screen.h" #include "KisPart.h" #include "KisApplicationArguments.h" #include #include "input/KisQtWidgetsTweaker.h" #if defined Q_OS_WIN #include #include #include #include #include #include #include #elif defined HAVE_X11 #include #endif #if defined HAVE_KCRASH #include #elif defined USE_DRMINGW namespace { void tryInitDrMingw() { wchar_t path[MAX_PATH]; QString pathStr = QCoreApplication::applicationDirPath().replace(L'/', L'\\') + QStringLiteral("\\exchndl.dll"); if (pathStr.size() > MAX_PATH - 1) { return; } int pathLen = pathStr.toWCharArray(path); path[pathLen] = L'\0'; // toWCharArray doesn't add NULL terminator HMODULE hMod = LoadLibraryW(path); if (!hMod) { return; } // No need to call ExcHndlInit since the crash handler is installed on DllMain auto myExcHndlSetLogFileNameA = reinterpret_cast(GetProcAddress(hMod, "ExcHndlSetLogFileNameA")); if (!myExcHndlSetLogFileNameA) { return; } // Set the log file path to %LocalAppData%\kritacrash.log QString logFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation).replace(L'/', L'\\') + QStringLiteral("\\kritacrash.log"); myExcHndlSetLogFileNameA(logFile.toLocal8Bit()); } typedef enum ORIENTATION_PREFERENCE { ORIENTATION_PREFERENCE_NONE = 0x0, ORIENTATION_PREFERENCE_LANDSCAPE = 0x1, ORIENTATION_PREFERENCE_PORTRAIT = 0x2, ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED = 0x4, ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED = 0x8 } ORIENTATION_PREFERENCE; typedef BOOL WINAPI (*pSetDisplayAutoRotationPreferences_t)( ORIENTATION_PREFERENCE orientation ); void resetRotation() { QLibrary user32Lib("user32"); if (!user32Lib.load()) { qWarning() << "Failed to load user32.dll! This really should not happen."; return; } pSetDisplayAutoRotationPreferences_t pSetDisplayAutoRotationPreferences = reinterpret_cast(user32Lib.resolve("SetDisplayAutoRotationPreferences")); if (!pSetDisplayAutoRotationPreferences) { qDebug() << "Failed to load function SetDisplayAutoRotationPreferences"; return; } bool result = pSetDisplayAutoRotationPreferences(ORIENTATION_PREFERENCE_NONE); qDebug() << "SetDisplayAutoRotationPreferences(ORIENTATION_PREFERENCE_NONE) returned" << result; } } // namespace #endif extern "C" int main(int argc, char **argv) { // The global initialization of the random generator qsrand(time(0)); bool runningInKDE = !qgetenv("KDE_FULL_SESSION").isEmpty(); #if defined HAVE_X11 qputenv("QT_QPA_PLATFORM", "xcb"); #endif KisLoggingManager::initialize(); // A per-user unique string, without /, because QLocalServer cannot use names with a / in it QString key = "Krita3" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation).replace("/", "_"); key = key.replace(":", "_").replace("\\","_"); QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true); QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); #if QT_VERSION >= 0x050900 QCoreApplication::setAttribute(Qt::AA_DisableShaderDiskCache, true); #endif const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); bool singleApplication = true; bool enableOpenGLDebug = false; bool openGLDebugSynchronous = false; { QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); singleApplication = kritarc.value("EnableSingleApplication", true).toBool(); #if QT_VERSION >= 0x050600 if (kritarc.value("EnableHiDPI", false).toBool()) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); } if (!qgetenv("KRITA_HIDPI").isEmpty()) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); } #endif if (!qgetenv("KRITA_OPENGL_DEBUG").isEmpty()) { enableOpenGLDebug = true; } else { enableOpenGLDebug = kritarc.value("EnableOpenGLDebug", false).toBool(); } if (enableOpenGLDebug && (qgetenv("KRITA_OPENGL_DEBUG") == "sync" || kritarc.value("OpenGLDebugSynchronous", false).toBool())) { openGLDebugSynchronous = true; } KisOpenGL::setDefaultFormat(enableOpenGLDebug, openGLDebugSynchronous); #ifdef Q_OS_WIN QString preferredOpenGLRenderer = kritarc.value("OpenGLRenderer", "auto").toString(); // Force ANGLE to use Direct3D11. D3D9 doesn't support OpenGL ES 3 and WARP // might get weird crashes atm. qputenv("QT_ANGLE_PLATFORM", "d3d11"); // Probe QPA auto OpenGL detection char *fakeArgv[2] = { argv[0], nullptr }; // Prevents QCoreApplication from modifying the real argc/argv KisOpenGL::probeWindowsQpaOpenGL(1, fakeArgv, preferredOpenGLRenderer); // HACK: https://bugs.kde.org/show_bug.cgi?id=390651 resetRotation(); #endif } QString root; QString language; { // Create a temporary application to get the root QCoreApplication app(argc, argv); Q_UNUSED(app); root = KoResourcePaths::getApplicationRoot(); QSettings languageoverride(configPath + QStringLiteral("/klanguageoverridesrc"), QSettings::IniFormat); languageoverride.beginGroup(QStringLiteral("Language")); language = languageoverride.value(qAppName(), "").toString(); } #ifdef Q_OS_LINUX { QByteArray originalXdgDataDirs = qgetenv("XDG_DATA_DIRS"); if (originalXdgDataDirs.isEmpty()) { // We don't want to completely override the default originalXdgDataDirs = "/usr/local/share/:/usr/share/"; } qputenv("XDG_DATA_DIRS", QFile::encodeName(root + "share") + ":" + originalXdgDataDirs); } #else qputenv("XDG_DATA_DIRS", QFile::encodeName(root + "share")); #endif qDebug() << "Setting XDG_DATA_DIRS" << qgetenv("XDG_DATA_DIRS"); // Now that the paths are set, set the language. First check the override from the language // selection dialog. qDebug() << "Override language:" << language; if (!language.isEmpty()) { KLocalizedString::setLanguages(language.split(":")); // And override Qt's locale, too - qputenv("LANG", language.split(":").first().toUtf8()); + qputenv("LANG", language.split(":").first().toLocal8Bit()); QLocale locale(language.split(":").first()); QLocale::setDefault(locale); } #ifndef Q_OS_LINUX else { // And if there isn't one, check the one set by the system. QLocale locale = QLocale::system(); if (locale.name() != QStringLiteral("en")) { - qDebug() << "Setting Krita's language to:" << locale; - qputenv("LANG", locale.name().toLatin1()); - KLocalizedString::setLanguages(QStringList() << locale.name()); + QStringList uiLanguages = locale.uiLanguages(); + for (QString &uiLanguage : uiLanguages) { + uiLanguage.replace(QChar('-'), QChar('_')); + } + for (int i = 0; i < uiLanguages.size(); i++) { + QString uiLanguage = uiLanguages[i]; + // Strip the country code + int idx = uiLanguage.indexOf(QChar('_')); + if (idx != -1) { + uiLanguage = uiLanguage.left(idx); + uiLanguages.insert(i + 1, uiLanguage); + i++; + } + } + qDebug() << "Setting Krita's language to:" << uiLanguages; + qputenv("LANG", uiLanguages.first().toLocal8Bit()); + KLocalizedString::setLanguages(uiLanguages); } } #endif // first create the application so we can create a pixmap KisApplication app(key, argc, argv); KLocalizedString::setApplicationDomain("krita"); qDebug() << "Available translations" << KLocalizedString::availableApplicationTranslations(); qDebug() << "Available domain translations" << KLocalizedString::availableDomainTranslations("krita"); qDebug() << "Qt UI languages" << QLocale::system().uiLanguages() << qgetenv("LANG"); #ifdef Q_OS_WIN QDir appdir(KoResourcePaths::getApplicationRoot()); QString path = qgetenv("PATH"); qputenv("PATH", QFile::encodeName(appdir.absolutePath() + "/bin" + ";" + appdir.absolutePath() + "/lib" + ";" + appdir.absolutePath() + "/Frameworks" + ";" + appdir.absolutePath() + ";" + path)); qDebug() << "PATH" << qgetenv("PATH"); #endif if (qApp->applicationDirPath().contains(KRITA_BUILD_DIR)) { qFatal("FATAL: You're trying to run krita from the build location. You can only run Krita from the installation location."); } #if defined HAVE_KCRASH KCrash::initialize(); #elif defined USE_DRMINGW tryInitDrMingw(); #endif // If we should clear the config, it has to be done as soon as possible after // KisApplication has been created. Otherwise the config file may have been read // and stored in a KConfig object we have no control over. app.askClearConfig(); KisApplicationArguments args(app); if (singleApplication && app.isRunning()) { // only pass arguments to main instance if they are not for batch processing // any batch processing would be done in this separate instance const bool batchRun = (args.print() || args.exportAs() || args.exportAsPdf()); if (!batchRun) { QByteArray ba = args.serialize(); if (app.sendMessage(ba)) { return 0; } } } if (!runningInKDE) { // Icons in menus are ugly and distracting app.setAttribute(Qt::AA_DontShowIconsInMenus); } #if defined HAVE_X11 app.installNativeEventFilter(KisXi2EventFilter::instance()); #endif app.installEventFilter(KisQtWidgetsTweaker::instance()); if (!args.noSplash()) { // then create the pixmap from an xpm: we cannot get the // location of our datadir before we've started our components, // so use an xpm. QDate currentDate = QDate::currentDate(); QWidget *splash = 0; if (currentDate > QDate(currentDate.year(), 12, 4) || currentDate < QDate(currentDate.year(), 1, 9)) { splash = new KisSplashScreen(app.applicationVersion(), QPixmap(splash_holidays_xpm), QPixmap(splash_holidays_x2_xpm)); } else { splash = new KisSplashScreen(app.applicationVersion(), QPixmap(splash_screen_xpm), QPixmap(splash_screen_x2_xpm)); } app.setSplashScreen(splash); } #if defined Q_OS_WIN { KisConfig cfg; if (cfg.useWin8PointerInput() && !KisTabletSupportWin8::isAvailable()) { cfg.setUseWin8PointerInput(false); } if (!cfg.useWin8PointerInput()) { bool hasWinTab = KisTabletSupportWin::init(); if (!hasWinTab) { if (KisTabletSupportWin8::isPenDeviceAvailable()) { // Use WinInk automatically cfg.setUseWin8PointerInput(true); } else if (!cfg.readEntry("WarnedAboutMissingWinTab", false)) { if (KisTabletSupportWin8::isAvailable()) { QMessageBox::information(nullptr, i18n("Krita Tablet Support"), i18n("Cannot load WinTab driver and no Windows Ink pen devices are found. If you have a drawing tablet, please make sure the tablet driver is properly installed."), QMessageBox::Ok, QMessageBox::Ok); } else { QMessageBox::information(nullptr, i18n("Krita Tablet Support"), i18n("Cannot load WinTab driver. If you have a drawing tablet, please make sure the tablet driver is properly installed."), QMessageBox::Ok, QMessageBox::Ok); } cfg.writeEntry("WarnedAboutMissingWinTab", true); } } } if (cfg.useWin8PointerInput()) { KisTabletSupportWin8 *penFilter = new KisTabletSupportWin8(); if (penFilter->init()) { // penFilter.registerPointerDeviceNotifications(); app.installNativeEventFilter(penFilter); qDebug() << "Using Win8 Pointer Input for tablet support"; } else { qDebug() << "No Win8 Pointer Input available"; delete penFilter; } } } #endif if (!app.start(args)) { return 1; } #if QT_VERSION >= 0x050700 app.setAttribute(Qt::AA_CompressHighFrequencyEvents, false); #endif // Set up remote arguments. QObject::connect(&app, SIGNAL(messageReceived(QByteArray,QObject*)), &app, SLOT(remoteArguments(QByteArray,QObject*))); QObject::connect(&app, SIGNAL(fileOpenRequest(QString)), &app, SLOT(fileOpenRequested(QString))); int state = app.exec(); { QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); kritarc.setValue("canvasState", "OPENGL_SUCCESS"); } return state; } diff --git a/krita/org.kde.krita.appdata.xml b/krita/org.kde.krita.appdata.xml index f0e739d963..e42d03b8d9 100644 --- a/krita/org.kde.krita.appdata.xml +++ b/krita/org.kde.krita.appdata.xml @@ -1,201 +1,241 @@ - org.kde.krita.desktop + org.kde.krita + org.kde.krita.desktop CC0-1.0 + GPL-3.0-only + Krita Foundation + Fundació Krita + Krita Foundation + Fundação do Krita + Krita-stiftelsen + Фундація Krita + xxKrita Foundationxx + foundation@krita.org Krita كريتا Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita + Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita xxKritaxx Krita Krita Digital Painting, Creative Freedom رسم رقميّ، حريّة إبداعيّة Pintura dixital, llibertá creativa Digitalno crtanje, kreativna sloboda Dibuix digital, Llibertat creativa Dibuix digital, Llibertat creativa Digitální malování, svoboda tvorby Digital tegning, kunstnerisk frihed Digitales Malen, kreative Freiheit Ψηφιακή ζωγραφική, δημιουργική ελευθερία Digital Painting, Creative Freedom Pintura digital, libertad creativa Digitaalne joonistamine, loominguline vabadus Digitaalimaalaus, luova vapaus Peinture numérique, liberté créatrice Debuxo dixital, liberdade creativa Pictura digital, Libertate creative + Pelukisan Digital, Kebebasan Berkreatif Pittura digitale, libertà creativa Digital Painting, Creative Freedom Cyfrowe malowanie, Wolność Twórcza Pintura Digital, Liberdade Criativa Pintura digital, liberdade criativa Цифровое рисование. Творческая свобода Digitálne maľovanie, kreatívna sloboda Digital målning, kreativ frihet Sayısal Boyama, Yaratıcı Özgürlük Цифрове малювання, творча свобода xxDigital Painting, Creative Freedomxx - 数码绘图,自由创作 + 自由挥洒数字绘画的无限创意 數位繪畫,創作自由

Krita is the full-featured digital art studio.

Krita ye l'estudiu completu d'arte dixital.

Krita je potpuni digitalni umjetnički studio.

Krita és l'estudi d'art digital ple de funcionalitats.

Krita és l'estudi d'art digital ple de funcionalitats.

Krita ist ein digitales Designstudio mit umfangreichen Funktionen.

Το Krita είναι ένα πλήρες χαρακτηριστικών ψηφιακό ατελιέ.

Krita is the full-featured digital art studio.

Krita es un estudio de arte digital completo

Krita on rohkete võimalustega digitaalkunstistuudio.

Krita on täyspiirteinen digitaiteen ateljee.

Krita est le studio d'art numérique complet.

Krita é un estudio completo de arte dixital.

Krita es le studio de arte digital complete.

+

Krita adalah studio seni digital yang penuh dengan fitur.

Krita è uno studio d'arte digitale completo.

Krita は、フル機能を備えたデジタルなアートスタジオです。

Krita is de digitale kunststudio vol mogelijkheden.

Krita jest pełnowymiarowym, cyfrowym studiem artystycznym

O Krita é o estúdio de arte digital completo.

O Krita é o estúdio de arte digital completo.

Krita полнофункциональный инструмент для создания цифровой графики.

Krita je plne vybavené digitálne umelecké štúdio.

Krita är den fullfjädrade digitala konststudion.

Krita, tam özellikli dijital sanat stüdyosudur.

Krita — повноцінний комплекс для створення цифрових художніх творів.

xxKrita is the full-featured digital art studio.xx

-

Krita 是全功能的数字艺术工作室。

+

Krita 是功能齐全的数字艺术工作室软件。

Krita 是全功能的數位藝術工作室。

It is perfect for sketching and painting, and presents an end–to–end solution for creating digital painting files from scratch by masters.

On je savršen za skiciranje i slikanje i predstavlja finalno rješenje za kreiranje digitalnih slika od nule s majstorima

És perfecte per fer esbossos i pintar, i presenta una solució final per crear fitxers de dibuix digital des de zero per a mestres.

És perfecte per fer esbossos i pintar, i presenta una solució final per crear fitxers de dibuix digital des de zero per a mestres.

Είναι ιδανικό για σκιτσογραφία και ζωγραφική, και παρουσιάζει μια από άκρη σε άκρη λύση για τη δημιουργία από το μηδέν αρχείων ψηφιακης ζωγραφικής από τους δασκάλους της τέχνης.

It is perfect for sketching and painting, and presents an end–to–end solution for creating digital painting files from scratch by masters.

Es perfecto para diseñar y pintar, y ofrece una solución completa para crear desde cero archivos de pintura digital apta para profesionales.

See on suurepärane töövahend visandite ja joonistuste valmistamiseks ning annab andekatele kunstnikele võimaluse luua digitaalpilt algusest lõpuni just oma käe järgi.

Se on täydellinen luonnosteluun ja maalaukseen ja tarjoaa kokonaisratkaisun digitaalisten kuvatiedostojen luomiseen alusta alkaen.

Il est parfait pour crayonner et peindre, et constitue une solution de bout en bout pour créer des fichier de peinture numérique depuis la feuille blanche jusqu'au épreuves finales.

Resulta perfecto para debuxar e pintar, e presenta unha solución completa que permite aos mestres crear ficheiros de debuxo dixital desde cero.

Illo es perfecte pro schizzar e pinger, e presenta un solution ab fin al fin pro crear files de pictura digital ab grattamentos per maestros.

+

Ini adalah sempurna untuk mensketsa dan melukis, dan menghadirkan sebuah solusi untuk menciptakan file-file pelukisan digital dari goresan si pelukis ulung.

Perfetto per fare schizzi e dipingere, prevede una soluzione completa che consente agli artisti di creare file di dipinti digitali partendo da zero.

Het is perfect voor schetsen en schilderen en zet een end–to–end oplossing voor het maken van digitale bestanden voor schilderingen vanuit het niets door meesters.

Nadaje się perfekcyjnie do szkicowania i malowania i dostarcza zupełnego rozwiązania dla tworzenia plików malowideł cyfrowych od zalążka.

É perfeito para desenhos e pinturas, oferecendo uma solução final para criar ficheiros de pintura digital do zero por mestres.

É perfeito para desenhos e pinturas, oferecendo uma solução final para criar arquivos de desenho digital feitos a partir do zero por mestres.

Она превосходно подходит для набросков и рисования, предоставляя мастерам самодостаточный инструмент для создания цифровой живописи с нуля.

Je ideálna na skicovanie a maľovanie a poskytuje end-to-end riešenie na vytváranie súborov digitálneho maľovania od základu od profesionálov.

Den är perfekt för att skissa och måla, samt erbjuder en helomfattande lösning för att skapa digitala målningsfiler från grunden av mästare.

Eskiz ve boyama için mükemmeldir ve ustaların sıfırdan dijital boyama dosyaları oluşturmak için uçtan-uca bir çözüm sunar.

Цей комплекс чудово пасує для створення ескізів та художніх зображень і є самодостатнім набором для створення файлів цифрових полотен «з нуля» для справжніх художників.

xxIt is perfect for sketching and painting, and presents an end–to–end solution for creating digital painting files from scratch by masters.xx

-

适合做草图和绘画,为艺术大师提供了从草稿到数码绘画的完整解决方案。

+

它专门为数字绘画设计,为美术工作者提供了一个从起草、上色到完成作品等整个创作流程的完整解决方案。

它是素描和繪畫的完美選擇,並提供了一個從零開始建立數位繪畫檔的端到端解決方案。

Krita is a great choice for creating concept art, comics, textures for rendering and matte paintings. Krita supports many colorspaces like RGB and CMYK at 8 and 16 bits integer channels, as well as 16 and 32 bits floating point channels.

Krita je odličan izbor za kreiranje konceptualne umjetnosti, stripove, teksture za obradu i mat slike. Krita podržava mnoge prostore boja kao RGB i CMIK na 8 i 16 bitnim cjelobrojnim kanalimaa, kao i 16 i 32 bita floating point kanalima.

El Krita és una gran elecció per crear art conceptual, còmics, textures per renderitzar i pintures «matte». El Krita permet molts espais de color com el RGB i el CMYK a 8 i 16 bits de canals sencers, així com 16 i 32 bits de canals de coma flotant.

El Krita és una gran elecció per crear art conceptual, còmics, textures per renderitzar i pintures «matte». El Krita permet molts espais de color com el RGB i el CMYK a 8 i 16 bits de canals sencers, així com 16 i 32 bits de canals de coma flotant.

Το Krita είναι μια εξαιρετική επιλογή για τη δημιουργία αφηρημένης τέχνης, ιστοριών με εικόνες, υφής για ζωγραφική αποτύπωσης και διάχυσης φωτός. Το Krita υποστηρίζει πολλούς χρωματικούς χώρους όπως τα RGB και CMYK σε 8 και 16 bit κανάλια ακεραίων καθώς επίσης και σε 16 και 32 bit κανάλια κινητής υποδιαστολής,

Krita is a great choice for creating concept art, comics, textures for rendering and matte paintings. Krita supports many colourspaces like RGB and CMYK at 8 and 16 bits integer channels, as well as 16 and 32 bits floating point channels.

Krita es una gran elección para crear arte conceptual, cómics, texturas para renderizar y «matte paintings». Krita permite el uso de muchos espacios de color, como, por ejemplo, RGB y CMYK, tanto en canales de enteros de 8 y 16 bits, así como en canales de coma flotante de 16 y 32 bits.

Krita on üks paremaid valikuid kontseptuaalkunsti, koomiksite, tekstuuride ja digitaalmaalide loomiseks. Krita toetab paljusid värviruume, näiteks RGB ja CMYK 8 ja 16 täisarvulise bitiga kanali kohta, samuti 16 ja 32 ujukomabitiga kanali kohta.

Krita on hyvä valinta konseptikuvituksen, sarjakuvien, pintakuvioiden ja maalausten luomiseen. Krita tukee useita väriavaruuksia kuten RGB:tä ja CMYK:ta 8 ja 16 bitin kokonaisluku- samoin kuin 16 ja 32 bitin liukulukukanavin.

Krita est un très bon choix pour créer des concepts arts, des bandes-dessinées, des textures de rendu et des peintures. Krita prend en charge plusieurs espaces de couleurs comme RVB et CMJN avec les canaux de 8 et 16 bits entiers ainsi que les canaux de 16 et 32 bits flottants.

Krita é unha gran opción para crear arte conceptual, texturas para renderización e pinturas mate. Krita permite usar moitos espazos de cores como RGB e CMYK con canles de 8 e 16 bits, así como canles de coma flotante de 16 e 32 bits.

Krita es un grande selection pro crear arte de concepto, comics, texturas pro rendering e picturas opac. Krita supporta multe spatios de colores como RGB e CMYK con canales de integer a 8 e 16 bits, como anque canales floating point a 16 e 32 bits.

+

Krita adalah pilihan yang cocok untuk menciptakan konsep seni, komik, tekstur untuk rendering dan lukisan matte. Krita mendukung banyak ruang warna seperti RGB dan CMYK pada channel integer 8 dan 16 bit, serta 16 dan 32 bit channel titik mengambang.

Krita rappresenta una scelta ottimale per la creazione di arte concettuale, fumetti e texture per il rendering e il matte painting. Krita supporta molti spazi colori come RGB e CMYK a 8 e 16 bit per canali interi e 16 e 32 bit per canali a virgola mobile.

コンセプトアート、コミック、3DCG 用テクスチャ、マットペイントを制作する方にとって、Krita は最適な選択です。Krita は、8/16 ビット整数/チャンネル、および 16/32 ビット浮動小数点/チャンネルの RGB や CMYK をはじめ、さまざまな色空間をサポートしています。

Krita is een goede keuze voor het maken van kunstconcepten, strips, textuur voor weergeven en matte schilderijen. Krita ondersteunt vele kleurruimten zoals RGB en CMYK in 8 en 16 bits kanalen met gehele getallen, evenals 16 en 32 bits kanalen met drijvende komma.

Krita jest świetnym wyborem przy tworzeniu koncepcyjnej sztuki, komiksów, tekstur do wyświetlania i kaszet. Krita obsługuje wiele przestrzeni barw takich jak RGB oraz CMYK dla kanałów 8 oraz 16 bitowych wyrażonych w l. całkowitych, a także 16 oraz 32 bitowych wyrażonych w l. zmiennoprzecinkowych.

O Krita é uma óptima escolha para criar arte conceptual, banda desenhada, texturas para desenho e pinturas. O Krita suporta diversos espaços de cores como o RGB e o CMYK com canais de cores inteiros a 8 e 16 bits, assim como canais de vírgula flutuante a 16 e a 32 bits.

O Krita é uma ótima escolha para criação de arte conceitual, histórias em quadrinhos, texturas para desenhos e pinturas. O Krita tem suporte a diversos espaços de cores como RGB e CMYK com canais de cores inteiros de 8 e 16 bits, assim como canais de ponto flutuante de 16 e 32 bits.

Krita - отличный выбор для создания концепт-артов, комиксов, текстур для рендеринга и рисования. Она поддерживает множество цветовых пространств включая RGB и CMYK с 8 и 16 целыми битами на канал, а также 16 и 32 битами с плавающей запятой на канал.

Krita je výborná voľba pre vytváranie konceptového umenia, textúr na renderovanie a matné kresby. Krita podporuje mnoho farebných priestorov ako RGB a CMYK na 8 a 16 bitových celočíselných kanáloch ako aj 16 a 32 bitových reálnych kanáloch.

Krita är ett utmärkt val för att skapa concept art, serier, strukturer för återgivning och bakgrundsmålningar. Krita stöder många färgrymder som RGB och CMYK med 8- och 16-bitars heltal, samt 16- och 32-bitars flyttal.

Krita, konsept sanat, çizgi roman, kaplama ve mat resimler için dokular oluşturmak için mükemmel bir seçimdir. Krita, 8 ve 16 bit tamsayı kanallarında RGB ve CMYK gibi birçok renk alanını ve 16 ve 32 bit kayan nokta kanallarını desteklemektedir.

Krita — чудовий інструмент для створення концептуального живопису, коміксів, текстур для моделей та декорацій. У Krita передбачено підтримку багатьох просторів кольорів, зокрема RGB та CMYK з 8-бітовими та 16-бітовими цілими значеннями, а також 16-бітовими та 32-бітовими значеннями з рухомою крапкою для каналів кольорів.

xxKrita is a great choice for creating concept art, comics, textures for rendering and matte paintings. Krita supports many colorspaces like RGB and CMYK at 8 and 16 bits integer channels, as well as 16 and 32 bits floating point channels.xx

-

Krita 是创建抽象艺术、漫画、渲染纹理和亚光绘画的理想选择。Krita 支持非常多的色彩空间,比如 8 位和 16 位整数通道以及 16 位和 32 位浮点通道的 RGB 和 CMYK。

+

Krita 是绘制概念美术、漫画、渲染纹理和电影布景的理想选择。Krita 支持多种色彩空间,如 8 位和 16 位整数及 16 位和 32 位浮点的 RGB 和 CMYK 颜色模型。

Krita 是創造概念藝術、漫畫、彩現紋理和場景繪畫的絕佳選擇。Krita 在 8 位元和 16 位元整數色版,以及 16 位元和 32 位元浮點色版中支援 RGB 和 CMYK 等多種色彩空間。

Have fun painting with the advanced brush engines, amazing filters and many handy features that make Krita enormously productive.

Zabavite se kreirajući napredne pogone četki, filtere i mnoge praktične osobine koje čine Krita vrlo produktivnim.

Gaudiu pintant amb els motors de pinzells avançats, els filtres impressionants i moltes funcionalitats útils que fan el Krita molt productiu.

Gaudiu pintant amb els motors de pinzells avançats, els filtres impressionants i moltes funcionalitats útils que fan el Krita molt productiu.

Διασκεδάστε ζωγραφίζοντας με τις προηγμένες μηχανές πινέλων, με εκπληκτικά φίλτρα και πολλά εύκολης χρήσης χαρακτηριστικά που παρέχουν στο Krita εξαιρετικά αυξημένη παραγωγικότητα.

Have fun painting with the advanced brush engines, amazing filters and many handy features that make Krita enormously productive.

Diviértase pintando con los avanzados motores de pinceles, los espectaculares filtros y muchas funcionalidades prácticas que hacen que Krita sea enormemente productivo.

Joonistamise muudavad tunduvalt lõbusamaks võimsad pintslimootorid, imetabased filtrid ja veel paljud käepärased võimalused, mis muudavad Krita kasutaja tohutult tootlikuks.

Pidä hauskaa maalatessasi edistyneillä sivellinmoottoreilla, hämmästyttävillä suotimilla ja monilla muilla kätevillä ominaisuuksilla, jotka tekevät Kritasta tavattoman tehokkaan.

Amusez-vous à peindre avec les outils de brosse avancés, les filtres incroyables et les nombreuses fonctionnalités pratiques qui rendent Krita extrêmement productif.

Goza debuxando con motores de pincel avanzados, filtros fantásticos e moitas outras funcionalidades útiles que fan de Krita un programa extremadamente produtivo.

Amusa te a pinger con le motores de pincel avantiate, filtros stupende e multe characteristicas amical que face Krita enormemente productive.

+

Bersenang-senanglah melukis dengan mesin kuas canggih, filter luar biasa dan banyak fitur berguna yang membuat Krita sangat produktif.

Divertiti a dipingere con gli avanzati sistemi di pennelli, i sorprendenti filtri e molte altre utili caratteristiche che fanno di Krita un software enormemente produttivo.

Krita のソフトウェアとしての生産性を高めている先進的なブラシエンジンや素晴らしいフィルタのほか、便利な機能の数々をお楽しみください。

Veel plezier met schilderen met the geavanceerde penseel-engines, filters vol verbazing en vele handige mogelijkheden die maken dat Krita enorm productief is.

Baw się przy malowaniu przy użyciu zaawansowanych silników pędzli, zadziwiających filtrów i wielu innych przydatnych cech, które czynią z Krity bardzo produktywną.

Divirta-se a pintar com os motores de pincéis avançados, os filtros espantosos e muitas outras funcionalidades úteis que tornam o Krita altamente produtivo.

Divirta-se pintando com os mecanismos de pincéis avançados, filtros maravilhosos e muitas outras funcionalidades úteis que tornam o Krita altamente produtivo.

Получайте удовольствие от использования особых кистевых движков, впечатляющих фильтров и множества других функций, делающих Krita сверхпродуктивной.

Užívajte si maľovanie s pokročilými kresliacimi enginmi, úžasnými filtrami a mnohými užitočnými funkciami, ktoré robia Kritu veľmi produktívnu.

Ha det så kul vid målning med de avancerade penselfunktionerna, fantastiska filtren och många praktiska funktioner som gör Krita så enormt produktiv.

Gelişmiş fırça motorları, şaşırtıcı filtreler ve Krita'yı son derece üretken yapan bir çok kullanışlı özellikli boya ile iyi eğlenceler.

Отримуйте задоволення від малювання за допомогою пензлів з найширшими можливостями, чудових фільтрів та багатьох зручних можливостей, які роблять Krita надзвичайно продуктивним засобом малювання.

xxHave fun painting with the advanced brush engines, amazing filters and many handy features that make Krita enormously productive.xx

-

尽情使用高级笔刷引擎,超赞的滤镜和很多手绘特性,发挥 Krita 绝佳的创造力。

+

Krita 具有功能强大的笔刷引擎、特效惊艳的滤镜以及便于操作的交互设计,可让你尽情、高效地挥洒无限创意。

使用先進的筆刷引擎、驚人的濾鏡和許多方便的功能來開心地繪畫,讓 Krita 擁有巨大的生產力。

https://www.krita.org/ https://krita.org/about/faq/ https://krita.org/support-us/donations/ https://docs.krita.org/Category:Tutorials https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_001.png https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_002.png https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_003.png https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_004.png https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_005.png https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_006.png - foundation@krita.org + + none + none + none + none + none + none + none + none + none + none + none + none + none + none + none + none + none + none + none + none + + + Graphics + KDE krita
diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index b646c31288..dca1ba7a86 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -1,23 +1,23 @@ add_subdirectory( version ) add_subdirectory( global ) add_subdirectory( koplugin ) add_subdirectory( widgetutils ) add_subdirectory( widgets ) add_subdirectory( store ) add_subdirectory( odf ) add_subdirectory( flake ) add_subdirectory( basicflakes ) add_subdirectory( pigment ) add_subdirectory( command ) add_subdirectory( brush ) add_subdirectory( psd ) add_subdirectory( color ) add_subdirectory( image ) add_subdirectory( ui ) add_subdirectory( vectorimage ) add_subdirectory( impex ) add_subdirectory( libkis ) -if (HAVE_QT_QUICK) +if (NOT APPLE AND HAVE_QT_QUICK) add_subdirectory( libqml ) endif() diff --git a/libs/brush/tests/CMakeLists.txt b/libs/brush/tests/CMakeLists.txt index 7ae6ecbb8a..df88d5d23e 100644 --- a/libs/brush/tests/CMakeLists.txt +++ b/libs/brush/tests/CMakeLists.txt @@ -1,31 +1,30 @@ ########### next target ############### set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) -include_directories( - ${CMAKE_SOURCE_DIR}/libs/image/metadata - ${CMAKE_SOURCE_DIR}/sdk/tests +include_directories( + ${CMAKE_SOURCE_DIR}/libs/image/metadata + ${CMAKE_SOURCE_DIR}/sdk/tests ) include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR} ) if(HAVE_VC) include_directories(SYSTEM ${Vc_INCLUDE_DIR} ) endif() macro_add_unittest_definitions() include(ECMAddTests) ecm_add_tests( kis_auto_brush_test.cpp kis_auto_brush_factory_test.cpp kis_gbr_brush_test.cpp kis_boundary_test.cpp kis_imagepipe_brush_test.cpp NAME_PREFIX "krita-libbrush-" LINK_LIBRARIES kritaimage kritalibbrush Qt5::Test ) - diff --git a/libs/flake/tools/PathToolOptionWidgetBase.ui b/libs/flake/tools/PathToolOptionWidgetBase.ui index d7ec32cfc9..fb992dc888 100644 --- a/libs/flake/tools/PathToolOptionWidgetBase.ui +++ b/libs/flake/tools/PathToolOptionWidgetBase.ui @@ -1,271 +1,271 @@ PathToolOptionWidgetBase 0 0 321 208 0 0 0 0 Qt::Horizontal 1 0 - 0 + 1 0 0 0 0 3 ... ... ... Qt::Horizontal 40 20 ... ... ... ... ... ... ... ... ... ... Qt::Vertical Qt::Vertical Qt::Vertical QSizePolicy::Minimum 20 0 0 1 1 0 0 0 0 0 - Convert To Path + Convert to Path Qt::Horizontal 40 20 Qt::Vertical 20 150 diff --git a/libs/image/kis_antialiasing_fade_maker.h b/libs/image/kis_antialiasing_fade_maker.h index cc9b05d412..642b2fa29a 100644 --- a/libs/image/kis_antialiasing_fade_maker.h +++ b/libs/image/kis_antialiasing_fade_maker.h @@ -1,192 +1,208 @@ /* * 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_ANTIALIASING_FADE_MAKER_H #define __KIS_ANTIALIASING_FADE_MAKER_H #include "kis_global.h" template class KisAntialiasingFadeMaker1D { public: KisAntialiasingFadeMaker1D(const BaseFade &baseFade, bool enableAntialiasing) : m_radius(0.0), m_fadeStartValue(0), m_antialiasingFadeStart(0), m_antialiasingFadeCoeff(0), m_enableAntialiasing(enableAntialiasing), m_baseFade(baseFade) { } KisAntialiasingFadeMaker1D(const KisAntialiasingFadeMaker1D &rhs, const BaseFade &baseFade) : m_radius(rhs.m_radius), m_fadeStartValue(rhs.m_fadeStartValue), m_antialiasingFadeStart(rhs.m_antialiasingFadeStart), m_antialiasingFadeCoeff(rhs.m_antialiasingFadeCoeff), m_enableAntialiasing(rhs.m_enableAntialiasing), m_baseFade(baseFade) { } void setSquareNormCoeffs(qreal xcoeff, qreal ycoeff) { m_radius = 1.0; qreal xf = qMax(0.0, ((1.0 / xcoeff) - 1.0) * xcoeff); qreal yf = qMax(0.0, ((1.0 / ycoeff) - 1.0) * ycoeff); m_antialiasingFadeStart = pow2(0.5 * (xf + yf)); m_fadeStartValue = m_baseFade.value(m_antialiasingFadeStart); m_antialiasingFadeCoeff = qMax(0.0, 255.0 - m_fadeStartValue) / (m_radius - m_antialiasingFadeStart); } void setRadius(qreal radius) { m_radius = radius; m_antialiasingFadeStart = qMax(0.0, m_radius - 1.0); m_fadeStartValue = m_baseFade.value(m_antialiasingFadeStart); m_antialiasingFadeCoeff = qMax(0.0, 255.0 - m_fadeStartValue) / (m_radius - m_antialiasingFadeStart); } inline bool needFade(qreal dist, quint8 *value) { if (dist > m_radius) { *value = 255; return true; } if (!m_enableAntialiasing) { return false; } if (dist > m_antialiasingFadeStart) { *value = m_fadeStartValue + (dist - m_antialiasingFadeStart) * m_antialiasingFadeCoeff; return true; } return false; } + qreal getRadius(){ + return m_radius; + } + + qreal getAntialiasingFadeStart(){ + return m_antialiasingFadeStart; + } + + qreal getFadeStartValue() { + return m_fadeStartValue; + } + + qreal getAntialiasingFadeCoeff(){ + return m_antialiasingFadeCoeff; + } + private: qreal m_radius; quint8 m_fadeStartValue; qreal m_antialiasingFadeStart; qreal m_antialiasingFadeCoeff; bool m_enableAntialiasing; const BaseFade &m_baseFade; }; template class KisAntialiasingFadeMaker2D { public: KisAntialiasingFadeMaker2D(const BaseFade &baseFade, bool enableAntialiasing) : m_xLimit(0), m_yLimit(0), m_xFadeLimitStart(0), m_yFadeLimitStart(0), m_xFadeCoeff(0), m_yFadeCoeff(0), m_enableAntialiasing(enableAntialiasing), m_baseFade(baseFade) { } KisAntialiasingFadeMaker2D(const KisAntialiasingFadeMaker2D &rhs, const BaseFade &baseFade) : m_xLimit(rhs.m_xLimit), m_yLimit(rhs.m_yLimit), m_xFadeLimitStart(rhs.m_xFadeLimitStart), m_yFadeLimitStart(rhs.m_yFadeLimitStart), m_xFadeCoeff(rhs.m_xFadeCoeff), m_yFadeCoeff(rhs.m_yFadeCoeff), m_enableAntialiasing(rhs.m_enableAntialiasing), m_baseFade(baseFade) { } void setLimits(qreal halfWidth, qreal halfHeight) { m_xLimit = halfWidth; m_yLimit = halfHeight; m_xFadeLimitStart = m_xLimit - 1.0; m_yFadeLimitStart = m_yLimit - 1.0; m_xFadeCoeff = 1.0 / (m_xLimit - m_xFadeLimitStart); m_yFadeCoeff = 1.0 / (m_yLimit - m_yFadeLimitStart); } inline bool needFade(qreal x, qreal y, quint8 *value) { x = qAbs(x); y = qAbs(y); if (x > m_xLimit) { *value = 255; return true; } if (y > m_yLimit) { *value = 255; return true; } if (!m_enableAntialiasing) { return false; } if (x > m_xFadeLimitStart) { quint8 baseValue = m_baseFade.value(x, y); *value = baseValue + (255.0 - baseValue) * (x - m_xFadeLimitStart) * m_xFadeCoeff; if (y > m_yFadeLimitStart && *value < 255) { *value += (255.0 - *value) * (y - m_yFadeLimitStart) * m_yFadeCoeff; } return true; } if (y > m_yFadeLimitStart) { quint8 baseValue = m_baseFade.value(x, y); *value = baseValue + (255.0 - baseValue) * (y - m_yFadeLimitStart) * m_yFadeCoeff; if (x > m_xFadeLimitStart && *value < 255) { *value += (255.0 - *value) * (x - m_xFadeLimitStart) * m_xFadeCoeff; } return true; } return false; } private: qreal m_xLimit; qreal m_yLimit; qreal m_xFadeLimitStart; qreal m_yFadeLimitStart; qreal m_xFadeCoeff; qreal m_yFadeCoeff; bool m_enableAntialiasing; const BaseFade &m_baseFade; }; #endif /* __KIS_ANTIALIASING_FADE_MAKER_H */ diff --git a/libs/image/kis_brush_mask_applicator_factories.cpp b/libs/image/kis_brush_mask_applicator_factories.cpp index 2c54a61ef8..049d9120f9 100644 --- a/libs/image/kis_brush_mask_applicator_factories.cpp +++ b/libs/image/kis_brush_mask_applicator_factories.cpp @@ -1,137 +1,253 @@ /* * Copyright (c) 2012 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_brush_mask_applicator_factories.h" #include "kis_circle_mask_generator.h" #include "kis_circle_mask_generator_p.h" +#include "kis_gauss_circle_mask_generator_p.h" #include "kis_brush_mask_applicators.h" #include "kis_brush_mask_applicator_base.h" #define a(_s) #_s #define b(_s) a(_s) template<> template<> MaskApplicatorFactory::ReturnType MaskApplicatorFactory::create(ParamType maskGenerator) { return new KisBrushMaskScalarApplicator(maskGenerator); } template<> template<> MaskApplicatorFactory::ReturnType MaskApplicatorFactory::create(ParamType maskGenerator) { return new KisBrushMaskVectorApplicator(maskGenerator); } +template<> +template<> +MaskApplicatorFactory::ReturnType +MaskApplicatorFactory::create(ParamType maskGenerator) +{ + return new KisBrushMaskVectorApplicator(maskGenerator); +} + #if defined HAVE_VC struct KisCircleMaskGenerator::FastRowProcessor { FastRowProcessor(KisCircleMaskGenerator *maskGenerator) : d(maskGenerator->d.data()) {} template void process(float* buffer, int width, float y, float cosa, float sina, float centerX, float centerY); KisCircleMaskGenerator::Private *d; }; template<> void KisCircleMaskGenerator:: FastRowProcessor::process(float* buffer, int width, float y, float cosa, float sina, float centerX, float centerY) { const bool useSmoothing = d->copyOfAntialiasEdges; const bool noFading = d->noFading; float y_ = y - centerY; float sinay_ = sina * y_; float cosay_ = cosa * y_; float* bufferPointer = buffer; Vc::float_v currentIndices = Vc::float_v::IndexesFromZero(); Vc::float_v increment((float)Vc::float_v::size()); Vc::float_v vCenterX(centerX); Vc::float_v vCosa(cosa); Vc::float_v vSina(sina); Vc::float_v vCosaY_(cosay_); Vc::float_v vSinaY_(sinay_); Vc::float_v vXCoeff(d->xcoef); Vc::float_v vYCoeff(d->ycoef); Vc::float_v vTransformedFadeX(d->transformedFadeX); Vc::float_v vTransformedFadeY(d->transformedFadeY); Vc::float_v vOne(Vc::One); for (int i=0; i < width; i+= Vc::float_v::size()){ Vc::float_v x_ = currentIndices - vCenterX; Vc::float_v xr = x_ * vCosa - vSinaY_; Vc::float_v yr = x_ * vSina + vCosaY_; Vc::float_v n = pow2(xr * vXCoeff) + pow2(yr * vYCoeff); Vc::float_m outsideMask = n > vOne; if (!outsideMask.isFull()) { if (noFading) { Vc::float_v vFade(Vc::Zero); vFade(outsideMask) = vOne; vFade.store(bufferPointer, Vc::Aligned); } else { if (useSmoothing) { xr = Vc::abs(xr) + vOne; yr = Vc::abs(yr) + vOne; } Vc::float_v vNormFade = pow2(xr * vTransformedFadeX) + pow2(yr * vTransformedFadeY); //255 * n * (normeFade - 1) / (normeFade - n) Vc::float_v vFade = n * (vNormFade - vOne) / (vNormFade - n); // Mask in the inner circe of the mask Vc::float_m mask = vNormFade < vOne; vFade.setZero(mask); // Mask out the outer circe of the mask vFade(outsideMask) = vOne; vFade.store(bufferPointer, Vc::Aligned); } } else { // Mask out everything outside the circle vOne.store(bufferPointer, Vc::Aligned); } currentIndices = currentIndices + increment; bufferPointer += Vc::float_v::size(); } } + +struct KisGaussCircleMaskGenerator::FastRowProcessor +{ + FastRowProcessor(KisGaussCircleMaskGenerator *maskGenerator) + : d(maskGenerator->d.data()) {} + + template + void process(float* buffer, int width, float y, float cosa, float sina, + float centerX, float centerY); + + KisGaussCircleMaskGenerator::Private *d; +}; + +template<> void KisGaussCircleMaskGenerator:: +FastRowProcessor::process(float* buffer, int width, float y, float cosa, float sina, + float centerX, float centerY) +{ + const bool antialiasOn = d->fadeMaker.getAliasingEnabled(); + + float y_ = y - centerY; + float sinay_ = sina * y_; + float cosay_ = cosa * y_; + + float* bufferPointer = buffer; + + Vc::float_v currentIndices = Vc::float_v::IndexesFromZero(); + + Vc::float_v increment((float)Vc::float_v::size()); + Vc::float_v vCenterX(centerX); + Vc::float_v vCenter(d->center); + + Vc::float_v vCosa(cosa); + Vc::float_v vSina(sina); + Vc::float_v vCosaY_(cosay_); + Vc::float_v vSinaY_(sinay_); + + Vc::float_v vYCoeff(d->ycoef); + Vc::float_v vDistfactor(d->distfactor); + Vc::float_v vAlphafactor(d->alphafactor); + + Vc::float_v vFadeRadius(d->fadeMaker.getRadius()); + Vc::float_v vFadeStartValue(d->fadeMaker.getFadeStartValue()); + Vc::float_v vFadeAFadeStart(d->fadeMaker.getAntialiasingFadeStart()); + Vc::float_v vFadeAFadeCoeff(d->fadeMaker.getAntialiasingFadeCoeff()); + + Vc::float_v vOne(Vc::One); + Vc::float_v vZero(Vc::Zero); + Vc::float_v vValMax(255.f); + + for (int i=0; i < width; i+= Vc::float_v::size()){ + + Vc::float_v x_ = currentIndices - vCenterX; + + Vc::float_v xr = x_ * vCosa - vSinaY_; + Vc::float_v yr = x_ * vSina + vCosaY_; + + Vc::float_v dist = sqrt(pow2(xr) + pow2(yr * vYCoeff)); + + // BEGIN FadeMaker needFade vectorized + // follow fademaker rules for outsideMask + Vc::float_m outsideMask = dist > vFadeRadius; + dist(outsideMask) = vOne; + + Vc::float_m fadeStartMask(false); + // if antialias is off, do not process + if(antialiasOn){ + Vc::float_m fadeStartMask = dist > vFadeAFadeStart; + dist((outsideMask ^ fadeStartMask) & fadeStartMask) = (vFadeStartValue + (dist - vFadeAFadeStart) * vFadeAFadeCoeff) / vValMax; + } + + Vc::float_m excludeMask = outsideMask | fadeStartMask; + + if (!excludeMask.isFull()) { + Vc::float_v valDist = dist * vDistfactor; + Vc::float_v fullFade = vAlphafactor * ( d->vErf(valDist + vCenter) - d->vErf(valDist - vCenter)); + + Vc::float_m mask; + // Mask in the inner circe of the mask + mask = fullFade < vZero; + fullFade.setZero(mask); + + // Mask (value - value), presicion errors. + mask = fullFade >= vValMax; + Vc::float_v vFade = (vValMax - fullFade) / vValMax; + vFade(mask) = vZero; + + // filter nan and inf. Vc uses float, imprecission errors are frequent + mask = Vc::isfinite(vFade); + vFade(!mask) = vOne; + + // Mask the inner circe of the mask + mask = vFade < vZero; + vFade(mask) = vZero; + + // return original dist values before vFade transform + vFade(excludeMask) = dist; + vFade.store(bufferPointer, Vc::Aligned); + + } else { + dist.store(bufferPointer, Vc::Aligned); + } + currentIndices = currentIndices + increment; + + bufferPointer += Vc::float_v::size(); + } +} + #endif /* defined HAVE_VC */ diff --git a/libs/image/kis_circle_mask_generator.cpp b/libs/image/kis_circle_mask_generator.cpp index a27db05731..6c64dcf3c1 100644 --- a/libs/image/kis_circle_mask_generator.cpp +++ b/libs/image/kis_circle_mask_generator.cpp @@ -1,141 +1,146 @@ /* * Copyright (c) 2004,2007-2009 Cyrille Berger * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2012 Sven Langkamp * * 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 #include #ifdef HAVE_VC #if defined(__clang__) #pragma GCC diagnostic ignored "-Wundef" #pragma GCC diagnostic ignored "-Wlocal-type-template-args" #endif #if defined _MSC_VER // Lets shut up the "possible loss of data" and "forcing value to bool 'true' or 'false' #pragma warning ( push ) #pragma warning ( disable : 4244 ) #pragma warning ( disable : 4800 ) #endif #include #include #if defined _MSC_VER #pragma warning ( pop ) #endif #endif #include #include "kis_fast_math.h" #include "kis_circle_mask_generator.h" #include "kis_circle_mask_generator_p.h" #include "kis_base_mask_generator.h" #include "kis_brush_mask_applicator_factories.h" #include "kis_brush_mask_applicator_base.h" KisCircleMaskGenerator::KisCircleMaskGenerator(qreal diameter, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges) : KisMaskGenerator(diameter, ratio, fh, fv, spikes, antialiasEdges, CIRCLE, DefaultId), d(new Private) { setScale(1.0, 1.0); // store the variable locally to allow vector implementation read it easily d->copyOfAntialiasEdges = antialiasEdges; d->applicator.reset(createOptimizedClass >(this)); } KisCircleMaskGenerator::KisCircleMaskGenerator(const KisCircleMaskGenerator &rhs) : KisMaskGenerator(rhs), d(new Private(*rhs.d)) { d->applicator.reset(createOptimizedClass >(this)); } KisMaskGenerator* KisCircleMaskGenerator::clone() const { return new KisCircleMaskGenerator(*this); } void KisCircleMaskGenerator::setScale(qreal scaleX, qreal scaleY) { KisMaskGenerator::setScale(scaleX, scaleY); d->xcoef = 2.0 / effectiveSrcWidth(); d->ycoef = 2.0 / effectiveSrcHeight(); d->xfadecoef = (horizontalFade() == 0) ? 1 : (2.0 / (horizontalFade() * effectiveSrcWidth())); d->yfadecoef = (verticalFade() == 0) ? 1 : (2.0 / (verticalFade() * effectiveSrcHeight())); d->transformedFadeX = KisMaskGenerator::softness() * d->xfadecoef; d->transformedFadeY = KisMaskGenerator::softness() * d->yfadecoef; d->noFading = !d->copyOfAntialiasEdges && qFuzzyCompare(d->xcoef, d->transformedFadeX) && qFuzzyCompare(d->ycoef, d->transformedFadeY); } KisCircleMaskGenerator::~KisCircleMaskGenerator() { } bool KisCircleMaskGenerator::shouldSupersample() const { return effectiveSrcWidth() < 10 || effectiveSrcHeight() < 10; } bool KisCircleMaskGenerator::shouldVectorize() const { return !shouldSupersample() && spikes() == 2; } KisBrushMaskApplicatorBase* KisCircleMaskGenerator::applicator() { return d->applicator.data(); } quint8 KisCircleMaskGenerator::valueAt(qreal x, qreal y) const { if (isEmpty()) return 255; qreal xr = (x /*- m_xcenter*/); qreal yr = qAbs(y /*- m_ycenter*/); fixRotation(xr, yr); qreal n = norme(xr * d->xcoef, yr * d->ycoef); if (n > 1.0) return 255; // we add +1.0 to ensure correct antialiasing on the border if (antialiasEdges()) { xr = qAbs(xr) + 1.0; yr = qAbs(yr) + 1.0; } qreal nf = norme(xr * d->transformedFadeX, yr * d->transformedFadeY); if (nf < 1.0) return 0; return 255 * n * (nf - 1.0) / (nf - n); } void KisCircleMaskGenerator::setSoftness(qreal softness) { KisMaskGenerator::setSoftness(softness); qreal safeSoftnessCoeff = qreal(1.0) / qMax(qreal(0.01), softness); d->transformedFadeX = d->xfadecoef * safeSoftnessCoeff; d->transformedFadeY = d->yfadecoef * safeSoftnessCoeff; } + +void KisCircleMaskGenerator::resetMaskApplicator(bool forceScalar) +{ + d->applicator.reset(createOptimizedClass >(this,forceScalar)); +} diff --git a/libs/image/kis_circle_mask_generator.h b/libs/image/kis_circle_mask_generator.h index c6aaa04143..0fdb389d9a 100644 --- a/libs/image/kis_circle_mask_generator.h +++ b/libs/image/kis_circle_mask_generator.h @@ -1,63 +1,65 @@ /* * Copyright (c) 2008-2009 Cyrille Berger * * 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_CIRCLE_MASK_GENERATOR_H_ #define _KIS_CIRCLE_MASK_GENERATOR_H_ #include "kritaimage_export.h" #include "kis_mask_generator.h" #include /** * Create, serialize and deserialize an elliptical 8-bit mask. */ class KRITAIMAGE_EXPORT KisCircleMaskGenerator : public KisMaskGenerator { public: struct FastRowProcessor; public: KisCircleMaskGenerator(qreal radius, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges); KisCircleMaskGenerator(const KisCircleMaskGenerator &rhs); ~KisCircleMaskGenerator() override; KisMaskGenerator* clone() const override; quint8 valueAt(qreal x, qreal y) const override; bool shouldSupersample() const override; bool shouldVectorize() const override; KisBrushMaskApplicatorBase* applicator() override; void setSoftness(qreal softness) override; void setScale(qreal scaleX, qreal scaleY) override; + void resetMaskApplicator(bool forceScalar); + private: qreal norme(qreal a, qreal b) const { return a*a + b * b; } private: struct Private; const QScopedPointer d; }; #endif diff --git a/libs/image/kis_edge_detection_kernel.h b/libs/image/kis_edge_detection_kernel.h index 334e4afc7b..b6630da26f 100644 --- a/libs/image/kis_edge_detection_kernel.h +++ b/libs/image/kis_edge_detection_kernel.h @@ -1,129 +1,129 @@ /* * Copyright (c) 2017 Wolthera van Hövell tot Westerflier * * 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_EDGE_DETECTION_KERNEL_H #define KIS_EDGE_DETECTION_KERNEL_H #include "kritaimage_export.h" #include "kis_types.h" #include class QRect; class KRITAIMAGE_EXPORT KisEdgeDetectionKernel { public: KisEdgeDetectionKernel(); enum FilterType { Simple, //A weird simple method used in our old sobel filter Prewit, //The simpler prewitt detection, which doesn't smooth. - SobolVector //Sobol does smooth. The creation of bigger kernels is based on an approach regarding vectors. + SobelVector //Sobel does smooth. The creation of bigger kernels is based on an approach regarding vectors. }; enum FilterOutput { pythagorean, xGrowth, xFall, yGrowth, yFall, radian }; /** * @brief createHorizontalMatrix * @param radius the radius. 1 makes a 3x3 kernel. * @param type One of the entries in the enum Filtertype * @param reverse which direction the gradient goes. * The horizontal gradient by default detects the rightmost edges. * Reversed it selects the leftmost edges. * @return */ static Eigen::Matrix createHorizontalMatrix(qreal radius, FilterType type, bool reverse = false); /** * @brief createVerticalMatrix * @param radius the radius. 1 makes a 3x3 kernel. * @param type One of the entries in the enum Filtertype * @param reverse which direction the gradient goes. * The vertical gradient by default detects the topmost edges. * Reversed it selects the bottommost edges. * @return */ static Eigen::Matrix createVerticalMatrix(qreal radius, FilterType type, bool reverse = false); static KisConvolutionKernelSP createHorizontalKernel(qreal radius, FilterType type, bool denormalize = true, bool reverse = false); static KisConvolutionKernelSP createVerticalKernel(qreal radius, FilterType type, bool denormalize = true, bool reverse = false); static int kernelSizeFromRadius(qreal radius); static qreal sigmaFromRadius(qreal radius); /** * @brief applyEdgeDetection * This applies the edge detection filter to the device. * @param device the device to apply to. * @param rect the affected rect. * @param xRadius the radius of the horizontal sampling, radius of 0 is effectively disabling it. * @param yRadius the radius of the vertical sampling, refius of 0 is effectively disabling it. - * @param type the type can be prewitt, sobol or simple, each of which + * @param type the type can be prewitt, sobel or simple, each of which * have a different sampling for the eventual edge detection. * @param channelFlags the affected channels. * @param progressUpdater the progress updater if it exists. * @param writeToAlpha whether or not to have the result applied to the transparency than the color channels, * this is useful for fringe effects. */ static void applyEdgeDetection(KisPaintDeviceSP device, const QRect& rect, qreal xRadius, qreal yRadius, FilterType type, const QBitArray &channelFlags, KoUpdater *progressUpdater, FilterOutput output = pythagorean, bool writeToAlpha = false); /** * @brief converToNormalMap * Conver a channel of the device to a normal map. The channel will be interpretted as a heightmap. * @param device the device * @param rect the rectangle to apply this to. * @param xRadius the xradius * @param yRadius the yradius * @param type the edge detection filter. * @param channelToConvert the channel to use as a grayscale. * @param channelOrder the order in which the xyz coordinates ought to be written to the pixels. * @param channelFlags * @param progressUpdater */ static void convertToNormalMap(KisPaintDeviceSP device, const QRect & rect, qreal xRadius, qreal yRadius, FilterType type, int channelToConvert, QVector channelOrder, QVector channelFlip, const QBitArray &channelFlags, KoUpdater *progressUpdater); }; #endif // KIS_EDGE_DETECTION_KERNEL_H diff --git a/libs/image/kis_gauss_circle_mask_generator.cpp b/libs/image/kis_gauss_circle_mask_generator.cpp index 50977c95f6..91e08a7cd7 100644 --- a/libs/image/kis_gauss_circle_mask_generator.cpp +++ b/libs/image/kis_gauss_circle_mask_generator.cpp @@ -1,127 +1,150 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2011 Geoffry Song * * 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 //MSVC requires that Vc come first #include +#include +#ifdef HAVE_VC +#if defined(__clang__) +#pragma GCC diagnostic ignored "-Wundef" +#pragma GCC diagnostic ignored "-Wlocal-type-template-args" +#endif +#if defined _MSC_VER +// Lets shut up the "possible loss of data" and "forcing value to bool 'true' or 'false' +#pragma warning ( push ) +#pragma warning ( disable : 4244 ) +#pragma warning ( disable : 4800 ) +#endif +#include +#include +#if defined _MSC_VER +#pragma warning ( pop ) +#endif +#endif + #include #include #include #include #include "kis_fast_math.h" #include "kis_base_mask_generator.h" -#include "kis_gauss_circle_mask_generator.h" #include "kis_antialiasing_fade_maker.h" +#include "kis_brush_mask_applicator_factories.h" +#include "kis_brush_mask_applicator_base.h" +#include "kis_gauss_circle_mask_generator.h" +#include "kis_gauss_circle_mask_generator_p.h" #define M_SQRT_2 1.41421356237309504880 #ifdef Q_OS_WIN // on windows we get our erf() from boost #include #define erf(x) boost::math::erf(x) #endif -struct Q_DECL_HIDDEN KisGaussCircleMaskGenerator::Private -{ - Private(bool enableAntialiasing) - : fadeMaker(*this, enableAntialiasing) - { - } - - Private(const Private &rhs) - : ycoef(rhs.ycoef), - fade(rhs.fade), - center(rhs.center), - distfactor(rhs.distfactor), - alphafactor(rhs.alphafactor), - fadeMaker(rhs.fadeMaker, *this) - { - } - - qreal ycoef; - qreal fade; - qreal center, distfactor, alphafactor; - KisAntialiasingFadeMaker1D fadeMaker; - - inline quint8 value(qreal dist) const; -}; - KisGaussCircleMaskGenerator::KisGaussCircleMaskGenerator(qreal diameter, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges) : KisMaskGenerator(diameter, ratio, fh, fv, spikes, antialiasEdges, CIRCLE, GaussId), d(new Private(antialiasEdges)) { d->ycoef = 1.0 / ratio; d->fade = 1.0 - (fh + fv) / 2.0; + if (d->fade == 0.0) d->fade = 1e-6; else if (d->fade == 1.0) d->fade = 1.0 - 1e-6; // would become undefined for fade == 0 or 1 + d->center = (2.5 * (6761.0*d->fade-10000.0))/(M_SQRT_2*6761.0*d->fade); d->alphafactor = 255.0 / (2.0 * erf(d->center)); + + d->applicator.reset(createOptimizedClass >(this)); + } KisGaussCircleMaskGenerator::KisGaussCircleMaskGenerator(const KisGaussCircleMaskGenerator &rhs) : KisMaskGenerator(rhs), d(new Private(*rhs.d)) { + d->applicator.reset(createOptimizedClass >(this)); } KisMaskGenerator* KisGaussCircleMaskGenerator::clone() const { return new KisGaussCircleMaskGenerator(*this); } void KisGaussCircleMaskGenerator::setScale(qreal scaleX, qreal scaleY) { KisMaskGenerator::setScale(scaleX, scaleY); d->ycoef = scaleX / (scaleY * ratio()); d->distfactor = M_SQRT_2 * 12500.0 / (6761.0 * d->fade * effectiveSrcWidth() / 2.0); d->fadeMaker.setRadius(0.5 * effectiveSrcWidth()); } KisGaussCircleMaskGenerator::~KisGaussCircleMaskGenerator() { } inline quint8 KisGaussCircleMaskGenerator::Private::value(qreal dist) const { dist *= distfactor; quint8 ret = alphafactor * (erf(dist + center) - erf(dist - center)); return (quint8) 255 - ret; } +bool KisGaussCircleMaskGenerator::shouldSupersample() const +{ + return effectiveSrcWidth() < 10 || effectiveSrcHeight() < 10; +} + +bool KisGaussCircleMaskGenerator::shouldVectorize() const +{ + return !shouldSupersample() && spikes() == 2; +} + +KisBrushMaskApplicatorBase* KisGaussCircleMaskGenerator::applicator() +{ + return d->applicator.data(); +} + quint8 KisGaussCircleMaskGenerator::valueAt(qreal x, qreal y) const { if (isEmpty()) return 255; qreal xr = x; qreal yr = qAbs(y); fixRotation(xr, yr); qreal dist = sqrt(norme(xr, yr * d->ycoef)); quint8 value; if (d->fadeMaker.needFade(dist, &value)) { return value; } return d->value(dist); } + +void KisGaussCircleMaskGenerator::resetMaskApplicator(bool forceScalar) +{ + d->applicator.reset(createOptimizedClass >(this,forceScalar)); +} diff --git a/libs/image/kis_gauss_circle_mask_generator.h b/libs/image/kis_gauss_circle_mask_generator.h index ae6158c9ee..1872641f48 100644 --- a/libs/image/kis_gauss_circle_mask_generator.h +++ b/libs/image/kis_gauss_circle_mask_generator.h @@ -1,55 +1,64 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2011 Geoffry Song * * 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_GAUSS_MASK_GENERATOR_H_ #define _KIS_GAUSS_MASK_GENERATOR_H_ -#include #include "kritaimage_export.h" +#include "kis_mask_generator.h" +#include /** * This mask generator uses a Gaussian-blurred circle */ class KRITAIMAGE_EXPORT KisGaussCircleMaskGenerator : public KisMaskGenerator { - +public: + struct FastRowProcessor; public: KisGaussCircleMaskGenerator(qreal diameter, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges); KisGaussCircleMaskGenerator(const KisGaussCircleMaskGenerator &rhs); ~KisGaussCircleMaskGenerator() override; KisMaskGenerator* clone() const override; quint8 valueAt(qreal x, qreal y) const override; void setScale(qreal scaleX, qreal scaleY) override; + bool shouldSupersample() const override; + + bool shouldVectorize() const override; + KisBrushMaskApplicatorBase* applicator() override; + + void resetMaskApplicator(bool forceScalar); + private: qreal norme(qreal a, qreal b) const { return a*a + b*b; } private: struct Private; const QScopedPointer d; }; #endif diff --git a/libs/image/kis_gauss_circle_mask_generator_p.h b/libs/image/kis_gauss_circle_mask_generator_p.h new file mode 100644 index 0000000000..663bc2827f --- /dev/null +++ b/libs/image/kis_gauss_circle_mask_generator_p.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2008-2009 Cyrille Berger + * + * 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_GAUSS_MASK_GENERATOR_P_H_ +#define _KIS_GAUSS_MASK_GENERATOR_P_H_ + +#include "kis_antialiasing_fade_maker.h" + +struct Q_DECL_HIDDEN KisGaussCircleMaskGenerator::Private +{ + Private(bool enableAntialiasing) + : fadeMaker(*this, enableAntialiasing) + { + } + + Private(const Private &rhs) + : ycoef(rhs.ycoef), + fade(rhs.fade), + center(rhs.center), + distfactor(rhs.distfactor), + alphafactor(rhs.alphafactor), + fadeMaker(rhs.fadeMaker, *this) + { + } + + qreal ycoef; + qreal fade; + qreal center; + qreal distfactor; + qreal alphafactor; + KisAntialiasingFadeMaker1D fadeMaker; + + QScopedPointer applicator; + + inline quint8 value(qreal dist) const; + + #if defined HAVE_VC + // vectorized erf function, precision 1e-5 + Vc::float_v vErf(Vc::float_v x) { + Vc::float_v xa = abs(x); + Vc::float_v sign(Vc::One); + Vc::float_m invertMask = x < 0.f; + sign(invertMask) = -1.f; + + // CONSTANTS + float a1 = 0.254829592; + float a2 = -0.284496736; + float a3 = 1.421413741; + float a4 = -1.453152027; + float a5 = 1.061405429; + float p = 0.3275911; + + Vc::float_v t = 1.0f / (1.0f + p * xa); + Vc::float_v y = 1.0f - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * exp(-xa * xa); + return sign * y; + } + #endif /* defined HAVE_VC */ +}; + +#endif /* _KIS_GAUSS_MASK_GENERATOR_P_H_ */ diff --git a/libs/image/kis_image.cc b/libs/image/kis_image.cc index 108ccfec4b..51be3f46df 100644 --- a/libs/image/kis_image.cc +++ b/libs/image/kis_image.cc @@ -1,1773 +1,1778 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2007 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 "kis_image.h" #include // WORDS_BIGENDIAN #include #include #include #include #include #include #include #include #include #include #include "KoColorSpaceRegistry.h" #include "KoColor.h" #include "KoColorProfile.h" #include #include "KisProofingConfiguration.h" #include "recorder/kis_action_recorder.h" #include "kis_adjustment_layer.h" #include "kis_annotation.h" #include "kis_change_profile_visitor.h" #include "kis_colorspace_convert_visitor.h" #include "kis_count_visitor.h" #include "kis_filter_strategy.h" #include "kis_group_layer.h" #include "commands/kis_image_commands.h" #include "kis_layer.h" #include "kis_meta_data_merge_strategy_registry.h" #include "kis_name_server.h" #include "kis_paint_layer.h" +#include "kis_projection_leaf.h" #include "kis_painter.h" #include "kis_selection.h" #include "kis_transaction.h" #include "kis_meta_data_merge_strategy.h" #include "kis_memory_statistics_server.h" #include "kis_image_config.h" #include "kis_update_scheduler.h" #include "kis_image_signal_router.h" #include "kis_image_animation_interface.h" #include "kis_stroke_strategy.h" #include "kis_simple_stroke_strategy.h" #include "kis_image_barrier_locker.h" #include "kis_undo_stores.h" #include "kis_legacy_undo_adapter.h" #include "kis_post_execution_undo_adapter.h" #include "kis_transform_worker.h" #include "kis_processing_applicator.h" #include "processing/kis_crop_processing_visitor.h" #include "processing/kis_crop_selections_processing_visitor.h" #include "processing/kis_transform_processing_visitor.h" #include "commands_new/kis_image_resize_command.h" #include "commands_new/kis_image_set_resolution_command.h" #include "commands_new/kis_activate_selection_mask_command.h" #include "kis_composite_progress_proxy.h" #include "kis_layer_composition.h" #include "kis_wrapped_rect.h" #include "kis_crop_saved_extra_data.h" #include "kis_layer_utils.h" #include "kis_lod_transform.h" #include "kis_suspend_projection_updates_stroke_strategy.h" #include "kis_sync_lod_cache_stroke_strategy.h" #include "kis_projection_updates_filter.h" #include "kis_layer_projection_plane.h" #include "kis_update_time_monitor.h" #include "kis_image_barrier_locker.h" #include #include #include "kis_time_range.h" // #define SANITY_CHECKS #ifdef SANITY_CHECKS #define SANITY_CHECK_LOCKED(name) \ if (!locked()) warnKrita() << "Locking policy failed:" << name \ << "has been called without the image" \ "being locked"; #else #define SANITY_CHECK_LOCKED(name) #endif struct KisImageSPStaticRegistrar { KisImageSPStaticRegistrar() { qRegisterMetaType("KisImageSP"); } }; static KisImageSPStaticRegistrar __registrar; class KisImage::KisImagePrivate { public: KisImagePrivate(KisImage *_q, qint32 w, qint32 h, const KoColorSpace *c, KisUndoStore *undo, KisImageAnimationInterface *_animationInterface) : q(_q) , lockedForReadOnly(false) , width(w) , height(h) , colorSpace(c ? c : KoColorSpaceRegistry::instance()->rgb8()) , nserver(1) , undoStore(undo ? undo : new KisDumbUndoStore()) , legacyUndoAdapter(undoStore.data(), _q) , postExecutionUndoAdapter(undoStore.data(), _q) , recorder(_q) , signalRouter(_q) , animationInterface(_animationInterface) , scheduler(_q, _q) , axesCenter(QPointF(0.5, 0.5)) { { KisImageConfig cfg; if (cfg.enableProgressReporting()) { scheduler.setProgressProxy(&compositeProgressProxy); } // Each of these lambdas defines a new factory function. scheduler.setLod0ToNStrokeStrategyFactory( [=](bool forgettable) { return KisLodSyncPair( new KisSyncLodCacheStrokeStrategy(KisImageWSP(q), forgettable), KisSyncLodCacheStrokeStrategy::createJobsData(KisImageWSP(q))); }); scheduler.setSuspendUpdatesStrokeStrategyFactory( [=]() { return KisSuspendResumePair( new KisSuspendProjectionUpdatesStrokeStrategy(KisImageWSP(q), true), KisSuspendProjectionUpdatesStrokeStrategy::createSuspendJobsData(KisImageWSP(q))); }); scheduler.setResumeUpdatesStrokeStrategyFactory( [=]() { return KisSuspendResumePair( new KisSuspendProjectionUpdatesStrokeStrategy(KisImageWSP(q), false), KisSuspendProjectionUpdatesStrokeStrategy::createResumeJobsData(KisImageWSP(q))); }); } connect(q, SIGNAL(sigImageModified()), KisMemoryStatisticsServer::instance(), SLOT(notifyImageChanged())); } ~KisImagePrivate() { /** * Stop animation interface. It may use the rootLayer. */ delete animationInterface; /** * First delete the nodes, while strokes * and undo are still alive */ rootLayer.clear(); } KisImage *q; quint32 lockCount = 0; bool lockedForReadOnly; qint32 width; qint32 height; double xres = 1.0; double yres = 1.0; const KoColorSpace * colorSpace; KisProofingConfigurationSP proofingConfig; KisSelectionSP deselectedGlobalSelection; KisGroupLayerSP rootLayer; // The layers are contained in here QList compositions; KisNodeSP isolatedRootNode; bool wrapAroundModePermitted = false; KisNameServer nserver; QScopedPointer undoStore; KisLegacyUndoAdapter legacyUndoAdapter; KisPostExecutionUndoAdapter postExecutionUndoAdapter; KisActionRecorder recorder; vKisAnnotationSP annotations; QAtomicInt disableUIUpdateSignals; KisProjectionUpdatesFilterSP projectionUpdatesFilter; KisImageSignalRouter signalRouter; KisImageAnimationInterface *animationInterface; KisUpdateScheduler scheduler; QAtomicInt disableDirtyRequests; KisCompositeProgressProxy compositeProgressProxy; bool blockLevelOfDetail = false; QPointF axesCenter; bool tryCancelCurrentStrokeAsync(); void notifyProjectionUpdatedInPatches(const QRect &rc); }; KisImage::KisImage(KisUndoStore *undoStore, qint32 width, qint32 height, const KoColorSpace * colorSpace, const QString& name) : QObject(0) , KisShared() , m_d(new KisImagePrivate(this, width, height, colorSpace, undoStore, new KisImageAnimationInterface(this))) { // make sure KisImage belongs to the GUI thread moveToThread(qApp->thread()); connect(this, SIGNAL(sigInternalStopIsolatedModeRequested()), SLOT(stopIsolatedMode())); setObjectName(name); setRootLayer(new KisGroupLayer(this, "root", OPACITY_OPAQUE_U8)); } KisImage::~KisImage() { dbgImage << "deleting kisimage" << objectName(); /** * Request the tools to end currently running strokes */ waitForDone(); delete m_d; disconnect(); // in case Qt gets confused } KisImage *KisImage::clone(bool exactCopy) { return new KisImage(*this, 0, exactCopy); } KisImage::KisImage(const KisImage& rhs, KisUndoStore *undoStore, bool exactCopy) : KisNodeFacade(), KisNodeGraphListener(), KisShared(), m_d(new KisImagePrivate(this, rhs.width(), rhs.height(), rhs.colorSpace(), undoStore ? undoStore : new KisDumbUndoStore(), new KisImageAnimationInterface(*rhs.animationInterface(), this))) { // make sure KisImage belongs to the GUI thread moveToThread(qApp->thread()); connect(this, SIGNAL(sigInternalStopIsolatedModeRequested()), SLOT(stopIsolatedMode())); setObjectName(rhs.objectName()); m_d->xres = rhs.m_d->xres; m_d->yres = rhs.m_d->yres; if (rhs.m_d->proofingConfig) { m_d->proofingConfig = toQShared(new KisProofingConfiguration(*rhs.m_d->proofingConfig)); } KisNodeSP newRoot = rhs.root()->clone(); newRoot->setGraphListener(this); newRoot->setImage(this); m_d->rootLayer = dynamic_cast(newRoot.data()); setRoot(newRoot); if (exactCopy || rhs.m_d->isolatedRootNode) { QQueue linearizedNodes; KisLayerUtils::recursiveApplyNodes(rhs.root(), [&linearizedNodes](KisNodeSP node) { linearizedNodes.enqueue(node); }); KisLayerUtils::recursiveApplyNodes(newRoot, [&linearizedNodes, exactCopy, &rhs, this](KisNodeSP node) { KisNodeSP refNode = linearizedNodes.dequeue(); if (exactCopy) { node->setUuid(refNode->uuid()); } if (rhs.m_d->isolatedRootNode && rhs.m_d->isolatedRootNode == refNode) { m_d->isolatedRootNode = node; } }); } Q_FOREACH (KisLayerCompositionSP comp, rhs.m_d->compositions) { m_d->compositions << toQShared(new KisLayerComposition(*comp, this)); } rhs.m_d->nserver = KisNameServer(rhs.m_d->nserver); vKisAnnotationSP newAnnotations; Q_FOREACH (KisAnnotationSP annotation, rhs.m_d->annotations) { newAnnotations << annotation->clone(); } m_d->annotations = newAnnotations; KIS_ASSERT_RECOVER_NOOP(!rhs.m_d->projectionUpdatesFilter); KIS_ASSERT_RECOVER_NOOP(!rhs.m_d->disableUIUpdateSignals); KIS_ASSERT_RECOVER_NOOP(!rhs.m_d->disableDirtyRequests); m_d->blockLevelOfDetail = rhs.m_d->blockLevelOfDetail; } void KisImage::aboutToAddANode(KisNode *parent, int index) { KisNodeGraphListener::aboutToAddANode(parent, index); SANITY_CHECK_LOCKED("aboutToAddANode"); } void KisImage::nodeHasBeenAdded(KisNode *parent, int index) { KisNodeGraphListener::nodeHasBeenAdded(parent, index); SANITY_CHECK_LOCKED("nodeHasBeenAdded"); m_d->signalRouter.emitNodeHasBeenAdded(parent, index); KisNodeSP newNode = parent->at(index); if (!dynamic_cast(newNode.data())) { emit sigInternalStopIsolatedModeRequested(); } } void KisImage::aboutToRemoveANode(KisNode *parent, int index) { KisNodeSP deletedNode = parent->at(index); if (!dynamic_cast(deletedNode.data())) { emit sigInternalStopIsolatedModeRequested(); } KisNodeGraphListener::aboutToRemoveANode(parent, index); SANITY_CHECK_LOCKED("aboutToRemoveANode"); m_d->signalRouter.emitAboutToRemoveANode(parent, index); } void KisImage::nodeChanged(KisNode* node) { KisNodeGraphListener::nodeChanged(node); requestStrokeEnd(); m_d->signalRouter.emitNodeChanged(node); } void KisImage::invalidateAllFrames() { invalidateFrames(KisTimeRange::infinite(0), QRect()); } KisSelectionSP KisImage::globalSelection() const { KisSelectionMaskSP selectionMask = m_d->rootLayer->selectionMask(); if (selectionMask) { return selectionMask->selection(); } else { return 0; } } void KisImage::setGlobalSelection(KisSelectionSP globalSelection) { KisSelectionMaskSP selectionMask = m_d->rootLayer->selectionMask(); if (!globalSelection) { if (selectionMask) { removeNode(selectionMask); } } else { if (!selectionMask) { selectionMask = new KisSelectionMask(this); selectionMask->initSelection(m_d->rootLayer); addNode(selectionMask); // If we do not set the selection now, the setActive call coming next // can be very, very expensive, depending on the size of the image. selectionMask->setSelection(globalSelection); selectionMask->setActive(true); } else { selectionMask->setSelection(globalSelection); } Q_ASSERT(m_d->rootLayer->childCount() > 0); Q_ASSERT(m_d->rootLayer->selectionMask()); } m_d->deselectedGlobalSelection = 0; m_d->legacyUndoAdapter.emitSelectionChanged(); } void KisImage::deselectGlobalSelection() { KisSelectionSP savedSelection = globalSelection(); setGlobalSelection(0); m_d->deselectedGlobalSelection = savedSelection; } bool KisImage::canReselectGlobalSelection() { return m_d->deselectedGlobalSelection; } void KisImage::reselectGlobalSelection() { if(m_d->deselectedGlobalSelection) { setGlobalSelection(m_d->deselectedGlobalSelection); } } QString KisImage::nextLayerName(const QString &_baseName) const { QString baseName = _baseName; if (m_d->nserver.currentSeed() == 0) { m_d->nserver.number(); return i18n("background"); } if (baseName.isEmpty()) { baseName = i18n("Layer"); } return QString("%1 %2").arg(baseName).arg(m_d->nserver.number()); } void KisImage::rollBackLayerName() { m_d->nserver.rollback(); } KisCompositeProgressProxy* KisImage::compositeProgressProxy() { return &m_d->compositeProgressProxy; } bool KisImage::locked() const { return m_d->lockCount != 0; } void KisImage::barrierLock(bool readOnly) { if (!locked()) { requestStrokeEnd(); m_d->scheduler.barrierLock(); m_d->lockedForReadOnly = readOnly; } else { m_d->lockedForReadOnly &= readOnly; } m_d->lockCount++; } bool KisImage::tryBarrierLock(bool readOnly) { bool result = true; if (!locked()) { result = m_d->scheduler.tryBarrierLock(); m_d->lockedForReadOnly = readOnly; } if (result) { m_d->lockCount++; m_d->lockedForReadOnly &= readOnly; } return result; } bool KisImage::isIdle(bool allowLocked) { return (allowLocked || !locked()) && m_d->scheduler.isIdle(); } void KisImage::lock() { if (!locked()) { requestStrokeEnd(); m_d->scheduler.lock(); } m_d->lockCount++; m_d->lockedForReadOnly = false; } void KisImage::unlock() { Q_ASSERT(locked()); if (locked()) { m_d->lockCount--; if (m_d->lockCount == 0) { m_d->scheduler.unlock(!m_d->lockedForReadOnly); } } } void KisImage::blockUpdates() { m_d->scheduler.blockUpdates(); } void KisImage::unblockUpdates() { m_d->scheduler.unblockUpdates(); } void KisImage::setSize(const QSize& size) { m_d->width = size.width(); m_d->height = size.height(); } void KisImage::resizeImageImpl(const QRect& newRect, bool cropLayers) { if (newRect == bounds() && !cropLayers) return; KUndo2MagicString actionName = cropLayers ? kundo2_i18n("Crop Image") : kundo2_i18n("Resize Image"); KisImageSignalVector emitSignals; emitSignals << ComplexSizeChangedSignal(newRect, newRect.size()); emitSignals << ModifiedSignal; KisCropSavedExtraData *extraData = new KisCropSavedExtraData(cropLayers ? KisCropSavedExtraData::CROP_IMAGE : KisCropSavedExtraData::RESIZE_IMAGE, newRect); KisProcessingApplicator applicator(this, m_d->rootLayer, KisProcessingApplicator::RECURSIVE | KisProcessingApplicator::NO_UI_UPDATES, emitSignals, actionName, extraData); if (cropLayers || !newRect.topLeft().isNull()) { KisProcessingVisitorSP visitor = new KisCropProcessingVisitor(newRect, cropLayers, true); applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT); } applicator.applyCommand(new KisImageResizeCommand(this, newRect.size())); applicator.end(); } void KisImage::resizeImage(const QRect& newRect) { resizeImageImpl(newRect, false); } void KisImage::cropImage(const QRect& newRect) { resizeImageImpl(newRect, true); } void KisImage::cropNode(KisNodeSP node, const QRect& newRect) { bool isLayer = qobject_cast(node.data()); KUndo2MagicString actionName = isLayer ? kundo2_i18n("Crop Layer") : kundo2_i18n("Crop Mask"); KisImageSignalVector emitSignals; emitSignals << ModifiedSignal; KisCropSavedExtraData *extraData = new KisCropSavedExtraData(KisCropSavedExtraData::CROP_LAYER, newRect, node); KisProcessingApplicator applicator(this, node, KisProcessingApplicator::RECURSIVE, emitSignals, actionName, extraData); KisProcessingVisitorSP visitor = new KisCropProcessingVisitor(newRect, true, false); applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT); applicator.end(); } void KisImage::scaleImage(const QSize &size, qreal xres, qreal yres, KisFilterStrategy *filterStrategy) { bool resolutionChanged = xres != xRes() && yres != yRes(); bool sizeChanged = size != this->size(); if (!resolutionChanged && !sizeChanged) return; KisImageSignalVector emitSignals; if (resolutionChanged) emitSignals << ResolutionChangedSignal; if (sizeChanged) emitSignals << ComplexSizeChangedSignal(bounds(), size); emitSignals << ModifiedSignal; KUndo2MagicString actionName = sizeChanged ? kundo2_i18n("Scale Image") : kundo2_i18n("Change Image Resolution"); KisProcessingApplicator::ProcessingFlags signalFlags = (resolutionChanged || sizeChanged) ? KisProcessingApplicator::NO_UI_UPDATES : KisProcessingApplicator::NONE; KisProcessingApplicator applicator(this, m_d->rootLayer, KisProcessingApplicator::RECURSIVE | signalFlags, emitSignals, actionName); qreal sx = qreal(size.width()) / this->size().width(); qreal sy = qreal(size.height()) / this->size().height(); QTransform shapesCorrection; if (resolutionChanged) { shapesCorrection = QTransform::fromScale(xRes() / xres, yRes() / yres); } KisProcessingVisitorSP visitor = new KisTransformProcessingVisitor(sx, sy, 0, 0, QPointF(), 0, 0, 0, filterStrategy, shapesCorrection); applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT); if (resolutionChanged) { KUndo2Command *parent = new KisResetShapesCommand(m_d->rootLayer); new KisImageSetResolutionCommand(this, xres, yres, parent); applicator.applyCommand(parent); } if (sizeChanged) { applicator.applyCommand(new KisImageResizeCommand(this, size)); } applicator.end(); } void KisImage::scaleNode(KisNodeSP node, qreal scaleX, qreal scaleY, KisFilterStrategy *filterStrategy) { KUndo2MagicString actionName(kundo2_i18n("Scale Layer")); KisImageSignalVector emitSignals; emitSignals << ModifiedSignal; KisProcessingApplicator applicator(this, node, KisProcessingApplicator::RECURSIVE, emitSignals, actionName); KisProcessingVisitorSP visitor = new KisTransformProcessingVisitor(scaleX, scaleY, 0, 0, QPointF(), 0, 0, 0, filterStrategy); applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT); applicator.end(); } void KisImage::rotateImpl(const KUndo2MagicString &actionName, KisNodeSP rootNode, bool resizeImage, double radians) { QPointF offset; QSize newSize; { KisTransformWorker worker(0, 1.0, 1.0, 0, 0, 0, 0, radians, 0, 0, 0, 0); QTransform transform = worker.transform(); if (resizeImage) { QRect newRect = transform.mapRect(bounds()); newSize = newRect.size(); offset = -newRect.topLeft(); } else { QPointF origin = QRectF(rootNode->exactBounds()).center(); newSize = size(); offset = -(transform.map(origin) - origin); } } bool sizeChanged = resizeImage && (newSize.width() != width() || newSize.height() != height()); // These signals will be emitted after processing is done KisImageSignalVector emitSignals; if (sizeChanged) emitSignals << ComplexSizeChangedSignal(bounds(), newSize); emitSignals << ModifiedSignal; // These flags determine whether updates are transferred to the UI during processing KisProcessingApplicator::ProcessingFlags signalFlags = sizeChanged ? KisProcessingApplicator::NO_UI_UPDATES : KisProcessingApplicator::NONE; KisProcessingApplicator applicator(this, rootNode, KisProcessingApplicator::RECURSIVE | signalFlags, emitSignals, actionName); KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->value("Bicubic"); KisProcessingVisitorSP visitor = new KisTransformProcessingVisitor(1.0, 1.0, 0.0, 0.0, QPointF(), radians, offset.x(), offset.y(), filter); applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT); if (sizeChanged) { applicator.applyCommand(new KisImageResizeCommand(this, newSize)); } applicator.end(); } void KisImage::rotateImage(double radians) { rotateImpl(kundo2_i18n("Rotate Image"), root(), true, radians); } void KisImage::rotateNode(KisNodeSP node, double radians) { if (node->inherits("KisMask")) { rotateImpl(kundo2_i18n("Rotate Mask"), node, false, radians); } else { rotateImpl(kundo2_i18n("Rotate Layer"), node, false, radians); } } void KisImage::shearImpl(const KUndo2MagicString &actionName, KisNodeSP rootNode, bool resizeImage, double angleX, double angleY, const QPointF &origin) { //angleX, angleY are in degrees const qreal pi = 3.1415926535897932385; const qreal deg2rad = pi / 180.0; qreal tanX = tan(angleX * deg2rad); qreal tanY = tan(angleY * deg2rad); QPointF offset; QSize newSize; { KisTransformWorker worker(0, 1.0, 1.0, tanX, tanY, origin.x(), origin.y(), 0, 0, 0, 0, 0); QRect newRect = worker.transform().mapRect(bounds()); newSize = newRect.size(); if (resizeImage) offset = -newRect.topLeft(); } if (newSize == size()) return; KisImageSignalVector emitSignals; if (resizeImage) emitSignals << ComplexSizeChangedSignal(bounds(), newSize); emitSignals << ModifiedSignal; KisProcessingApplicator::ProcessingFlags signalFlags = KisProcessingApplicator::RECURSIVE; if (resizeImage) signalFlags |= KisProcessingApplicator::NO_UI_UPDATES; KisProcessingApplicator applicator(this, rootNode, signalFlags, emitSignals, actionName); KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->value("Bilinear"); KisProcessingVisitorSP visitor = new KisTransformProcessingVisitor(1.0, 1.0, tanX, tanY, origin, 0, offset.x(), offset.y(), filter); applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT); if (resizeImage) { applicator.applyCommand(new KisImageResizeCommand(this, newSize)); } applicator.end(); } void KisImage::shearNode(KisNodeSP node, double angleX, double angleY) { QPointF shearOrigin = QRectF(bounds()).center(); if (node->inherits("KisMask")) { shearImpl(kundo2_i18n("Shear Mask"), node, false, angleX, angleY, shearOrigin); } else { shearImpl(kundo2_i18n("Shear Layer"), node, false, angleX, angleY, shearOrigin); } } void KisImage::shear(double angleX, double angleY) { shearImpl(kundo2_i18n("Shear Image"), m_d->rootLayer, true, angleX, angleY, QPointF()); } void KisImage::convertImageColorSpace(const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { if (!dstColorSpace) return; const KoColorSpace *srcColorSpace = m_d->colorSpace; undoAdapter()->beginMacro(kundo2_i18n("Convert Image Color Space")); undoAdapter()->addCommand(new KisImageLockCommand(KisImageWSP(this), true)); undoAdapter()->addCommand(new KisImageSetProjectionColorSpaceCommand(KisImageWSP(this), dstColorSpace)); KisColorSpaceConvertVisitor visitor(this, srcColorSpace, dstColorSpace, renderingIntent, conversionFlags); m_d->rootLayer->accept(visitor); undoAdapter()->addCommand(new KisImageLockCommand(KisImageWSP(this), false)); undoAdapter()->endMacro(); setModified(); } bool KisImage::assignImageProfile(const KoColorProfile *profile) { if (!profile) return false; const KoColorSpace *dstCs = KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile); const KoColorSpace *srcCs = colorSpace(); if (!dstCs) return false; m_d->colorSpace = dstCs; KisChangeProfileVisitor visitor(srcCs, dstCs); bool retval = m_d->rootLayer->accept(visitor); m_d->signalRouter.emitNotification(ProfileChangedSignal); return retval; } void KisImage::convertProjectionColorSpace(const KoColorSpace *dstColorSpace) { if (*m_d->colorSpace == *dstColorSpace) return; undoAdapter()->beginMacro(kundo2_i18n("Convert Projection Color Space")); undoAdapter()->addCommand(new KisImageLockCommand(KisImageWSP(this), true)); undoAdapter()->addCommand(new KisImageSetProjectionColorSpaceCommand(KisImageWSP(this), dstColorSpace)); undoAdapter()->addCommand(new KisImageLockCommand(KisImageWSP(this), false)); undoAdapter()->endMacro(); setModified(); } void KisImage::setProjectionColorSpace(const KoColorSpace * colorSpace) { m_d->colorSpace = colorSpace; m_d->rootLayer->resetCache(); m_d->signalRouter.emitNotification(ColorSpaceChangedSignal); } const KoColorSpace * KisImage::colorSpace() const { return m_d->colorSpace; } const KoColorProfile * KisImage::profile() const { return colorSpace()->profile(); } double KisImage::xRes() const { return m_d->xres; } double KisImage::yRes() const { return m_d->yres; } void KisImage::setResolution(double xres, double yres) { m_d->xres = xres; m_d->yres = yres; m_d->signalRouter.emitNotification(ResolutionChangedSignal); } QPointF KisImage::documentToPixel(const QPointF &documentCoord) const { return QPointF(documentCoord.x() * xRes(), documentCoord.y() * yRes()); } QPoint KisImage::documentToImagePixelFloored(const QPointF &documentCoord) const { QPointF pixelCoord = documentToPixel(documentCoord); return QPoint(qFloor(pixelCoord.x()), qFloor(pixelCoord.y())); } QRectF KisImage::documentToPixel(const QRectF &documentRect) const { return QRectF(documentToPixel(documentRect.topLeft()), documentToPixel(documentRect.bottomRight())); } QPointF KisImage::pixelToDocument(const QPointF &pixelCoord) const { return QPointF(pixelCoord.x() / xRes(), pixelCoord.y() / yRes()); } QPointF KisImage::pixelToDocument(const QPoint &pixelCoord) const { return QPointF((pixelCoord.x() + 0.5) / xRes(), (pixelCoord.y() + 0.5) / yRes()); } QRectF KisImage::pixelToDocument(const QRectF &pixelCoord) const { return QRectF(pixelToDocument(pixelCoord.topLeft()), pixelToDocument(pixelCoord.bottomRight())); } qint32 KisImage::width() const { return m_d->width; } qint32 KisImage::height() const { return m_d->height; } KisGroupLayerSP KisImage::rootLayer() const { Q_ASSERT(m_d->rootLayer); return m_d->rootLayer; } KisPaintDeviceSP KisImage::projection() const { if (m_d->isolatedRootNode) { return m_d->isolatedRootNode->projection(); } Q_ASSERT(m_d->rootLayer); KisPaintDeviceSP projection = m_d->rootLayer->projection(); Q_ASSERT(projection); return projection; } qint32 KisImage::nlayers() const { QStringList list; list << "KisLayer"; KisCountVisitor visitor(list, KoProperties()); m_d->rootLayer->accept(visitor); return visitor.count(); } qint32 KisImage::nHiddenLayers() const { QStringList list; list << "KisLayer"; KoProperties properties; properties.setProperty("visible", false); KisCountVisitor visitor(list, properties); m_d->rootLayer->accept(visitor); return visitor.count(); } void KisImage::flatten() { KisLayerUtils::flattenImage(this); } void KisImage::mergeMultipleLayers(QList mergedNodes, KisNodeSP putAfter) { if (!KisLayerUtils::tryMergeSelectionMasks(this, mergedNodes, putAfter)) { KisLayerUtils::mergeMultipleLayers(this, mergedNodes, putAfter); } } void KisImage::mergeDown(KisLayerSP layer, const KisMetaData::MergeStrategy* strategy) { KisLayerUtils::mergeDown(this, layer, strategy); } void KisImage::flattenLayer(KisLayerSP layer) { KisLayerUtils::flattenLayer(this, layer); } void KisImage::setModified() { m_d->signalRouter.emitNotification(ModifiedSignal); } QImage KisImage::convertToQImage(QRect imageRect, const KoColorProfile * profile) { qint32 x; qint32 y; qint32 w; qint32 h; imageRect.getRect(&x, &y, &w, &h); return convertToQImage(x, y, w, h, profile); } QImage KisImage::convertToQImage(qint32 x, qint32 y, qint32 w, qint32 h, const KoColorProfile * profile) { KisPaintDeviceSP dev = projection(); if (!dev) return QImage(); QImage image = dev->convertToQImage(const_cast(profile), x, y, w, h, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); return image; } QImage KisImage::convertToQImage(const QSize& scaledImageSize, const KoColorProfile *profile) { if (scaledImageSize.isEmpty()) { return QImage(); } KisPaintDeviceSP dev = new KisPaintDevice(colorSpace()); KisPainter gc; gc.copyAreaOptimized(QPoint(0, 0), projection(), dev, bounds()); gc.end(); double scaleX = qreal(scaledImageSize.width()) / width(); double scaleY = qreal(scaledImageSize.height()) / height(); QPointer updater = new KoDummyUpdater(); KisTransformWorker worker(dev, scaleX, scaleY, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, updater, KisFilterStrategyRegistry::instance()->value("Bicubic")); worker.run(); delete updater; return dev->convertToQImage(profile); } void KisImage::notifyLayersChanged() { m_d->signalRouter.emitNotification(LayersChangedSignal); } QRect KisImage::bounds() const { return QRect(0, 0, width(), height()); } QRect KisImage::effectiveLodBounds() const { QRect boundRect = bounds(); const int lod = currentLevelOfDetail(); if (lod > 0) { KisLodTransform t(lod); boundRect = t.map(boundRect); } return boundRect; } KisPostExecutionUndoAdapter* KisImage::postExecutionUndoAdapter() const { const int lod = currentLevelOfDetail(); return lod > 0 ? m_d->scheduler.lodNPostExecutionUndoAdapter() : &m_d->postExecutionUndoAdapter; } void KisImage::setUndoStore(KisUndoStore *undoStore) { m_d->legacyUndoAdapter.setUndoStore(undoStore); m_d->postExecutionUndoAdapter.setUndoStore(undoStore); m_d->undoStore.reset(undoStore); } KisUndoStore* KisImage::undoStore() { return m_d->undoStore.data(); } KisUndoAdapter* KisImage::undoAdapter() const { return &m_d->legacyUndoAdapter; } KisActionRecorder* KisImage::actionRecorder() const { return &m_d->recorder; } void KisImage::setDefaultProjectionColor(const KoColor &color) { KIS_ASSERT_RECOVER_RETURN(m_d->rootLayer); m_d->rootLayer->setDefaultProjectionColor(color); } KoColor KisImage::defaultProjectionColor() const { KIS_ASSERT_RECOVER(m_d->rootLayer) { return KoColor(Qt::transparent, m_d->colorSpace); } return m_d->rootLayer->defaultProjectionColor(); } void KisImage::setRootLayer(KisGroupLayerSP rootLayer) { emit sigInternalStopIsolatedModeRequested(); KoColor defaultProjectionColor(Qt::transparent, m_d->colorSpace); if (m_d->rootLayer) { m_d->rootLayer->setGraphListener(0); m_d->rootLayer->disconnect(); KisPaintDeviceSP original = m_d->rootLayer->original(); defaultProjectionColor = original->defaultPixel(); } m_d->rootLayer = rootLayer; m_d->rootLayer->disconnect(); m_d->rootLayer->setGraphListener(this); m_d->rootLayer->setImage(this); KisPaintDeviceSP newOriginal = m_d->rootLayer->original(); newOriginal->setDefaultPixel(defaultProjectionColor); setRoot(m_d->rootLayer.data()); } void KisImage::addAnnotation(KisAnnotationSP annotation) { // Find the icc annotation, if there is one vKisAnnotationSP_it it = m_d->annotations.begin(); while (it != m_d->annotations.end()) { if ((*it)->type() == annotation->type()) { *it = annotation; return; } ++it; } m_d->annotations.push_back(annotation); } KisAnnotationSP KisImage::annotation(const QString& type) { vKisAnnotationSP_it it = m_d->annotations.begin(); while (it != m_d->annotations.end()) { if ((*it)->type() == type) { return *it; } ++it; } return KisAnnotationSP(0); } void KisImage::removeAnnotation(const QString& type) { vKisAnnotationSP_it it = m_d->annotations.begin(); while (it != m_d->annotations.end()) { if ((*it)->type() == type) { m_d->annotations.erase(it); return; } ++it; } } vKisAnnotationSP_it KisImage::beginAnnotations() { return m_d->annotations.begin(); } vKisAnnotationSP_it KisImage::endAnnotations() { return m_d->annotations.end(); } void KisImage::notifyAboutToBeDeleted() { emit sigAboutToBeDeleted(); } KisImageSignalRouter* KisImage::signalRouter() { return &m_d->signalRouter; } void KisImage::waitForDone() { requestStrokeEnd(); m_d->scheduler.waitForDone(); } KisStrokeId KisImage::startStroke(KisStrokeStrategy *strokeStrategy) { /** * Ask open strokes to end gracefully. All the strokes clients * (including the one calling this method right now) will get * a notification that they should probably end their strokes. * However this is purely their choice whether to end a stroke * or not. */ if (strokeStrategy->requestsOtherStrokesToEnd()) { requestStrokeEnd(); } /** * Some of the strokes can cancel their work with undoing all the * changes they did to the paint devices. The problem is that undo * stack will know nothing about it. Therefore, just notify it * explicitly */ if (strokeStrategy->clearsRedoOnStart()) { m_d->undoStore->purgeRedoState(); } return m_d->scheduler.startStroke(strokeStrategy); } void KisImage::KisImagePrivate::notifyProjectionUpdatedInPatches(const QRect &rc) { KisImageConfig imageConfig; int patchWidth = imageConfig.updatePatchWidth(); int patchHeight = imageConfig.updatePatchHeight(); for (int y = 0; y < rc.height(); y += patchHeight) { for (int x = 0; x < rc.width(); x += patchWidth) { QRect patchRect(x, y, patchWidth, patchHeight); patchRect &= rc; QtConcurrent::run(std::bind(&KisImage::notifyProjectionUpdated, q, patchRect)); } } } bool KisImage::startIsolatedMode(KisNodeSP node) { struct StartIsolatedModeStroke : public KisSimpleStrokeStrategy { StartIsolatedModeStroke(KisNodeSP node, KisImageSP image) : KisSimpleStrokeStrategy("start-isolated-mode", kundo2_noi18n("start-isolated-mode")), m_node(node), m_image(image) { - this->enableJob(JOB_INIT); + this->enableJob(JOB_INIT, true, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); setClearsRedoOnStart(false); } void initStrokeCallback() { + // pass-though node don't have any projection prepared, so we should + // explicitly regenerate it before activating isolated mode. + m_node->projectionLeaf()->explicitlyRegeneratePassThroughProjection(); + m_image->m_d->isolatedRootNode = m_node; emit m_image->sigIsolatedModeChanged(); // the GUI uses our thread to do the color space conversion so we // need to emit this signal in multiple threads m_image->m_d->notifyProjectionUpdatedInPatches(m_image->bounds()); m_image->invalidateAllFrames(); } private: KisNodeSP m_node; KisImageSP m_image; }; KisStrokeId id = startStroke(new StartIsolatedModeStroke(node, this)); endStroke(id); return true; } void KisImage::stopIsolatedMode() { if (!m_d->isolatedRootNode) return; struct StopIsolatedModeStroke : public KisSimpleStrokeStrategy { StopIsolatedModeStroke(KisImageSP image) : KisSimpleStrokeStrategy("stop-isolated-mode", kundo2_noi18n("stop-isolated-mode")), m_image(image) { this->enableJob(JOB_INIT); setClearsRedoOnStart(false); } void initStrokeCallback() { if (!m_image->m_d->isolatedRootNode) return; //KisNodeSP oldRootNode = m_image->m_d->isolatedRootNode; m_image->m_d->isolatedRootNode = 0; emit m_image->sigIsolatedModeChanged(); // the GUI uses our thread to do the color space conversion so we // need to emit this signal in multiple threads m_image->m_d->notifyProjectionUpdatedInPatches(m_image->bounds()); m_image->invalidateAllFrames(); // TODO: Substitute notifyProjectionUpdated() with this code // when update optimization is implemented // // QRect updateRect = bounds() | oldRootNode->extent(); // oldRootNode->setDirty(updateRect); } private: KisImageSP m_image; }; KisStrokeId id = startStroke(new StopIsolatedModeStroke(this)); endStroke(id); } KisNodeSP KisImage::isolatedModeRoot() const { return m_d->isolatedRootNode; } void KisImage::addJob(KisStrokeId id, KisStrokeJobData *data) { KisUpdateTimeMonitor::instance()->reportJobStarted(data); m_d->scheduler.addJob(id, data); } void KisImage::endStroke(KisStrokeId id) { m_d->scheduler.endStroke(id); } bool KisImage::cancelStroke(KisStrokeId id) { return m_d->scheduler.cancelStroke(id); } bool KisImage::KisImagePrivate::tryCancelCurrentStrokeAsync() { return scheduler.tryCancelCurrentStrokeAsync(); } void KisImage::requestUndoDuringStroke() { emit sigUndoDuringStrokeRequested(); } void KisImage::requestStrokeCancellation() { if (!m_d->tryCancelCurrentStrokeAsync()) { emit sigStrokeCancellationRequested(); } } UndoResult KisImage::tryUndoUnfinishedLod0Stroke() { return m_d->scheduler.tryUndoLastStrokeAsync(); } void KisImage::requestStrokeEnd() { emit sigStrokeEndRequested(); emit sigStrokeEndRequestedActiveNodeFiltered(); } void KisImage::requestStrokeEndActiveNode() { emit sigStrokeEndRequested(); } void KisImage::refreshGraph(KisNodeSP root) { refreshGraph(root, bounds(), bounds()); } void KisImage::refreshGraph(KisNodeSP root, const QRect &rc, const QRect &cropRect) { if (!root) root = m_d->rootLayer; m_d->animationInterface->notifyNodeChanged(root.data(), rc, true); m_d->scheduler.fullRefresh(root, rc, cropRect); } void KisImage::initialRefreshGraph() { /** * NOTE: Tricky part. We set crop rect to null, so the clones * will not rely on precalculated projections of their sources */ refreshGraphAsync(0, bounds(), QRect()); waitForDone(); } void KisImage::refreshGraphAsync(KisNodeSP root) { refreshGraphAsync(root, bounds(), bounds()); } void KisImage::refreshGraphAsync(KisNodeSP root, const QRect &rc) { refreshGraphAsync(root, rc, bounds()); } void KisImage::refreshGraphAsync(KisNodeSP root, const QRect &rc, const QRect &cropRect) { if (!root) root = m_d->rootLayer; m_d->animationInterface->notifyNodeChanged(root.data(), rc, true); m_d->scheduler.fullRefreshAsync(root, rc, cropRect); } void KisImage::requestProjectionUpdateNoFilthy(KisNodeSP pseudoFilthy, const QRect &rc, const QRect &cropRect) { KIS_ASSERT_RECOVER_RETURN(pseudoFilthy); m_d->animationInterface->notifyNodeChanged(pseudoFilthy.data(), rc, false); m_d->scheduler.updateProjectionNoFilthy(pseudoFilthy, rc, cropRect); } void KisImage::addSpontaneousJob(KisSpontaneousJob *spontaneousJob) { m_d->scheduler.addSpontaneousJob(spontaneousJob); } void KisImage::setProjectionUpdatesFilter(KisProjectionUpdatesFilterSP filter) { // update filters are *not* recursive! KIS_ASSERT_RECOVER_NOOP(!filter || !m_d->projectionUpdatesFilter); m_d->projectionUpdatesFilter = filter; } KisProjectionUpdatesFilterSP KisImage::projectionUpdatesFilter() const { return m_d->projectionUpdatesFilter; } void KisImage::disableDirtyRequests() { setProjectionUpdatesFilter(KisProjectionUpdatesFilterSP(new KisDropAllProjectionUpdatesFilter())); } void KisImage::enableDirtyRequests() { setProjectionUpdatesFilter(KisProjectionUpdatesFilterSP()); } void KisImage::disableUIUpdates() { m_d->disableUIUpdateSignals.ref(); } void KisImage::enableUIUpdates() { m_d->disableUIUpdateSignals.deref(); } void KisImage::notifyProjectionUpdated(const QRect &rc) { KisUpdateTimeMonitor::instance()->reportUpdateFinished(rc); if (!m_d->disableUIUpdateSignals) { int lod = currentLevelOfDetail(); QRect dirtyRect = !lod ? rc : KisLodTransform::upscaledRect(rc, lod); if (dirtyRect.isEmpty()) return; emit sigImageUpdated(dirtyRect); } } void KisImage::setWorkingThreadsLimit(int value) { m_d->scheduler.setThreadsLimit(value); } int KisImage::workingThreadsLimit() const { return m_d->scheduler.threadsLimit(); } void KisImage::notifySelectionChanged() { /** * The selection is calculated asynchromously, so it is not * handled by disableUIUpdates() and other special signals of * KisImageSignalRouter */ m_d->legacyUndoAdapter.emitSelectionChanged(); /** * Editing of selection masks doesn't necessary produce a * setDirty() call, so in the end of the stroke we need to request * direct update of the UI's cache. */ if (m_d->isolatedRootNode && dynamic_cast(m_d->isolatedRootNode.data())) { notifyProjectionUpdated(bounds()); } } void KisImage::requestProjectionUpdateImpl(KisNode *node, const QVector &rects, const QRect &cropRect) { if (rects.isEmpty()) return; m_d->scheduler.updateProjection(node, rects, cropRect); } void KisImage::requestProjectionUpdate(KisNode *node, const QVector &rects, bool resetAnimationCache) { if (m_d->projectionUpdatesFilter && m_d->projectionUpdatesFilter->filter(this, node, rects, resetAnimationCache)) { return; } if (resetAnimationCache) { m_d->animationInterface->notifyNodeChanged(node, rects, false); } /** * Here we use 'permitted' instead of 'active' intentively, * because the updates may come after the actual stroke has been * finished. And having some more updates for the stroke not * supporting the wrap-around mode will not make much harm. */ if (m_d->wrapAroundModePermitted) { QVector allSplitRects; const QRect boundRect = effectiveLodBounds(); Q_FOREACH (const QRect &rc, rects) { KisWrappedRect splitRect(rc, boundRect); allSplitRects.append(splitRect); } requestProjectionUpdateImpl(node, allSplitRects, boundRect); } else { requestProjectionUpdateImpl(node, rects, bounds()); } KisNodeGraphListener::requestProjectionUpdate(node, rects, resetAnimationCache); } void KisImage::invalidateFrames(const KisTimeRange &range, const QRect &rect) { m_d->animationInterface->invalidateFrames(range, rect); } void KisImage::requestTimeSwitch(int time) { m_d->animationInterface->requestTimeSwitchNonGUI(time); } QList KisImage::compositions() { return m_d->compositions; } void KisImage::addComposition(KisLayerCompositionSP composition) { m_d->compositions.append(composition); } void KisImage::removeComposition(KisLayerCompositionSP composition) { m_d->compositions.removeAll(composition); } bool checkMasksNeedConversion(KisNodeSP root, const QRect &bounds) { KisSelectionMask *mask = dynamic_cast(root.data()); if (mask && (!bounds.contains(mask->paintDevice()->exactBounds()) || mask->selection()->hasShapeSelection())) { return true; } KisNodeSP node = root->firstChild(); while (node) { if (checkMasksNeedConversion(node, bounds)) { return true; } node = node->nextSibling(); } return false; } void KisImage::setWrapAroundModePermitted(bool value) { if (m_d->wrapAroundModePermitted != value) { requestStrokeEnd(); } m_d->wrapAroundModePermitted = value; if (m_d->wrapAroundModePermitted && checkMasksNeedConversion(root(), bounds())) { KisProcessingApplicator applicator(this, root(), KisProcessingApplicator::RECURSIVE, KisImageSignalVector() << ModifiedSignal, kundo2_i18n("Crop Selections")); KisProcessingVisitorSP visitor = new KisCropSelectionsProcessingVisitor(bounds()); applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT); applicator.end(); } } bool KisImage::wrapAroundModePermitted() const { return m_d->wrapAroundModePermitted; } bool KisImage::wrapAroundModeActive() const { return m_d->wrapAroundModePermitted && m_d->scheduler.wrapAroundModeSupported(); } void KisImage::setDesiredLevelOfDetail(int lod) { if (m_d->blockLevelOfDetail) { qWarning() << "WARNING: KisImage::setDesiredLevelOfDetail()" << "was called while LoD functionality was being blocked!"; return; } m_d->scheduler.setDesiredLevelOfDetail(lod); } int KisImage::currentLevelOfDetail() const { if (m_d->blockLevelOfDetail) { return 0; } return m_d->scheduler.currentLevelOfDetail(); } void KisImage::setLevelOfDetailBlocked(bool value) { KisImageBarrierLockerRaw l(this); if (value && !m_d->blockLevelOfDetail) { m_d->scheduler.setDesiredLevelOfDetail(0); } m_d->blockLevelOfDetail = value; } void KisImage::explicitRegenerateLevelOfDetail() { if (!m_d->blockLevelOfDetail) { m_d->scheduler.explicitRegenerateLevelOfDetail(); } } bool KisImage::levelOfDetailBlocked() const { return m_d->blockLevelOfDetail; } void KisImage::notifyNodeCollpasedChanged() { emit sigNodeCollapsedChanged(); } KisImageAnimationInterface* KisImage::animationInterface() const { return m_d->animationInterface; } void KisImage::setProofingConfiguration(KisProofingConfigurationSP proofingConfig) { m_d->proofingConfig = proofingConfig; emit sigProofingConfigChanged(); } KisProofingConfigurationSP KisImage::proofingConfiguration() const { if (m_d->proofingConfig) { return m_d->proofingConfig; } return KisProofingConfigurationSP(); } QPointF KisImage::mirrorAxesCenter() const { return m_d->axesCenter; } void KisImage::setMirrorAxesCenter(const QPointF &value) const { m_d->axesCenter = value; } diff --git a/libs/image/kis_keyframe.cpp b/libs/image/kis_keyframe.cpp index 8a043c9eda..765736191a 100644 --- a/libs/image/kis_keyframe.cpp +++ b/libs/image/kis_keyframe.cpp @@ -1,129 +1,133 @@ /* * 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_image_config.h" #include "kis_keyframe.h" #include "kis_keyframe_channel.h" #include "kis_types.h" #include struct KisKeyframeSPStaticRegistrar { KisKeyframeSPStaticRegistrar() { qRegisterMetaType("KisKeyframeSP"); } }; static KisKeyframeSPStaticRegistrar __registrar; struct KisKeyframe::Private { QPointer channel; int time; InterpolationMode interpolationMode; InterpolationTangentsMode tangentsMode; QPointF leftTangent; QPointF rightTangent; int colorLabel{0}; Private(KisKeyframeChannel *channel, int time) : channel(channel), time(time), interpolationMode(Constant) {} }; KisKeyframe::KisKeyframe(KisKeyframeChannel *channel, int time) : m_d(new Private(channel, time)) { KisImageConfig config; m_d->colorLabel = config.defaultFrameColorLabel(); } KisKeyframe::KisKeyframe(const KisKeyframe *rhs, KisKeyframeChannel *channel) : m_d(new Private(channel, rhs->time())) { m_d->interpolationMode = rhs->m_d->interpolationMode; m_d->tangentsMode = rhs->m_d->tangentsMode; m_d->leftTangent = rhs->m_d->leftTangent; m_d->rightTangent = rhs->m_d->rightTangent; m_d->colorLabel = rhs->m_d->colorLabel; } KisKeyframe::~KisKeyframe() {} int KisKeyframe::time() const { return m_d->time; } void KisKeyframe::setTime(int time) { m_d->time = time; } void KisKeyframe::setInterpolationMode(KisKeyframe::InterpolationMode mode) { m_d->interpolationMode = mode; } KisKeyframe::InterpolationMode KisKeyframe::interpolationMode() const { return m_d->interpolationMode; } void KisKeyframe::setTangentsMode(KisKeyframe::InterpolationTangentsMode mode) { m_d->tangentsMode = mode; } KisKeyframe::InterpolationTangentsMode KisKeyframe::tangentsMode() const { return m_d->tangentsMode; } void KisKeyframe::setInterpolationTangents(QPointF leftTangent, QPointF rightTangent) { m_d->leftTangent = leftTangent; m_d->rightTangent = rightTangent; } QPointF KisKeyframe::leftTangent() const { return m_d->leftTangent; } QPointF KisKeyframe::rightTangent() const { return m_d->rightTangent; } int KisKeyframe::colorLabel() const { return m_d->colorLabel; } void KisKeyframe::setColorLabel(int label) { m_d->colorLabel = label; } +bool KisKeyframe::hasContent() const { + return true; +} + KisKeyframeChannel *KisKeyframe::channel() const { return m_d->channel; } diff --git a/libs/image/kis_keyframe.h b/libs/image/kis_keyframe.h index 201946edc6..8e04d15f45 100644 --- a/libs/image/kis_keyframe.h +++ b/libs/image/kis_keyframe.h @@ -1,80 +1,81 @@ /* * 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_KEYFRAME_H #define KIS_KEYFRAME_H #include #include #include #include "kritaimage_export.h" #include "kis_types.h" class KisKeyframeChannel; class KRITAIMAGE_EXPORT KisKeyframe { public: enum InterpolationMode { Constant, Linear, Bezier }; enum InterpolationTangentsMode { Sharp, Smooth }; KisKeyframe(KisKeyframeChannel *channel, int time); virtual ~KisKeyframe(); /** * Create a copy of the keyframe for insertion into given channel. * Used when constructing a copy of a keyframe channel. */ virtual KisKeyframeSP cloneFor(KisKeyframeChannel *channel) const = 0; int time() const; void setTime(int time); void setInterpolationMode(InterpolationMode mode); InterpolationMode interpolationMode() const; void setTangentsMode(InterpolationTangentsMode mode); InterpolationTangentsMode tangentsMode() const; void setInterpolationTangents(QPointF leftTangent, QPointF rightTangent); QPointF leftTangent() const; QPointF rightTangent() const; int colorLabel() const; void setColorLabel(int label); + virtual bool hasContent() const; // does any content exist in keyframe, or is it empty? KisKeyframeChannel *channel() const; protected: KisKeyframe(const KisKeyframe *rhs, KisKeyframeChannel *channel); private: struct Private; QScopedPointer m_d; }; Q_DECLARE_METATYPE(KisKeyframe*) Q_DECLARE_METATYPE(KisKeyframeSP) #endif diff --git a/libs/image/kis_layer.cc b/libs/image/kis_layer.cc index 85ce35c0b3..005a079d17 100644 --- a/libs/image/kis_layer.cc +++ b/libs/image/kis_layer.cc @@ -1,948 +1,948 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2005 C. Boemann * Copyright (c) 2009 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_layer.h" #include #include #include #include #include #include #include #include #include #include #include #include "kis_debug.h" #include "kis_image.h" #include "kis_painter.h" #include "kis_mask.h" #include "kis_effect_mask.h" #include "kis_selection_mask.h" #include "kis_meta_data_store.h" #include "kis_selection.h" #include "kis_paint_layer.h" #include "kis_raster_keyframe_channel.h" #include "kis_clone_layer.h" #include "kis_psd_layer_style.h" #include "kis_layer_projection_plane.h" #include "layerstyles/kis_layer_style_projection_plane.h" #include "krita_utils.h" #include "kis_layer_properties_icons.h" #include "kis_layer_utils.h" class KisSafeProjection { public: KisPaintDeviceSP getDeviceLazy(KisPaintDeviceSP prototype) { QMutexLocker locker(&m_lock); if (!m_reusablePaintDevice) { m_reusablePaintDevice = new KisPaintDevice(*prototype); } if(!m_projection || *m_projection->colorSpace() != *prototype->colorSpace()) { m_projection = m_reusablePaintDevice; m_projection->makeCloneFromRough(prototype, prototype->extent()); m_projection->setProjectionDevice(true); } return m_projection; } void freeDevice() { QMutexLocker locker(&m_lock); m_projection = 0; if(m_reusablePaintDevice) { m_reusablePaintDevice->clear(); } } private: QMutex m_lock; KisPaintDeviceSP m_projection; KisPaintDeviceSP m_reusablePaintDevice; }; class KisCloneLayersList { public: void addClone(KisCloneLayerWSP cloneLayer) { m_clonesList.append(cloneLayer); } void removeClone(KisCloneLayerWSP cloneLayer) { m_clonesList.removeOne(cloneLayer); } void setDirty(const QRect &rect) { Q_FOREACH (KisCloneLayerSP clone, m_clonesList) { if (clone) { clone->setDirtyOriginal(rect); } } } const QList registeredClones() const { return m_clonesList; } bool hasClones() const { return !m_clonesList.isEmpty(); } private: QList m_clonesList; }; struct Q_DECL_HIDDEN KisLayer::Private { KisImageWSP image; QBitArray channelFlags; KisMetaData::Store* metaDataStore; KisSafeProjection safeProjection; KisCloneLayersList clonesList; KisPSDLayerStyleSP layerStyle; KisAbstractProjectionPlaneSP layerStyleProjectionPlane; KisAbstractProjectionPlaneSP projectionPlane; KisSelectionMaskSP selectionMask; QList effectMasks; }; KisLayer::KisLayer(KisImageWSP image, const QString &name, quint8 opacity) : KisNode() , m_d(new Private) { setName(name); setOpacity(opacity); m_d->image = image; m_d->metaDataStore = new KisMetaData::Store(); m_d->projectionPlane = toQShared(new KisLayerProjectionPlane(this)); notifyChildMaskChanged(KisNodeSP()); } KisLayer::KisLayer(const KisLayer& rhs) : KisNode(rhs) , m_d(new Private()) { if (this != &rhs) { m_d->image = rhs.m_d->image; m_d->metaDataStore = new KisMetaData::Store(*rhs.m_d->metaDataStore); m_d->channelFlags = rhs.m_d->channelFlags; setName(rhs.name()); m_d->projectionPlane = toQShared(new KisLayerProjectionPlane(this)); if (rhs.m_d->layerStyle) { setLayerStyle(rhs.m_d->layerStyle->clone()); } notifyChildMaskChanged(KisNodeSP()); } } KisLayer::~KisLayer() { delete m_d->metaDataStore; delete m_d; } const KoColorSpace * KisLayer::colorSpace() const { KisImageSP image = m_d->image.toStrongRef(); if (!image) { return nullptr; } return image->colorSpace(); } const KoCompositeOp * KisLayer::compositeOp() const { /** * FIXME: This function duplicates the same function from * KisMask. We can't move it to KisBaseNode as it doesn't * know anything about parent() method of KisNode * Please think it over... */ KisNodeSP parentNode = parent(); if (!parentNode) return 0; if (!parentNode->colorSpace()) return 0; const KoCompositeOp* op = parentNode->colorSpace()->compositeOp(compositeOpId()); return op ? op : parentNode->colorSpace()->compositeOp(COMPOSITE_OVER); } KisPSDLayerStyleSP KisLayer::layerStyle() const { return m_d->layerStyle; } void KisLayer::setLayerStyle(KisPSDLayerStyleSP layerStyle) { if (layerStyle) { m_d->layerStyle = layerStyle; KisAbstractProjectionPlaneSP plane = !layerStyle->isEmpty() ? KisAbstractProjectionPlaneSP(new KisLayerStyleProjectionPlane(this)) : KisAbstractProjectionPlaneSP(0); m_d->layerStyleProjectionPlane = plane; } else { m_d->layerStyleProjectionPlane.clear(); m_d->layerStyle.clear(); } } KisBaseNode::PropertyList KisLayer::sectionModelProperties() const { KisBaseNode::PropertyList l = KisBaseNode::sectionModelProperties(); l << KisBaseNode::Property(KoID("opacity", i18n("Opacity")), i18n("%1%", percentOpacity())); const KoCompositeOp * compositeOp = this->compositeOp(); if (compositeOp) { - l << KisBaseNode::Property(KoID("compositeop", i18n("Composite Mode")), compositeOp->description()); + l << KisBaseNode::Property(KoID("compositeop", i18n("Blending Mode")), compositeOp->description()); } if (m_d->layerStyle && !m_d->layerStyle->isEmpty()) { l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::layerStyle, m_d->layerStyle->isEnabled()); } l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::inheritAlpha, alphaChannelDisabled()); return l; } void KisLayer::setSectionModelProperties(const KisBaseNode::PropertyList &properties) { KisBaseNode::setSectionModelProperties(properties); Q_FOREACH (const KisBaseNode::Property &property, properties) { if (property.id == KisLayerPropertiesIcons::inheritAlpha.id()) { disableAlphaChannel(property.state.toBool()); } if (property.id == KisLayerPropertiesIcons::layerStyle.id()) { if (m_d->layerStyle && m_d->layerStyle->isEnabled() != property.state.toBool()) { m_d->layerStyle->setEnabled(property.state.toBool()); baseNodeChangedCallback(); baseNodeInvalidateAllFramesCallback(); } } } } void KisLayer::disableAlphaChannel(bool disable) { QBitArray newChannelFlags = m_d->channelFlags; if(newChannelFlags.isEmpty()) newChannelFlags = colorSpace()->channelFlags(true, true); if(disable) newChannelFlags &= colorSpace()->channelFlags(true, false); else newChannelFlags |= colorSpace()->channelFlags(false, true); setChannelFlags(newChannelFlags); } bool KisLayer::alphaChannelDisabled() const { QBitArray flags = colorSpace()->channelFlags(false, true) & m_d->channelFlags; return flags.count(true) == 0 && !m_d->channelFlags.isEmpty(); } void KisLayer::setChannelFlags(const QBitArray & channelFlags) { Q_ASSERT(channelFlags.isEmpty() ||((quint32)channelFlags.count() == colorSpace()->channelCount())); if (KritaUtils::compareChannelFlags(channelFlags, this->channelFlags())) { return; } if (!channelFlags.isEmpty() && channelFlags == QBitArray(channelFlags.size(), true)) { m_d->channelFlags.clear(); } else { m_d->channelFlags = channelFlags; } baseNodeChangedCallback(); baseNodeInvalidateAllFramesCallback(); } QBitArray & KisLayer::channelFlags() const { return m_d->channelFlags; } bool KisLayer::temporary() const { return nodeProperties().boolProperty("temporary", false); } void KisLayer::setTemporary(bool t) { setNodeProperty("temporary", t); } KisImageWSP KisLayer::image() const { return m_d->image; } void KisLayer::setImage(KisImageWSP image) { m_d->image = image; // we own the projection device, so we should take care about it KisPaintDeviceSP projection = this->projection(); if (projection && projection != original()) { projection->setDefaultBounds(new KisDefaultBounds(image)); } KisNodeSP node = firstChild(); while (node) { KisLayerUtils::recursiveApplyNodes(node, [image] (KisNodeSP node) { node->setImage(image); }); node = node->nextSibling(); } } bool KisLayer::canMergeAndKeepBlendOptions(KisLayerSP otherLayer) { return this->compositeOpId() == otherLayer->compositeOpId() && this->opacity() == otherLayer->opacity() && this->channelFlags() == otherLayer->channelFlags() && !this->layerStyle() && !otherLayer->layerStyle() && (this->colorSpace() == otherLayer->colorSpace() || *this->colorSpace() == *otherLayer->colorSpace()); } KisLayerSP KisLayer::createMergedLayerTemplate(KisLayerSP prevLayer) { const bool keepBlendingOptions = canMergeAndKeepBlendOptions(prevLayer); KisLayerSP newLayer = new KisPaintLayer(image(), prevLayer->name(), OPACITY_OPAQUE_U8); if (keepBlendingOptions) { newLayer->setCompositeOpId(compositeOpId()); newLayer->setOpacity(opacity()); newLayer->setChannelFlags(channelFlags()); } return newLayer; } void KisLayer::fillMergedLayerTemplate(KisLayerSP dstLayer, KisLayerSP prevLayer) { const bool keepBlendingOptions = canMergeAndKeepBlendOptions(prevLayer); QRect layerProjectionExtent = this->projection()->extent(); QRect prevLayerProjectionExtent = prevLayer->projection()->extent(); bool alphaDisabled = this->alphaChannelDisabled(); bool prevAlphaDisabled = prevLayer->alphaChannelDisabled(); KisPaintDeviceSP mergedDevice = dstLayer->paintDevice(); if (!keepBlendingOptions) { KisPainter gc(mergedDevice); KisImageSP imageSP = image().toStrongRef(); if (!imageSP) { return; } //Copy the pixels of previous layer with their actual alpha value prevLayer->disableAlphaChannel(false); prevLayer->projectionPlane()->apply(&gc, prevLayerProjectionExtent | imageSP->bounds()); //Restore the previous prevLayer disableAlpha status for correct undo/redo prevLayer->disableAlphaChannel(prevAlphaDisabled); //Paint the pixels of the current layer, using their actual alpha value if (alphaDisabled == prevAlphaDisabled) { this->disableAlphaChannel(false); } this->projectionPlane()->apply(&gc, layerProjectionExtent | imageSP->bounds()); //Restore the layer disableAlpha status for correct undo/redo this->disableAlphaChannel(alphaDisabled); } else { //Copy prevLayer KisPaintDeviceSP srcDev = prevLayer->projection(); mergedDevice->makeCloneFrom(srcDev, srcDev->extent()); //Paint layer on the copy KisPainter gc(mergedDevice); gc.bitBlt(layerProjectionExtent.topLeft(), this->projection(), layerProjectionExtent); } } void KisLayer::registerClone(KisCloneLayerWSP clone) { m_d->clonesList.addClone(clone); } void KisLayer::unregisterClone(KisCloneLayerWSP clone) { m_d->clonesList.removeClone(clone); } const QList KisLayer::registeredClones() const { return m_d->clonesList.registeredClones(); } bool KisLayer::hasClones() const { return m_d->clonesList.hasClones(); } void KisLayer::updateClones(const QRect &rect) { m_d->clonesList.setDirty(rect); } void KisLayer::notifyChildMaskChanged(KisNodeSP changedChildMask) { Q_UNUSED(changedChildMask); updateSelectionMask(); updateEffectMasks(); } KisSelectionMaskSP KisLayer::selectionMask() const { return m_d->selectionMask; } void KisLayer::updateSelectionMask() { KoProperties properties; properties.setProperty("active", true); properties.setProperty("visible", true); QList masks = childNodes(QStringList("KisSelectionMask"), properties); // return the first visible mask Q_FOREACH (KisNodeSP mask, masks) { if (mask) { m_d->selectionMask = dynamic_cast(mask.data()); return; } } m_d->selectionMask = KisSelectionMaskSP(); } KisSelectionSP KisLayer::selection() const { KisSelectionMaskSP mask = selectionMask(); if (mask) { return mask->selection(); } KisImageSP image = m_d->image.toStrongRef(); if (image) { return image->globalSelection(); } return KisSelectionSP(); } /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// const QList &KisLayer::effectMasks() const { return m_d->effectMasks; } QList KisLayer::effectMasks(KisNodeSP lastNode) const { if (lastNode.isNull()) { return effectMasks(); } else { // happens rarely. return searchEffectMasks(lastNode); } } void KisLayer::updateEffectMasks() { m_d->effectMasks = searchEffectMasks(KisNodeSP()); } QList KisLayer::searchEffectMasks(KisNodeSP lastNode) const { QList masks; if (childCount() > 0) { KoProperties properties; properties.setProperty("visible", true); QList nodes = childNodes(QStringList("KisEffectMask"), properties); Q_FOREACH (const KisNodeSP& node, nodes) { if (node == lastNode) break; KisEffectMaskSP mask = dynamic_cast(const_cast(node.data())); if (mask) masks.append(mask); } } return masks; } bool KisLayer::hasEffectMasks() const { return !m_d->effectMasks.empty(); } QRect KisLayer::masksChangeRect(const QList &masks, const QRect &requestedRect, bool &rectVariesFlag) const { rectVariesFlag = false; QRect prevChangeRect = requestedRect; /** * We set default value of the change rect for the case * when there is no mask at all */ QRect changeRect = requestedRect; Q_FOREACH (const KisEffectMaskSP& mask, masks) { changeRect = mask->changeRect(prevChangeRect); if (changeRect != prevChangeRect) rectVariesFlag = true; prevChangeRect = changeRect; } return changeRect; } QRect KisLayer::masksNeedRect(const QList &masks, const QRect &changeRect, QStack &applyRects, bool &rectVariesFlag) const { rectVariesFlag = false; QRect prevNeedRect = changeRect; QRect needRect; for (qint32 i = masks.size() - 1; i >= 0; i--) { applyRects.push(prevNeedRect); needRect = masks[i]->needRect(prevNeedRect); if (prevNeedRect != needRect) rectVariesFlag = true; prevNeedRect = needRect; } return needRect; } KisNode::PositionToFilthy calculatePositionToFilthy(KisNodeSP nodeInQuestion, KisNodeSP filthy, KisNodeSP parent) { if (parent == filthy || parent != filthy->parent()) { return KisNode::N_ABOVE_FILTHY; } if (nodeInQuestion == filthy) { return KisNode::N_FILTHY; } KisNodeSP node = nodeInQuestion->prevSibling(); while (node) { if (node == filthy) { return KisNode::N_ABOVE_FILTHY; } node = node->prevSibling(); } return KisNode::N_BELOW_FILTHY; } QRect KisLayer::applyMasks(const KisPaintDeviceSP source, KisPaintDeviceSP destination, const QRect &requestedRect, KisNodeSP filthyNode, KisNodeSP lastNode) const { Q_ASSERT(source); Q_ASSERT(destination); QList masks = effectMasks(lastNode); QRect changeRect; QRect needRect; if (masks.isEmpty()) { changeRect = requestedRect; if (source != destination) { copyOriginalToProjection(source, destination, requestedRect); } } else { QStack applyRects; bool changeRectVaries; bool needRectVaries; /** * FIXME: Assume that varying of the changeRect has already * been taken into account while preparing walkers */ changeRectVaries = false; changeRect = requestedRect; //changeRect = masksChangeRect(masks, requestedRect, // changeRectVaries); needRect = masksNeedRect(masks, changeRect, applyRects, needRectVaries); if (!changeRectVaries && !needRectVaries) { /** * A bit of optimization: * All filters will read/write exactly from/to the requested * rect so we needn't create temporary paint device, * just apply it onto destination */ Q_ASSERT(needRect == requestedRect); if (source != destination) { copyOriginalToProjection(source, destination, needRect); } Q_FOREACH (const KisEffectMaskSP& mask, masks) { const QRect maskApplyRect = applyRects.pop(); const QRect maskNeedRect = applyRects.isEmpty() ? needRect : applyRects.top(); PositionToFilthy maskPosition = calculatePositionToFilthy(mask, filthyNode, const_cast(this)); mask->apply(destination, maskApplyRect, maskNeedRect, maskPosition); } Q_ASSERT(applyRects.isEmpty()); } else { /** * We can't eliminate additional copy-op * as filters' behaviour may be quite insane here, * so let them work on their own paintDevice =) */ KisPaintDeviceSP tempDevice = new KisPaintDevice(colorSpace()); tempDevice->prepareClone(source); copyOriginalToProjection(source, tempDevice, needRect); QRect maskApplyRect = applyRects.pop(); QRect maskNeedRect = needRect; Q_FOREACH (const KisEffectMaskSP& mask, masks) { PositionToFilthy maskPosition = calculatePositionToFilthy(mask, filthyNode, const_cast(this)); mask->apply(tempDevice, maskApplyRect, maskNeedRect, maskPosition); if (!applyRects.isEmpty()) { maskNeedRect = maskApplyRect; maskApplyRect = applyRects.pop(); } } Q_ASSERT(applyRects.isEmpty()); KisPainter::copyAreaOptimized(changeRect.topLeft(), tempDevice, destination, changeRect); } } return changeRect; } QRect KisLayer::updateProjection(const QRect& rect, KisNodeSP filthyNode) { QRect updatedRect = rect; KisPaintDeviceSP originalDevice = original(); if (!rect.isValid() || !visible() || !originalDevice) return QRect(); if (!needProjection() && !hasEffectMasks()) { m_d->safeProjection.freeDevice(); } else { if (!updatedRect.isEmpty()) { KisPaintDeviceSP projection = m_d->safeProjection.getDeviceLazy(originalDevice); updatedRect = applyMasks(originalDevice, projection, updatedRect, filthyNode, 0); } } return updatedRect; } QRect KisLayer::partialChangeRect(KisNodeSP lastNode, const QRect& rect) { bool changeRectVaries = false; QRect changeRect = outgoingChangeRect(rect); changeRect = masksChangeRect(effectMasks(lastNode), changeRect, changeRectVaries); return changeRect; } /** * \p rect is a dirty rect in layer's original() coordinates! */ void KisLayer::buildProjectionUpToNode(KisPaintDeviceSP projection, KisNodeSP lastNode, const QRect& rect) { QRect changeRect = partialChangeRect(lastNode, rect); KisPaintDeviceSP originalDevice = original(); KIS_ASSERT_RECOVER_RETURN(needProjection() || hasEffectMasks()); if (!changeRect.isEmpty()) { applyMasks(originalDevice, projection, changeRect, this, lastNode); } } bool KisLayer::needProjection() const { return false; } void KisLayer::copyOriginalToProjection(const KisPaintDeviceSP original, KisPaintDeviceSP projection, const QRect& rect) const { KisPainter::copyAreaOptimized(rect.topLeft(), original, projection, rect); } KisAbstractProjectionPlaneSP KisLayer::projectionPlane() const { return m_d->layerStyleProjectionPlane ? m_d->layerStyleProjectionPlane : m_d->projectionPlane; } KisAbstractProjectionPlaneSP KisLayer::internalProjectionPlane() const { return m_d->projectionPlane; } KisPaintDeviceSP KisLayer::projection() const { KisPaintDeviceSP originalDevice = original(); return needProjection() || hasEffectMasks() ? m_d->safeProjection.getDeviceLazy(originalDevice) : originalDevice; } QRect KisLayer::changeRect(const QRect &rect, PositionToFilthy pos) const { QRect changeRect = rect; changeRect = incomingChangeRect(changeRect); if(pos == KisNode::N_FILTHY) { QRect projectionToBeUpdated = projection()->exactBoundsAmortized() & changeRect; bool changeRectVaries; changeRect = outgoingChangeRect(changeRect); changeRect = masksChangeRect(effectMasks(), changeRect, changeRectVaries); /** * If the projection contains some dirty areas we should also * add them to the change rect, because they might have * changed. E.g. when a visibility of the mask has chnaged * while the parent layer was invinisble. */ if (!projectionToBeUpdated.isEmpty() && !changeRect.contains(projectionToBeUpdated)) { changeRect |= projectionToBeUpdated; } } // TODO: string comparizon: optimize! if (pos != KisNode::N_FILTHY && pos != KisNode::N_FILTHY_PROJECTION && compositeOpId() != COMPOSITE_COPY) { changeRect |= rect; } return changeRect; } void KisLayer::childNodeChanged(KisNodeSP changedChildNode) { if (dynamic_cast(changedChildNode.data())) { notifyChildMaskChanged(changedChildNode); } } QRect KisLayer::incomingChangeRect(const QRect &rect) const { return rect; } QRect KisLayer::outgoingChangeRect(const QRect &rect) const { return rect; } QImage KisLayer::createThumbnail(qint32 w, qint32 h) { if (w == 0 || h == 0) { return QImage(); } KisPaintDeviceSP originalDevice = original(); return originalDevice ? originalDevice->createThumbnail(w, h, 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) : QImage(); } QImage KisLayer::createThumbnailForFrame(qint32 w, qint32 h, int time) { if (w == 0 || h == 0) { return QImage(); } KisPaintDeviceSP originalDevice = original(); if (originalDevice) { KisRasterKeyframeChannel *channel = originalDevice->keyframeChannel(); if (channel) { KisPaintDeviceSP targetDevice = new KisPaintDevice(colorSpace()); KisKeyframeSP keyframe = channel->activeKeyframeAt(time); channel->fetchFrame(keyframe, targetDevice); return targetDevice->createThumbnail(w, h, 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); } } return createThumbnail(w, h); } qint32 KisLayer::x() const { KisPaintDeviceSP originalDevice = original(); return originalDevice ? originalDevice->x() : 0; } qint32 KisLayer::y() const { KisPaintDeviceSP originalDevice = original(); return originalDevice ? originalDevice->y() : 0; } void KisLayer::setX(qint32 x) { KisPaintDeviceSP originalDevice = original(); if (originalDevice) originalDevice->setX(x); } void KisLayer::setY(qint32 y) { KisPaintDeviceSP originalDevice = original(); if (originalDevice) originalDevice->setY(y); } QRect KisLayer::layerExtentImpl(bool needExactBounds) const { QRect additionalMaskExtent = QRect(); QList effectMasks = this->effectMasks(); Q_FOREACH(KisEffectMaskSP mask, effectMasks) { additionalMaskExtent |= mask->nonDependentExtent(); } KisPaintDeviceSP originalDevice = original(); QRect layerExtent; if (originalDevice) { layerExtent = needExactBounds ? originalDevice->exactBounds() : originalDevice->extent(); } QRect additionalCompositeOpExtent; if (compositeOpId() == COMPOSITE_DESTINATION_IN || compositeOpId() == COMPOSITE_DESTINATION_ATOP) { additionalCompositeOpExtent = originalDevice->defaultBounds()->bounds(); } return layerExtent | additionalMaskExtent | additionalCompositeOpExtent; } QRect KisLayer::extent() const { return layerExtentImpl(false); } QRect KisLayer::exactBounds() const { return layerExtentImpl(true); } KisLayerSP KisLayer::parentLayer() const { return qobject_cast(parent().data()); } KisMetaData::Store* KisLayer::metaData() { return m_d->metaDataStore; } diff --git a/libs/image/kis_mask.cc b/libs/image/kis_mask.cc index 5e9625068d..37afc516b0 100644 --- a/libs/image/kis_mask.cc +++ b/libs/image/kis_mask.cc @@ -1,427 +1,460 @@ /* * Copyright (c) 2006 Boudewijn Rempt * (c) 2009 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_mask.h" #include // to prevent incomplete class types on "delete selection->flatten();" #include #include #include #include #include #include "kis_paint_device.h" #include "kis_selection.h" #include "kis_pixel_selection.h" #include "kis_painter.h" #include "kis_image.h" #include "kis_layer.h" #include "kis_cached_paint_device.h" #include "kis_mask_projection_plane.h" #include "kis_raster_keyframe_channel.h" struct Q_DECL_HIDDEN KisMask::Private { Private(KisMask *_q) : q(_q), projectionPlane(new KisMaskProjectionPlane(q)) { } mutable KisSelectionSP selection; KisCachedPaintDevice paintDeviceCache; KisMask *q; /** * Due to the design of the Kra format the X,Y offset of the paint * device belongs to the node, but not to the device itself. So * the offset is set when the node is created, but not when the * selection is initialized. This causes the X,Y values to be * lost, since the selection doen not exist at the moment. That is * why we save it separately. */ QScopedPointer deferredSelectionOffset; KisAbstractProjectionPlaneSP projectionPlane; KisCachedSelection cachedSelection; void initSelectionImpl(KisSelectionSP copyFrom, KisLayerSP parentLayer, KisPaintDeviceSP copyFromDevice); }; KisMask::KisMask(const QString & name) : KisNode() , m_d(new Private(this)) { setName(name); } KisMask::KisMask(const KisMask& rhs) : KisNode(rhs) , KisIndirectPaintingSupport() , m_d(new Private(this)) { setName(rhs.name()); if (rhs.m_d->selection) { m_d->selection = new KisSelection(*rhs.m_d->selection.data()); m_d->selection->setParentNode(this); KisPixelSelectionSP pixelSelection = m_d->selection->pixelSelection(); if (pixelSelection->framesInterface()) { addKeyframeChannel(pixelSelection->keyframeChannel()); enableAnimation(); } } } KisMask::~KisMask() { delete m_d; } void KisMask::setImage(KisImageWSP image) { KisPaintDeviceSP parentPaintDevice = parent() ? parent()->original() : 0; KisDefaultBoundsBaseSP defaultBounds = new KisSelectionDefaultBounds(parentPaintDevice, image); if (m_d->selection) { m_d->selection->setDefaultBounds(defaultBounds); } } bool KisMask::allowAsChild(KisNodeSP node) const { Q_UNUSED(node); return false; } const KoColorSpace * KisMask::colorSpace() const { KisNodeSP parentNode = parent(); return parentNode ? parentNode->colorSpace() : 0; } const KoCompositeOp * KisMask::compositeOp() const { /** * FIXME: This function duplicates the same function from * KisLayer. We can't move it to KisBaseNode as it doesn't * know anything about parent() method of KisNode * Please think it over... */ const KoColorSpace *colorSpace = this->colorSpace(); if (!colorSpace) return 0; const KoCompositeOp* op = colorSpace->compositeOp(compositeOpId()); return op ? op : colorSpace->compositeOp(COMPOSITE_OVER); } void KisMask::initSelection(KisSelectionSP copyFrom, KisLayerSP parentLayer) { m_d->initSelectionImpl(copyFrom, parentLayer, 0); } void KisMask::initSelection(KisPaintDeviceSP copyFromDevice, KisLayerSP parentLayer) { m_d->initSelectionImpl(0, parentLayer, copyFromDevice); } void KisMask::initSelection(KisLayerSP parentLayer) { m_d->initSelectionImpl(0, parentLayer, 0); } void KisMask::Private::initSelectionImpl(KisSelectionSP copyFrom, KisLayerSP parentLayer, KisPaintDeviceSP copyFromDevice) { Q_ASSERT(parentLayer); KisPaintDeviceSP parentPaintDevice = parentLayer->original(); if (copyFrom) { /** * We can't use setSelection as we may not have parent() yet */ selection = new KisSelection(*copyFrom); selection->setDefaultBounds(new KisSelectionDefaultBounds(parentPaintDevice, parentLayer->image())); if (copyFrom->hasShapeSelection()) { delete selection->flatten(); } } else if (copyFromDevice) { selection = new KisSelection(new KisSelectionDefaultBounds(parentPaintDevice, parentLayer->image())); QRect rc(copyFromDevice->extent()); KisPainter::copyAreaOptimized(rc.topLeft(), copyFromDevice, selection->pixelSelection(), rc); selection->pixelSelection()->invalidateOutlineCache(); } else { selection = new KisSelection(new KisSelectionDefaultBounds(parentPaintDevice, parentLayer->image())); selection->pixelSelection()->setDefaultPixel(KoColor(Qt::white, selection->pixelSelection()->colorSpace())); if (deferredSelectionOffset) { selection->setX(deferredSelectionOffset->x()); selection->setY(deferredSelectionOffset->y()); deferredSelectionOffset.reset(); } } selection->setParentNode(q); selection->updateProjection(); } KisSelectionSP KisMask::selection() const { return m_d->selection; } KisPaintDeviceSP KisMask::paintDevice() const { return selection()->pixelSelection(); } KisPaintDeviceSP KisMask::original() const { return paintDevice(); } KisPaintDeviceSP KisMask::projection() const { return paintDevice(); } KisAbstractProjectionPlaneSP KisMask::projectionPlane() const { return m_d->projectionPlane; } void KisMask::setSelection(KisSelectionSP selection) { m_d->selection = selection; if (parent()) { const KisLayer *parentLayer = qobject_cast(parent()); m_d->selection->setDefaultBounds(new KisDefaultBounds(parentLayer->image())); } m_d->selection->setParentNode(this); } void KisMask::select(const QRect & rc, quint8 selectedness) { KisSelectionSP sel = selection(); KisPixelSelectionSP psel = sel->pixelSelection(); psel->select(rc, selectedness); sel->updateProjection(rc); } QRect KisMask::decorateRect(KisPaintDeviceSP &src, KisPaintDeviceSP &dst, const QRect & rc, PositionToFilthy maskPos) const { Q_UNUSED(src); Q_UNUSED(dst); Q_UNUSED(maskPos); Q_ASSERT_X(0, "KisMask::decorateRect", "Should be overridden by successors"); return rc; } void KisMask::apply(KisPaintDeviceSP projection, const QRect &applyRect, const QRect &needRect, PositionToFilthy maskPos) const { if (selection()) { m_d->selection->updateProjection(applyRect); KisSelectionSP effectiveSelection = m_d->selection; QRect effectiveExtent = effectiveSelection->selectedRect(); { // Access temporary target under the lock held KisIndirectPaintingSupport::ReadLocker l(this); if (hasTemporaryTarget()) { effectiveExtent |= temporaryTarget()->extent(); } if(!effectiveExtent.intersects(applyRect)) { return; } if (hasTemporaryTarget()) { effectiveSelection = m_d->cachedSelection.getSelection(); effectiveSelection->setDefaultBounds(m_d->selection->pixelSelection()->defaultBounds()); KisPainter::copyAreaOptimized(applyRect.topLeft(), m_d->selection->pixelSelection(), effectiveSelection->pixelSelection(), applyRect); KisPainter gc(effectiveSelection->pixelSelection()); setupTemporaryPainter(&gc); gc.bitBlt(applyRect.topLeft(), temporaryTarget(), applyRect); } } KisPaintDeviceSP cacheDevice = m_d->paintDeviceCache.getDevice(projection); QRect updatedRect = decorateRect(projection, cacheDevice, applyRect, maskPos); // masks don't have any compositioning KisPainter::copyAreaOptimized(updatedRect.topLeft(), cacheDevice, projection, updatedRect, effectiveSelection); m_d->paintDeviceCache.putDevice(cacheDevice); if (effectiveSelection != m_d->selection) { m_d->cachedSelection.putSelection(effectiveSelection); } } else { KisPaintDeviceSP cacheDevice = m_d->paintDeviceCache.getDevice(projection); cacheDevice->makeCloneFromRough(projection, needRect); projection->clear(needRect); decorateRect(cacheDevice, projection, applyRect, maskPos); m_d->paintDeviceCache.putDevice(cacheDevice); } } QRect KisMask::needRect(const QRect &rect, PositionToFilthy pos) const { Q_UNUSED(pos); QRect resultRect = rect; - if (m_d->selection) - resultRect &= m_d->selection->selectedRect(); + if (m_d->selection) { + QRect selectionExtent = m_d->selection->selectedRect(); + + // copy for thread safety! + KisPaintDeviceSP temporaryTarget = this->temporaryTarget(); + + if (temporaryTarget) { + selectionExtent |= temporaryTarget->extent(); + } + + resultRect &= selectionExtent; + } return resultRect; } QRect KisMask::changeRect(const QRect &rect, PositionToFilthy pos) const { - Q_UNUSED(pos); - QRect resultRect = rect; - if (m_d->selection) - resultRect &= m_d->selection->selectedRect(); - - return resultRect; + return KisMask::needRect(rect, pos); } QRect KisMask::extent() const { - return m_d->selection ? m_d->selection->selectedRect() : - parent() ? parent()->extent() : QRect(); + QRect resultRect; + + if (m_d->selection) { + resultRect = m_d->selection->selectedRect(); + + // copy for thread safety! + KisPaintDeviceSP temporaryTarget = this->temporaryTarget(); + + if (temporaryTarget) { + resultRect |= temporaryTarget->extent(); + } + } else if (KisNodeSP parent = this->parent()) { + resultRect = parent->extent(); + } + + return resultRect; } QRect KisMask::exactBounds() const { - return m_d->selection ? m_d->selection->selectedExactRect() : - parent() ? parent()->exactBounds() : QRect(); + QRect resultRect; + + if (m_d->selection) { + resultRect = m_d->selection->selectedExactRect(); + + // copy for thread safety! + KisPaintDeviceSP temporaryTarget = this->temporaryTarget(); + + if (temporaryTarget) { + resultRect |= temporaryTarget->exactBounds(); + } + } else if (KisNodeSP parent = this->parent()) { + resultRect = parent->exactBounds(); + } + + return resultRect; } qint32 KisMask::x() const { return m_d->selection ? m_d->selection->x() : m_d->deferredSelectionOffset ? m_d->deferredSelectionOffset->x() : parent() ? parent()->x() : 0; } qint32 KisMask::y() const { return m_d->selection ? m_d->selection->y() : m_d->deferredSelectionOffset ? m_d->deferredSelectionOffset->y() : parent() ? parent()->y() : 0; } void KisMask::setX(qint32 x) { if (m_d->selection) { m_d->selection->setX(x); } else if (!m_d->deferredSelectionOffset) { m_d->deferredSelectionOffset.reset(new QPoint(x, 0)); } else { m_d->deferredSelectionOffset->rx() = x; } } void KisMask::setY(qint32 y) { if (m_d->selection) { m_d->selection->setY(y); } else if (!m_d->deferredSelectionOffset) { m_d->deferredSelectionOffset.reset(new QPoint(0, y)); } else { m_d->deferredSelectionOffset->ry() = y; } } QRect KisMask::nonDependentExtent() const { return QRect(); } QImage KisMask::createThumbnail(qint32 w, qint32 h) { KisPaintDeviceSP originalDevice = selection() ? selection()->projection() : 0; return originalDevice ? originalDevice->createThumbnail(w, h, 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) : QImage(); } void KisMask::testingInitSelection(const QRect &rect, KisLayerSP parentLayer) { if (parentLayer) { m_d->selection = new KisSelection(new KisSelectionDefaultBounds(parentLayer->paintDevice(), parentLayer->image())); } else { m_d->selection = new KisSelection(); } m_d->selection->pixelSelection()->select(rect, OPACITY_OPAQUE_U8); m_d->selection->updateProjection(rect); m_d->selection->setParentNode(this); } KisKeyframeChannel *KisMask::requestKeyframeChannel(const QString &id) { if (id == KisKeyframeChannel::Content.id()) { KisPaintDeviceSP device = paintDevice(); if (device) { KisRasterKeyframeChannel *contentChannel = device->createKeyframeChannel(KisKeyframeChannel::Content); contentChannel->setFilenameSuffix(".pixelselection"); return contentChannel; } } return KisNode::requestKeyframeChannel(id); } void KisMask::baseNodeChangedCallback() { KisNodeSP up = parent(); KisLayer *layer = dynamic_cast(up.data()); if (layer) { layer->notifyChildMaskChanged(this); } KisNode::baseNodeChangedCallback(); } diff --git a/libs/image/kis_paint_device.cc b/libs/image/kis_paint_device.cc index c12dc9d33f..83f0f363e3 100644 --- a/libs/image/kis_paint_device.cc +++ b/libs/image/kis_paint_device.cc @@ -1,2170 +1,2172 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 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 "kis_paint_device.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_image.h" #include "kis_random_sub_accessor.h" #include "kis_selection.h" #include "kis_node.h" #include "kis_datamanager.h" #include "kis_paint_device_writer.h" #include "kis_selection_component.h" #include "kis_pixel_selection.h" #include "kis_repeat_iterators_pixel.h" #include "kis_fixed_paint_device.h" #include "tiles3/kis_hline_iterator.h" #include "tiles3/kis_vline_iterator.h" #include "tiles3/kis_random_accessor.h" #include "kis_default_bounds.h" #include "kis_lod_transform.h" #include "kis_raster_keyframe_channel.h" #include "kis_paint_device_cache.h" #include "kis_paint_device_data.h" #include "kis_paint_device_frames_interface.h" #include "kis_transform_worker.h" #include "kis_filter_strategy.h" #include "krita_utils.h" struct KisPaintDeviceSPStaticRegistrar { KisPaintDeviceSPStaticRegistrar() { qRegisterMetaType("KisPaintDeviceSP"); } }; static KisPaintDeviceSPStaticRegistrar __registrar; struct KisPaintDevice::Private { /** * Used when the paint device is loading to ensure no lod/animation * interferes the process. */ static const KisDefaultBoundsSP transitionalDefaultBounds; public: class KisPaintDeviceStrategy; class KisPaintDeviceWrappedStrategy; Private(KisPaintDevice *paintDevice); ~Private(); KisPaintDevice *q; KisNodeWSP parent; QScopedPointer contentChannel; KisDefaultBoundsBaseSP defaultBounds; QScopedPointer basicStrategy; QScopedPointer wrappedStrategy; QMutex m_wrappedStrategyMutex; QScopedPointer framesInterface; bool isProjectionDevice; KisPaintDeviceStrategy* currentStrategy(); void init(const KoColorSpace *cs, const quint8 *defaultPixel); KUndo2Command* convertColorSpace(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags); bool assignProfile(const KoColorProfile * profile); inline const KoColorSpace* colorSpace() const { return currentData()->colorSpace(); } inline KisDataManagerSP dataManager() const { return currentData()->dataManager(); } inline qint32 x() const { return currentData()->x(); } inline qint32 y() const { return currentData()->y(); } inline void setX(qint32 x) { currentData()->setX(x); } inline void setY(qint32 y) { currentData()->setY(y); } inline KisPaintDeviceCache* cache() { return currentData()->cache(); } inline KisIteratorCompleteListener* cacheInvalidator() { return currentData()->cacheInvalidator(); } void cloneAllDataObjects(Private *rhs, bool copyFrames) { m_lodData.reset(); m_externalFrameData.reset(); if (!m_frames.isEmpty()) { m_frames.clear(); } if (!copyFrames) { if (m_data) { m_data->prepareClone(rhs->currentNonLodData(), true); } else { m_data = toQShared(new KisPaintDeviceData(rhs->currentNonLodData(), true)); } } else { if (m_data && !rhs->m_data) { m_data.clear(); } else if (!m_data && rhs->m_data) { m_data = toQShared(new KisPaintDeviceData(rhs->m_data.data(), true)); } else if (m_data && rhs->m_data) { m_data->prepareClone(rhs->m_data.data(), true); } if (!rhs->m_frames.isEmpty()) { FramesHash::const_iterator it = rhs->m_frames.constBegin(); FramesHash::const_iterator end = rhs->m_frames.constEnd(); for (; it != end; ++it) { DataSP data = toQShared(new KisPaintDeviceData(it.value().data(), true)); m_frames.insert(it.key(), data); } } m_nextFreeFrameId = rhs->m_nextFreeFrameId; } if (rhs->m_lodData) { m_lodData.reset(new KisPaintDeviceData(rhs->m_lodData.data(), true)); } } void prepareClone(KisPaintDeviceSP src) { prepareCloneImpl(src, src->m_d->currentData()); Q_ASSERT(fastBitBltPossible(src)); } bool fastBitBltPossible(KisPaintDeviceSP src) { return fastBitBltPossibleImpl(src->m_d->currentData()); } int currentFrameId() const { KIS_ASSERT_RECOVER(contentChannel) { return -1; } return !defaultBounds->currentLevelOfDetail() ? contentChannel->frameIdAt(defaultBounds->currentTime()) : -1; } KisDataManagerSP frameDataManager(int frameId) const { DataSP data = m_frames[frameId]; return data->dataManager(); } void invalidateFrameCache(int frameId) { DataSP data = m_frames[frameId]; return data->cache()->invalidate(); } private: typedef KisPaintDeviceData Data; typedef QSharedPointer DataSP; typedef QHash FramesHash; class FrameInsertionCommand : public KUndo2Command { public: FrameInsertionCommand(FramesHash *hash, DataSP data, int frameId, bool insert, KUndo2Command *parentCommand) : KUndo2Command(parentCommand), m_hash(hash), m_data(data), m_frameId(frameId), m_insert(insert) { } void redo() override { doSwap(m_insert); } void undo() override { doSwap(!m_insert); } private: void doSwap(bool insert) { if (insert) { m_hash->insert(m_frameId, m_data); } else { DataSP deletedData = m_hash->take(m_frameId); } } private: FramesHash *m_hash; DataSP m_data; int m_frameId; bool m_insert; }; public: int getNextFrameId() { int frameId = 0; while (m_frames.contains(frameId = m_nextFreeFrameId++)); KIS_SAFE_ASSERT_RECOVER_NOOP(!m_frames.contains(frameId)); return frameId; } int createFrame(bool copy, int copySrc, const QPoint &offset, KUndo2Command *parentCommand) { KIS_ASSERT_RECOVER(parentCommand) { return -1; } DataSP data; bool initialFrame = false; if (m_frames.isEmpty()) { /** * Here we move the contents of the paint device to the * new frame and clear m_data to make the "background" for * the areas where there is no frame at all. */ data = toQShared(new Data(m_data.data(), true)); m_data->dataManager()->clear(); m_data->cache()->invalidate(); initialFrame = true; } else if (copy) { DataSP srcData = m_frames[copySrc]; data = toQShared(new Data(srcData.data(), true)); } else { DataSP srcData = m_frames.begin().value(); data = toQShared(new Data(srcData.data(), false)); } if (!initialFrame && !copy) { data->setX(offset.x()); data->setY(offset.y()); } int frameId = getNextFrameId(); KUndo2Command *cmd = new FrameInsertionCommand(&m_frames, data, frameId, true, parentCommand); cmd->redo(); return frameId; } void deleteFrame(int frame, KUndo2Command *parentCommand) { KIS_ASSERT_RECOVER_RETURN(m_frames.contains(frame)); KIS_ASSERT_RECOVER_RETURN(parentCommand); DataSP deletedData = m_frames[frame]; KUndo2Command *cmd = new FrameInsertionCommand(&m_frames, deletedData, frame, false, parentCommand); cmd->redo(); } QRect frameBounds(int frameId) { DataSP data = m_frames[frameId]; QRect extent = data->dataManager()->extent(); extent.translate(data->x(), data->y()); return extent; } QPoint frameOffset(int frameId) const { DataSP data = m_frames[frameId]; return QPoint(data->x(), data->y()); } void setFrameOffset(int frameId, const QPoint &offset) { DataSP data = m_frames[frameId]; data->setX(offset.x()); data->setY(offset.y()); } const QList frameIds() const { return m_frames.keys(); } bool readFrame(QIODevice *stream, int frameId) { bool retval = false; DataSP data = m_frames[frameId]; retval = data->dataManager()->read(stream); data->cache()->invalidate(); return retval; } bool writeFrame(KisPaintDeviceWriter &store, int frameId) { DataSP data = m_frames[frameId]; return data->dataManager()->write(store); } void setFrameDefaultPixel(const KoColor &defPixel, int frameId) { DataSP data = m_frames[frameId]; KoColor color(defPixel); color.convertTo(data->colorSpace()); data->dataManager()->setDefaultPixel(color.data()); } KoColor frameDefaultPixel(int frameId) const { DataSP data = m_frames[frameId]; return KoColor(data->dataManager()->defaultPixel(), data->colorSpace()); } void fetchFrame(int frameId, KisPaintDeviceSP targetDevice); void uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice); void uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice); void uploadFrameData(DataSP srcData, DataSP dstData); struct LodDataStructImpl; LodDataStruct* createLodDataStruct(int lod); void updateLodDataStruct(LodDataStruct *dst, const QRect &srcRect); void uploadLodDataStruct(LodDataStruct *dst); QRegion regionForLodSyncing() const; void tesingFetchLodDevice(KisPaintDeviceSP targetDevice); private: qint64 estimateDataSize(Data *data) const { const QRect &rc = data->dataManager()->extent(); return rc.width() * rc.height() * data->colorSpace()->pixelSize(); } public: void estimateMemoryStats(qint64 &imageData, qint64 &temporaryData, qint64 &lodData) const { imageData = 0; temporaryData = 0; lodData = 0; if (m_data) { imageData += estimateDataSize(m_data.data()); } if (m_lodData) { lodData += estimateDataSize(m_lodData.data()); } if (m_externalFrameData) { temporaryData += estimateDataSize(m_externalFrameData.data()); } Q_FOREACH (DataSP value, m_frames.values()) { imageData += estimateDataSize(value.data()); } } private: QRegion syncWholeDevice(Data *srcData); inline DataSP currentFrameData() const { DataSP data; const int numberOfFrames = contentChannel->keyframeCount(); if (numberOfFrames > 1) { int frameId = contentChannel->frameIdAt(defaultBounds->currentTime()); if (frameId == -1) { data = m_data; } else { KIS_ASSERT_RECOVER(m_frames.contains(frameId)) { return m_frames.begin().value(); } data = m_frames[frameId]; } } else if (numberOfFrames == 1) { data = m_frames.begin().value(); } else { data = m_data; } return data; } inline Data* currentNonLodData() const { Data *data = m_data.data(); if (contentChannel) { data = currentFrameData().data(); } else if (isProjectionDevice && defaultBounds->externalFrameActive()) { if (!m_externalFrameData) { QMutexLocker l(&m_dataSwitchLock); if (!m_externalFrameData) { m_externalFrameData.reset(new Data(m_data.data(), false)); } } data = m_externalFrameData.data(); } return data; } inline void ensureLodDataPresent() const { if (!m_lodData) { Data *srcData = currentNonLodData(); QMutexLocker l(&m_dataSwitchLock); if (!m_lodData) { m_lodData.reset(new Data(srcData, false)); } } } inline Data* currentData() const { Data *data; if (defaultBounds->currentLevelOfDetail()) { ensureLodDataPresent(); data = m_lodData.data(); } else { data = currentNonLodData(); } return data; } void prepareCloneImpl(KisPaintDeviceSP src, Data *srcData) { currentData()->prepareClone(srcData); q->setDefaultPixel(KoColor(srcData->dataManager()->defaultPixel(), colorSpace())); q->setDefaultBounds(src->defaultBounds()); } bool fastBitBltPossibleImpl(Data *srcData) { return x() == srcData->x() && y() == srcData->y() && *colorSpace() == *srcData->colorSpace(); } QList allDataObjects() const { QList dataObjects; if (m_frames.isEmpty()) { dataObjects << m_data.data(); } dataObjects << m_lodData.data(); dataObjects << m_externalFrameData.data(); Q_FOREACH (DataSP value, m_frames.values()) { dataObjects << value.data(); } return dataObjects; } void transferFromData(Data *data, KisPaintDeviceSP targetDevice); struct Q_DECL_HIDDEN StrategyPolicy; typedef KisSequentialIteratorBase, StrategyPolicy> InternalSequentialConstIterator; typedef KisSequentialIteratorBase, StrategyPolicy> InternalSequentialIterator; private: friend class KisPaintDeviceFramesInterface; private: DataSP m_data; mutable QScopedPointer m_lodData; mutable QScopedPointer m_externalFrameData; mutable QMutex m_dataSwitchLock; FramesHash m_frames; int m_nextFreeFrameId; }; const KisDefaultBoundsSP KisPaintDevice::Private::transitionalDefaultBounds = new KisDefaultBounds(); #include "kis_paint_device_strategies.h" KisPaintDevice::Private::Private(KisPaintDevice *paintDevice) : q(paintDevice), basicStrategy(new KisPaintDeviceStrategy(paintDevice, this)), isProjectionDevice(false), m_data(new Data(paintDevice)), m_nextFreeFrameId(0) { } KisPaintDevice::Private::~Private() { m_frames.clear(); } KisPaintDevice::Private::KisPaintDeviceStrategy* KisPaintDevice::Private::currentStrategy() { if (!defaultBounds->wrapAroundMode()) { return basicStrategy.data(); } const QRect wrapRect = defaultBounds->bounds(); if (!wrappedStrategy || wrappedStrategy->wrapRect() != wrapRect) { QMutexLocker locker(&m_wrappedStrategyMutex); if (!wrappedStrategy) { wrappedStrategy.reset(new KisPaintDeviceWrappedStrategy(wrapRect, q, this)); } else if (wrappedStrategy->wrapRect() != wrapRect) { wrappedStrategy->setWrapRect(wrapRect); } } return wrappedStrategy.data(); } struct KisPaintDevice::Private::StrategyPolicy { StrategyPolicy(KisPaintDevice::Private::KisPaintDeviceStrategy *strategy, KisDataManager *dataManager, qint32 offsetX, qint32 offsetY) : m_strategy(strategy), m_dataManager(dataManager), m_offsetX(offsetX), m_offsetY(offsetY) { } KisHLineConstIteratorSP createConstIterator(const QRect &rect) { return m_strategy->createHLineConstIteratorNG(m_dataManager, rect.x(), rect.y(), rect.width(), m_offsetX, m_offsetY); } KisHLineIteratorSP createIterator(const QRect &rect) { return m_strategy->createHLineIteratorNG(m_dataManager, rect.x(), rect.y(), rect.width(), m_offsetX, m_offsetY); } int pixelSize() const { return m_dataManager->pixelSize(); } KisPaintDeviceStrategy *m_strategy; KisDataManager *m_dataManager; int m_offsetX; int m_offsetY; }; struct KisPaintDevice::Private::LodDataStructImpl : public KisPaintDevice::LodDataStruct { LodDataStructImpl(Data *_lodData) : lodData(_lodData) {} QScopedPointer lodData; }; QRegion KisPaintDevice::Private::regionForLodSyncing() const { Data *srcData = currentNonLodData(); return srcData->dataManager()->region().translated(srcData->x(), srcData->y()); } KisPaintDevice::LodDataStruct* KisPaintDevice::Private::createLodDataStruct(int newLod) { + KIS_SAFE_ASSERT_RECOVER_NOOP(newLod > 0); + Data *srcData = currentNonLodData(); Data *lodData = new Data(srcData, false); LodDataStruct *lodStruct = new LodDataStructImpl(lodData); int expectedX = KisLodTransform::coordToLodCoord(srcData->x(), newLod); int expectedY = KisLodTransform::coordToLodCoord(srcData->y(), newLod); /** * We compare color spaces as pure pointers, because they must be * exactly the same, since they come from the common source. */ if (lodData->levelOfDetail() != newLod || lodData->colorSpace() != srcData->colorSpace() || lodData->x() != expectedX || lodData->y() != expectedY) { lodData->prepareClone(srcData); lodData->setLevelOfDetail(newLod); lodData->setX(expectedX); lodData->setY(expectedY); // FIXME: different kind of synchronization } //QRegion dirtyRegion = syncWholeDevice(srcData); lodData->cache()->invalidate(); return lodStruct; } void KisPaintDevice::Private::updateLodDataStruct(LodDataStruct *_dst, const QRect &originalRect) { LodDataStructImpl *dst = dynamic_cast(_dst); KIS_SAFE_ASSERT_RECOVER_RETURN(dst); Data *lodData = dst->lodData.data(); Data *srcData = currentNonLodData(); const int lod = lodData->levelOfDetail(); const int srcStepSize = 1 << lod; KIS_ASSERT_RECOVER_RETURN(lod > 0); const QRect srcRect = KisLodTransform::alignedRect(originalRect, lod); const QRect dstRect = KisLodTransform::scaledRect(srcRect, lod); if (!srcRect.isValid() || !dstRect.isValid()) return; KIS_ASSERT_RECOVER_NOOP(srcRect.width() / srcStepSize == dstRect.width()); const int pixelSize = srcData->dataManager()->pixelSize(); int rowsAccumulated = 0; int columnsAccumulated = 0; KoMixColorsOp *mixOp = colorSpace()->mixColorsOp(); QScopedArrayPointer blendData(new quint8[srcStepSize * srcRect.width() * pixelSize]); quint8 *blendDataPtr = blendData.data(); int blendDataOffset = 0; const int srcCellSize = srcStepSize * srcStepSize; const int srcCellStride = srcCellSize * pixelSize; const int srcStepStride = srcStepSize * pixelSize; const int srcColumnStride = (srcStepSize - 1) * srcStepStride; QScopedArrayPointer weights(new qint16[srcCellSize]); { const qint16 averageWeight = qCeil(255.0 / srcCellSize); const qint16 extraWeight = averageWeight * srcCellSize - 255; KIS_ASSERT_RECOVER_NOOP(extraWeight == 1); for (int i = 0; i < srcCellSize - 1; i++) { weights[i] = averageWeight; } weights[srcCellSize - 1] = averageWeight - extraWeight; } InternalSequentialConstIterator srcIntIt(StrategyPolicy(currentStrategy(), srcData->dataManager().data(), srcData->x(), srcData->y()), srcRect); InternalSequentialIterator dstIntIt(StrategyPolicy(currentStrategy(), lodData->dataManager().data(), lodData->x(), lodData->y()), dstRect); int rowsRemaining = srcRect.height(); while (rowsRemaining > 0) { int colsRemaining = srcRect.width(); while (colsRemaining > 0 && srcIntIt.nextPixel()) { memcpy(blendDataPtr, srcIntIt.rawDataConst(), pixelSize); blendDataPtr += pixelSize; columnsAccumulated++; if (columnsAccumulated >= srcStepSize) { blendDataPtr += srcColumnStride; columnsAccumulated = 0; } colsRemaining--; } rowsAccumulated++; if (rowsAccumulated >= srcStepSize) { // blend and write the final data blendDataPtr = blendData.data(); int colsRemaining = dstRect.width(); while (colsRemaining > 0 && dstIntIt.nextPixel()) { mixOp->mixColors(blendDataPtr, weights.data(), srcCellSize, dstIntIt.rawData()); blendDataPtr += srcCellStride; colsRemaining--; } // reset counters rowsAccumulated = 0; blendDataPtr = blendData.data(); blendDataOffset = 0; } else { blendDataOffset += srcStepStride; blendDataPtr = blendData.data() + blendDataOffset; } rowsRemaining--; } } void KisPaintDevice::Private::uploadLodDataStruct(LodDataStruct *_dst) { LodDataStructImpl *dst = dynamic_cast(_dst); KIS_SAFE_ASSERT_RECOVER_RETURN(dst); KIS_SAFE_ASSERT_RECOVER_RETURN( dst->lodData->levelOfDetail() == defaultBounds->currentLevelOfDetail()); ensureLodDataPresent(); m_lodData->prepareClone(dst->lodData.data()); m_lodData->dataManager()->bitBltRough(dst->lodData->dataManager(), dst->lodData->dataManager()->extent()); } void KisPaintDevice::Private::transferFromData(Data *data, KisPaintDeviceSP targetDevice) { QRect extent = data->dataManager()->extent(); extent.translate(data->x(), data->y()); targetDevice->m_d->prepareCloneImpl(q, data); targetDevice->m_d->currentStrategy()->fastBitBltRough(data->dataManager(), extent); } void KisPaintDevice::Private::fetchFrame(int frameId, KisPaintDeviceSP targetDevice) { DataSP data = m_frames[frameId]; transferFromData(data.data(), targetDevice); } void KisPaintDevice::Private::uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice) { DataSP dstData = m_frames[dstFrameId]; KIS_ASSERT_RECOVER_RETURN(dstData); DataSP srcData = srcDevice->m_d->m_frames[srcFrameId]; KIS_ASSERT_RECOVER_RETURN(srcData); uploadFrameData(srcData, dstData); } void KisPaintDevice::Private::uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice) { DataSP dstData = m_frames[dstFrameId]; KIS_ASSERT_RECOVER_RETURN(dstData); DataSP srcData = srcDevice->m_d->m_data; KIS_ASSERT_RECOVER_RETURN(srcData); uploadFrameData(srcData, dstData); } void KisPaintDevice::Private::uploadFrameData(DataSP srcData, DataSP dstData) { if (srcData->colorSpace() != dstData->colorSpace() && *srcData->colorSpace() != *dstData->colorSpace()) { KUndo2Command tempCommand; srcData = toQShared(new Data(srcData.data(), true)); srcData->convertDataColorSpace(dstData->colorSpace(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags(), &tempCommand); } dstData->dataManager()->clear(); dstData->cache()->invalidate(); const QRect rect = srcData->dataManager()->extent(); dstData->dataManager()->bitBltRough(srcData->dataManager(), rect); dstData->setX(srcData->x()); dstData->setY(srcData->y()); } void KisPaintDevice::Private::tesingFetchLodDevice(KisPaintDeviceSP targetDevice) { Data *data = m_lodData.data(); Q_ASSERT(data); transferFromData(data, targetDevice); } KUndo2Command* KisPaintDevice::Private::convertColorSpace(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { class DeviceChangeColorSpaceCommand : public KUndo2Command { public: DeviceChangeColorSpaceCommand(KisPaintDeviceSP device) : m_firstRun(true), m_device(device) { } void emitNotifications() { m_device->emitColorSpaceChanged(); m_device->setDirty(); } void redo() override { KUndo2Command::redo(); if (!m_firstRun) { m_firstRun = false; return; } emitNotifications(); } void undo() override { KUndo2Command::undo(); emitNotifications(); } private: bool m_firstRun; KisPaintDeviceSP m_device; }; KUndo2Command *parentCommand = new DeviceChangeColorSpaceCommand(q); QList dataObjects = allDataObjects(); Q_FOREACH (Data *data, dataObjects) { if (!data) continue; data->convertDataColorSpace(dstColorSpace, renderingIntent, conversionFlags, parentCommand); } if (!parentCommand->childCount()) { delete parentCommand; parentCommand = 0; } else { q->emitColorSpaceChanged(); } return parentCommand; } bool KisPaintDevice::Private::assignProfile(const KoColorProfile * profile) { if (!profile) return false; const KoColorSpace *dstColorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile); if (!dstColorSpace) return false; QList dataObjects = allDataObjects(); Q_FOREACH (Data *data, dataObjects) { if (!data) continue; data->assignColorSpace(dstColorSpace); } q->emitProfileChanged(); // no undo information is provided here return true; } void KisPaintDevice::Private::init(const KoColorSpace *cs, const quint8 *defaultPixel) { QList dataObjects = allDataObjects(); Q_FOREACH (Data *data, dataObjects) { if (!data) continue; KisDataManagerSP dataManager = new KisDataManager(cs->pixelSize(), defaultPixel); data->init(cs, dataManager); } } KisPaintDevice::KisPaintDevice(const KoColorSpace * colorSpace, const QString& name) : QObject(0) , m_d(new Private(this)) { init(colorSpace, new KisDefaultBounds(), 0, name); } KisPaintDevice::KisPaintDevice(KisNodeWSP parent, const KoColorSpace * colorSpace, KisDefaultBoundsBaseSP defaultBounds, const QString& name) : QObject(0) , m_d(new Private(this)) { init(colorSpace, defaultBounds, parent, name); } void KisPaintDevice::init(const KoColorSpace *colorSpace, KisDefaultBoundsBaseSP defaultBounds, KisNodeWSP parent, const QString& name) { Q_ASSERT(colorSpace); setObjectName(name); // temporary def. bounds object for the initialization phase only m_d->defaultBounds = m_d->transitionalDefaultBounds; if (!defaultBounds) { // Reuse transitionalDefaultBounds here. Change if you change // semantics of transitionalDefaultBounds defaultBounds = m_d->transitionalDefaultBounds; } QScopedArrayPointer defaultPixel(new quint8[colorSpace->pixelSize()]); colorSpace->fromQColor(Qt::transparent, defaultPixel.data()); m_d->init(colorSpace, defaultPixel.data()); Q_ASSERT(m_d->colorSpace()); setDefaultBounds(defaultBounds); setParentNode(parent); } KisPaintDevice::KisPaintDevice(const KisPaintDevice& rhs, KritaUtils::DeviceCopyMode copyMode, KisNode *newParentNode) : QObject() , KisShared() , m_d(new Private(this)) { if (this != &rhs) { // temporary def. bounds object for the initialization phase only m_d->defaultBounds = m_d->transitionalDefaultBounds; // copy data objects with or without frames m_d->cloneAllDataObjects(rhs.m_d, copyMode == KritaUtils::CopyAllFrames); if (copyMode == KritaUtils::CopyAllFrames && rhs.m_d->framesInterface) { KIS_ASSERT_RECOVER_RETURN(rhs.m_d->framesInterface); KIS_ASSERT_RECOVER_RETURN(rhs.m_d->contentChannel); m_d->framesInterface.reset(new KisPaintDeviceFramesInterface(this)); m_d->contentChannel.reset(new KisRasterKeyframeChannel(*rhs.m_d->contentChannel.data(), newParentNode, this)); } setDefaultBounds(rhs.m_d->defaultBounds); setParentNode(newParentNode); } } KisPaintDevice::~KisPaintDevice() { delete m_d; } void KisPaintDevice::setProjectionDevice(bool value) { m_d->isProjectionDevice = value; } void KisPaintDevice::prepareClone(KisPaintDeviceSP src) { m_d->prepareClone(src); Q_ASSERT(fastBitBltPossible(src)); } void KisPaintDevice::makeCloneFrom(KisPaintDeviceSP src, const QRect &rect) { prepareClone(src); // we guarantee that *this is totally empty, so copy pixels that // are areally present on the source image only const QRect optimizedRect = rect & src->extent(); fastBitBlt(src, optimizedRect); } void KisPaintDevice::makeCloneFromRough(KisPaintDeviceSP src, const QRect &minimalRect) { prepareClone(src); // we guarantee that *this is totally empty, so copy pixels that // are areally present on the source image only const QRect optimizedRect = minimalRect & src->extent(); fastBitBltRough(src, optimizedRect); } void KisPaintDevice::setDirty() { m_d->cache()->invalidate(); if (m_d->parent.isValid()) m_d->parent->setDirty(); } void KisPaintDevice::setDirty(const QRect & rc) { m_d->cache()->invalidate(); if (m_d->parent.isValid()) m_d->parent->setDirty(rc); } void KisPaintDevice::setDirty(const QRegion & region) { m_d->cache()->invalidate(); if (m_d->parent.isValid()) m_d->parent->setDirty(region); } void KisPaintDevice::setDirty(const QVector rects) { m_d->cache()->invalidate(); if (m_d->parent.isValid()) m_d->parent->setDirty(rects); } void KisPaintDevice::requestTimeSwitch(int time) { if (m_d->parent.isValid()) { m_d->parent->requestTimeSwitch(time); } } int KisPaintDevice::sequenceNumber() const { return m_d->cache()->sequenceNumber(); } void KisPaintDevice::estimateMemoryStats(qint64 &imageData, qint64 &temporaryData, qint64 &lodData) const { m_d->estimateMemoryStats(imageData, temporaryData, lodData); } void KisPaintDevice::setParentNode(KisNodeWSP parent) { m_d->parent = parent; } // for testing purposes only KisNodeWSP KisPaintDevice::parentNode() const { return m_d->parent; } void KisPaintDevice::setDefaultBounds(KisDefaultBoundsBaseSP defaultBounds) { m_d->defaultBounds = defaultBounds; m_d->cache()->invalidate(); } KisDefaultBoundsBaseSP KisPaintDevice::defaultBounds() const { return m_d->defaultBounds; } void KisPaintDevice::moveTo(const QPoint &pt) { m_d->currentStrategy()->move(pt); m_d->cache()->invalidate(); } QPoint KisPaintDevice::offset() const { return QPoint(x(), y()); } void KisPaintDevice::moveTo(qint32 x, qint32 y) { moveTo(QPoint(x, y)); } void KisPaintDevice::setX(qint32 x) { moveTo(QPoint(x, m_d->y())); } void KisPaintDevice::setY(qint32 y) { moveTo(QPoint(m_d->x(), y)); } qint32 KisPaintDevice::x() const { return m_d->x(); } qint32 KisPaintDevice::y() const { return m_d->y(); } void KisPaintDevice::extent(qint32 &x, qint32 &y, qint32 &w, qint32 &h) const { QRect rc = extent(); x = rc.x(); y = rc.y(); w = rc.width(); h = rc.height(); } QRect KisPaintDevice::extent() const { return m_d->currentStrategy()->extent(); } QRegion KisPaintDevice::region() const { return m_d->currentStrategy()->region(); } QRect KisPaintDevice::nonDefaultPixelArea() const { return m_d->cache()->nonDefaultPixelArea(); } QRect KisPaintDevice::exactBounds() const { return m_d->cache()->exactBounds(); } QRect KisPaintDevice::exactBoundsAmortized() const { return m_d->cache()->exactBoundsAmortized(); } namespace Impl { struct CheckFullyTransparent { CheckFullyTransparent(const KoColorSpace *colorSpace) : m_colorSpace(colorSpace) { } bool isPixelEmpty(const quint8 *pixelData) { return m_colorSpace->opacityU8(pixelData) == OPACITY_TRANSPARENT_U8; } private: const KoColorSpace *m_colorSpace; }; struct CheckNonDefault { CheckNonDefault(int pixelSize, const quint8 *defaultPixel) : m_pixelSize(pixelSize), m_defaultPixel(defaultPixel) { } bool isPixelEmpty(const quint8 *pixelData) { return memcmp(m_defaultPixel, pixelData, m_pixelSize) == 0; } private: int m_pixelSize; const quint8 *m_defaultPixel; }; template QRect calculateExactBoundsImpl(const KisPaintDevice *device, const QRect &startRect, const QRect &endRect, ComparePixelOp compareOp) { if (startRect == endRect) return startRect; // the passed extent might have weird invalid structure that // can overflow integer precision when calling startRect.right() if (!startRect.isValid()) return QRect(); // Solution n°2 int x, y, w, h; int boundLeft, boundTop, boundRight, boundBottom; int endDirN, endDirE, endDirS, endDirW; startRect.getRect(&x, &y, &w, &h); if (endRect.isEmpty()) { endDirS = startRect.bottom(); endDirN = startRect.top(); endDirE = startRect.right(); endDirW = startRect.left(); startRect.getCoords(&boundLeft, &boundTop, &boundRight, &boundBottom); } else { endDirS = endRect.top() - 1; endDirN = endRect.bottom() + 1; endDirE = endRect.left() - 1; endDirW = endRect.right() + 1; endRect.getCoords(&boundLeft, &boundTop, &boundRight, &boundBottom); } // XXX: a small optimization is possible by using H/V line iterators in the first // and third cases, at the cost of making the code a bit more complex KisRandomConstAccessorSP accessor = device->createRandomConstAccessorNG(x, y); bool found = false; { for (qint32 y2 = y; y2 <= endDirS; ++y2) { for (qint32 x2 = x; x2 < x + w || found; ++ x2) { accessor->moveTo(x2, y2); if (!compareOp.isPixelEmpty(accessor->rawDataConst())) { boundTop = y2; found = true; break; } } if (found) break; } } /** * If the first pass hasn't found any opaque pixel, there is no * reason to check that 3 more times. They will not appear in the * meantime. Just return an empty bounding rect. */ if (!found && endRect.isEmpty()) { return QRect(); } found = false; for (qint32 y2 = y + h - 1; y2 >= endDirN ; --y2) { for (qint32 x2 = x + w - 1; x2 >= x || found; --x2) { accessor->moveTo(x2, y2); if (!compareOp.isPixelEmpty(accessor->rawDataConst())) { boundBottom = y2; found = true; break; } } if (found) break; } found = false; { for (qint32 x2 = x; x2 <= endDirE ; ++x2) { for (qint32 y2 = y; y2 < y + h || found; ++y2) { accessor->moveTo(x2, y2); if (!compareOp.isPixelEmpty(accessor->rawDataConst())) { boundLeft = x2; found = true; break; } } if (found) break; } } found = false; // Look for right edge ) { for (qint32 x2 = x + w - 1; x2 >= endDirW; --x2) { for (qint32 y2 = y + h - 1; y2 >= y || found; --y2) { accessor->moveTo(x2, y2); if (!compareOp.isPixelEmpty(accessor->rawDataConst())) { boundRight = x2; found = true; break; } } if (found) break; } } return QRect(boundLeft, boundTop, boundRight - boundLeft + 1, boundBottom - boundTop + 1); } } QRect KisPaintDevice::calculateExactBounds(bool nonDefaultOnly) const { QRect startRect = extent(); QRect endRect; quint8 defaultOpacity = defaultPixel().opacityU8(); if (defaultOpacity != OPACITY_TRANSPARENT_U8) { if (!nonDefaultOnly) { /** * We will calculate exact bounds only outside of the * image bounds, and that'll be nondefault area only. */ endRect = defaultBounds()->bounds(); nonDefaultOnly = true; } else { startRect = region().boundingRect(); } } if (nonDefaultOnly) { const KoColor defaultPixel = this->defaultPixel(); Impl::CheckNonDefault compareOp(pixelSize(), defaultPixel.data()); endRect = Impl::calculateExactBoundsImpl(this, startRect, endRect, compareOp); } else { Impl::CheckFullyTransparent compareOp(m_d->colorSpace()); endRect = Impl::calculateExactBoundsImpl(this, startRect, endRect, compareOp); } return endRect; } QRegion KisPaintDevice::regionExact() const { QRegion resultRegion; QVector rects = region().rects(); const KoColor defaultPixel = this->defaultPixel(); Impl::CheckNonDefault compareOp(pixelSize(), defaultPixel.data()); Q_FOREACH (const QRect &rc1, rects) { const int patchSize = 64; QVector smallerRects = KritaUtils::splitRectIntoPatches(rc1, QSize(patchSize, patchSize)); Q_FOREACH (const QRect &rc2, smallerRects) { const QRect result = Impl::calculateExactBoundsImpl(this, rc2, QRect(), compareOp); if (!result.isEmpty()) { resultRegion += result; } } } return resultRegion; } void KisPaintDevice::crop(qint32 x, qint32 y, qint32 w, qint32 h) { crop(QRect(x, y, w, h)); } void KisPaintDevice::crop(const QRect &rect) { m_d->currentStrategy()->crop(rect); } void KisPaintDevice::purgeDefaultPixels() { KisDataManagerSP dm = m_d->dataManager(); dm->purge(dm->extent()); } void KisPaintDevice::setDefaultPixel(const KoColor &defPixel) { KoColor color(defPixel); color.convertTo(colorSpace()); m_d->dataManager()->setDefaultPixel(color.data()); m_d->cache()->invalidate(); } KoColor KisPaintDevice::defaultPixel() const { return KoColor(m_d->dataManager()->defaultPixel(), colorSpace()); } void KisPaintDevice::clear() { m_d->dataManager()->clear(); m_d->cache()->invalidate(); } void KisPaintDevice::clear(const QRect & rc) { m_d->currentStrategy()->clear(rc); } void KisPaintDevice::fill(const QRect & rc, const KoColor &color) { KIS_ASSERT_RECOVER_RETURN(*color.colorSpace() == *colorSpace()); m_d->currentStrategy()->fill(rc, color.data()); } void KisPaintDevice::fill(qint32 x, qint32 y, qint32 w, qint32 h, const quint8 *fillPixel) { m_d->currentStrategy()->fill(QRect(x, y, w, h), fillPixel); } bool KisPaintDevice::write(KisPaintDeviceWriter &store) { return m_d->dataManager()->write(store); } bool KisPaintDevice::read(QIODevice *stream) { bool retval; retval = m_d->dataManager()->read(stream); m_d->cache()->invalidate(); return retval; } void KisPaintDevice::emitColorSpaceChanged() { emit colorSpaceChanged(m_d->colorSpace()); } void KisPaintDevice::emitProfileChanged() { emit profileChanged(m_d->colorSpace()->profile()); } KUndo2Command* KisPaintDevice::convertTo(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { KUndo2Command *command = m_d->convertColorSpace(dstColorSpace, renderingIntent, conversionFlags); return command; } bool KisPaintDevice::setProfile(const KoColorProfile * profile) { return m_d->assignProfile(profile); } KisDataManagerSP KisPaintDevice::dataManager() const { return m_d->dataManager(); } void KisPaintDevice::convertFromQImage(const QImage& _image, const KoColorProfile *profile, qint32 offsetX, qint32 offsetY) { QImage image = _image; if (image.format() != QImage::Format_ARGB32) { image = image.convertToFormat(QImage::Format_ARGB32); } // Don't convert if not no profile is given and both paint dev and qimage are rgba. if (!profile && colorSpace()->id() == "RGBA") { writeBytes(image.constBits(), offsetX, offsetY, image.width(), image.height()); } else { try { quint8 * dstData = new quint8[image.width() * image.height() * pixelSize()]; KoColorSpaceRegistry::instance() ->colorSpace(RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), profile) ->convertPixelsTo(image.constBits(), dstData, colorSpace(), image.width() * image.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); writeBytes(dstData, offsetX, offsetY, image.width(), image.height()); delete[] dstData; } catch (std::bad_alloc) { warnKrita << "KisPaintDevice::convertFromQImage: Could not allocate" << image.width() * image.height() * pixelSize() << "bytes"; return; } } m_d->cache()->invalidate(); } QImage KisPaintDevice::convertToQImage(const KoColorProfile *dstProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { qint32 x1; qint32 y1; qint32 w; qint32 h; QRect rc = exactBounds(); x1 = rc.x(); y1 = rc.y(); w = rc.width(); h = rc.height(); return convertToQImage(dstProfile, x1, y1, w, h, renderingIntent, conversionFlags); } QImage KisPaintDevice::convertToQImage(const KoColorProfile *dstProfile, const QRect &rc, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { return convertToQImage(dstProfile, rc.x(), rc.y(), rc.width(), rc.height(), renderingIntent, conversionFlags); } QImage KisPaintDevice::convertToQImage(const KoColorProfile *dstProfile, qint32 x1, qint32 y1, qint32 w, qint32 h, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { if (w < 0) return QImage(); if (h < 0) return QImage(); quint8 *data = 0; try { data = new quint8 [w * h * pixelSize()]; } catch (std::bad_alloc) { warnKrita << "KisPaintDevice::convertToQImage std::bad_alloc for " << w << " * " << h << " * " << pixelSize(); //delete[] data; // data is not allocated, so don't free it return QImage(); } Q_CHECK_PTR(data); // XXX: Is this really faster than converting line by line and building the QImage directly? // This copies potentially a lot of data. readBytes(data, x1, y1, w, h); QImage image = colorSpace()->convertToQImage(data, w, h, dstProfile, renderingIntent, conversionFlags); delete[] data; return image; } inline bool moveBy(KisSequentialConstIterator& iter, int numPixels) { int pos = 0; while (pos < numPixels) { int step = std::min(iter.nConseqPixels(), numPixels - pos); if (!iter.nextPixels(step)) return false; pos += step; } return true; } static KisPaintDeviceSP createThumbnailDeviceInternal(const KisPaintDevice* srcDev, qint32 srcX0, qint32 srcY0, qint32 srcWidth, qint32 srcHeight, qint32 w, qint32 h, QRect outputRect) { KisPaintDeviceSP thumbnail = new KisPaintDevice(srcDev->colorSpace()); qint32 pixelSize = srcDev->pixelSize(); KisRandomConstAccessorSP srcIter = srcDev->createRandomConstAccessorNG(0, 0); KisRandomAccessorSP dstIter = thumbnail->createRandomAccessorNG(0, 0); for (qint32 y = outputRect.y(); y < outputRect.y() + outputRect.height(); ++y) { qint32 iY = srcY0 + (y * srcHeight) / h; for (qint32 x = outputRect.x(); x < outputRect.x() + outputRect.width(); ++x) { qint32 iX = srcX0 + (x * srcWidth) / w; srcIter->moveTo(iX, iY); dstIter->moveTo(x, y); memcpy(dstIter->rawData(), srcIter->rawDataConst(), pixelSize); } } return thumbnail; } QSize fixThumbnailSize(QSize size) { if (!size.width() && size.height()) { size.setWidth(1); } if (size.width() && !size.height()) { size.setHeight(1); } return size; } KisPaintDeviceSP KisPaintDevice::createThumbnailDevice(qint32 w, qint32 h, QRect rect, QRect outputRect) const { QSize thumbnailSize(w, h); QRect imageRect = rect.isValid() ? rect : extent(); if ((thumbnailSize.width() > imageRect.width()) || (thumbnailSize.height() > imageRect.height())) { thumbnailSize.scale(imageRect.size(), Qt::KeepAspectRatio); } thumbnailSize = fixThumbnailSize(thumbnailSize); //can't create thumbnail for an empty device, e.g. layer thumbnail for empty image if (imageRect.isEmpty() || thumbnailSize.isEmpty()) { return new KisPaintDevice(colorSpace()); } int srcWidth, srcHeight; int srcX0, srcY0; imageRect.getRect(&srcX0, &srcY0, &srcWidth, &srcHeight); if (!outputRect.isValid()) { outputRect = QRect(0, 0, w, h); } KisPaintDeviceSP thumbnail = createThumbnailDeviceInternal(this, imageRect.x(), imageRect.y(), imageRect.width(), imageRect.height(), thumbnailSize.width(), thumbnailSize.height(), outputRect); return thumbnail; } KisPaintDeviceSP KisPaintDevice::createThumbnailDeviceOversampled(qint32 w, qint32 h, qreal oversample, QRect rect, QRect outputTileRect) const { QSize thumbnailSize(w, h); qreal oversampleAdjusted = qMax(oversample, 1.); QSize thumbnailOversampledSize = oversampleAdjusted * thumbnailSize; QRect outputRect; QRect imageRect = rect.isValid() ? rect : extent(); qint32 hstart = thumbnailOversampledSize.height(); if ((thumbnailOversampledSize.width() > imageRect.width()) || (thumbnailOversampledSize.height() > imageRect.height())) { thumbnailOversampledSize.scale(imageRect.size(), Qt::KeepAspectRatio); } thumbnailOversampledSize = fixThumbnailSize(thumbnailOversampledSize); //can't create thumbnail for an empty device, e.g. layer thumbnail for empty image if (imageRect.isEmpty() || thumbnailSize.isEmpty() || thumbnailOversampledSize.isEmpty()) { return new KisPaintDevice(colorSpace()); } oversampleAdjusted *= (hstart > 0) ? ((qreal)thumbnailOversampledSize.height() / hstart) : 1.; //readjusting oversample ratio, given that we had to adjust thumbnail size outputRect = QRect(0, 0, thumbnailOversampledSize.width(), thumbnailOversampledSize.height()); if (outputTileRect.isValid()) { //compensating output rectangle for oversampling outputTileRect = QRect(oversampleAdjusted * outputTileRect.topLeft(), oversampleAdjusted * outputTileRect.bottomRight()); outputRect = outputRect.intersected(outputTileRect); } KisPaintDeviceSP thumbnail = createThumbnailDeviceInternal(this, imageRect.x(), imageRect.y(), imageRect.width(), imageRect.height(), thumbnailOversampledSize.width(), thumbnailOversampledSize.height(), outputRect); if (oversample != 1. && oversampleAdjusted != 1.) { KoDummyUpdater updater; KisTransformWorker worker(thumbnail, 1 / oversampleAdjusted, 1 / oversampleAdjusted, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, &updater, KisFilterStrategyRegistry::instance()->value("Bilinear")); worker.run(); } return thumbnail; } QImage KisPaintDevice::createThumbnail(qint32 w, qint32 h, QRect rect, qreal oversample, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { QSize size = fixThumbnailSize(QSize(w, h)); KisPaintDeviceSP dev = createThumbnailDeviceOversampled(size.width(), size.height(), oversample, rect); QImage thumbnail = dev->convertToQImage(KoColorSpaceRegistry::instance()->rgb8()->profile(), 0, 0, w, h, renderingIntent, conversionFlags); return thumbnail; } QImage KisPaintDevice::createThumbnail(qint32 w, qint32 h, qreal oversample, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { QSize size = fixThumbnailSize(QSize(w, h)); return m_d->cache()->createThumbnail(size.width(), size.height(), oversample, renderingIntent, conversionFlags); } KisHLineIteratorSP KisPaintDevice::createHLineIteratorNG(qint32 x, qint32 y, qint32 w) { m_d->cache()->invalidate(); return m_d->currentStrategy()->createHLineIteratorNG(m_d->dataManager().data(), x, y, w, m_d->x(), m_d->y()); } KisHLineConstIteratorSP KisPaintDevice::createHLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const { return m_d->currentStrategy()->createHLineConstIteratorNG(m_d->dataManager().data(), x, y, w, m_d->x(), m_d->y()); } KisVLineIteratorSP KisPaintDevice::createVLineIteratorNG(qint32 x, qint32 y, qint32 w) { m_d->cache()->invalidate(); return m_d->currentStrategy()->createVLineIteratorNG(x, y, w); } KisVLineConstIteratorSP KisPaintDevice::createVLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const { return m_d->currentStrategy()->createVLineConstIteratorNG(x, y, w); } KisRepeatHLineConstIteratorSP KisPaintDevice::createRepeatHLineConstIterator(qint32 x, qint32 y, qint32 w, const QRect& _dataWidth) const { return new KisRepeatHLineConstIteratorNG(m_d->dataManager().data(), x, y, w, m_d->x(), m_d->y(), _dataWidth, m_d->cacheInvalidator()); } KisRepeatVLineConstIteratorSP KisPaintDevice::createRepeatVLineConstIterator(qint32 x, qint32 y, qint32 h, const QRect& _dataWidth) const { return new KisRepeatVLineConstIteratorNG(m_d->dataManager().data(), x, y, h, m_d->x(), m_d->y(), _dataWidth, m_d->cacheInvalidator()); } KisRandomAccessorSP KisPaintDevice::createRandomAccessorNG(qint32 x, qint32 y) { m_d->cache()->invalidate(); return m_d->currentStrategy()->createRandomAccessorNG(x, y); } KisRandomConstAccessorSP KisPaintDevice::createRandomConstAccessorNG(qint32 x, qint32 y) const { return m_d->currentStrategy()->createRandomConstAccessorNG(x, y); } KisRandomSubAccessorSP KisPaintDevice::createRandomSubAccessor() const { KisPaintDevice* pd = const_cast(this); return new KisRandomSubAccessor(pd); } void KisPaintDevice::clearSelection(KisSelectionSP selection) { const KoColorSpace *colorSpace = m_d->colorSpace(); QRect r = selection->selectedExactRect() & m_d->defaultBounds->bounds(); if (r.isValid()) { KisHLineIteratorSP devIt = createHLineIteratorNG(r.x(), r.y(), r.width()); KisHLineConstIteratorSP selectionIt = selection->projection()->createHLineConstIteratorNG(r.x(), r.y(), r.width()); const KoColor defaultPixel = this->defaultPixel(); bool transparentDefault = (defaultPixel.opacityU8() == OPACITY_TRANSPARENT_U8); for (qint32 y = 0; y < r.height(); y++) { do { // XXX: Optimize by using stretches colorSpace->applyInverseAlphaU8Mask(devIt->rawData(), selectionIt->rawDataConst(), 1); if (transparentDefault && colorSpace->opacityU8(devIt->rawData()) == OPACITY_TRANSPARENT_U8) { memcpy(devIt->rawData(), defaultPixel.data(), colorSpace->pixelSize()); } } while (devIt->nextPixel() && selectionIt->nextPixel()); devIt->nextRow(); selectionIt->nextRow(); } m_d->dataManager()->purge(r.translated(-m_d->x(), -m_d->y())); setDirty(r); } } bool KisPaintDevice::pixel(qint32 x, qint32 y, QColor *c) const { KisHLineConstIteratorSP iter = createHLineConstIteratorNG(x, y, 1); const quint8 *pix = iter->rawDataConst(); if (!pix) return false; colorSpace()->toQColor(pix, c); return true; } bool KisPaintDevice::pixel(qint32 x, qint32 y, KoColor * kc) const { KisHLineConstIteratorSP iter = createHLineConstIteratorNG(x, y, 1); const quint8 *pix = iter->rawDataConst(); if (!pix) return false; kc->setColor(pix, m_d->colorSpace()); return true; } bool KisPaintDevice::setPixel(qint32 x, qint32 y, const QColor& c) { KisHLineIteratorSP iter = createHLineIteratorNG(x, y, 1); colorSpace()->fromQColor(c, iter->rawData()); m_d->cache()->invalidate(); return true; } bool KisPaintDevice::setPixel(qint32 x, qint32 y, const KoColor& kc) { const quint8 * pix; KisHLineIteratorSP iter = createHLineIteratorNG(x, y, 1); if (kc.colorSpace() != m_d->colorSpace()) { KoColor kc2(kc, m_d->colorSpace()); pix = kc2.data(); memcpy(iter->rawData(), pix, m_d->colorSpace()->pixelSize()); } else { pix = kc.data(); memcpy(iter->rawData(), pix, m_d->colorSpace()->pixelSize()); } m_d->cache()->invalidate(); return true; } bool KisPaintDevice::fastBitBltPossible(KisPaintDeviceSP src) { return m_d->fastBitBltPossible(src); } void KisPaintDevice::fastBitBlt(KisPaintDeviceSP src, const QRect &rect) { m_d->currentStrategy()->fastBitBlt(src, rect); } void KisPaintDevice::fastBitBltOldData(KisPaintDeviceSP src, const QRect &rect) { m_d->currentStrategy()->fastBitBltOldData(src, rect); } void KisPaintDevice::fastBitBltRough(KisPaintDeviceSP src, const QRect &rect) { m_d->currentStrategy()->fastBitBltRough(src, rect); } void KisPaintDevice::fastBitBltRoughOldData(KisPaintDeviceSP src, const QRect &rect) { m_d->currentStrategy()->fastBitBltRoughOldData(src, rect); } void KisPaintDevice::readBytes(quint8 * data, qint32 x, qint32 y, qint32 w, qint32 h) const { readBytes(data, QRect(x, y, w, h)); } void KisPaintDevice::readBytes(quint8 *data, const QRect &rect) const { m_d->currentStrategy()->readBytes(data, rect); } void KisPaintDevice::writeBytes(const quint8 *data, qint32 x, qint32 y, qint32 w, qint32 h) { writeBytes(data, QRect(x, y, w, h)); } void KisPaintDevice::writeBytes(const quint8 *data, const QRect &rect) { m_d->currentStrategy()->writeBytes(data, rect); } QVector KisPaintDevice::readPlanarBytes(qint32 x, qint32 y, qint32 w, qint32 h) const { return m_d->currentStrategy()->readPlanarBytes(x, y, w, h); } void KisPaintDevice::writePlanarBytes(QVector planes, qint32 x, qint32 y, qint32 w, qint32 h) { m_d->currentStrategy()->writePlanarBytes(planes, x, y, w, h); } quint32 KisPaintDevice::pixelSize() const { quint32 _pixelSize = m_d->colorSpace()->pixelSize(); Q_ASSERT(_pixelSize > 0); return _pixelSize; } quint32 KisPaintDevice::channelCount() const { quint32 _channelCount = m_d->colorSpace()->channelCount(); Q_ASSERT(_channelCount > 0); return _channelCount; } KisRasterKeyframeChannel *KisPaintDevice::createKeyframeChannel(const KoID &id) { Q_ASSERT(!m_d->framesInterface); m_d->framesInterface.reset(new KisPaintDeviceFramesInterface(this)); Q_ASSERT(!m_d->contentChannel); m_d->contentChannel.reset(new KisRasterKeyframeChannel(id, this, m_d->defaultBounds)); // Raster channels always have at least one frame (representing a static image) KUndo2Command tempParentCommand; m_d->contentChannel->addKeyframe(0, &tempParentCommand); return m_d->contentChannel.data(); } KisRasterKeyframeChannel* KisPaintDevice::keyframeChannel() const { Q_ASSERT(m_d->contentChannel); return m_d->contentChannel.data(); } const KoColorSpace* KisPaintDevice::colorSpace() const { Q_ASSERT(m_d->colorSpace() != 0); return m_d->colorSpace(); } KisPaintDeviceSP KisPaintDevice::createCompositionSourceDevice() const { KisPaintDeviceSP device = new KisPaintDevice(compositionSourceColorSpace()); device->setDefaultBounds(defaultBounds()); return device; } KisPaintDeviceSP KisPaintDevice::createCompositionSourceDevice(KisPaintDeviceSP cloneSource) const { KisPaintDeviceSP clone = new KisPaintDevice(*cloneSource); clone->setDefaultBounds(defaultBounds()); clone->convertTo(compositionSourceColorSpace(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); return clone; } KisPaintDeviceSP KisPaintDevice::createCompositionSourceDevice(KisPaintDeviceSP cloneSource, const QRect roughRect) const { KisPaintDeviceSP clone = new KisPaintDevice(colorSpace()); clone->setDefaultBounds(defaultBounds()); clone->makeCloneFromRough(cloneSource, roughRect); clone->convertTo(compositionSourceColorSpace(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); return clone; } KisFixedPaintDeviceSP KisPaintDevice::createCompositionSourceDeviceFixed() const { return new KisFixedPaintDevice(compositionSourceColorSpace()); } const KoColorSpace* KisPaintDevice::compositionSourceColorSpace() const { return colorSpace(); } QVector KisPaintDevice::channelSizes() const { QVector sizes; QList channels = colorSpace()->channels(); std::sort(channels.begin(), channels.end()); Q_FOREACH (KoChannelInfo * channelInfo, channels) { sizes.append(channelInfo->size()); } return sizes; } KisPaintDevice::MemoryReleaseObject::~MemoryReleaseObject() { KisDataManager::releaseInternalPools(); } KisPaintDevice::MemoryReleaseObject* KisPaintDevice::createMemoryReleaseObject() { return new MemoryReleaseObject(); } KisPaintDevice::LodDataStruct::~LodDataStruct() { } QRegion KisPaintDevice::regionForLodSyncing() const { return m_d->regionForLodSyncing(); } KisPaintDevice::LodDataStruct* KisPaintDevice::createLodDataStruct(int lod) { return m_d->createLodDataStruct(lod); } void KisPaintDevice::updateLodDataStruct(LodDataStruct *dst, const QRect &srcRect) { m_d->updateLodDataStruct(dst, srcRect); } void KisPaintDevice::uploadLodDataStruct(LodDataStruct *dst) { m_d->uploadLodDataStruct(dst); } KisPaintDeviceFramesInterface* KisPaintDevice::framesInterface() { return m_d->framesInterface.data(); } /******************************************************************/ /* KisPaintDeviceFramesInterface */ /******************************************************************/ KisPaintDeviceFramesInterface::KisPaintDeviceFramesInterface(KisPaintDevice *parentDevice) : q(parentDevice) { } QList KisPaintDeviceFramesInterface::frames() { return q->m_d->frameIds(); } int KisPaintDeviceFramesInterface::createFrame(bool copy, int copySrc, const QPoint &offset, KUndo2Command *parentCommand) { return q->m_d->createFrame(copy, copySrc, offset, parentCommand); } void KisPaintDeviceFramesInterface::deleteFrame(int frame, KUndo2Command *parentCommand) { return q->m_d->deleteFrame(frame, parentCommand); } void KisPaintDeviceFramesInterface::fetchFrame(int frameId, KisPaintDeviceSP targetDevice) { q->m_d->fetchFrame(frameId, targetDevice); } void KisPaintDeviceFramesInterface::uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice) { q->m_d->uploadFrame(srcFrameId, dstFrameId, srcDevice); } void KisPaintDeviceFramesInterface::uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice) { q->m_d->uploadFrame(dstFrameId, srcDevice); } QRect KisPaintDeviceFramesInterface::frameBounds(int frameId) { return q->m_d->frameBounds(frameId); } QPoint KisPaintDeviceFramesInterface::frameOffset(int frameId) const { return q->m_d->frameOffset(frameId); } void KisPaintDeviceFramesInterface::setFrameDefaultPixel(const KoColor &defPixel, int frameId) { KIS_ASSERT_RECOVER_RETURN(frameId >= 0); q->m_d->setFrameDefaultPixel(defPixel, frameId); } KoColor KisPaintDeviceFramesInterface::frameDefaultPixel(int frameId) const { KIS_ASSERT_RECOVER(frameId >= 0) { return KoColor(Qt::red, q->m_d->colorSpace()); } return q->m_d->frameDefaultPixel(frameId); } bool KisPaintDeviceFramesInterface::writeFrame(KisPaintDeviceWriter &store, int frameId) { KIS_ASSERT_RECOVER(frameId >= 0) { return false; } return q->m_d->writeFrame(store, frameId); } bool KisPaintDeviceFramesInterface::readFrame(QIODevice *stream, int frameId) { KIS_ASSERT_RECOVER(frameId >= 0) { return false; } return q->m_d->readFrame(stream, frameId); } int KisPaintDeviceFramesInterface::currentFrameId() const { return q->m_d->currentFrameId(); } KisDataManagerSP KisPaintDeviceFramesInterface::frameDataManager(int frameId) const { KIS_ASSERT_RECOVER(frameId >= 0) { return q->m_d->dataManager(); } return q->m_d->frameDataManager(frameId); } void KisPaintDeviceFramesInterface::invalidateFrameCache(int frameId) { KIS_ASSERT_RECOVER_RETURN(frameId >= 0); return q->m_d->invalidateFrameCache(frameId); } void KisPaintDeviceFramesInterface::setFrameOffset(int frameId, const QPoint &offset) { KIS_ASSERT_RECOVER_RETURN(frameId >= 0); return q->m_d->setFrameOffset(frameId, offset); } KisPaintDeviceFramesInterface::TestingDataObjects KisPaintDeviceFramesInterface::testingGetDataObjects() const { TestingDataObjects objects; objects.m_data = q->m_d->m_data.data(); objects.m_lodData = q->m_d->m_lodData.data(); objects.m_externalFrameData = q->m_d->m_externalFrameData.data(); typedef KisPaintDevice::Private::FramesHash FramesHash; FramesHash::const_iterator it = q->m_d->m_frames.constBegin(); FramesHash::const_iterator end = q->m_d->m_frames.constEnd(); for (; it != end; ++it) { objects.m_frames.insert(it.key(), it.value().data()); } objects.m_currentData = q->m_d->currentData(); return objects; } QList KisPaintDeviceFramesInterface::testingGetDataObjectsList() const { return q->m_d->allDataObjects(); } void KisPaintDevice::tesingFetchLodDevice(KisPaintDeviceSP targetDevice) { m_d->tesingFetchLodDevice(targetDevice); } diff --git a/libs/image/kis_raster_keyframe_channel.cpp b/libs/image/kis_raster_keyframe_channel.cpp index c8f154597d..8fbc73df2f 100644 --- a/libs/image/kis_raster_keyframe_channel.cpp +++ b/libs/image/kis_raster_keyframe_channel.cpp @@ -1,296 +1,312 @@ /* * 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_raster_keyframe_channel.h" #include "kis_node.h" #include "kis_dom_utils.h" #include "kis_global.h" #include "kis_paint_device.h" #include "kis_paint_device_frames_interface.h" #include "kis_time_range.h" #include "kundo2command.h" #include "kis_onion_skin_compositor.h" struct KisRasterKeyframe : public KisKeyframe { KisRasterKeyframe(KisRasterKeyframeChannel *channel, int time, int frameId) : KisKeyframe(channel, time) , frameId(frameId) {} KisRasterKeyframe(const KisRasterKeyframe *rhs, KisKeyframeChannel *channel) : KisKeyframe(rhs, channel) , frameId(rhs->frameId) {} int frameId; KisKeyframeSP cloneFor(KisKeyframeChannel *channel) const override { return toQShared(new KisRasterKeyframe(this, channel)); } + bool hasContent() const override { + KisRasterKeyframeChannel *channel = dynamic_cast(this->channel()); + KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(channel, true); + + return channel->keyframeHasContent(this); + } }; struct KisRasterKeyframeChannel::Private { Private(KisPaintDeviceWSP paintDevice, const QString filenameSuffix) : paintDevice(paintDevice), filenameSuffix(filenameSuffix), onionSkinsEnabled(false) {} KisPaintDeviceWSP paintDevice; QMap frameFilenames; QString filenameSuffix; bool onionSkinsEnabled; }; KisRasterKeyframeChannel::KisRasterKeyframeChannel(const KoID &id, const KisPaintDeviceWSP paintDevice, KisDefaultBoundsBaseSP defaultBounds) : KisKeyframeChannel(id, defaultBounds), m_d(new Private(paintDevice, QString())) { } KisRasterKeyframeChannel::KisRasterKeyframeChannel(const KisRasterKeyframeChannel &rhs, KisNode *newParentNode, const KisPaintDeviceWSP newPaintDevice) : KisKeyframeChannel(rhs, newParentNode), m_d(new Private(newPaintDevice, rhs.m_d->filenameSuffix)) { KIS_ASSERT_RECOVER_NOOP(&rhs != this); m_d->frameFilenames = rhs.m_d->frameFilenames; m_d->onionSkinsEnabled = rhs.m_d->onionSkinsEnabled; } KisRasterKeyframeChannel::~KisRasterKeyframeChannel() { } int KisRasterKeyframeChannel::frameId(KisKeyframeSP keyframe) const { - KisRasterKeyframe *key = dynamic_cast(keyframe.data()); + return frameId(keyframe.data()); +} + +int KisRasterKeyframeChannel::frameId(const KisKeyframe *keyframe) const +{ + const KisRasterKeyframe *key = dynamic_cast(keyframe); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(key, -1); return key->frameId; } int KisRasterKeyframeChannel::frameIdAt(int time) const { KisKeyframeSP activeKey = activeKeyframeAt(time); if (activeKey.isNull()) return -1; return frameId(activeKey); } void KisRasterKeyframeChannel::fetchFrame(KisKeyframeSP keyframe, KisPaintDeviceSP targetDevice) { m_d->paintDevice->framesInterface()->fetchFrame(frameId(keyframe), targetDevice); } void KisRasterKeyframeChannel::importFrame(int time, KisPaintDeviceSP sourceDevice, KUndo2Command *parentCommand) { KisKeyframeSP keyframe = addKeyframe(time, parentCommand); const int frame = frameId(keyframe); m_d->paintDevice->framesInterface()->uploadFrame(frame, sourceDevice); } QRect KisRasterKeyframeChannel::frameExtents(KisKeyframeSP keyframe) { return m_d->paintDevice->framesInterface()->frameBounds(frameId(keyframe)); } QString KisRasterKeyframeChannel::frameFilename(int frameId) const { return m_d->frameFilenames.value(frameId, QString()); } void KisRasterKeyframeChannel::setFilenameSuffix(const QString &suffix) { m_d->filenameSuffix = suffix; } void KisRasterKeyframeChannel::setFrameFilename(int frameId, const QString &filename) { Q_ASSERT(!m_d->frameFilenames.contains(frameId)); m_d->frameFilenames.insert(frameId, filename); } QString KisRasterKeyframeChannel::chooseFrameFilename(int frameId, const QString &layerFilename) { QString filename; if (m_d->frameFilenames.isEmpty()) { // Use legacy naming convention for first keyframe filename = layerFilename + m_d->filenameSuffix; } else { filename = layerFilename + m_d->filenameSuffix + ".f" + QString::number(frameId); } setFrameFilename(frameId, filename); return filename; } KisKeyframeSP KisRasterKeyframeChannel::createKeyframe(int time, const KisKeyframeSP copySrc, KUndo2Command *parentCommand) { KisRasterKeyframe *keyframe; if (!copySrc) { int frameId = m_d->paintDevice->framesInterface()->createFrame(false, 0, QPoint(), parentCommand); keyframe = new KisRasterKeyframe(this, time, frameId); } else { int srcFrame = frameId(copySrc); int frameId = m_d->paintDevice->framesInterface()->createFrame(true, srcFrame, QPoint(), parentCommand); KisRasterKeyframe *srcKeyframe = dynamic_cast(copySrc.data()); Q_ASSERT(srcKeyframe); keyframe = new KisRasterKeyframe(srcKeyframe, this); keyframe->setTime(time); keyframe->frameId = frameId; } return toQShared(keyframe); } void KisRasterKeyframeChannel::destroyKeyframe(KisKeyframeSP key, KUndo2Command *parentCommand) { m_d->paintDevice->framesInterface()->deleteFrame(frameId(key), parentCommand); } void KisRasterKeyframeChannel::uploadExternalKeyframe(KisKeyframeChannel *srcChannel, int srcTime, KisKeyframeSP dstFrame) { KisRasterKeyframeChannel *srcRasterChannel = dynamic_cast(srcChannel); KIS_ASSERT_RECOVER_RETURN(srcRasterChannel); const int srcId = srcRasterChannel->frameIdAt(srcTime); const int dstId = frameId(dstFrame); m_d->paintDevice->framesInterface()-> uploadFrame(srcId, dstId, srcRasterChannel->m_d->paintDevice); } QRect KisRasterKeyframeChannel::affectedRect(KisKeyframeSP key) { KeyframesMap::iterator it = keys().find(key->time()); QRect rect; // Calculate changed area as the union of the current and previous keyframe. // This makes sure there are no artifacts left over from the previous frame // where the new one doesn't cover the area. if (it == keys().begin()) { // Using the *next* keyframe at the start of the timeline avoids artifacts // when deleting or moving the first key it++; } else { it--; } if (it != keys().end()) { rect = m_d->paintDevice->framesInterface()->frameBounds(frameId(it.value())); } rect |= m_d->paintDevice->framesInterface()->frameBounds(frameId(key)); if (m_d->onionSkinsEnabled) { const QRect dirtyOnionSkinsRect = KisOnionSkinCompositor::instance()->calculateFullExtent(m_d->paintDevice); rect |= dirtyOnionSkinsRect; } return rect; } QDomElement KisRasterKeyframeChannel::toXML(QDomDocument doc, const QString &layerFilename) { m_d->frameFilenames.clear(); return KisKeyframeChannel::toXML(doc, layerFilename); } void KisRasterKeyframeChannel::loadXML(const QDomElement &channelNode) { m_d->frameFilenames.clear(); KisKeyframeChannel::loadXML(channelNode); } void KisRasterKeyframeChannel::saveKeyframe(KisKeyframeSP keyframe, QDomElement keyframeElement, const QString &layerFilename) { int frame = frameId(keyframe); QString filename = frameFilename(frame); if (filename.isEmpty()) { filename = chooseFrameFilename(frame, layerFilename); } keyframeElement.setAttribute("frame", filename); QPoint offset = m_d->paintDevice->framesInterface()->frameOffset(frame); KisDomUtils::saveValue(&keyframeElement, "offset", offset); } KisKeyframeSP KisRasterKeyframeChannel::loadKeyframe(const QDomElement &keyframeNode) { int time = keyframeNode.attribute("time").toUInt(); QPoint offset; KisDomUtils::loadValue(keyframeNode, "offset", &offset); QString frameFilename = keyframeNode.attribute("frame"); KisKeyframeSP keyframe; if (m_d->frameFilenames.isEmpty()) { // First keyframe loaded: use the existing frame Q_ASSERT(keyframeCount() == 1); keyframe = constKeys().begin().value(); // Remove from keys. It will get reinserted with new time once we return keys().remove(keyframe->time()); keyframe->setTime(time); m_d->paintDevice->framesInterface()->setFrameOffset(frameId(keyframe), offset); } else { KUndo2Command tempCommand; int frameId = m_d->paintDevice->framesInterface()->createFrame(false, 0, offset, &tempCommand); keyframe = toQShared(new KisRasterKeyframe(this, time, frameId)); } setFrameFilename(frameId(keyframe), frameFilename); return keyframe; } +bool KisRasterKeyframeChannel::keyframeHasContent(const KisKeyframe *keyframe) const +{ + return !m_d->paintDevice->framesInterface()->frameBounds(frameId(keyframe)).isEmpty(); +} + bool KisRasterKeyframeChannel::hasScalarValue() const { return false; } void KisRasterKeyframeChannel::setOnionSkinsEnabled(bool value) { m_d->onionSkinsEnabled = value; } bool KisRasterKeyframeChannel::onionSkinsEnabled() const { return m_d->onionSkinsEnabled; } diff --git a/libs/image/kis_raster_keyframe_channel.h b/libs/image/kis_raster_keyframe_channel.h index a7cb20271c..2ccec11eec 100644 --- a/libs/image/kis_raster_keyframe_channel.h +++ b/libs/image/kis_raster_keyframe_channel.h @@ -1,91 +1,95 @@ /* * 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_RASTER_KEYFRAME_CHANNEL_H #define _KIS_RASTER_KEYFRAME_CHANNEL_H #include "kis_keyframe_channel.h" class KRITAIMAGE_EXPORT KisRasterKeyframeChannel : public KisKeyframeChannel { Q_OBJECT public: KisRasterKeyframeChannel(const KoID& id, const KisPaintDeviceWSP paintDevice, KisDefaultBoundsBaseSP defaultBounds); KisRasterKeyframeChannel(const KisRasterKeyframeChannel &rhs, KisNode *newParentNode, const KisPaintDeviceWSP newPaintDevice); ~KisRasterKeyframeChannel() override; public: /** * Return the ID of the active frame at a given time. The active frame is * defined by the keyframe at the given time or the last keyframe before it. * @param time * @return active frame id */ int frameIdAt(int time) const; /** * Copy the active frame at given time to target device. * @param keyframe keyframe to copy from * @param targetDevice device to copy the frame to */ void fetchFrame(KisKeyframeSP keyframe, KisPaintDeviceSP targetDevice); /** * Copy the content of the sourceDevice into a new keyframe at given time * @param time position of new keyframe * @param sourceDevice source for content */ void importFrame(int time, KisPaintDeviceSP sourceDevice, KUndo2Command *parentCommand); QRect frameExtents(KisKeyframeSP keyframe); QString frameFilename(int frameId) const; /** * When choosing filenames for frames, this will be appended to the node filename */ void setFilenameSuffix(const QString &suffix); bool hasScalarValue() const override; QDomElement toXML(QDomDocument doc, const QString &layerFilename) override; void loadXML(const QDomElement &channelNode) override; void setOnionSkinsEnabled(bool value); bool onionSkinsEnabled() const; protected: KisKeyframeSP createKeyframe(int time, const KisKeyframeSP copySrc, KUndo2Command *parentCommand) override; void destroyKeyframe(KisKeyframeSP key, KUndo2Command *parentCommand) override; void uploadExternalKeyframe(KisKeyframeChannel *srcChannel, int srcTime, KisKeyframeSP dstFrame) override; QRect affectedRect(KisKeyframeSP key) override; void saveKeyframe(KisKeyframeSP keyframe, QDomElement keyframeElement, const QString &layerFilename) override; KisKeyframeSP loadKeyframe(const QDomElement &keyframeNode) override; + friend class KisRasterKeyframe; + bool keyframeHasContent(const KisKeyframe *keyframe) const; + private: void setFrameFilename(int frameId, const QString &filename); QString chooseFrameFilename(int frameId, const QString &layerFilename); int frameId(KisKeyframeSP keyframe) const; + int frameId(const KisKeyframe *keyframe) const; struct Private; QScopedPointer m_d; }; #endif diff --git a/libs/image/lazybrush/kis_colorize_mask.cpp b/libs/image/lazybrush/kis_colorize_mask.cpp index 6e79443ad8..079f4b1585 100644 --- a/libs/image/lazybrush/kis_colorize_mask.cpp +++ b/libs/image/lazybrush/kis_colorize_mask.cpp @@ -1,1167 +1,1167 @@ /* * 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 "kis_colorize_mask.h" #include #include #include #include "kis_pixel_selection.h" #include "kis_icon_utils.h" #include "kis_node_visitor.h" #include "kis_processing_visitor.h" #include "kis_painter.h" #include "kis_fill_painter.h" #include "kis_lazy_fill_tools.h" #include "kis_cached_paint_device.h" #include "kis_paint_device_debug_utils.h" #include "kis_layer_properties_icons.h" -#include "kis_signal_compressor.h" +#include "kis_thread_safe_signal_compressor.h" #include "kis_colorize_stroke_strategy.h" #include "kis_multiway_cut.h" #include "kis_image.h" #include "kis_layer.h" #include "kis_macro_based_undo_store.h" #include "kis_post_execution_undo_adapter.h" #include "kis_command_utils.h" #include "kis_processing_applicator.h" #include "krita_utils.h" using namespace KisLazyFillTools; struct KisColorizeMask::Private { Private(KisColorizeMask *_q) : q(_q), coloringProjection(new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8())), fakePaintDevice(new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8())), filteredSource(new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8())), needAddCurrentKeyStroke(false), showKeyStrokes(true), showColoring(true), needsUpdate(true), originalSequenceNumber(-1), - updateCompressor(1000, KisSignalCompressor::FIRST_ACTIVE_POSTPONE_NEXT, _q), - dirtyParentUpdateCompressor(200, KisSignalCompressor::FIRST_ACTIVE_POSTPONE_NEXT, _q), - prefilterRecalculationCompressor(1000, KisSignalCompressor::POSTPONE, _q), + updateCompressor(1000, KisSignalCompressor::FIRST_ACTIVE_POSTPONE_NEXT), + dirtyParentUpdateCompressor(200, KisSignalCompressor::FIRST_ACTIVE_POSTPONE_NEXT), + prefilterRecalculationCompressor(1000, KisSignalCompressor::POSTPONE), updateIsRunning(false), filteringOptions(false, 4.0, 15, 0.7), limitToDeviceBounds(false) { } Private(const Private &rhs, KisColorizeMask *_q) : q(_q), coloringProjection(new KisPaintDevice(*rhs.coloringProjection)), fakePaintDevice(new KisPaintDevice(*rhs.fakePaintDevice)), filteredSource(new KisPaintDevice(*rhs.filteredSource)), filteredDeviceBounds(rhs.filteredDeviceBounds), needAddCurrentKeyStroke(rhs.needAddCurrentKeyStroke), showKeyStrokes(rhs.showKeyStrokes), showColoring(rhs.showColoring), needsUpdate(false), originalSequenceNumber(-1), - updateCompressor(1000, KisSignalCompressor::FIRST_ACTIVE_POSTPONE_NEXT, _q), - dirtyParentUpdateCompressor(200, KisSignalCompressor::FIRST_ACTIVE_POSTPONE_NEXT, _q), - prefilterRecalculationCompressor(1000, KisSignalCompressor::POSTPONE, _q), + updateCompressor(1000, KisSignalCompressor::FIRST_ACTIVE_POSTPONE_NEXT), + dirtyParentUpdateCompressor(200, KisSignalCompressor::FIRST_ACTIVE_POSTPONE_NEXT), + prefilterRecalculationCompressor(1000, KisSignalCompressor::POSTPONE), offset(rhs.offset), updateIsRunning(false), filteringOptions(rhs.filteringOptions), limitToDeviceBounds(rhs.limitToDeviceBounds) { Q_FOREACH (const KeyStroke &stroke, rhs.keyStrokes) { keyStrokes << KeyStroke(KisPaintDeviceSP(new KisPaintDevice(*stroke.dev)), stroke.color, stroke.isTransparent); } } KisColorizeMask *q = 0; QList keyStrokes; KisPaintDeviceSP coloringProjection; KisPaintDeviceSP fakePaintDevice; KisPaintDeviceSP filteredSource; QRect filteredDeviceBounds; KoColor currentColor; KisPaintDeviceSP currentKeyStrokeDevice; bool needAddCurrentKeyStroke; bool showKeyStrokes; bool showColoring; KisCachedSelection cachedSelection; KisCachedSelection cachedConversionSelection; bool needsUpdate; int originalSequenceNumber; - KisSignalCompressor updateCompressor; - KisSignalCompressor dirtyParentUpdateCompressor; - KisSignalCompressor prefilterRecalculationCompressor; + KisThreadSafeSignalCompressor updateCompressor; + KisThreadSafeSignalCompressor dirtyParentUpdateCompressor; + KisThreadSafeSignalCompressor prefilterRecalculationCompressor; QPoint offset; bool updateIsRunning; QStack extentBeforeUpdateStart; FilteringOptions filteringOptions; bool filteringDirty = true; bool limitToDeviceBounds = false; bool filteredSourceValid(KisPaintDeviceSP parentDevice) { return !filteringDirty && originalSequenceNumber == parentDevice->sequenceNumber(); } void setNeedsUpdateImpl(bool value, bool requestedByUser); bool shouldShowFilteredSource() const; bool shouldShowColoring() const; }; KisColorizeMask::KisColorizeMask() : m_d(new Private(this)) { connect(&m_d->updateCompressor, SIGNAL(timeout()), SLOT(slotUpdateRegenerateFilling())); connect(this, SIGNAL(sigUpdateOnDirtyParent()), &m_d->dirtyParentUpdateCompressor, SLOT(start())); connect(&m_d->dirtyParentUpdateCompressor, SIGNAL(timeout()), SLOT(slotUpdateOnDirtyParent())); connect(&m_d->prefilterRecalculationCompressor, SIGNAL(timeout()), SLOT(slotRecalculatePrefilteredImage())); m_d->updateCompressor.moveToThread(qApp->thread()); } KisColorizeMask::~KisColorizeMask() { } KisColorizeMask::KisColorizeMask(const KisColorizeMask& rhs) : KisEffectMask(rhs), m_d(new Private(*rhs.m_d, this)) { connect(&m_d->updateCompressor, SIGNAL(timeout()), SLOT(slotUpdateRegenerateFilling())); connect(this, SIGNAL(sigUpdateOnDirtyParent()), &m_d->dirtyParentUpdateCompressor, SLOT(start())); connect(&m_d->dirtyParentUpdateCompressor, SIGNAL(timeout()), SLOT(slotUpdateOnDirtyParent())); m_d->updateCompressor.moveToThread(qApp->thread()); } void KisColorizeMask::initializeCompositeOp() { KisLayerSP parentLayer(qobject_cast(parent().data())); if (!parentLayer || !parentLayer->original()) return; KisImageSP image = parentLayer->image(); if (!image) return; const qreal samplePortion = 0.1; const qreal alphaPortion = KritaUtils::estimatePortionOfTransparentPixels(parentLayer->original(), image->bounds(), samplePortion); setCompositeOpId(alphaPortion > 0.3 ? COMPOSITE_BEHIND : COMPOSITE_MULT); } const KoColorSpace* KisColorizeMask::colorSpace() const { return m_d->fakePaintDevice->colorSpace(); } struct SetKeyStrokesColorSpaceCommand : public KUndo2Command { SetKeyStrokesColorSpaceCommand(const KoColorSpace *dstCS, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags, QList *list, KisColorizeMaskSP node) : m_dstCS(dstCS), m_renderingIntent(renderingIntent), m_conversionFlags(conversionFlags), m_list(list), m_node(node) {} void undo() override { KIS_ASSERT_RECOVER_RETURN(m_list->size() == m_oldColors.size()); for (int i = 0; i < m_list->size(); i++) { (*m_list)[i].color = m_oldColors[i]; } m_node->setNeedsUpdate(true); } void redo() override { if (m_oldColors.isEmpty()) { Q_FOREACH(const KeyStroke &stroke, *m_list) { m_oldColors << stroke.color; m_newColors << stroke.color; m_newColors.last().convertTo(m_dstCS, m_renderingIntent, m_conversionFlags); } } KIS_ASSERT_RECOVER_RETURN(m_list->size() == m_newColors.size()); for (int i = 0; i < m_list->size(); i++) { (*m_list)[i].color = m_newColors[i]; } m_node->setNeedsUpdate(true); } private: QVector m_oldColors; QVector m_newColors; const KoColorSpace *m_dstCS; KoColorConversionTransformation::Intent m_renderingIntent; KoColorConversionTransformation::ConversionFlags m_conversionFlags; QList *m_list; KisColorizeMaskSP m_node; }; void KisColorizeMask::setProfile(const KoColorProfile *profile) { // WARNING: there is no undo information, used only while loading! m_d->fakePaintDevice->setProfile(profile); m_d->coloringProjection->setProfile(profile); for (auto stroke : m_d->keyStrokes) { stroke.color.setProfile(profile); } } KUndo2Command* KisColorizeMask::setColorSpace(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { using namespace KisCommandUtils; CompositeCommand *composite = new CompositeCommand(); composite->addCommand(m_d->fakePaintDevice->convertTo(dstColorSpace, renderingIntent, conversionFlags)); composite->addCommand(m_d->coloringProjection->convertTo(dstColorSpace, renderingIntent, conversionFlags)); KUndo2Command *strokesConversionCommand = new SetKeyStrokesColorSpaceCommand( dstColorSpace, renderingIntent, conversionFlags, &m_d->keyStrokes, KisColorizeMaskSP(this)); strokesConversionCommand->redo(); composite->addCommand(new SkipFirstRedoWrapper(strokesConversionCommand)); return composite; } bool KisColorizeMask::needsUpdate() const { return m_d->needsUpdate; } void KisColorizeMask::setNeedsUpdate(bool value) { m_d->setNeedsUpdateImpl(value, true); } void KisColorizeMask::Private::setNeedsUpdateImpl(bool value, bool requestedByUser) { if (value != needsUpdate) { needsUpdate = value; q->baseNodeChangedCallback(); if (!value && requestedByUser) { updateCompressor.start(); } } } void KisColorizeMask::slotUpdateRegenerateFilling(bool prefilterOnly) { KisPaintDeviceSP src = parent()->original(); KIS_ASSERT_RECOVER_RETURN(src); const bool filteredSourceValid = m_d->filteredSourceValid(src); m_d->originalSequenceNumber = src->sequenceNumber(); m_d->filteringDirty = false; if (!prefilterOnly) { m_d->coloringProjection->clear(); } KisLayerSP parentLayer(qobject_cast(parent().data())); if (!parentLayer) return; KisImageSP image = parentLayer->image(); if (image) { m_d->updateIsRunning = true; QRect fillBounds; if (m_d->limitToDeviceBounds) { fillBounds |= src->exactBounds(); Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) { fillBounds |= stroke.dev->exactBounds(); } fillBounds &= image->bounds(); } else { fillBounds = image->bounds(); } m_d->filteredDeviceBounds = fillBounds; KisColorizeStrokeStrategy *strategy = new KisColorizeStrokeStrategy(src, m_d->coloringProjection, m_d->filteredSource, filteredSourceValid, fillBounds, this, prefilterOnly); strategy->setFilteringOptions(m_d->filteringOptions); Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) { const KoColor color = !stroke.isTransparent ? stroke.color : KoColor(Qt::transparent, stroke.color.colorSpace()); strategy->addKeyStroke(stroke.dev, color); } m_d->extentBeforeUpdateStart.push(extent()); connect(strategy, SIGNAL(sigFinished(bool)), SLOT(slotRegenerationFinished(bool))); connect(strategy, SIGNAL(sigCancelled()), SLOT(slotRegenerationCancelled())); KisStrokeId id = image->startStroke(strategy); image->endStroke(id); } } void KisColorizeMask::slotUpdateOnDirtyParent() { KIS_ASSERT_RECOVER_RETURN(parent()); KisPaintDeviceSP src = parent()->original(); KIS_ASSERT_RECOVER_RETURN(src); if (!m_d->filteredSourceValid(src)) { const QRect &oldExtent = extent(); m_d->setNeedsUpdateImpl(true, false); m_d->filteringDirty = true; setDirty(oldExtent | extent()); } } void KisColorizeMask::slotRecalculatePrefilteredImage() { slotUpdateRegenerateFilling(true); } void KisColorizeMask::slotRegenerationFinished(bool prefilterOnly) { m_d->updateIsRunning = false; if (!prefilterOnly) { m_d->setNeedsUpdateImpl(false, false); } QRect oldExtent; if (!m_d->extentBeforeUpdateStart.isEmpty()) { oldExtent = m_d->extentBeforeUpdateStart.pop(); } else { KIS_SAFE_ASSERT_RECOVER_NOOP(!m_d->extentBeforeUpdateStart.isEmpty()); // always fail! } setDirty(oldExtent | extent()); } void KisColorizeMask::slotRegenerationCancelled() { slotRegenerationFinished(true); } KisBaseNode::PropertyList KisColorizeMask::sectionModelProperties() const { KisBaseNode::PropertyList l = KisMask::sectionModelProperties(); l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::colorizeNeedsUpdate, needsUpdate()); l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::colorizeEditKeyStrokes, showKeyStrokes()); l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::colorizeShowColoring, showColoring()); return l; } void KisColorizeMask::setSectionModelProperties(const KisBaseNode::PropertyList &properties) { KisMask::setSectionModelProperties(properties); Q_FOREACH (const KisBaseNode::Property &property, properties) { if (property.id == KisLayerPropertiesIcons::colorizeNeedsUpdate.id()) { if (m_d->needsUpdate && m_d->needsUpdate != property.state.toBool()) { setNeedsUpdate(property.state.toBool()); } } if (property.id == KisLayerPropertiesIcons::colorizeEditKeyStrokes.id()) { if (m_d->showKeyStrokes != property.state.toBool()) { setShowKeyStrokes(property.state.toBool()); } } if (property.id == KisLayerPropertiesIcons::colorizeShowColoring.id()) { if (m_d->showColoring != property.state.toBool()) { setShowColoring(property.state.toBool()); } } } } KisPaintDeviceSP KisColorizeMask::paintDevice() const { return m_d->showKeyStrokes && !m_d->updateIsRunning ? m_d->fakePaintDevice : KisPaintDeviceSP(); } KisPaintDeviceSP KisColorizeMask::coloringProjection() const { return m_d->coloringProjection; } KisPaintDeviceSP KisColorizeMask::colorPickSourceDevice() const { return m_d->shouldShowColoring() && !m_d->coloringProjection->extent().isEmpty() ? m_d->coloringProjection : projection(); } QIcon KisColorizeMask::icon() const { return KisIconUtils::loadIcon("colorizeMask"); } bool KisColorizeMask::accept(KisNodeVisitor &v) { return v.visit(this); } void KisColorizeMask::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) { return visitor.visit(this, undoAdapter); } bool KisColorizeMask::Private::shouldShowFilteredSource() const { return !updateIsRunning && showKeyStrokes && !filteringDirty && filteredSource && !filteredSource->extent().isEmpty(); } bool KisColorizeMask::Private::shouldShowColoring() const { return !updateIsRunning && showColoring && coloringProjection; } QRect KisColorizeMask::decorateRect(KisPaintDeviceSP &src, KisPaintDeviceSP &dst, const QRect &rect, PositionToFilthy maskPos) const { Q_UNUSED(maskPos); if (maskPos == N_ABOVE_FILTHY) { // the source layer has changed, we should update the filtered cache! if (!m_d->filteringDirty) { emit sigUpdateOnDirtyParent(); } } KIS_ASSERT(dst != src); // Draw the filling and the original layer { KisPainter gc(dst); if (m_d->shouldShowFilteredSource()) { const QRect drawRect = m_d->limitToDeviceBounds ? rect & m_d->filteredDeviceBounds : rect; gc.setOpacity(128); gc.bitBlt(drawRect.topLeft(), m_d->filteredSource, drawRect); } else { gc.setOpacity(255); gc.bitBlt(rect.topLeft(), src, rect); } if (m_d->shouldShowColoring()) { gc.setOpacity(opacity()); gc.setCompositeOp(compositeOpId()); gc.bitBlt(rect.topLeft(), m_d->coloringProjection, rect); } } // Draw the key strokes if (m_d->showKeyStrokes) { KisIndirectPaintingSupport::ReadLocker locker(this); KisSelectionSP selection = m_d->cachedSelection.getSelection(); KisSelectionSP conversionSelection = m_d->cachedConversionSelection.getSelection(); KisPixelSelectionSP tempSelection = conversionSelection->pixelSelection(); KisPaintDeviceSP temporaryTarget = this->temporaryTarget(); const bool isTemporaryTargetErasing = temporaryCompositeOp() == COMPOSITE_ERASE; const QRect temporaryExtent = temporaryTarget ? temporaryTarget->extent() : QRect(); KisFillPainter gc(dst); QList extendedStrokes = m_d->keyStrokes; if (m_d->currentKeyStrokeDevice && m_d->needAddCurrentKeyStroke && !isTemporaryTargetErasing) { extendedStrokes << KeyStroke(m_d->currentKeyStrokeDevice, m_d->currentColor); } Q_FOREACH (const KeyStroke &stroke, extendedStrokes) { selection->pixelSelection()->makeCloneFromRough(stroke.dev, rect); gc.setSelection(selection); if (stroke.color == m_d->currentColor || (isTemporaryTargetErasing && temporaryExtent.intersects(selection->pixelSelection()->selectedRect()))) { if (temporaryTarget) { tempSelection->copyAlphaFrom(temporaryTarget, rect); KisPainter selectionPainter(selection->pixelSelection()); setupTemporaryPainter(&selectionPainter); selectionPainter.bitBlt(rect.topLeft(), tempSelection, rect); } } gc.fillSelection(rect, stroke.color); } m_d->cachedSelection.putSelection(selection); m_d->cachedSelection.putSelection(conversionSelection); } return rect; } struct DeviceExtentPolicy { inline QRect operator() (const KisPaintDevice *dev) { return dev->extent(); } }; struct DeviceExactBoundsPolicy { inline QRect operator() (const KisPaintDevice *dev) { return dev->exactBounds(); } }; template QRect KisColorizeMask::calculateMaskBounds(DeviceMetricPolicy boundsPolicy) const { QRect rc; if (m_d->shouldShowFilteredSource()) { rc |= boundsPolicy(m_d->filteredSource); } if (m_d->shouldShowColoring()) { rc |= boundsPolicy(m_d->coloringProjection); } if (m_d->showKeyStrokes) { Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) { rc |= boundsPolicy(stroke.dev); } KisIndirectPaintingSupport::ReadLocker locker(this); KisPaintDeviceSP temporaryTarget = this->temporaryTarget(); if (temporaryTarget) { rc |= boundsPolicy(temporaryTarget); } } return rc; } QRect KisColorizeMask::extent() const { return calculateMaskBounds(DeviceExtentPolicy()); } QRect KisColorizeMask::exactBounds() const { return calculateMaskBounds(DeviceExactBoundsPolicy()); } QRect KisColorizeMask::nonDependentExtent() const { return extent(); } KisImageSP KisColorizeMask::fetchImage() const { KisLayerSP parentLayer(qobject_cast(parent().data())); if (!parentLayer) return KisImageSP(); return parentLayer->image(); } void KisColorizeMask::setImage(KisImageWSP image) { KisDefaultBoundsSP bounds(new KisDefaultBounds(image)); auto it = m_d->keyStrokes.begin(); for(; it != m_d->keyStrokes.end(); ++it) { it->dev->setDefaultBounds(bounds); } m_d->coloringProjection->setDefaultBounds(bounds); m_d->fakePaintDevice->setDefaultBounds(bounds); m_d->filteredSource->setDefaultBounds(bounds); } void KisColorizeMask::setCurrentColor(const KoColor &_color) { KoColor color = _color; color.convertTo(colorSpace()); WriteLocker locker(this); m_d->setNeedsUpdateImpl(true, false); QList::const_iterator it = std::find_if(m_d->keyStrokes.constBegin(), m_d->keyStrokes.constEnd(), [color] (const KeyStroke &s) { return s.color == color; }); KisPaintDeviceSP activeDevice; bool newKeyStroke = false; if (it == m_d->keyStrokes.constEnd()) { activeDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); activeDevice->setParentNode(this); activeDevice->setDefaultBounds(KisDefaultBoundsBaseSP(new KisDefaultBounds(fetchImage()))); newKeyStroke = true; } else { activeDevice = it->dev; } m_d->currentColor = color; m_d->currentKeyStrokeDevice = activeDevice; m_d->needAddCurrentKeyStroke = newKeyStroke; } struct KeyStrokeAddRemoveCommand : public KisCommandUtils::FlipFlopCommand { KeyStrokeAddRemoveCommand(bool add, int index, KeyStroke stroke, QList *list, KisColorizeMaskSP node) : FlipFlopCommand(!add), m_index(index), m_stroke(stroke), m_list(list), m_node(node) {} void init() override { m_list->insert(m_index, m_stroke); m_node->setNeedsUpdate(true); emit m_node->sigKeyStrokesListChanged(); } void end() override { KIS_ASSERT_RECOVER_RETURN((*m_list)[m_index] == m_stroke); m_list->removeAt(m_index); m_node->setNeedsUpdate(true); emit m_node->sigKeyStrokesListChanged(); } private: int m_index; KeyStroke m_stroke; QList *m_list; KisColorizeMaskSP m_node; }; void KisColorizeMask::mergeToLayer(KisNodeSP layer, KisPostExecutionUndoAdapter *undoAdapter, const KUndo2MagicString &transactionText,int timedID) { Q_UNUSED(layer); WriteLocker locker(this); KisPaintDeviceSP temporaryTarget = this->temporaryTarget(); const bool isTemporaryTargetErasing = temporaryCompositeOp() == COMPOSITE_ERASE; const QRect temporaryExtent = temporaryTarget ? temporaryTarget->extent() : QRect(); KisSavedMacroCommand *macro = undoAdapter->createMacro(transactionText); KisMacroBasedUndoStore store(macro); KisPostExecutionUndoAdapter fakeUndoAdapter(&store, undoAdapter->strokesFacade()); /** * Add a new key stroke plane */ if (m_d->needAddCurrentKeyStroke && !isTemporaryTargetErasing) { KeyStroke key(m_d->currentKeyStrokeDevice, m_d->currentColor); KUndo2Command *cmd = new KeyStrokeAddRemoveCommand( true, m_d->keyStrokes.size(), key, &m_d->keyStrokes, KisColorizeMaskSP(this)); cmd->redo(); fakeUndoAdapter.addCommand(toQShared(cmd)); } /** * When erasing, the brush affects all the key strokes, not only * the current one. */ if (!isTemporaryTargetErasing) { mergeToLayerImpl(m_d->currentKeyStrokeDevice, &fakeUndoAdapter, transactionText, timedID, false); } else { Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) { if (temporaryExtent.intersects(stroke.dev->extent())) { mergeToLayerImpl(stroke.dev, &fakeUndoAdapter, transactionText, timedID, false); } } } mergeToLayerImpl(m_d->fakePaintDevice, &fakeUndoAdapter, transactionText, timedID, false); m_d->currentKeyStrokeDevice = 0; m_d->currentColor = KoColor(); releaseResources(); /** * Try removing the key strokes that has been completely erased */ if (isTemporaryTargetErasing) { for (int index = 0; index < m_d->keyStrokes.size(); /*noop*/) { const KeyStroke &stroke = m_d->keyStrokes[index]; if (stroke.dev->exactBounds().isEmpty()) { KUndo2Command *cmd = new KeyStrokeAddRemoveCommand( false, index, stroke, &m_d->keyStrokes, KisColorizeMaskSP(this)); cmd->redo(); fakeUndoAdapter.addCommand(toQShared(cmd)); } else { index++; } } } undoAdapter->addMacro(macro); } void KisColorizeMask::writeMergeData(KisPainter *painter, KisPaintDeviceSP src) { const KoColorSpace *alpha8 = KoColorSpaceRegistry::instance()->alpha8(); const bool nonAlphaDst = !(*painter->device()->colorSpace() == *alpha8); if (nonAlphaDst) { Q_FOREACH (const QRect &rc, src->region().rects()) { painter->bitBlt(rc.topLeft(), src, rc); } } else { KisSelectionSP conversionSelection = m_d->cachedConversionSelection.getSelection(); KisPixelSelectionSP tempSelection = conversionSelection->pixelSelection(); Q_FOREACH (const QRect &rc, src->region().rects()) { tempSelection->copyAlphaFrom(src, rc); painter->bitBlt(rc.topLeft(), tempSelection, rc); } m_d->cachedSelection.putSelection(conversionSelection); } } bool KisColorizeMask::supportsNonIndirectPainting() const { return false; } bool KisColorizeMask::showColoring() const { return m_d->showColoring; } void KisColorizeMask::setShowColoring(bool value) { QRect savedExtent; if (m_d->showColoring && !value) { savedExtent = extent(); } m_d->showColoring = value; baseNodeChangedCallback(); if (!savedExtent.isEmpty()) { setDirty(savedExtent); } } bool KisColorizeMask::showKeyStrokes() const { return m_d->showKeyStrokes; } void KisColorizeMask::setShowKeyStrokes(bool value) { QRect savedExtent; if (m_d->showKeyStrokes && !value) { savedExtent = extent(); } m_d->showKeyStrokes = value; baseNodeChangedCallback(); if (!savedExtent.isEmpty()) { setDirty(savedExtent); } regeneratePrefilteredDeviceIfNeeded(); } KisColorizeMask::KeyStrokeColors KisColorizeMask::keyStrokesColors() const { KeyStrokeColors colors; // TODO: thread safety! for (int i = 0; i < m_d->keyStrokes.size(); i++) { colors.colors << m_d->keyStrokes[i].color; if (m_d->keyStrokes[i].isTransparent) { colors.transparentIndex = i; } } return colors; } struct SetKeyStrokeColorsCommand : public KUndo2Command { SetKeyStrokeColorsCommand(const QList newList, QList *list, KisColorizeMaskSP node) : m_newList(newList), m_oldList(*list), m_list(list), m_node(node) {} void redo() override { *m_list = m_newList; m_node->setNeedsUpdate(true); emit m_node->sigKeyStrokesListChanged(); m_node->setDirty(); } void undo() override { *m_list = m_oldList; m_node->setNeedsUpdate(true); emit m_node->sigKeyStrokesListChanged(); m_node->setDirty(); } private: QList m_newList; QList m_oldList; QList *m_list; KisColorizeMaskSP m_node; }; void KisColorizeMask::setKeyStrokesColors(KeyStrokeColors colors) { KIS_ASSERT_RECOVER_RETURN(colors.colors.size() == m_d->keyStrokes.size()); QList newList = m_d->keyStrokes; for (int i = 0; i < newList.size(); i++) { newList[i].color = colors.colors[i]; newList[i].color.convertTo(colorSpace()); newList[i].isTransparent = colors.transparentIndex == i; } KisProcessingApplicator applicator(fetchImage(), KisNodeSP(this), KisProcessingApplicator::NONE, KisImageSignalVector(), kundo2_i18n("Change Key Stroke Color")); applicator.applyCommand( new SetKeyStrokeColorsCommand( newList, &m_d->keyStrokes, KisColorizeMaskSP(this))); applicator.end(); } void KisColorizeMask::removeKeyStroke(const KoColor &_color) { KoColor color = _color; color.convertTo(colorSpace()); QList::iterator it = std::find_if(m_d->keyStrokes.begin(), m_d->keyStrokes.end(), [color] (const KeyStroke &s) { return s.color == color; }); KIS_SAFE_ASSERT_RECOVER_RETURN(it != m_d->keyStrokes.end()); const int index = it - m_d->keyStrokes.begin(); KisProcessingApplicator applicator(KisImageWSP(fetchImage()), KisNodeSP(this), KisProcessingApplicator::NONE, KisImageSignalVector(), kundo2_i18n("Remove Key Stroke")); applicator.applyCommand( new KeyStrokeAddRemoveCommand( false, index, *it, &m_d->keyStrokes, KisColorizeMaskSP(this))); applicator.end(); } QVector KisColorizeMask::allPaintDevices() const { QVector devices; Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) { devices << stroke.dev; } devices << m_d->coloringProjection; devices << m_d->fakePaintDevice; return devices; } void KisColorizeMask::resetCache() { m_d->filteredSource->clear(); m_d->originalSequenceNumber = -1; m_d->filteringDirty = true; rerenderFakePaintDevice(); slotUpdateRegenerateFilling(true); } void KisColorizeMask::setUseEdgeDetection(bool value) { m_d->filteringOptions.useEdgeDetection = value; m_d->filteringDirty = true; setNeedsUpdate(true); } bool KisColorizeMask::useEdgeDetection() const { return m_d->filteringOptions.useEdgeDetection; } void KisColorizeMask::setEdgeDetectionSize(qreal value) { m_d->filteringOptions.edgeDetectionSize = value; m_d->filteringDirty = true; setNeedsUpdate(true); } qreal KisColorizeMask::edgeDetectionSize() const { return m_d->filteringOptions.edgeDetectionSize; } void KisColorizeMask::setFuzzyRadius(qreal value) { m_d->filteringOptions.fuzzyRadius = value; m_d->filteringDirty = true; setNeedsUpdate(true); } qreal KisColorizeMask::fuzzyRadius() const { return m_d->filteringOptions.fuzzyRadius; } void KisColorizeMask::setCleanUpAmount(qreal value) { m_d->filteringOptions.cleanUpAmount = value; setNeedsUpdate(true); } qreal KisColorizeMask::cleanUpAmount() const { return m_d->filteringOptions.cleanUpAmount; } void KisColorizeMask::setLimitToDeviceBounds(bool value) { m_d->limitToDeviceBounds = value; m_d->filteringDirty = true; setNeedsUpdate(true); } bool KisColorizeMask::limitToDeviceBounds() const { return m_d->limitToDeviceBounds; } void KisColorizeMask::rerenderFakePaintDevice() { m_d->fakePaintDevice->clear(); KisFillPainter gc(m_d->fakePaintDevice); KisSelectionSP selection = m_d->cachedSelection.getSelection(); Q_FOREACH (const KeyStroke &stroke, m_d->keyStrokes) { const QRect rect = stroke.dev->extent(); selection->pixelSelection()->makeCloneFromRough(stroke.dev, rect); gc.setSelection(selection); gc.fillSelection(rect, stroke.color); } m_d->cachedSelection.putSelection(selection); } void KisColorizeMask::testingAddKeyStroke(KisPaintDeviceSP dev, const KoColor &color, bool isTransparent) { m_d->keyStrokes << KeyStroke(dev, color, isTransparent); } void KisColorizeMask::testingRegenerateMask() { slotUpdateRegenerateFilling(); } KisPaintDeviceSP KisColorizeMask::testingFilteredSource() const { return m_d->filteredSource; } QList KisColorizeMask::fetchKeyStrokesDirect() const { return m_d->keyStrokes; } void KisColorizeMask::setKeyStrokesDirect(const QList &strokes) { m_d->keyStrokes = strokes; for (auto it = m_d->keyStrokes.begin(); it != m_d->keyStrokes.end(); ++it) { it->dev->setParentNode(this); } KisImageSP image = fetchImage(); KIS_SAFE_ASSERT_RECOVER_RETURN(image); setImage(image); } qint32 KisColorizeMask::x() const { return m_d->offset.x(); } qint32 KisColorizeMask::y() const { return m_d->offset.y(); } void KisColorizeMask::setX(qint32 x) { const QPoint oldOffset = m_d->offset; m_d->offset.rx() = x; moveAllInternalDevices(m_d->offset - oldOffset); } void KisColorizeMask::setY(qint32 y) { const QPoint oldOffset = m_d->offset; m_d->offset.ry() = y; moveAllInternalDevices(m_d->offset - oldOffset); } KisPaintDeviceList KisColorizeMask::getLodCapableDevices() const { KisPaintDeviceList list; auto it = m_d->keyStrokes.begin(); for(; it != m_d->keyStrokes.end(); ++it) { list << it->dev; } list << m_d->coloringProjection; list << m_d->fakePaintDevice; list << m_d->filteredSource; return list; } void KisColorizeMask::regeneratePrefilteredDeviceIfNeeded() { if (!parent()) return; KisPaintDeviceSP src = parent()->original(); KIS_ASSERT_RECOVER_RETURN(src); if (!m_d->filteredSourceValid(src)) { // update the prefiltered source if needed slotUpdateRegenerateFilling(true); } } void KisColorizeMask::moveAllInternalDevices(const QPoint &diff) { QVector devices = allPaintDevices(); Q_FOREACH (KisPaintDeviceSP dev, devices) { dev->moveTo(dev->offset() + diff); } } diff --git a/libs/image/metadata/kis_exif_info_visitor.h b/libs/image/metadata/kis_exif_info_visitor.h index 4f9cee3ed4..998f7a1353 100644 --- a/libs/image/metadata/kis_exif_info_visitor.h +++ b/libs/image/metadata/kis_exif_info_visitor.h @@ -1,95 +1,105 @@ /* * Copyright (c) 2005 Cyrille Berger * * 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_EXIF_INFO_VISITOR_H #define KIS_EXIF_INFO_VISITOR_H #include #include #include #include #include +/** + * @brief The KisExifInfoVisitor class looks for a layer with metadata. + * + * If there is more than one layer with metadata, the metadata provided + * by the visitor is the metadata associated with the last layer that + * had metadata on it. Only use the metadata if only one layer with + * metadata was found. + * + * The metadata pointer is OWNED by the layer. + * + */ class KisExifInfoVisitor : public KisNodeVisitor { public: - KisExifInfoVisitor() : - m_exifInfo(0), - m_countPaintLayer(0) { } -public: - + KisExifInfoVisitor() { } bool visit(KisNode*) override { return true; } bool visit(KisCloneLayer*) override { return true; } bool visit(KisFilterMask*) override { return true; } bool visit(KisTransformMask*) override { return true; } bool visit(KisTransparencyMask*) override { return true; } bool visit(KisSelectionMask*) override { return true; } bool visit(KisColorizeMask*) override { return true; } bool visit(KisExternalLayer*) override { return true; } bool visit(KisGeneratorLayer*) override { return true; } bool visit(KisAdjustmentLayer*) override { return true; } bool visit(KisPaintLayer* layer) override { - m_countPaintLayer++; if (!layer->metaData()->empty()) { + m_metaDataObjectsEncountered++; m_exifInfo = layer->metaData(); } return true; } bool visit(KisGroupLayer* layer) override { dbgMetaData << "Visiting on grouplayer" << layer->name() << ""; return visitAll(layer, true); } - public: - inline uint countPaintLayer() { - return m_countPaintLayer; + inline uint metaDataCount() + { + qDebug() << "number of layers with metadata" << m_metaDataObjectsEncountered; + return m_metaDataObjectsEncountered; } - inline KisMetaData::Store* exifInfo() { + + inline KisMetaData::Store* exifInfo() + { return m_exifInfo; } private: - KisMetaData::Store* m_exifInfo; - uint m_countPaintLayer; + KisMetaData::Store *m_exifInfo {0}; + int m_metaDataObjectsEncountered {0}; }; #endif diff --git a/libs/image/tests/CMakeLists.txt b/libs/image/tests/CMakeLists.txt index 90a5e6289e..979f93f1dd 100644 --- a/libs/image/tests/CMakeLists.txt +++ b/libs/image/tests/CMakeLists.txt @@ -1,246 +1,248 @@ # cmake in some versions for some not yet known reasons fails to run automoc # on random targets (changing target names already has an effect) # As temporary workaround skipping build of tests on these versions for now # See https://mail.kde.org/pipermail/kde-buildsystem/2015-June/010819.html # extend range of affected cmake versions as needed if(NOT ${CMAKE_VERSION} VERSION_LESS 3.1.3 AND NOT ${CMAKE_VERSION} VERSION_GREATER 3.2.3) message(WARNING "Skipping krita/image/tests, CMake in at least versions 3.1.3 - 3.2.3 seems to have a problem with automoc. \n(FRIENDLY REMINDER: PLEASE DON'T BREAK THE TESTS!)") set (HAVE_FAILING_CMAKE TRUE) else() set (HAVE_FAILING_CMAKE FALSE) endif() include_directories( ${CMAKE_SOURCE_DIR}/libs/image/metadata ${CMAKE_BINARY_DIR}/libs/image/ ${CMAKE_SOURCE_DIR}/libs/image/ ${CMAKE_SOURCE_DIR}/libs/image/brushengine ${CMAKE_SOURCE_DIR}/libs/image/tiles3 ${CMAKE_SOURCE_DIR}/libs/image/tiles3/swap ${CMAKE_SOURCE_DIR}/sdk/tests ) include_Directories(SYSTEM ${EIGEN3_INCLUDE_DIR} ) if(HAVE_VC) include_directories(${Vc_INCLUDE_DIR}) endif() include(ECMAddTests) include(KritaAddBrokenUnitTest) macro_add_unittest_definitions() set(KisRandomGeneratorDemoSources kis_random_generator_demo.cpp kimageframe.cpp) ki18n_wrap_ui(KisRandomGeneratorDemoSources kis_random_generator_demo.ui) add_executable(KisRandomGeneratorDemo ${KisRandomGeneratorDemoSources}) target_link_libraries(KisRandomGeneratorDemo kritaimage) ecm_mark_as_test(KisRandomGeneratorDemo) ecm_add_tests( kis_base_node_test.cpp kis_fast_math_test.cpp kis_node_test.cpp kis_node_facade_test.cpp kis_fixed_paint_device_test.cpp kis_layer_test.cpp kis_effect_mask_test.cpp kis_iterator_test.cpp kis_painter_test.cpp kis_selection_test.cpp kis_count_visitor_test.cpp kis_projection_test.cpp kis_properties_configuration_test.cpp kis_transaction_test.cpp kis_pixel_selection_test.cpp kis_group_layer_test.cpp kis_paint_layer_test.cpp kis_adjustment_layer_test.cpp kis_annotation_test.cpp kis_change_profile_visitor_test.cpp kis_clone_layer_test.cpp kis_colorspace_convert_visitor_test.cpp kis_convolution_painter_test.cpp kis_crop_processing_visitor_test.cpp kis_processing_applicator_test.cpp kis_datamanager_test.cpp kis_fill_painter_test.cpp kis_filter_configuration_test.cpp kis_filter_test.cpp kis_filter_processing_information_test.cpp kis_filter_registry_test.cpp kis_filter_strategy_test.cpp kis_gradient_painter_test.cpp kis_image_commands_test.cpp kis_image_test.cpp kis_image_signal_router_test.cpp kis_iterators_ng_test.cpp kis_iterator_benchmark.cpp kis_updater_context_test.cpp kis_simple_update_queue_test.cpp kis_stroke_test.cpp kis_simple_stroke_strategy_test.cpp kis_stroke_strategy_undo_command_based_test.cpp kis_strokes_queue_test.cpp kis_macro_test.cpp kis_mask_test.cpp kis_math_toolbox_test.cpp kis_name_server_test.cpp kis_node_commands_test.cpp kis_node_graph_listener_test.cpp kis_node_visitor_test.cpp kis_paint_information_test.cpp kis_distance_information_test.cpp kis_paintop_test.cpp kis_pattern_test.cpp kis_recorded_action_factory_registry_test.cpp kis_recorded_action_test.cpp kis_recorded_filter_action_test.cpp kis_selection_mask_test.cpp kis_shared_ptr_test.cpp kis_bsplines_test.cpp kis_warp_transform_worker_test.cpp kis_liquify_transform_worker_test.cpp kis_transparency_mask_test.cpp kis_types_test.cpp kis_vec_test.cpp kis_filter_config_widget_test.cpp kis_mask_generator_test.cpp kis_cubic_curve_test.cpp kis_node_query_path_test.cpp kis_fixed_point_maths_test.cpp kis_filter_weights_buffer_test.cpp kis_filter_weights_applicator_test.cpp kis_fill_interval_test.cpp kis_fill_interval_map_test.cpp kis_scanline_fill_test.cpp kis_psd_layer_style_test.cpp kis_layer_style_projection_plane_test.cpp kis_lod_capable_layer_offset_test.cpp kis_algebra_2d_test.cpp kis_marker_painter_test.cpp kis_lazy_brush_test.cpp kis_colorize_mask_test.cpp + kis_mask_similarity_test.cpp + KisMaskGeneratorBenchmark.cpp NAME_PREFIX "krita-image-" LINK_LIBRARIES kritaimage Qt5::Test) ecm_add_test(kis_layer_style_filter_environment_test.cpp TEST_NAME kritaimage-layer_style_filter_environment_test LINK_LIBRARIES ${KDE4_KDEUI_LIBS} kritaimage Qt5::Test) ecm_add_test(kis_asl_parser_test.cpp TEST_NAME kritalibpsd-asl_parser_test LINK_LIBRARIES kritapsd kritapigment kritawidgetutils kritacommand Qt5::Xml Qt5::Test) ecm_add_test(KisPerStrokeRandomSourceTest.cpp TEST_NAME KisPerStrokeRandomSourceTest LINK_LIBRARIES kritaimage Qt5::Test) ecm_add_test(KisWatershedWorkerTest.cpp TEST_NAME KisWatershedWorkerTest LINK_LIBRARIES kritaimage Qt5::Test) # ecm_add_test(kis_dom_utils_test.cpp # TEST_NAME krita-image-DomUtils-Test # LINK_LIBRARIES kritaimage Qt5::Test) # kisdoc dep # kis_transform_worker_test.cpp # TEST_NAME krita-image-KisTransformWorkerTest #LINK_LIBRARIES kritaimage Qt5::Test) # kisdoc # kis_perspective_transform_worker_test.cpp # TEST_NAME krita-image-KisPerspectiveTransformWorkerTest #LINK_LIBRARIES kritaimage Qt5::Test) # kis_cs_conversion_test.cpp # TEST_NAME krita-image-KisCsConversionTest # LINK_LIBRARIES kritaimage Qt5::Test) # kisdoc # kis_processings_test.cpp # TEST_NAME krita-image-KisProcessingsTest #LINK_LIBRARIES kritaimage Qt5::Test) # image/tests cannot use stuff that needs kisdocument # kis_projection_leaf_test.cpp # TEST_NAME kritaimage-projection_leaf_test # LINK_LIBRARIES kritaimage Qt5::Test) if (NOT HAVE_FAILING_CMAKE) krita_add_broken_unit_test(kis_paint_device_test.cpp TEST_NAME krita-image-KisPaintDeviceTest LINK_LIBRARIES kritaimage kritaodf Qt5::Test) else() message(WARNING "Skipping KisPaintDeviceTest!!!!!!!!!!!!!!") endif() if (NOT HAVE_FAILING_CMAKE) krita_add_broken_unit_test(kis_filter_mask_test.cpp TEST_NAME krita-image-KisFilterMaskTest LINK_LIBRARIES kritaimage Qt5::Test) else() message(WARNING "Skipping KisFilterMaskTest!!!!!!!!!!!!!!") endif() krita_add_broken_unit_test(kis_transform_mask_test.cpp TEST_NAME krita-image-KisTransformMaskTest LINK_LIBRARIES kritaimage Qt5::Test) krita_add_broken_unit_test(kis_histogram_test.cpp TEST_NAME krita-image-KisHistogramTest LINK_LIBRARIES kritaimage Qt5::Test) krita_add_broken_unit_test(kis_walkers_test.cpp TEST_NAME krita-image-KisWalkersTest LINK_LIBRARIES kritaimage Qt5::Test) #krita_add_broken_unit_test(kis_async_merger_test.cpp # TEST_NAME krita-image-KisAsyncMergerTest # LINK_LIBRARIES kritaimage Qt5::Test) krita_add_broken_unit_test(kis_update_scheduler_test.cpp TEST_NAME krita-image-KisUpdateSchedulerTest LINK_LIBRARIES kritaimage Qt5::Test) krita_add_broken_unit_test(kis_queues_progress_updater_test.cpp TEST_NAME krita-image-KisQueuesProgressUpdaterTest LINK_LIBRARIES kritaimage Qt5::Test) krita_add_broken_unit_test(kis_cage_transform_worker_test.cpp TEST_NAME krita-image-KisCageTransformWorkerTest LINK_LIBRARIES kritaimage Qt5::Test) krita_add_broken_unit_test(kis_meta_data_test.cpp TEST_NAME krita-image-KisMetaDataTest LINK_LIBRARIES kritaimage Qt5::Test) krita_add_broken_unit_test(kis_random_generator_test.cpp TEST_NAME krita-image-KisRandomGeneratorTest LINK_LIBRARIES kritaimage Qt5::Test) krita_add_broken_unit_test(kis_keyframing_test.cpp TEST_NAME krita-image-Keyframing-Test LINK_LIBRARIES kritaimage Qt5::Test) krita_add_broken_unit_test(kis_image_animation_interface_test.cpp TEST_NAME krita-image-ImageAnimationInterface-Test LINK_LIBRARIES ${KDE4_KDEUI_LIBS} kritaimage Qt5::Test) krita_add_broken_unit_test(kis_onion_skin_compositor_test.cpp TEST_NAME krita-image-OnionSkinCompositor-Test LINK_LIBRARIES ${KDE4_KDEUI_LIBS} kritaimage Qt5::Test) krita_add_broken_unit_test(kis_layer_styles_test.cpp TEST_NAME krita-image-LayerStylesTest LINK_LIBRARIES kritaimage Qt5::Test) diff --git a/libs/image/tests/KisMaskGeneratorBenchmark.cpp b/libs/image/tests/KisMaskGeneratorBenchmark.cpp new file mode 100644 index 0000000000..6875ef9b99 --- /dev/null +++ b/libs/image/tests/KisMaskGeneratorBenchmark.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2018 Iván Santa María + * + * 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 +#include +#include + +#include "KisMaskGeneratorBenchmark.h" + +#include "kis_mask_similarity_test.h" + +#include "kis_brush_mask_applicator_base.h" +#include "kis_mask_generator.h" +#include "krita_utils.h" + +#include "testutil.h" + +class KisMaskGeneratorBenchmarkTester +{ +public: + KisMaskGeneratorBenchmarkTester(KisBrushMaskApplicatorBase *_applicatorBase, QRect _bounds) + : applicatorBase(_applicatorBase) + , m_bounds(_bounds) + { + KisFixedPaintDeviceSP m_paintDev = new KisFixedPaintDevice(m_colorSpace); + m_paintDev->setRect(m_bounds); + m_paintDev->initialize(255); + + MaskProcessingData data(m_paintDev, m_colorSpace, + 0.0, 1.0, + m_bounds.width() / 2.0, m_bounds.height() / 2.0,0); + + // Start Benchmark + applicatorBase->initializeData(&data); + + QElapsedTimer maskGenerationTime; + maskGenerationTime.start(); + + QBENCHMARK { + + applicatorBase->process(m_bounds); + } + } + +protected: + const KoColorSpace *m_colorSpace = KoColorSpaceRegistry::instance()->rgb8(); + QRect m_bounds; + + KisBrushMaskApplicatorBase *applicatorBase; + KisFixedPaintDeviceSP m_paintDev; +}; + +void KisMaskGeneratorBenchmark::testDefaultScalarMask() +{ + QRect bounds(0,0,1000,1000); + { + KisCircleMaskGenerator circScalar(1000, 1.0, 0.5, 0.5, 2, true); + circScalar.resetMaskApplicator(true); // Force usage of scalar backend + + KisMaskGeneratorBenchmarkTester(circScalar.applicator(), bounds); + } +} + +void KisMaskGeneratorBenchmark::testDefaultVectorMask() +{ + QRect bounds(0,0,1000,1000); + { + KisCircleMaskGenerator circVectr(1000, 1.0, 0.5, 0.5, 2, true); + KisMaskGeneratorBenchmarkTester(circVectr.applicator(), bounds); + } +} + +void KisMaskGeneratorBenchmark::testCircularGaussScalarMask() +{ + QRect bounds(0,0,1000,1000); + { + KisGaussCircleMaskGenerator circScalar(1000, 1.0, 0.5, 0.5, 2, true); + circScalar.setDiameter(1000); + circScalar.resetMaskApplicator(true); // Force usage of scalar backend + + KisMaskGeneratorBenchmarkTester(circScalar.applicator(), bounds); + } +} + +void KisMaskGeneratorBenchmark::testCircularGaussVectorMask() +{ + QRect bounds(0,0,1000,1000); + { + KisGaussCircleMaskGenerator circVectr(1000, 1.0, 0.5, 0.5, 2, true); + circVectr.setDiameter(1000); + KisMaskGeneratorBenchmarkTester(circVectr.applicator(), bounds); + } +} + +QTEST_MAIN(KisMaskGeneratorBenchmark) diff --git a/libs/ui/dialogs/kis_about_application.h b/libs/image/tests/KisMaskGeneratorBenchmark.h similarity index 65% copy from libs/ui/dialogs/kis_about_application.h copy to libs/image/tests/KisMaskGeneratorBenchmark.h index 23a94d2dcb..da1e2efb51 100644 --- a/libs/ui/dialogs/kis_about_application.h +++ b/libs/image/tests/KisMaskGeneratorBenchmark.h @@ -1,35 +1,36 @@ /* - * Copyright (c) 2014 Boudewijn Rempt + * Copyright (c) 2018 Iván Santa María * * 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_ABOUT_APPLICATION_H -#define KIS_ABOUT_APPLICATION_H -#include +#ifndef KISMASKGENERATORBENCHMARK_H +#define KISMASKGENERATORBENCHMARK_H -class KisAboutApplication : public QDialog +#include + +class KisMaskGeneratorBenchmark : public QObject { Q_OBJECT -public: - explicit KisAboutApplication(QWidget *parent = 0); - -Q_SIGNALS: +private Q_SLOTS: -public Q_SLOTS: + void testDefaultScalarMask(); + void testDefaultVectorMask(); + void testCircularGaussScalarMask(); + void testCircularGaussVectorMask(); }; -#endif // KIS_ABOUT_APPLICATION_H +#endif // KISMASKGENERATORBENCHMARK_H diff --git a/libs/image/tests/kis_mask_similarity_test.cpp b/libs/image/tests/kis_mask_similarity_test.cpp new file mode 100644 index 0000000000..1e6885bbfb --- /dev/null +++ b/libs/image/tests/kis_mask_similarity_test.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2018 Iván Santa María + * + * 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_mask_similarity_test.h" + +#include +#include +#include + +#include "kis_brush_mask_applicator_base.h" +#include "kis_mask_generator.h" +#include "krita_utils.h" + + +class KisMaskSimilarityTester +{ +public: + KisMaskSimilarityTester(KisBrushMaskApplicatorBase *_legacy, KisBrushMaskApplicatorBase *_vectorized, QRect _bounds) + : legacy(_legacy) + , vectorized(_vectorized) + , m_bounds(_bounds) + { + KisFixedPaintDeviceSP m_paintDev = new KisFixedPaintDevice(m_colorSpace); + m_paintDev->setRect(m_bounds); + m_paintDev->initialize(255); + + MaskProcessingData data(m_paintDev, m_colorSpace, + 0.0, 1.0, + m_bounds.width() / 2.0, m_bounds.height() / 2.0,0); + + // Start legacy scalar processing + legacy->initializeData(&data); + legacy->process(m_bounds); + + QImage scalarImage(m_paintDev->convertToQImage(m_colorSpace->profile())); + scalarImage.invertPixels(); // Make pixel color black + scalarImage.save(QString("scalar_mask.png"),"PNG"); + + // Start vector processing + m_paintDev->initialize(255); + vectorized->initializeData(&data); + vectorized->process(m_bounds); + + QImage vectorImage(m_paintDev->convertToQImage(m_colorSpace->profile())); + vectorImage.invertPixels(); // Make pixel color black + vectorImage.save(QString("vector_mask.png"),"PNG"); + +// Development debug. +// Count number of identical pixels +// int equals = 0; +// for (int i = 0; i < scalarImage.width(); ++i) { +// for (int j = 0; j < scalarImage.height(); ++j) { +// if (scalarImage.pixelColor(i,j) == vectorImage.pixelColor(i,j)){ +// equals++; +// } else { +// qDebug() << scalarImage.pixelColor(i,j) << " " << vectorImage.pixelColor(i,j); +// } +// } +// } +// qDebug() << "Equal Pixels: " << equals; + + // Check for differences, max error .5% of pixel mismatch + int tolerance = m_bounds.width() * m_bounds.height() * .005f; + QPoint tmpPt; + QVERIFY(TestUtil::compareQImages(tmpPt,scalarImage, vectorImage, 0, 1, tolerance)); + } + +private: + +protected: + const KoColorSpace *m_colorSpace = KoColorSpaceRegistry::instance()->rgb8(); + + KisBrushMaskApplicatorBase *legacy; + KisBrushMaskApplicatorBase *vectorized; + QRect m_bounds; + KisFixedPaintDeviceSP m_paintDev; +}; + + +void KisMaskSimilarityTest::testCircleMask() +{ + QRect bounds(0,0,500,500); + { + KisCircleMaskGenerator circVectr(480, 1.0, 0.5, 0.5, 2, true); + KisCircleMaskGenerator circScalar(circVectr); + + circScalar.resetMaskApplicator(true); // Force usage of scalar backend + KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), bounds); + } +} + +void KisMaskSimilarityTest::testGaussCircleMask() +{ + QRect bounds(0,0,500,500); + { + KisGaussCircleMaskGenerator circVectr(480, 1.0, 0.5, 0.5, 2, true); + circVectr.setDiameter(480); + KisGaussCircleMaskGenerator circScalar(circVectr); + + circScalar.resetMaskApplicator(true); // Force usage of scalar backend + KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), bounds); + } +} + +QTEST_MAIN(KisMaskSimilarityTest) diff --git a/libs/ui/dialogs/kis_about_application.h b/libs/image/tests/kis_mask_similarity_test.h similarity index 71% copy from libs/ui/dialogs/kis_about_application.h copy to libs/image/tests/kis_mask_similarity_test.h index 23a94d2dcb..2f3c9f873e 100644 --- a/libs/ui/dialogs/kis_about_application.h +++ b/libs/image/tests/kis_mask_similarity_test.h @@ -1,35 +1,33 @@ /* - * Copyright (c) 2014 Boudewijn Rempt + * Copyright (c) 2018 Iván Santa María * * 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_ABOUT_APPLICATION_H -#define KIS_ABOUT_APPLICATION_H -#include +#ifndef KIS_MASK_SIMILARITY_TEST +#define KIS_MASK_SIMILARITY_TEST -class KisAboutApplication : public QDialog +#include + +class KisMaskSimilarityTest : public QObject { Q_OBJECT -public: - explicit KisAboutApplication(QWidget *parent = 0); - -Q_SIGNALS: - -public Q_SLOTS: +private Q_SLOTS: + void testCircleMask(); + void testGaussCircleMask(); }; -#endif // KIS_ABOUT_APPLICATION_H +#endif diff --git a/libs/koplugin/KisMimeDatabase.cpp b/libs/koplugin/KisMimeDatabase.cpp index 6ff8dd7957..6fcf7f874f 100644 --- a/libs/koplugin/KisMimeDatabase.cpp +++ b/libs/koplugin/KisMimeDatabase.cpp @@ -1,282 +1,292 @@ /* * 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 "KisMimeDatabase.h" #include #include #include #include #include QList KisMimeDatabase::s_mimeDatabase; QString KisMimeDatabase::mimeTypeForFile(const QString &file, bool checkExistingFiles) { fillMimeData(); QFileInfo fi(file); QString suffix = fi.suffix().toLower(); Q_FOREACH(const KisMimeDatabase::KisMimeType &mimeType, s_mimeDatabase) { if (mimeType.suffixes.contains(suffix)) { debugPlugin << "mimeTypeForFile(). KisMimeDatabase returned" << mimeType.mimeType << "for" << file; return mimeType.mimeType; } } QMimeDatabase db; QMimeType mime; if (checkExistingFiles && fi.size() > 0) { mime = db.mimeTypeForFile(file, QMimeDatabase::MatchContent); if (mime.name() != "application/octet-stream" && mime.name() != "application/zip") { debugPlugin << "mimeTypeForFile(). QMimeDatabase returned" << mime.name() << "for" << file; return mime.name(); } } mime = db.mimeTypeForFile(file); if (mime.name() != "application/octet-stream") { debugPlugin << "mimeTypeForFile(). QMimeDatabase returned" << mime.name() << "for" << file; return mime.name(); } return ""; } QString KisMimeDatabase::mimeTypeForSuffix(const QString &suffix) { fillMimeData(); QMimeDatabase db; QString s = suffix.toLower(); Q_FOREACH(const KisMimeDatabase::KisMimeType &mimeType, s_mimeDatabase) { if (mimeType.suffixes.contains(s)) { debugPlugin << "mimeTypeForSuffix(). KisMimeDatabase returned" << mimeType.mimeType << "for" << s; return mimeType.mimeType; } } // make the file look like a file so Qt would recognize it s = "file." + s; return mimeTypeForFile(s); } QString KisMimeDatabase::mimeTypeForData(const QByteArray ba) { QMimeDatabase db; QMimeType mtp = db.mimeTypeForData(ba); debugPlugin << "mimeTypeForData(). QMimeDatabase returned" << mtp.name(); return mtp.name(); } QString KisMimeDatabase::descriptionForMimeType(const QString &mimeType) { fillMimeData(); Q_FOREACH(const KisMimeDatabase::KisMimeType &m, s_mimeDatabase) { if (m.mimeType == mimeType) { debugPlugin << "descriptionForMimeType. KisMimeDatabase returned" << m.description << "for" << mimeType; return m.description; } } QMimeDatabase db; QMimeType mime = db.mimeTypeForName(mimeType); if (mime.name() != "application/octet-stream") { debugPlugin << "descriptionForMimeType. QMimeDatabase returned" << mime.comment() << "for" << mimeType; return mime.comment(); } return mimeType; } QStringList KisMimeDatabase::suffixesForMimeType(const QString &mimeType) { fillMimeData(); Q_FOREACH(const KisMimeDatabase::KisMimeType &m, s_mimeDatabase) { if (m.mimeType == mimeType) { debugPlugin << "suffixesForMimeType. KisMimeDatabase returned" << m.suffixes; return m.suffixes; } } QMimeDatabase db; QMimeType mime = db.mimeTypeForName(mimeType); if (mime.name() != "application/octet-stream" && !mime.suffixes().isEmpty()) { QString preferredSuffix = mime.preferredSuffix(); if (mimeType == "image/x-tga") { preferredSuffix = "tga"; } if (mimeType == "image/jpeg") { preferredSuffix = "jpg"; } QStringList suffixes = mime.suffixes(); if (preferredSuffix != suffixes.first()) { suffixes.removeAll(preferredSuffix); suffixes.prepend(preferredSuffix); } debugPlugin << "suffixesForMimeType. QMimeDatabase returned" << suffixes; return suffixes; } return QStringList(); } QString KisMimeDatabase::iconNameForMimeType(const QString &mimeType) { QMimeDatabase db; QMimeType mime = db.mimeTypeForName(mimeType); debugPlugin << "iconNameForMimeType" << mime.iconName(); return mime.iconName(); } void KisMimeDatabase::fillMimeData() { // This should come from the import/export plugins, but the json files aren't translated, // which is bad for the description field if (s_mimeDatabase.isEmpty()) { KisMimeType mimeType; mimeType.mimeType = "image/x-gimp-brush"; mimeType.description = i18nc("description of a file type", "Gimp Brush"); mimeType.suffixes = QStringList() << "gbr" << "vbr"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/x-gimp-brush-animated"; mimeType.description = i18nc("description of a file type", "Gimp Image Hose Brush"); mimeType.suffixes = QStringList() << "gih"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/x-adobe-brushlibrary"; mimeType.description = i18nc("description of a file type", "Adobe Brush Library"); mimeType.suffixes = QStringList() << "abr"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-krita-paintoppreset"; mimeType.description = i18nc("description of a file type", "Krita Brush Preset"); mimeType.suffixes = QStringList() << "kpp"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-krita-assistant"; mimeType.description = i18nc("description of a file type", "Krita Assistant"); mimeType.suffixes = QStringList() << "paintingassistant"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/x-r32"; mimeType.description = i18nc("description of a file type", "R32 Heightmap"); mimeType.suffixes = QStringList() << "r32"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/x-r16"; mimeType.description = i18nc("description of a file type", "R16 Heightmap"); mimeType.suffixes = QStringList() << "r16"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/x-r8"; mimeType.description = i18nc("description of a file type", "R8 Heightmap"); mimeType.suffixes = QStringList() << "r8"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-spriter"; mimeType.description = i18nc("description of a file type", "Spriter SCML"); mimeType.suffixes = QStringList() << "scml"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/x-svm"; mimeType.description = i18nc("description of a file type", "Starview Metafile"); mimeType.suffixes = QStringList() << "svm"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/openraster"; mimeType.description = i18nc("description of a file type", "OpenRaster Image"); mimeType.suffixes = QStringList() << "ora"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-photoshop-style-library"; mimeType.description = i18nc("description of a file type", "Photoshop Layer Style Library"); mimeType.suffixes = QStringList() << "asl"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-gimp-color-palette"; mimeType.description = i18nc("description of a file type", "Color Palette"); mimeType.suffixes = QStringList() << "gpl" << "pal" << "act" << "aco" << "colors" << "xml" << "sbz"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-opencolorio-configuration"; mimeType.description = i18nc("description of a file type", "OpenColorIO Configuration"); mimeType.suffixes = QStringList() << "ocio"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-krita-recorded-macro"; mimeType.description = i18nc("description of a file type", "Krita Recorded Action"); mimeType.suffixes = QStringList() << "krarec"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-gimp-gradient"; mimeType.description = i18nc("description of a file type", "GIMP Gradients"); mimeType.suffixes = QStringList() << "ggr"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-gimp-pattern"; mimeType.description = i18nc("description of a file type", "GIMP Patterns"); mimeType.suffixes = QStringList() << "pat"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-karbon-gradient"; mimeType.description = i18nc("description of a file type", "Karbon Gradients"); mimeType.suffixes = QStringList() << "kgr"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-krita-bundle"; mimeType.description = i18nc("description of a file type", "Krita Resource Bundle"); mimeType.suffixes = QStringList() << "bundle"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-krita-workspace"; mimeType.description = i18nc("description of a file type", "Krita Workspace"); mimeType.suffixes = QStringList() << "kws"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-krita-taskset"; mimeType.description = i18nc("description of a file type", "Krita Taskset"); mimeType.suffixes = QStringList() << "kts"; s_mimeDatabase << mimeType; + mimeType.mimeType = "application/x-krita-reference-images"; + mimeType.description = i18nc("description of a file type", "Krita Reference Image Collection"); + mimeType.suffixes = QStringList() << "krf"; + s_mimeDatabase << mimeType; + mimeType.mimeType = "image/x-krita-raw"; mimeType.description = i18nc("description of a file type", "Camera Raw Files"); - mimeType.suffixes = QStringList() << "rw2" << "nef" << "cr2" << "sr2" << "crw" << "pef" << "x3f" << "kdc" << "mrw" << "arw" << "k25" << "dcr" << "orf" << "raw" << "raw" << "raf" << "srf" << "dng"; + mimeType.suffixes = QStringList() << "bay" << "bmq" << "cr2" << "crw" << "cs1" << "dc2" << "dcr" << "dng" << "erf" << "fff" << "hdr" << "k25" << "kdc" << "mdc" << "mos" << "mrw" << "nef" << "orf" << "pef" << "pxn" << "raf" << "raw" << "rdc" << "sr2" << "srf" << "x3f" << "arw" << "3fr" << "cine" << "ia" << "kc2" << "mef" << "nrw" << "qtk" << "rw2" << "sti" << "rwl" << "srw"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-extension-exr"; mimeType.description = i18nc("description of a file type", "OpenEXR (Extended)"); mimeType.suffixes = QStringList() << "exr"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/x-psb"; mimeType.description = i18nc("description of a file type", "Photoshop Image (Large)"); mimeType.suffixes = QStringList() << "psb"; s_mimeDatabase << mimeType; + mimeType.mimeType = "image/heic"; + mimeType.description = i18nc("description of a file type", "HEIC/HEIF Image"); + mimeType.suffixes = QStringList() << "heic" << "heif"; + s_mimeDatabase << mimeType; + debugPlugin << "Filled mimedatabase with" << s_mimeDatabase.count() << "special mimetypes"; } } diff --git a/libs/libkis/Document.cpp b/libs/libkis/Document.cpp index 488cac41f5..6b2a706793 100644 --- a/libs/libkis/Document.cpp +++ b/libs/libkis/Document.cpp @@ -1,826 +1,827 @@ /* * Copyright (c) 2016 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 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 Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "Document.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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct Document::Private { Private() {} QPointer document; }; Document::Document(KisDocument *document, QObject *parent) : QObject(parent) , d(new Private) { d->document = document; } Document::~Document() { delete d; } bool Document::operator==(const Document &other) const { return (d->document == other.d->document); } bool Document::operator!=(const Document &other) const { return !(operator==(other)); } bool Document::batchmode() const { if (!d->document) return false; return d->document->fileBatchMode(); } void Document::setBatchmode(bool value) { if (!d->document) return; d->document->setFileBatchMode(value); } Node *Document::activeNode() const { QList activeNodes; Q_FOREACH(QPointer view, KisPart::instance()->views()) { if (view && view->document() == d->document) { activeNodes << view->currentNode(); } } if (activeNodes.size() > 0) { QList nodes = LibKisUtils::createNodeList(activeNodes, d->document->image()); return nodes.first(); } return 0; } void Document::setActiveNode(Node* value) { if (!value->node()) return; KisMainWindow *mainWin = KisPart::instance()->currentMainwindow(); if (!mainWin) return; KisViewManager *viewManager = mainWin->viewManager(); if (!viewManager) return; if (viewManager->document() != d->document) return; KisNodeManager *nodeManager = viewManager->nodeManager(); if (!nodeManager) return; KisNodeSelectionAdapter *selectionAdapter = nodeManager->nodeSelectionAdapter(); if (!selectionAdapter) return; selectionAdapter->setActiveNode(value->node()); } QList Document::topLevelNodes() const { if (!d->document) return QList(); Node n(d->document->image(), d->document->image()->rootLayer()); return n.childNodes(); } Node *Document::nodeByName(const QString &name) const { if (!d->document) return 0; KisNodeSP node = d->document->image()->rootLayer()->findChildByName(name); return new Node(d->document->image(), node); } QString Document::colorDepth() const { if (!d->document) return ""; return d->document->image()->colorSpace()->colorDepthId().id(); } QString Document::colorModel() const { if (!d->document) return ""; return d->document->image()->colorSpace()->colorModelId().id(); } QString Document::colorProfile() const { if (!d->document) return ""; return d->document->image()->colorSpace()->profile()->name(); } bool Document::setColorProfile(const QString &value) { if (!d->document) return false; if (!d->document->image()) return false; const KoColorProfile *profile = KoColorSpaceRegistry::instance()->profileByName(value); if (!profile) return false; bool retval = d->document->image()->assignImageProfile(profile); d->document->image()->setModified(); d->document->image()->initialRefreshGraph(); return retval; } bool Document::setColorSpace(const QString &colorModel, const QString &colorDepth, const QString &colorProfile) { if (!d->document) return false; if (!d->document->image()) return false; const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorModel, colorDepth, colorProfile); if (!colorSpace) return false; d->document->image()->convertImageColorSpace(colorSpace, KoColorConversionTransformation::IntentPerceptual, KoColorConversionTransformation::HighQuality | KoColorConversionTransformation::NoOptimization); d->document->image()->setModified(); d->document->image()->initialRefreshGraph(); return true; } QColor Document::backgroundColor() { if (!d->document) return QColor(); if (!d->document->image()) return QColor(); const KoColor color = d->document->image()->defaultProjectionColor(); return color.toQColor(); } bool Document::setBackgroundColor(const QColor &color) { if (!d->document) return false; if (!d->document->image()) return false; KoColor background = KoColor(color, d->document->image()->colorSpace()); d->document->image()->setDefaultProjectionColor(background); d->document->image()->setModified(); d->document->image()->initialRefreshGraph(); return true; } QString Document::documentInfo() const { QDomDocument doc = KisDocument::createDomDocument("document-info" /*DTD name*/, "document-info" /*tag name*/, "1.1"); doc = d->document->documentInfo()->save(doc); return doc.toString(); } void Document::setDocumentInfo(const QString &document) { KoXmlDocument doc; QString errorMsg; int errorLine, errorColumn; doc.setContent(document, &errorMsg, &errorLine, &errorColumn); d->document->documentInfo()->load(doc); } QString Document::fileName() const { if (!d->document) return QString(); return d->document->url().toLocalFile(); } void Document::setFileName(QString value) { if (!d->document) return; QString mimeType = KisMimeDatabase::mimeTypeForFile(value, false); d->document->setMimeType(mimeType.toLatin1()); d->document->setUrl(QUrl::fromLocalFile(value)); } int Document::height() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return image->height(); } void Document::setHeight(int value) { if (!d->document) return; if (!d->document->image()) return; resizeImage(d->document->image()->bounds().x(), d->document->image()->bounds().y(), d->document->image()->width(), value); } QString Document::name() const { if (!d->document) return ""; return d->document->documentInfo()->aboutInfo("title"); } void Document::setName(QString value) { if (!d->document) return; d->document->documentInfo()->setAboutInfo("title", value); } int Document::resolution() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return qRound(d->document->image()->xRes() * 72); } void Document::setResolution(int value) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; d->document->image()->setResolution(value / 72.0, value / 72.0); } Node *Document::rootNode() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return new Node(image, image->root()); } Selection *Document::selection() const { if (!d->document) return 0; if (!d->document->image()) return 0; if (!d->document->image()->globalSelection()) return 0; return new Selection(d->document->image()->globalSelection()); } void Document::setSelection(Selection* value) { if (!d->document) return; if (!d->document->image()) return; if (value) { d->document->image()->setGlobalSelection(value->selection()); } else { d->document->image()->setGlobalSelection(0); } } int Document::width() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return image->width(); } void Document::setWidth(int value) { if (!d->document) return; if (!d->document->image()) return; resizeImage(d->document->image()->bounds().x(), d->document->image()->bounds().y(), value, d->document->image()->height()); } int Document::xOffset() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return image->bounds().x(); } void Document::setXOffset(int x) { if (!d->document) return; if (!d->document->image()) return; resizeImage(x, d->document->image()->bounds().y(), d->document->image()->width(), d->document->image()->height()); } int Document::yOffset() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return image->bounds().y(); } void Document::setYOffset(int y) { if (!d->document) return; if (!d->document->image()) return; resizeImage(d->document->image()->bounds().x(), y, d->document->image()->width(), d->document->image()->height()); } double Document::xRes() const { if (!d->document) return 0.0; if (!d->document->image()) return 0.0; return d->document->image()->xRes()*72.0; } void Document::setXRes(double xRes) const { if (!d->document) return; if (!d->document->image()) return; d->document->image()->setResolution(xRes/72.0, d->document->image()->yRes()); } double Document::yRes() const { if (!d->document) return 0.0; if (!d->document->image()) return 0.0; return d->document->image()->yRes()*72.0; } void Document::setYRes(double yRes) const { if (!d->document) return; if (!d->document->image()) return; d->document->image()->setResolution(d->document->image()->xRes(), yRes/72.0); } QByteArray Document::pixelData(int x, int y, int w, int h) const { QByteArray ba; if (!d->document) return ba; KisImageSP image = d->document->image(); if (!image) return ba; KisPaintDeviceSP dev = image->projection(); ba.resize(w * h * dev->pixelSize()); dev->readBytes(reinterpret_cast(ba.data()), x, y, w, h); return ba; } bool Document::close() { bool retval = d->document->closeUrl(false); Q_FOREACH(KisView *view, KisPart::instance()->views()) { if (view->document() == d->document) { view->close(); + view->closeView(); view->deleteLater(); } } KisPart::instance()->removeDocument(d->document); d->document = 0; return retval; } void Document::crop(int x, int y, int w, int h) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; QRect rc(x, y, w, h); image->cropImage(rc); } bool Document::exportImage(const QString &filename, const InfoObject &exportConfiguration) { if (!d->document) return false; const QString outputFormatString = KisMimeDatabase::mimeTypeForFile(filename, false); const QByteArray outputFormat = outputFormatString.toLatin1(); return d->document->exportDocumentSync(QUrl::fromLocalFile(filename), outputFormat, exportConfiguration.configuration()); } void Document::flatten() { if (!d->document) return; if (!d->document->image()) return; d->document->image()->flatten(); } void Document::resizeImage(int x, int y, int w, int h) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; QRect rc; rc.setX(x); rc.setY(y); rc.setWidth(w); rc.setHeight(h); image->resizeImage(rc); } void Document::scaleImage(int w, int h, int xres, int yres, QString strategy) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; QRect rc = image->bounds(); rc.setWidth(w); rc.setHeight(h); KisFilterStrategy *actualStrategy = KisFilterStrategyRegistry::instance()->get(strategy); if (!actualStrategy) actualStrategy = KisFilterStrategyRegistry::instance()->get("Bicubic"); image->scaleImage(rc.size(), xres/72, yres/72, actualStrategy); } void Document::rotateImage(double radians) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; image->rotateImage(radians); } void Document::shearImage(double angleX, double angleY) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; image->shear(angleX, angleY); } bool Document::save() { if (!d->document) return false; if (d->document->url().isEmpty()) return false; bool retval = d->document->save(true, 0); d->document->waitForSavingToComplete(); return retval; } bool Document::saveAs(const QString &filename) { if (!d->document) return false; const QString outputFormatString = KisMimeDatabase::mimeTypeForFile(filename, false); const QByteArray outputFormat = outputFormatString.toLatin1(); QUrl oldUrl = d->document->url(); d->document->setUrl(QUrl::fromLocalFile(filename)); bool retval = d->document->saveAs(QUrl::fromLocalFile(filename), outputFormat, true); d->document->waitForSavingToComplete(); d->document->setUrl(oldUrl); return retval; } Node* Document::createNode(const QString &name, const QString &nodeType) { if (!d->document) return 0; if (!d->document->image()) return 0; KisImageSP image = d->document->image(); Node *node = 0; if (nodeType.toLower()== "paintlayer") { node = new Node(image, new KisPaintLayer(image, name, OPACITY_OPAQUE_U8)); } else if (nodeType.toLower() == "grouplayer") { node = new Node(image, new KisGroupLayer(image, name, OPACITY_OPAQUE_U8)); } else if (nodeType.toLower() == "filelayer") { node = new Node(image, new KisFileLayer(image, name, OPACITY_OPAQUE_U8)); } else if (nodeType.toLower() == "filterlayer") { node = new Node(image, new KisAdjustmentLayer(image, name, 0, 0)); } else if (nodeType.toLower() == "filllayer") { node = new Node(image, new KisGeneratorLayer(image, name, 0, 0)); } else if (nodeType.toLower() == "clonelayer") { node = new Node(image, new KisCloneLayer(0, image, name, OPACITY_OPAQUE_U8)); } else if (nodeType.toLower() == "vectorlayer") { node = new Node(image, new KisShapeLayer(d->document->shapeController(), image, name, OPACITY_OPAQUE_U8)); } else if (nodeType.toLower() == "transparencymask") { node = new Node(image, new KisTransparencyMask()); } else if (nodeType.toLower() == "filtermask") { node = new Node(image, new KisFilterMask()); } else if (nodeType.toLower() == "transformmask") { node = new Node(image, new KisTransformMask()); } else if (nodeType.toLower() == "selectionmask") { node = new Node(image, new KisSelectionMask(image)); } return node; } GroupLayer *Document::createGroupLayer(const QString &name) { if (!d->document) return 0; if (!d->document->image()) return 0; KisImageSP image = d->document->image(); return new GroupLayer(image, name); } FileLayer *Document::createFileLayer(const QString &name, const QString fileName, const QString scalingMethod) { if (!d->document) return 0; if (!d->document->image()) return 0; KisImageSP image = d->document->image(); return new FileLayer(image, name, this->fileName(), fileName, scalingMethod); } FilterLayer *Document::createFilterLayer(const QString &name, Filter &filter, Selection &selection) { if (!d->document) return 0; if (!d->document->image()) return 0; KisImageSP image = d->document->image(); return new FilterLayer(image, name, filter, selection); } FillLayer *Document::createFillLayer(const QString &name, const QString generatorName, InfoObject &configuration, Selection &selection) { if (!d->document) return 0; if (!d->document->image()) return 0; KisImageSP image = d->document->image(); KisGeneratorSP generator = KisGeneratorRegistry::instance()->value(generatorName); if (generator) { KisFilterConfigurationSP config = generator->defaultConfiguration(); Q_FOREACH(const QString property, configuration.properties().keys()) { config->setProperty(property, configuration.property(property)); } return new FillLayer(image, name, config, selection); } return 0; } CloneLayer *Document::createCloneLayer(const QString &name, const Node *source) { if (!d->document) return 0; if (!d->document->image()) return 0; KisImageSP image = d->document->image(); KisLayerSP layer = qobject_cast(source->node().data()); return new CloneLayer(image, name, layer); } VectorLayer *Document::createVectorLayer(const QString &name) { if (!d->document) return 0; if (!d->document->image()) return 0; KisImageSP image = d->document->image(); return new VectorLayer(d->document->shapeController(), image, name); } FilterMask *Document::createFilterMask(const QString &name, Filter &filter) { if (!d->document) return 0; if (!d->document->image()) return 0; KisImageSP image = d->document->image(); return new FilterMask(image, name, filter); } SelectionMask *Document::createSelectionMask(const QString &name) { if (!d->document) return 0; if (!d->document->image()) return 0; KisImageSP image = d->document->image(); return new SelectionMask(image, name); } QImage Document::projection(int x, int y, int w, int h) const { if (!d->document || !d->document->image()) return QImage(); return d->document->image()->convertToQImage(x, y, w, h, 0); } QImage Document::thumbnail(int w, int h) const { if (!d->document || !d->document->image()) return QImage(); return d->document->generatePreview(QSize(w, h)).toImage(); } void Document::lock() { if (!d->document || !d->document->image()) return; d->document->image()->barrierLock(); } void Document::unlock() { if (!d->document || !d->document->image()) return; d->document->image()->unlock(); } void Document::waitForDone() { if (!d->document || !d->document->image()) return; d->document->image()->waitForDone(); } bool Document::tryBarrierLock() { if (!d->document || !d->document->image()) return false; return d->document->image()->tryBarrierLock(); } bool Document::isIdle() { if (!d->document || !d->document->image()) return false; return d->document->image()->isIdle(); } void Document::refreshProjection() { if (!d->document || !d->document->image()) return; d->document->image()->refreshGraph(); } QList Document::horizontalGuides() const { QList lines; if (!d->document || !d->document->image()) return lines; KisCoordinatesConverter converter; converter.setImage(d->document->image()); QTransform transform = converter.imageToDocumentTransform().inverted(); QList untransformedLines = d->document->guidesConfig().horizontalGuideLines(); for (int i = 0; i< untransformedLines.size(); i++) { qreal line = untransformedLines[i]; lines.append(transform.map(QPointF(line, line)).x()); } return lines; } QList Document::verticalGuides() const { QList lines; if (!d->document || !d->document->image()) return lines; KisCoordinatesConverter converter; converter.setImage(d->document->image()); QTransform transform = converter.imageToDocumentTransform().inverted(); QList untransformedLines = d->document->guidesConfig().verticalGuideLines(); for (int i = 0; i< untransformedLines.size(); i++) { qreal line = untransformedLines[i]; lines.append(transform.map(QPointF(line, line)).y()); } return lines; } bool Document::guidesVisible() const { return d->document->guidesConfig().lockGuides(); } bool Document::guidesLocked() const { return d->document->guidesConfig().showGuides(); } Document *Document::clone() const { if (!d->document) return 0; QPointer clone = d->document->clone(); Document * d = new Document(clone); clone->setParent(d); // It's owned by the document, not KisPart return d; } void Document::setHorizontalGuides(const QList &lines) { if (!d->document) return; KisGuidesConfig config = d->document->guidesConfig(); KisCoordinatesConverter converter; converter.setImage(d->document->image()); QTransform transform = converter.imageToDocumentTransform(); QList transformedLines; for (int i = 0; i< lines.size(); i++) { qreal line = lines[i]; transformedLines.append(transform.map(QPointF(line, line)).x()); } config.setHorizontalGuideLines(transformedLines); d->document->setGuidesConfig(config); } void Document::setVerticalGuides(const QList &lines) { if (!d->document) return; KisGuidesConfig config = d->document->guidesConfig(); KisCoordinatesConverter converter; converter.setImage(d->document->image()); QTransform transform = converter.imageToDocumentTransform(); QList transformedLines; for (int i = 0; i< lines.size(); i++) { qreal line = lines[i]; transformedLines.append(transform.map(QPointF(line, line)).y()); } config.setVerticalGuideLines(transformedLines); d->document->setGuidesConfig(config); } void Document::setGuidesVisible(bool visible) { if (!d->document) return; KisGuidesConfig config = d->document->guidesConfig(); config.setShowGuides(visible); d->document->setGuidesConfig(config); } void Document::setGuidesLocked(bool locked) { if (!d->document) return; KisGuidesConfig config = d->document->guidesConfig(); config.setLockGuides(locked); d->document->setGuidesConfig(config); } QPointer Document::document() const { return d->document; } diff --git a/libs/libkis/Krita.cpp b/libs/libkis/Krita.cpp index f1d0c15203..6764e5bc1a 100644 --- a/libs/libkis/Krita.cpp +++ b/libs/libkis/Krita.cpp @@ -1,416 +1,418 @@ /* * Copyright (c) 2016 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 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 Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "Krita.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 #include #include #include #include #include "View.h" #include "Document.h" #include "Window.h" #include "Extension.h" #include "DockWidgetFactoryBase.h" #include "Filter.h" #include "InfoObject.h" #include "Resource.h" Krita* Krita::s_instance = 0; struct Krita::Private { Private() {} QList extensions; bool batchMode {false}; Notifier *notifier{new Notifier()}; }; Krita::Krita(QObject *parent) : QObject(parent) , d(new Private) { qRegisterMetaType(); connect(KisPart::instance(), SIGNAL(sigWindowAdded(KisMainWindow*)), SLOT(mainWindowAdded(KisMainWindow*))); } Krita::~Krita() { qDeleteAll(d->extensions); delete d->notifier; delete d; } QList Krita::actions() const { KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow(); if (!mainWindow) { return QList(); } KActionCollection *actionCollection = mainWindow->actionCollection(); return actionCollection->actions(); } QAction *Krita::action(const QString &name) const { KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow(); if (!mainWindow) { return 0; } KActionCollection *actionCollection = mainWindow->actionCollection(); QAction *action = actionCollection->action(name); return action; } Document* Krita::activeDocument() const { KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow(); if (!mainWindow) { return 0; } KisView *view = mainWindow->activeView(); if (!view) { return 0; } KisDocument *document = view->document(); return new Document(document); } void Krita::setActiveDocument(Document* value) { Q_FOREACH(KisView *view, KisPart::instance()->views()) { if (view->document() == value->document().data()) { view->activateWindow(); break; } } } bool Krita::batchmode() const { return d->batchMode; } void Krita::setBatchmode(bool value) { d->batchMode = value; } QList Krita::documents() const { QList ret; foreach(QPointer doc, KisPart::instance()->documents()) { ret << new Document(doc); } return ret; } QStringList Krita::filters() const { QStringList ls = KisFilterRegistry::instance()->keys(); std::sort(ls.begin(), ls.end()); return ls; } Filter *Krita::filter(const QString &name) const { if (!filters().contains(name)) return 0; Filter *filter = new Filter(); filter->setName(name); KisFilterSP f = KisFilterRegistry::instance()->value(name); KisFilterConfigurationSP fc = f->defaultConfiguration(); InfoObject *info = new InfoObject(fc); filter->setConfiguration(info); return filter; } QStringList Krita::colorModels() const { QSet colorModelsIds; QList ids = KoColorSpaceRegistry::instance()->colorModelsList(KoColorSpaceRegistry::AllColorSpaces); Q_FOREACH(KoID id, ids) { colorModelsIds << id.id(); } return colorModelsIds.toList(); } QStringList Krita::colorDepths(const QString &colorModel) const { QSet colorDepthsIds; QList ids = KoColorSpaceRegistry::instance()->colorDepthList(colorModel, KoColorSpaceRegistry::AllColorSpaces); Q_FOREACH(KoID id, ids) { colorDepthsIds << id.id(); } return colorDepthsIds.toList(); } QStringList Krita::filterStrategies() const { return KisFilterStrategyRegistry::instance()->keys(); } QStringList Krita::profiles(const QString &colorModel, const QString &colorDepth) const { QSet profileNames; QString id = KoColorSpaceRegistry::instance()->colorSpaceId(colorModel, colorDepth); QList profiles = KoColorSpaceRegistry::instance()->profilesFor(id); Q_FOREACH(const KoColorProfile *profile, profiles) { profileNames << profile->name(); } return profileNames.toList(); } bool Krita::addProfile(const QString &profilePath) { KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); return iccEngine->addProfile(profilePath); } Notifier* Krita::notifier() const { return d->notifier; } QString Krita::version() const { return KritaVersionWrapper::versionString(true); } QList Krita::views() const { QList ret; foreach(QPointer view, KisPart::instance()->views()) { ret << new View(view); } return ret; } Window *Krita::activeWindow() const { KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow(); if (!mainWindow) { return 0; } return new Window(mainWindow); } QList Krita::windows() const { QList ret; foreach(QPointer mainWin, KisPart::instance()->mainWindows()) { ret << new Window(mainWin); } return ret; } QMap Krita::resources(const QString &type) const { QMap resources = QMap (); if (type.toLower() == "pattern") { KoResourceServer* server = KoResourceServerProvider::instance()->patternServer(); Q_FOREACH (KoResource *res, server->resources()) { resources[res->name()] = new Resource(res); } } else if (type.toLower() == "gradient") { KoResourceServer* server = KoResourceServerProvider::instance()->gradientServer(); Q_FOREACH (KoResource *res, server->resources()) { resources[res->name()] = new Resource(res); } } else if (type.toLower() == "brush") { KisBrushResourceServer* server = KisBrushServer::instance()->brushServer(); Q_FOREACH (KisBrushSP res, server->resources()) { resources[res->name()] = new Resource(res.data()); } } else if (type.toLower() == "preset") { KisPaintOpPresetResourceServer* server = KisResourceServerProvider::instance()->paintOpPresetServer(); Q_FOREACH (KisPaintOpPresetSP res, server->resources()) { resources[res->name()] = new Resource(res.data()); } } else if (type.toLower() == "palette") { KoResourceServer* server = KoResourceServerProvider::instance()->paletteServer(); Q_FOREACH (KoResource *res, server->resources()) { resources[res->name()] = new Resource(res); } } else if (type.toLower() == "workspace") { KoResourceServer< KisWorkspaceResource >* server = KisResourceServerProvider::instance()->workspaceServer(); Q_FOREACH (KoResource *res, server->resources()) { resources[res->name()] = new Resource(res); } } return resources; } QStringList Krita::recentDocuments() const { KConfigGroup grp = KSharedConfig::openConfig()->group(QString("RecentFiles")); QStringList keys = grp.keyList(); QStringList recentDocuments; for(int i = 0; i <= keys.filter("File").count(); i++) recentDocuments << grp.readEntry(QString("File%1").arg(i), QString("")); return recentDocuments; } Document* Krita::createDocument(int width, int height, const QString &name, const QString &colorModel, const QString &colorDepth, const QString &profile, double resolution) { KisDocument *document = KisPart::instance()->createDocument(); KisPart::instance()->addDocument(document); const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace(colorModel, colorDepth, profile); Q_ASSERT(cs); QColor qc(Qt::white); qc.setAlpha(0); KoColor bgColor(qc, cs); if (!document->newImage(name, width, height, cs, bgColor, true, 1, "", double(resolution / 72) )) { qDebug() << "Could not create a new image"; return 0; } Q_ASSERT(document->image()); qDebug() << document->image()->objectName(); return new Document(document); } Document* Krita::openDocument(const QString &filename) { KisDocument *document = KisPart::instance()->createDocument(); + document->setFileBatchMode(this->batchmode()); KisPart::instance()->addDocument(document); document->openUrl(QUrl::fromLocalFile(filename), KisDocument::DontAddToRecent); + document->setFileBatchMode(false); return new Document(document); } Window* Krita::openWindow() { KisMainWindow *mw = KisPart::instance()->createMainWindow(); return new Window(mw); } void Krita::addExtension(Extension* extension) { d->extensions.append(extension); } QList< Extension* > Krita::extensions() { return d->extensions; } void Krita::writeSetting(const QString &group, const QString &name, const QString &value) { KConfigGroup grp = KSharedConfig::openConfig()->group(group); grp.writeEntry(name, value); } QString Krita::readSetting(const QString &group, const QString &name, const QString &defaultValue) { KConfigGroup grp = KSharedConfig::openConfig()->group(group); return grp.readEntry(name, defaultValue); } QIcon Krita::icon(QString &iconName) const { return KisIconUtils::loadIcon(iconName); } void Krita::addDockWidgetFactory(DockWidgetFactoryBase* factory) { KoDockRegistry::instance()->add(factory); } Krita* Krita::instance() { if (!s_instance) { s_instance = new Krita; } return s_instance; } /** * Scripter.fromVariant(variant) * variant is a QVariant * returns instance of QObject-subclass * * This is a helper method for PyQt because PyQt cannot cast a variant to a QObject or QWidget */ QObject *Krita::fromVariant(const QVariant& v) { if (v.canConvert< QWidget* >()) { QObject* obj = qvariant_cast< QWidget* >(v); return obj; } else if (v.canConvert< QObject* >()) { QObject* obj = qvariant_cast< QObject* >(v); return obj; } else return 0; } void Krita::mainWindowAdded(KisMainWindow *kisWindow) { Q_FOREACH(Extension *extension, d->extensions) { Window window(kisWindow); extension->createActions(&window); } } diff --git a/libs/libkis/Node.cpp b/libs/libkis/Node.cpp index f9aac97720..f744ace45e 100644 --- a/libs/libkis/Node.cpp +++ b/libs/libkis/Node.cpp @@ -1,605 +1,609 @@ /* * Copyright (c) 2016 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 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 Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #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 #include #include #include "Krita.h" #include "Node.h" #include "Channel.h" #include "Filter.h" #include "Selection.h" #include "GroupLayer.h" #include "CloneLayer.h" #include "FilterLayer.h" #include "FillLayer.h" #include "FileLayer.h" #include "VectorLayer.h" #include "FilterMask.h" #include "SelectionMask.h" #include "LibKisUtils.h" struct Node::Private { Private() {} KisImageWSP image; KisNodeSP node; }; Node::Node(KisImageSP image, KisNodeSP node, QObject *parent) : QObject(parent) , d(new Private) { d->image = image; d->node = node; } Node::~Node() { delete d; } bool Node::operator==(const Node &other) const { return (d->node == other.d->node && d->image == other.d->image); } bool Node::operator!=(const Node &other) const { return !(operator==(other)); } Node *Node::clone() const { KisNodeSP clone = d->node->clone(); Node *node = new Node(0, clone); return node; } bool Node::alphaLocked() const { if (!d->node) return false; KisPaintLayerSP paintLayer = qobject_cast(d->node.data()); if (paintLayer) { return paintLayer->alphaLocked(); } return false; } void Node::setAlphaLocked(bool value) { if (!d->node) return; KisPaintLayerSP paintLayer = qobject_cast(d->node.data()); if (paintLayer) { paintLayer->setAlphaLocked(value); } } QString Node::blendingMode() const { if (!d->node) return QString(); return d->node->compositeOpId(); } void Node::setBlendingMode(QString value) { if (!d->node) return; d->node->setCompositeOpId(value); } QList Node::channels() const { QList channels; if (!d->node) return channels; if (!d->node->inherits("KisLayer")) return channels; Q_FOREACH(KoChannelInfo *info, d->node->colorSpace()->channels()) { Channel *channel = new Channel(d->node, info); channels << channel; } return channels; } QList Node::childNodes() const { QList nodes; if (d->node) { KisNodeList nodeList; int childCount = d->node->childCount(); for (int i = 0; i < childCount; ++i) { nodeList << d->node->at(i); } nodes = LibKisUtils::createNodeList(nodeList, d->image); } return nodes; } bool Node::addChildNode(Node *child, Node *above) { if (!d->node) return false; if (above) { return d->image->addNode(child->node(), d->node, above->node()); } else { return d->image->addNode(child->node(), d->node, d->node->childCount()); } } bool Node::removeChildNode(Node *child) { if (!d->node) return false; return d->image->removeNode(child->node()); } void Node::setChildNodes(QList nodes) { if (!d->node) return; KisNodeSP node = d->node->firstChild(); while (node) { d->image->removeNode(node); node = node->nextSibling(); } Q_FOREACH(Node *node, nodes) { d->image->addNode(node->node(), d->node); } } int Node::colorLabel() const { if (!d->node) return 0; return d->node->colorLabelIndex(); } void Node::setColorLabel(int index) { if (!d->node) return; d->node->setColorLabelIndex(index); } QString Node::colorDepth() const { if (!d->node) return ""; return d->node->colorSpace()->colorDepthId().id(); } QString Node::colorModel() const { if (!d->node) return ""; return d->node->colorSpace()->colorModelId().id(); } QString Node::colorProfile() const { if (!d->node) return ""; return d->node->colorSpace()->profile()->name(); } bool Node::setColorProfile(const QString &colorProfile) { if (!d->node) return false; if (!d->node->inherits("KisLayer")) return false; KisLayer *layer = qobject_cast(d->node.data()); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->profileByName(colorProfile); const KoColorSpace *srcCS = layer->colorSpace(); const KoColorSpace *dstCs = KoColorSpaceRegistry::instance()->colorSpace(srcCS->colorModelId().id(), srcCS->colorDepthId().id(), profile); KisChangeProfileVisitor v(srcCS, dstCs); return layer->accept(v); } bool Node::setColorSpace(const QString &colorModel, const QString &colorDepth, const QString &colorProfile) { if (!d->node) return false; if (!d->node->inherits("KisLayer")) return false; KisLayer *layer = qobject_cast(d->node.data()); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->profileByName(colorProfile); const KoColorSpace *srcCS = layer->colorSpace(); const KoColorSpace *dstCs = KoColorSpaceRegistry::instance()->colorSpace(colorModel, colorDepth, profile); KisColorSpaceConvertVisitor v(d->image, srcCS, dstCs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); return layer->accept(v); } bool Node::animated() const { if (!d->node) return false; return d->node->isAnimated(); } void Node::enableAnimation() const { if (!d->node) return; d->node->enableAnimation(); } bool Node::collapsed() const { if (!d->node) return false; return d->node->collapsed(); } void Node::setCollapsed(bool collapsed) { if (!d->node) return; d->node->setCollapsed(collapsed); } bool Node::inheritAlpha() const { if (!d->node) return false; if (!d->node->inherits("KisLayer")) return false; return qobject_cast(d->node)->alphaChannelDisabled(); } void Node::setInheritAlpha(bool value) { if (!d->node) return; if (!d->node->inherits("KisLayer")) return; const_cast(qobject_cast(d->node))->disableAlphaChannel(value); } bool Node::locked() const { if (!d->node) return false; return d->node->userLocked(); } void Node::setLocked(bool value) { if (!d->node) return; d->node->setUserLocked(value); } +bool Node::hasExtents() +{ + return !d->node->extent().isEmpty(); +} QString Node::name() const { if (!d->node) return QString(); return d->node->name(); } void Node::setName(QString name) { if (!d->node) return; d->node->setName(name); } int Node::opacity() const { if (!d->node) return 0; return d->node->opacity(); } void Node::setOpacity(int value) { if (!d->node) return; if (value < 0) value = 0; if (value > 255) value = 255; d->node->setOpacity(value); } Node* Node::parentNode() const { if (!d->node) return 0; return new Node(d->image, d->node->parent()); } QString Node::type() const { if (!d->node) return QString(); if (qobject_cast(d->node)) { return "paintlayer"; } else if (qobject_cast(d->node)) { return "grouplayer"; } if (qobject_cast(d->node)) { return "filelayer"; } if (qobject_cast(d->node)) { return "filterlayer"; } if (qobject_cast(d->node)) { return "filllayer"; } if (qobject_cast(d->node)) { return "clonelayer"; } if (qobject_cast(d->node)) { return "referenceimageslayer"; } if (qobject_cast(d->node)) { return "vectorlayer"; } if (qobject_cast(d->node)) { return "transparencymask"; } if (qobject_cast(d->node)) { return "filtermask"; } if (qobject_cast(d->node)) { return "transformmask"; } if (qobject_cast(d->node)) { return "selectionmask"; } if (qobject_cast(d->node)) { return "colorizemask"; } return QString(); } QIcon Node::icon() const { QIcon icon; if (d->node) { icon = d->node->icon(); } return icon; } bool Node::visible() const { if (!d->node) return false; return d->node->visible(); } void Node::setVisible(bool visible) { if (!d->node) return; d->node->setVisible(visible); } QByteArray Node::pixelData(int x, int y, int w, int h) const { QByteArray ba; if (!d->node) return ba; KisPaintDeviceSP dev = d->node->paintDevice(); if (!dev) return ba; ba.resize(w * h * dev->pixelSize()); dev->readBytes(reinterpret_cast(ba.data()), x, y, w, h); return ba; } QByteArray Node::pixelDataAtTime(int x, int y, int w, int h, int time) const { QByteArray ba; if (!d->node || !d->node->isAnimated()) return ba; // KisRasterKeyframeChannel *rkc = dynamic_cast(d->node->getKeyframeChannel(KisKeyframeChannel::Content.id())); if (!rkc) return ba; KisKeyframeSP frame = rkc->keyframeAt(time); if (!frame) return ba; KisPaintDeviceSP dev = d->node->paintDevice(); if (!dev) return ba; rkc->fetchFrame(frame, dev); ba.resize(w * h * dev->pixelSize()); dev->readBytes(reinterpret_cast(ba.data()), x, y, w, h); return ba; } QByteArray Node::projectionPixelData(int x, int y, int w, int h) const { QByteArray ba; if (!d->node) return ba; KisPaintDeviceSP dev = d->node->projection(); ba.resize(w * h * dev->pixelSize()); dev->readBytes(reinterpret_cast(ba.data()), x, y, w, h); return ba; } void Node::setPixelData(QByteArray value, int x, int y, int w, int h) { if (!d->node) return; KisPaintDeviceSP dev = d->node->paintDevice(); if (!dev) return; dev->writeBytes((const quint8*)value.constData(), x, y, w, h); } QRect Node::bounds() const { if (!d->node) return QRect(); return d->node->exactBounds(); } void Node::move(int x, int y) { if (!d->node) return; d->node->setX(x); d->node->setY(y); } QPoint Node::position() const { if (!d->node) return QPoint(); return QPoint(d->node->x(), d->node->y()); } bool Node::remove() { if (!d->node) return false; if (!d->node->parent()) return false; return d->image->removeNode(d->node); } Node* Node::duplicate() { if (!d->node) return 0; return new Node(d->image, d->node->clone()); } bool Node::save(const QString &filename, double xRes, double yRes) { if (!d->node) return false; if (filename.isEmpty()) return false; KisPaintDeviceSP projection = d->node->projection(); QRect bounds = d->node->exactBounds(); QString mimeType = KisMimeDatabase::mimeTypeForFile(filename, false); QScopedPointer doc(KisPart::instance()->createDocument()); KisImageSP dst = new KisImage(doc->createUndoStore(), bounds.right(), bounds.bottom(), projection->compositionSourceColorSpace(), d->node->name()); dst->setResolution(xRes, yRes); doc->setFileBatchMode(Krita::instance()->batchmode()); doc->setCurrentImage(dst); KisPaintLayer* paintLayer = new KisPaintLayer(dst, "paint device", d->node->opacity()); paintLayer->paintDevice()->makeCloneFrom(projection, bounds); dst->addNode(paintLayer, dst->rootLayer(), KisLayerSP(0)); dst->cropImage(bounds); dst->initialRefreshGraph(); bool r = doc->exportDocumentSync(QUrl::fromLocalFile(filename), mimeType.toLatin1()); if (!r) { qWarning() << doc->errorMessage(); } return r; } Node *Node::mergeDown() { if (!d->node) return 0; if (!qobject_cast(d->node.data())) return 0; if (!d->node->nextSibling()) return 0; if (!d->node->parent()) return 0; int index = d->node->parent()->index(d->node->prevSibling()); d->image->mergeDown(qobject_cast(d->node.data()), KisMetaData::MergeStrategyRegistry::instance()->get("Drop")); d->image->waitForDone(); return new Node(d->image, d->node->parent()->at(index)); } void Node::scaleNode(int width, int height, QString strategy) { if (!d->node) return; if (!qobject_cast(d->node.data())) return; if (!d->node->parent()) return; KisFilterStrategy *actualStrategy = KisFilterStrategyRegistry::instance()->get(strategy); if (!actualStrategy) actualStrategy = KisFilterStrategyRegistry::instance()->get("Bicubic"); d->image->scaleNode(d->node, width, height, actualStrategy); } void Node::rotateNode(double radians) { if (!d->node) return; if (!qobject_cast(d->node.data())) return; if (!d->node->parent()) return; d->image->rotateNode(d->node, radians); } void Node::cropNode(int x, int y, int w, int h) { if (!d->node) return; if (!qobject_cast(d->node.data())) return; if (!d->node->parent()) return; QRect rect = QRect(x, y, w, h); d->image->cropNode(d->node, rect); } void Node::shearNode(double angleX, double angleY) { if (!d->node) return; if (!qobject_cast(d->node.data())) return; if (!d->node->parent()) return; d->image->shearNode(d->node, angleX, angleY); } QImage Node::thumbnail(int w, int h) { if (!d->node) return QImage(); return d->node->createThumbnail(w, h); } KisPaintDeviceSP Node::paintDevice() const { return d->node->paintDevice(); } KisImageSP Node::image() const { return d->image; } KisNodeSP Node::node() const { return d->node; } diff --git a/libs/libkis/Node.h b/libs/libkis/Node.h index 334b965375..0c418d10b7 100644 --- a/libs/libkis/Node.h +++ b/libs/libkis/Node.h @@ -1,542 +1,549 @@ /* * Copyright (c) 2016 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 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 Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef LIBKIS_NODE_H #define LIBKIS_NODE_H #include #include #include "kritalibkis_export.h" #include "libkis.h" /** * Node represents a layer or mask in a Krita image's Node hierarchy. Group layers can contain * other layers and masks; layers can contain masks. * */ class KRITALIBKIS_EXPORT Node : public QObject { Q_OBJECT Q_DISABLE_COPY(Node) public: explicit Node(KisImageSP image, KisNodeSP node, QObject *parent = 0); ~Node() override; bool operator==(const Node &other) const; bool operator!=(const Node &other) const; public Q_SLOTS: /** * @brief clone clone the current node. The node is not associated with any image. */ Node *clone() const; /** * @brief alphaLocked checks whether the node is a paint layer and returns whether it is alpha locked * @return whether the paint layer is alpha locked, or false if the node is not a paint layer */ bool alphaLocked() const; /** * @brief setAlphaLocked set the layer to value if the node is paint layer. */ void setAlphaLocked(bool value); /** * @return the blending mode of the layer. The values of the blending modes are defined in @see KoCompositeOpRegistry.h */ QString blendingMode() const; /** * @brief setBlendingMode set the blending mode of the node to the given value * @param value one of the string values from @see KoCompositeOpRegistry.h */ void setBlendingMode(QString value); /** * @brief channels creates a list of Channel objects that can be used individually to * show or hide certain channels, and to retrieve the contents of each channel in a * node separately. * * Only layers have channels, masks do not, and calling channels on a Node that is a mask * will return an empty list. * * @return the list of channels ordered in by position of the channels in pixel position */ QList channels() const; /** * Return a list of child nodes of the current node. The nodes are ordered from the bottommost up. * The function is not recursive. */ QList childNodes() const; /** * @brief addChildNode adds the given node in the list of children. * @param child the node to be added * @param above the node above which this node will be placed * @return false if adding the node failed */ bool addChildNode(Node *child, Node *above); /** * @brief removeChildNode removes the given node from the list of children. * @param child the node to be removed */ bool removeChildNode(Node *child); /** * @brief setChildNodes this replaces the existing set of child nodes with the new set. * @param nodes The list of nodes that will become children, bottom-up -- the first node, * is the bottom-most node in the stack. */ void setChildNodes(QList nodes); /** * colorDepth A string describing the color depth of the image: *
    *
  • U8: unsigned 8 bits integer, the most common type
  • *
  • U16: unsigned 16 bits integer
  • *
  • F16: half, 16 bits floating point. Only available if Krita was built with OpenEXR
  • *
  • F32: 32 bits floating point
  • *
* @return the color depth. */ QString colorDepth() const; /** * @brief colorModel retrieve the current color model of this document: *
    *
  • A: Alpha mask
  • *
  • RGBA: RGB with alpha channel (The actual order of channels is most often BGR!)
  • *
  • XYZA: XYZ with alpha channel
  • *
  • LABA: LAB with alpha channel
  • *
  • CMYKA: CMYK with alpha channel
  • *
  • GRAYA: Gray with alpha channel
  • *
  • YCbCrA: YCbCr with alpha channel
  • *
* @return the internal color model string. */ QString colorModel() const; /** * @return the name of the current color profile */ QString colorProfile() const; /** * @brief setColorProfile set the color profile of the image to the given profile. The profile has to * be registered with krita and be compatible with the current color model and depth; the image data * is not converted. * @param colorProfile * @return if assigining the colorprofiel worked */ bool setColorProfile(const QString &colorProfile); /** * @brief setColorSpace convert the node to the given colorspace * @param colorModel A string describing the color model of the node: *
    *
  • A: Alpha mask
  • *
  • RGBA: RGB with alpha channel (The actual order of channels is most often BGR!)
  • *
  • XYZA: XYZ with alpha channel
  • *
  • LABA: LAB with alpha channel
  • *
  • CMYKA: CMYK with alpha channel
  • *
  • GRAYA: Gray with alpha channel
  • *
  • YCbCrA: YCbCr with alpha channel
  • *
* @param colorDepth A string describing the color depth of the image: *
    *
  • U8: unsigned 8 bits integer, the most common type
  • *
  • U16: unsigned 16 bits integer
  • *
  • F16: half, 16 bits floating point. Only available if Krita was built with OpenEXR
  • *
  • F32: 32 bits floating point
  • *
* @param colorProfile a valid color profile for this color model and color depth combination. */ bool setColorSpace(const QString &colorModel, const QString &colorDepth, const QString &colorProfile); /** * @brief Krita layers can be animated, i.e., have frames. * @return return true if the layer has frames. Currently, the scripting framework * does not give access to the animation features. */ bool animated() const; /** * @brief enableAnimation make the current layer animated, so it can have frames. */ void enableAnimation() const; /** * Sets the state of the node to the value of @param collapsed */ void setCollapsed(bool collapsed); /** * returns the collapsed state of this node */ bool collapsed() const; /** * Sets a color label index associated to the layer. The actual * color of the label and the number of available colors is * defined by Krita GUI configuration. */ int colorLabel() const; /** * @brief setColorLabel sets a color label index associated to the layer. The actual * color of the label and the number of available colors is * defined by Krita GUI configuration. * @param index an integer corresponding to the set of available color labels. */ void setColorLabel(int index); /** * @brief inheritAlpha checks whether this node has the inherits alpha flag set * @return true if the Inherit Alpha is set */ bool inheritAlpha() const; /** * set the Inherit Alpha flag to the given value */ void setInheritAlpha(bool value); /** * @brief locked checks whether the Node is locked. A locked node cannot be changed. * @return true if the Node is locked, false if it hasn't been locked. */ bool locked() const; /** * set the Locked flag to the give value */ void setLocked(bool value); + /** + * @brief does the node have any content in it? + * @return if node has any content in it + */ + bool hasExtents(); + + /** * @return the user-visible name of this node. */ QString name() const; /** * rename the Node to the given name */ void setName(QString name); /** * return the opacity of the Node. The opacity is a value between 0 and 255. */ int opacity() const; /** * set the opacity of the Node to the given value. The opacity is a value between 0 and 255. */ void setOpacity(int value); /** * return the Node that is the parent of the current Node, or 0 if this is the root Node. */ Node* parentNode() const; /** * @brief type Krita has several types of nodes, split in layers and masks. Group * layers can contain other layers, any layer can contain masks. * * @return The type of the node. Valid types are: *
    *
  • paintlayer *
  • grouplayer *
  • filelayer *
  • filterlayer *
  • filllayer *
  • clonelayer *
  • vectorlayer *
  • transparencymask *
  • filtermask *
  • transformmask *
  • selectionmask *
  • colorizemask *
* * If the Node object isn't wrapping a valid Krita layer or mask object, and * empty string is returned. */ virtual QString type() const; /** * @brief icon * @return the icon associated with the layer. */ QIcon icon() const; /** * Check whether the current Node is visible in the layer stack */ bool visible() const; /** * Set the visibility of the current node to @param visible */ void setVisible(bool visible); /** * @brief pixelData reads the given rectangle from the Node's paintable pixels, if those * exist, and returns it as a byte array. The pixel data starts top-left, and is ordered row-first. * * The byte array can be interpreted as follows: 8 bits images have one byte per channel, * and as many bytes as there are channels. 16 bits integer images have two bytes per channel, * representing an unsigned short. 16 bits float images have two bytes per channel, representing * a half, or 16 bits float. 32 bits float images have four bytes per channel, representing a * float. * * You can read outside the node boundaries; those pixels will be transparent black. * * The order of channels is: * *
    *
  • Integer RGBA: Blue, Green, Red, Alpha *
  • Float RGBA: Red, Green, Blue, Alpha *
  • GrayA: Gray, Alpha *
  • Selection: selectedness *
  • LabA: L, a, b, Alpha *
  • CMYKA: Cyan, Magenta, Yellow, Key, Alpha *
  • XYZA: X, Y, Z, A *
  • YCbCrA: Y, Cb, Cr, Alpha *
* * The byte array is a copy of the original node data. In Python, you can use bytes, bytearray * and the struct module to interpret the data and construct, for instance, a Pillow Image object. * * If you read the pixeldata of a mask, a filter or generator layer, you get the selection bytes, * which is one channel with values in the range from 0..255. * * If you want to change the pixels of a node you can write the pixels back after manipulation * with setPixelData(). This will only succeed on nodes with writable pixel data, e.g not on groups * or file layers. * * @param x x position from where to start reading * @param y y position from where to start reading * @param w row length to read * @param h number of rows to read * @return a QByteArray with the pixel data. The byte array may be empty. */ QByteArray pixelData(int x, int y, int w, int h) const; /** * @brief pixelDataAtTime a basic function to get pixeldata from an animated node at a given time. * @param x the position from the left to start reading. * @param y the position from the top to start reader * @param w the row length to read * @param h the number of rows to read * @param time the frame number * @return a QByteArray with the pixel data. The byte array may be empty. */ QByteArray pixelDataAtTime(int x, int y, int w, int h, int time) const; /** * @brief projectionPixelData reads the given rectangle from the Node's projection (that is, what the node * looks like after all sub-Nodes (like layers in a group or masks on a layer) have been applied, * and returns it as a byte array. The pixel data starts top-left, and is ordered row-first. * * The byte array can be interpreted as follows: 8 bits images have one byte per channel, * and as many bytes as there are channels. 16 bits integer images have two bytes per channel, * representing an unsigned short. 16 bits float images have two bytes per channel, representing * a half, or 16 bits float. 32 bits float images have four bytes per channel, representing a * float. * * You can read outside the node boundaries; those pixels will be transparent black. * * The order of channels is: * *
    *
  • Integer RGBA: Blue, Green, Red, Alpha *
  • Float RGBA: Red, Green, Blue, Alpha *
  • GrayA: Gray, Alpha *
  • Selection: selectedness *
  • LabA: L, a, b, Alpha *
  • CMYKA: Cyan, Magenta, Yellow, Key, Alpha *
  • XYZA: X, Y, Z, A *
  • YCbCrA: Y, Cb, Cr, Alpha *
* * The byte array is a copy of the original node data. In Python, you can use bytes, bytearray * and the struct module to interpret the data and construct, for instance, a Pillow Image object. * * If you read the projection of a mask, you get the selection bytes, which is one channel with * values in the range from 0..255. * * If you want to change the pixels of a node you can write the pixels back after manipulation * with setPixelData(). This will only succeed on nodes with writable pixel data, e.g not on groups * or file layers. * * @param x x position from where to start reading * @param y y position from where to start reading * @param w row length to read * @param h number of rows to read * @return a QByteArray with the pixel data. The byte array may be empty. */ QByteArray projectionPixelData(int x, int y, int w, int h) const; /** * @brief setPixelData writes the given bytes, of which there must be enough, into the * Node, if the Node has writable pixel data: * *
    *
  • paint layer: the layer's original pixels are overwritten *
  • filter layer, generator layer, any mask: the embedded selection's pixels are overwritten. * Note: for these *
* * File layers, Group layers, Clone layers cannot be written to. Calling setPixelData on * those layer types will silently do nothing. * * @param value the byte array representing the pixels. There must be enough bytes available. * Krita will take the raw pointer from the QByteArray and start reading, not stopping before * (number of channels * size of channel * w * h) bytes are read. * * @param x the x position to start writing from * @param y the y position to start writing from * @param w the width of each row * @param h the number of rows to write */ void setPixelData(QByteArray value, int x, int y, int w, int h); /** * @brief bounds return the exact bounds of the node's paint device * @return the bounds, or an empty QRect if the node has no paint device or is empty. */ QRect bounds() const; /** * move the pixels to the given x, y location in the image coordinate space. */ void move(int x, int y); /** * @brief position returns the position of the paint device of this node * @return the top-left position of the node */ QPoint position() const; /** * @brief remove removes this node from its parent image. */ bool remove(); /** * @brief duplicate returns a full copy of the current node. The node is not inserted in the graphc * @return a valid Node object or 0 if the node couldn't be duplicated. */ Node* duplicate(); /** * @brief save exports the given node with this filename. The extension of the filename determines the filetype. * @param filename the filename including extension * @param xRes the horizontal resolution in pixels per pt (there are 72 pts in an inch) * @param yRes the horizontal resolution in pixels per pt (there are 72 pts in an inch) * @return true if saving succeeded, false if it failed. */ bool save(const QString &filename, double xRes, double yRes); /** * @brief mergeDown merges the given node with the first visible node underneath this node in the layerstack. * This will drop all per-layer metadata. * @param node the node to merge down; this node will be removed from the layer stack */ Node *mergeDown(); /** * @brief scaleNode * @param width * @param height * @param strategy the scaling strategy. There's several ones amongst these that aren't available in the regular UI. *
    *
  • Hermite
  • *
  • Bicubic - Adds pixels using the color of surrounding pixels. Produces smoother tonal gradations than Bilinear.
  • *
  • Box - Replicate pixels in the image. Preserves all the original detail, but can produce jagged effects.
  • *
  • Bilinear - Adds pixels averaging the color values of surrounding pixels. Produces medium quality results when the image is scaled from half to two times the original size.
  • *
  • Bell
  • *
  • BSpline
  • *
  • Lanczos3 - Offers similar results than Bicubic, but maybe a little bit sharper. Can produce light and dark halos along strong edges.
  • *
  • Mitchell
  • *
*/ void scaleNode(int width, int height, QString strategy); /** * @brief rotateNode rotate this layer by the given radians. * @param radians amount the layer should be rotated in, in radians. */ void rotateNode(double radians); /** * @brief cropNode crop this layer. * @param x the left edge of the cropping rectangle. * @param y the top edge of the cropping rectangle * @param w the right edge of the cropping rectangle * @param h the bottom edge of the cropping rectangle */ void cropNode(int x, int y, int w, int h); /** * @brief shearNode perform a shear operation on this node. * @param angleX the X-angle in degrees to shear by * @param angleY the Y-angle in degrees to shear by */ void shearNode(double angleX, double angleY); /** * @brief thumbnail create a thumbnail of the given dimensions. The thumbnail is sized according * to the layer dimensions, not the image dimensions. If the requested size is too big a null * QImage is created. If the current node cannot generate a thumbnail, a transparent QImage of the * requested size is generated. * @return a QImage representing the layer contents. */ QImage thumbnail(int w, int h); private: friend class Filter; friend class Document; friend class Selection; friend class GroupLayer; friend class FileLayer; friend class FilterLayer; friend class FillLayer; friend class VectorLayer; friend class FilterMask; friend class SelectionMask; /** * @brief paintDevice gives access to the internal paint device of this Node * @return the paintdevice or 0 if the node does not have an editable paint device. */ KisPaintDeviceSP paintDevice() const; KisImageSP image() const; KisNodeSP node() const; struct Private; Private *const d; }; #endif // LIBKIS_NODE_H diff --git a/libs/pigment/KoCompositeOpRegistry.cpp b/libs/pigment/KoCompositeOpRegistry.cpp index 944d6df04a..22e810e8a6 100644 --- a/libs/pigment/KoCompositeOpRegistry.cpp +++ b/libs/pigment/KoCompositeOpRegistry.cpp @@ -1,216 +1,216 @@ /* * Copyright (c) 2005 Adrian Page * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; 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 "KoCompositeOpRegistry.h" #include #include #include #include #include "KoCompositeOp.h" #include "KoColorSpace.h" Q_GLOBAL_STATIC(KoCompositeOpRegistry, registry) KoCompositeOpRegistry::KoCompositeOpRegistry() { m_categories << KoID("arithmetic", i18n("Arithmetic")) << KoID("dark" , i18n("Darken")) << KoID("light" , i18n("Lighten")) << KoID("negative" , i18n("Negative")) << KoID("mix" , i18n("Mix")) << KoID("misc" , i18n("Misc")) << KoID("hsy" , i18n("HSY")) << KoID("hsi" , i18n("HSI")) << KoID("hsl" , i18n("HSL")) << KoID("hsv" , i18n("HSV")); m_map.insert(m_categories[0], KoID(COMPOSITE_ADD , i18n("Addition"))); m_map.insert(m_categories[0], KoID(COMPOSITE_SUBTRACT , i18n("Subtract"))); m_map.insert(m_categories[0], KoID(COMPOSITE_MULT , i18n("Multiply"))); m_map.insert(m_categories[0], KoID(COMPOSITE_DIVIDE , i18n("Divide"))); m_map.insert(m_categories[0], KoID(COMPOSITE_INVERSE_SUBTRACT, i18n("Inverse Subtract"))); m_map.insert(m_categories[1], KoID(COMPOSITE_BURN , i18n("Burn"))); m_map.insert(m_categories[1], KoID(COMPOSITE_LINEAR_BURN, i18n("Linear Burn"))); m_map.insert(m_categories[1], KoID(COMPOSITE_DARKEN , i18n("Darken"))); m_map.insert(m_categories[1], KoID(COMPOSITE_GAMMA_DARK , i18n("Gamma Dark"))); m_map.insert(m_categories[1], KoID(COMPOSITE_DARKER_COLOR , i18n("Darker Color"))); m_map.insert(m_categories[2], KoID(COMPOSITE_DODGE , i18n("Color Dodge"))); m_map.insert(m_categories[2], KoID(COMPOSITE_LINEAR_DODGE, i18n("Linear Dodge"))); m_map.insert(m_categories[2], KoID(COMPOSITE_LIGHTEN , i18n("Lighten"))); m_map.insert(m_categories[2], KoID(COMPOSITE_LINEAR_LIGHT, i18n("Linear Light"))); m_map.insert(m_categories[2], KoID(COMPOSITE_SCREEN , i18n("Screen"))); m_map.insert(m_categories[2], KoID(COMPOSITE_PIN_LIGHT , i18n("Pin Light"))); m_map.insert(m_categories[2], KoID(COMPOSITE_VIVID_LIGHT , i18n("Vivid Light"))); m_map.insert(m_categories[2], KoID(COMPOSITE_HARD_LIGHT , i18n("Hard Light"))); m_map.insert(m_categories[2], KoID(COMPOSITE_SOFT_LIGHT_PHOTOSHOP, i18n("Soft Light (Photoshop)"))); m_map.insert(m_categories[2], KoID(COMPOSITE_SOFT_LIGHT_SVG, i18n("Soft Light (SVG)"))); m_map.insert(m_categories[2], KoID(COMPOSITE_GAMMA_LIGHT , i18n("Gamma Light"))); m_map.insert(m_categories[2], KoID(COMPOSITE_LIGHTER_COLOR , i18n("Lighter Color"))); m_map.insert(m_categories[3], KoID(COMPOSITE_DIFF , i18n("Difference"))); m_map.insert(m_categories[3], KoID(COMPOSITE_EQUIVALENCE , i18n("Equivalence"))); m_map.insert(m_categories[3], KoID(COMPOSITE_ADDITIVE_SUBTRACTIVE, i18n("Additive Subtractive"))); m_map.insert(m_categories[3], KoID(COMPOSITE_EXCLUSION , i18n("Exclusion"))); m_map.insert(m_categories[3], KoID(COMPOSITE_ARC_TANGENT , i18n("Arcus Tangent"))); m_map.insert(m_categories[4], KoID(COMPOSITE_OVER , i18n("Normal"))); m_map.insert(m_categories[4], KoID(COMPOSITE_BEHIND , i18n("Behind"))); m_map.insert(m_categories[4], KoID(COMPOSITE_GREATER , i18n("Greater"))); m_map.insert(m_categories[4], KoID(COMPOSITE_OVERLAY , i18n("Overlay"))); m_map.insert(m_categories[4], KoID(COMPOSITE_ERASE , i18n("Erase"))); m_map.insert(m_categories[4], KoID(COMPOSITE_ALPHA_DARKEN , i18n("Alpha Darken"))); m_map.insert(m_categories[4], KoID(COMPOSITE_HARD_MIX , i18n("Hard Mix"))); m_map.insert(m_categories[4], KoID(COMPOSITE_HARD_MIX_PHOTOSHOP, i18n("Hard Mix (Photoshop)"))); m_map.insert(m_categories[4], KoID(COMPOSITE_GRAIN_MERGE , i18n("Grain Merge"))); m_map.insert(m_categories[4], KoID(COMPOSITE_GRAIN_EXTRACT , i18n("Grain Extract"))); m_map.insert(m_categories[4], KoID(COMPOSITE_PARALLEL , i18n("Parallel"))); m_map.insert(m_categories[4], KoID(COMPOSITE_ALLANON , i18n("Allanon"))); m_map.insert(m_categories[4], KoID(COMPOSITE_GEOMETRIC_MEAN , i18n("Geometric Mean"))); m_map.insert(m_categories[4], KoID(COMPOSITE_DESTINATION_ATOP, i18n("Destination Atop"))); m_map.insert(m_categories[4], KoID(COMPOSITE_DESTINATION_IN , i18n("Destination In"))); m_map.insert(m_categories[4], KoID(COMPOSITE_HARD_OVERLAY , i18n("Hard Overlay"))); m_map.insert(m_categories[5], KoID(COMPOSITE_BUMPMAP , i18n("Bumpmap"))); m_map.insert(m_categories[5], KoID(COMPOSITE_COMBINE_NORMAL, i18n("Combine Normal Map"))); m_map.insert(m_categories[5], KoID(COMPOSITE_DISSOLVE , i18n("Dissolve"))); m_map.insert(m_categories[5], KoID(COMPOSITE_COPY_RED , i18n("Copy Red"))); m_map.insert(m_categories[5], KoID(COMPOSITE_COPY_GREEN, i18n("Copy Green"))); m_map.insert(m_categories[5], KoID(COMPOSITE_COPY_BLUE , i18n("Copy Blue"))); m_map.insert(m_categories[5], KoID(COMPOSITE_COPY , i18n("Copy"))); m_map.insert(m_categories[5], KoID(COMPOSITE_TANGENT_NORMALMAP, i18n("Tangent Normalmap"))); m_map.insert(m_categories[6], KoID(COMPOSITE_COLOR , i18n("Color"))); m_map.insert(m_categories[6], KoID(COMPOSITE_HUE , i18n("Hue"))); m_map.insert(m_categories[6], KoID(COMPOSITE_SATURATION , i18n("Saturation"))); m_map.insert(m_categories[6], KoID(COMPOSITE_LUMINIZE , i18n("Luminosity"))); m_map.insert(m_categories[6], KoID(COMPOSITE_DEC_SATURATION, i18n("Decrease Saturation"))); m_map.insert(m_categories[6], KoID(COMPOSITE_INC_SATURATION, i18n("Increase Saturation"))); m_map.insert(m_categories[6], KoID(COMPOSITE_DEC_LUMINOSITY, i18n("Decrease Luminosity"))); m_map.insert(m_categories[6], KoID(COMPOSITE_INC_LUMINOSITY, i18n("Increase Luminosity"))); m_map.insert(m_categories[7], KoID(COMPOSITE_COLOR_HSI , i18n("Color HSI"))); m_map.insert(m_categories[7], KoID(COMPOSITE_HUE_HSI , i18n("Hue HSI"))); m_map.insert(m_categories[7], KoID(COMPOSITE_SATURATION_HSI , i18n("Saturation HSI"))); m_map.insert(m_categories[7], KoID(COMPOSITE_INTENSITY , i18n("Intensity"))); m_map.insert(m_categories[7], KoID(COMPOSITE_DEC_SATURATION_HSI, i18n("Decrease Saturation HSI"))); m_map.insert(m_categories[7], KoID(COMPOSITE_INC_SATURATION_HSI, i18n("Increase Saturation HSI"))); m_map.insert(m_categories[7], KoID(COMPOSITE_DEC_INTENSITY , i18n("Decrease Intensity"))); m_map.insert(m_categories[7], KoID(COMPOSITE_INC_INTENSITY , i18n("Increase Intensity"))); m_map.insert(m_categories[8], KoID(COMPOSITE_COLOR_HSL , i18n("Color HSL"))); m_map.insert(m_categories[8], KoID(COMPOSITE_HUE_HSL , i18n("Hue HSL"))); m_map.insert(m_categories[8], KoID(COMPOSITE_SATURATION_HSL , i18n("Saturation HSL"))); m_map.insert(m_categories[8], KoID(COMPOSITE_LIGHTNESS , i18n("Lightness"))); m_map.insert(m_categories[8], KoID(COMPOSITE_DEC_SATURATION_HSL, i18n("Decrease Saturation HSL"))); m_map.insert(m_categories[8], KoID(COMPOSITE_INC_SATURATION_HSL, i18n("Increase Saturation HSL"))); m_map.insert(m_categories[8], KoID(COMPOSITE_DEC_LIGHTNESS , i18n("Decrease Lightness"))); m_map.insert(m_categories[8], KoID(COMPOSITE_INC_LIGHTNESS , i18n("Increase Lightness"))); m_map.insert(m_categories[9], KoID(COMPOSITE_COLOR_HSV , i18n("Color HSV"))); m_map.insert(m_categories[9], KoID(COMPOSITE_HUE_HSV , i18n("Hue HSV"))); m_map.insert(m_categories[9], KoID(COMPOSITE_SATURATION_HSV , i18n("Saturation HSV"))); - m_map.insert(m_categories[9], KoID(COMPOSITE_VALUE , i18n("Value"))); + m_map.insert(m_categories[9], KoID(COMPOSITE_VALUE , i18nc("HSV Value", "Value"))); m_map.insert(m_categories[9], KoID(COMPOSITE_DEC_SATURATION_HSV, i18n("Decrease Saturation HSV"))); m_map.insert(m_categories[9], KoID(COMPOSITE_INC_SATURATION_HSV, i18n("Increase Saturation HSV"))); m_map.insert(m_categories[9], KoID(COMPOSITE_DEC_VALUE , i18n("Decrease Value"))); m_map.insert(m_categories[9], KoID(COMPOSITE_INC_VALUE , i18n("Increase Value"))); } const KoCompositeOpRegistry& KoCompositeOpRegistry::instance() { return *registry; } KoID KoCompositeOpRegistry::getDefaultCompositeOp() const { return KoID(COMPOSITE_OVER, i18n("Normal")); } KoID KoCompositeOpRegistry::getKoID(const QString& compositeOpID) const { KoIDMap::const_iterator itr = std::find(m_map.begin(), m_map.end(), KoID(compositeOpID)); return (itr != m_map.end()) ? *itr : KoID(); } KoCompositeOpRegistry::KoIDMap KoCompositeOpRegistry::getCompositeOps() const { return m_map; } KoCompositeOpRegistry::KoIDList KoCompositeOpRegistry::getCategories() const { return m_categories; } KoCompositeOpRegistry::KoIDList KoCompositeOpRegistry::getCompositeOps(const KoID& category, const KoColorSpace* colorSpace) const { qint32 num = m_map.count(category); KoIDMap::const_iterator beg = m_map.find(category); KoIDMap::const_iterator end = beg + num; KoIDList list; list.reserve(num); if(colorSpace) { for(; beg!=end; ++beg){ if(colorSpace->hasCompositeOp(beg->id())) list.push_back(*beg); } } else { for(; beg!=end; ++beg) list.push_back(*beg); } return list; } KoCompositeOpRegistry::KoIDList KoCompositeOpRegistry::getCompositeOps(const KoColorSpace* colorSpace) const { KoIDMap::const_iterator beg = m_map.begin(); KoIDMap::const_iterator end = m_map.end(); KoIDList list; list.reserve(m_map.size()); if(colorSpace) { for(; beg!=end; ++beg){ if(colorSpace->hasCompositeOp(beg->id())) list.push_back(*beg); } } else { for(; beg!=end; ++beg) list.push_back(*beg); } return list; } bool KoCompositeOpRegistry::colorSpaceHasCompositeOp(const KoColorSpace* colorSpace, const KoID& compositeOp) const { return colorSpace ? colorSpace->hasCompositeOp(compositeOp.id()) : false; } diff --git a/libs/pigment/compositeops/KoCompositeOps.h b/libs/pigment/compositeops/KoCompositeOps.h index ac92cd37e0..e908413e66 100644 --- a/libs/pigment/compositeops/KoCompositeOps.h +++ b/libs/pigment/compositeops/KoCompositeOps.h @@ -1,249 +1,249 @@ /* * Copyright (c) 2007 Cyrille Berger * Copyright (c) 2011 Silvio Heinrich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; 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 _KOCOMPOSITEOPS_H_ #define _KOCOMPOSITEOPS_H_ #include #include #include #include #include "compositeops/KoCompositeOpGeneric.h" #include "compositeops/KoCompositeOpOver.h" #include "compositeops/KoCompositeOpCopyChannel.h" #include "compositeops/KoCompositeOpAlphaDarken.h" #include "compositeops/KoCompositeOpErase.h" #include "compositeops/KoCompositeOpCopy2.h" #include "compositeops/KoCompositeOpDissolve.h" #include "compositeops/KoCompositeOpBehind.h" #include "compositeops/KoCompositeOpDestinationIn.h" #include "compositeops/KoCompositeOpDestinationAtop.h" #include "compositeops/KoCompositeOpGreater.h" #include "KoOptimizedCompositeOpFactory.h" namespace _Private { template struct AddGeneralOps { static void add(KoColorSpace* cs) { Q_UNUSED(cs); } }; template struct OptimizedOpsSelector { static KoCompositeOp* createAlphaDarkenOp(const KoColorSpace *cs) { return new KoCompositeOpAlphaDarken(cs); } static KoCompositeOp* createOverOp(const KoColorSpace *cs) { return new KoCompositeOpOver(cs); } }; template<> struct OptimizedOpsSelector { static KoCompositeOp* createAlphaDarkenOp(const KoColorSpace *cs) { return KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs); } static KoCompositeOp* createOverOp(const KoColorSpace *cs) { return KoOptimizedCompositeOpFactory::createOverOp32(cs); } }; template<> struct OptimizedOpsSelector { static KoCompositeOp* createAlphaDarkenOp(const KoColorSpace *cs) { return KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs); } static KoCompositeOp* createOverOp(const KoColorSpace *cs) { return KoOptimizedCompositeOpFactory::createOverOp32(cs); } }; template<> struct OptimizedOpsSelector { static KoCompositeOp* createAlphaDarkenOp(const KoColorSpace *cs) { return new KoCompositeOpAlphaDarken(cs); } static KoCompositeOp* createOverOp(const KoColorSpace *cs) { return KoOptimizedCompositeOpFactory::createOverOp128(cs); } }; template struct AddGeneralOps { typedef typename Traits::channels_type Arg; typedef Arg (*CompositeFunc)(Arg, Arg); static const qint32 alpha_pos = Traits::alpha_pos; template static void add(KoColorSpace* cs, const QString& id, const QString& description, const QString& category) { cs->addCompositeOp(new KoCompositeOpGenericSC(cs, id, description, category)); } static void add(KoColorSpace* cs) { cs->addCompositeOp(OptimizedOpsSelector::createOverOp(cs)); cs->addCompositeOp(OptimizedOpsSelector::createAlphaDarkenOp(cs)); cs->addCompositeOp(new KoCompositeOpCopy2(cs)); cs->addCompositeOp(new KoCompositeOpErase(cs)); cs->addCompositeOp(new KoCompositeOpBehind(cs)); cs->addCompositeOp(new KoCompositeOpDestinationIn(cs)); cs->addCompositeOp(new KoCompositeOpDestinationAtop(cs)); cs->addCompositeOp(new KoCompositeOpGreater(cs)); add<&cfOverlay >(cs, COMPOSITE_OVERLAY , i18n("Overlay") , KoCompositeOp::categoryMix()); add<&cfGrainMerge >(cs, COMPOSITE_GRAIN_MERGE , i18n("Grain Merge") , KoCompositeOp::categoryMix()); add<&cfGrainExtract >(cs, COMPOSITE_GRAIN_EXTRACT , i18n("Grain Extract") , KoCompositeOp::categoryMix()); add<&cfHardMix >(cs, COMPOSITE_HARD_MIX , i18n("Hard Mix") , KoCompositeOp::categoryMix()); add<&cfHardMixPhotoshop>(cs, COMPOSITE_HARD_MIX_PHOTOSHOP, i18n("Hard Mix (Photoshop)") , KoCompositeOp::categoryMix()); add<&cfGeometricMean >(cs, COMPOSITE_GEOMETRIC_MEAN, i18n("Geometric Mean"), KoCompositeOp::categoryMix()); add<&cfParallel >(cs, COMPOSITE_PARALLEL , i18n("Parallel") , KoCompositeOp::categoryMix()); add<&cfAllanon >(cs, COMPOSITE_ALLANON , i18n("Allanon") , KoCompositeOp::categoryMix()); add<&cfHardOverlay >(cs, COMPOSITE_HARD_OVERLAY , i18n("Hard Overlay") , KoCompositeOp::categoryMix()); add<&cfScreen >(cs, COMPOSITE_SCREEN , i18n("Screen") , KoCompositeOp::categoryLight()); add<&cfColorDodge >(cs, COMPOSITE_DODGE , i18n("Color Dodge") , KoCompositeOp::categoryLight()); add<&cfAddition >(cs, COMPOSITE_LINEAR_DODGE, i18n("Linear Dodge"), KoCompositeOp::categoryLight()); add<&cfLightenOnly >(cs, COMPOSITE_LIGHTEN , i18n("Lighten") , KoCompositeOp::categoryLight()); add<&cfHardLight >(cs, COMPOSITE_HARD_LIGHT , i18n("Hard Light") , KoCompositeOp::categoryLight()); add<&cfSoftLightSvg >(cs, COMPOSITE_SOFT_LIGHT_SVG, i18n("Soft Light (SVG)") , KoCompositeOp::categoryLight()); add<&cfSoftLight >(cs, COMPOSITE_SOFT_LIGHT_PHOTOSHOP, i18n("Soft Light (Photoshop)") , KoCompositeOp::categoryLight()); add<&cfGammaLight >(cs, COMPOSITE_GAMMA_LIGHT , i18n("Gamma Light") , KoCompositeOp::categoryLight()); add<&cfVividLight >(cs, COMPOSITE_VIVID_LIGHT , i18n("Vivid Light") , KoCompositeOp::categoryLight()); add<&cfPinLight >(cs, COMPOSITE_PIN_LIGHT , i18n("Pin Light") , KoCompositeOp::categoryLight()); add<&cfLinearLight >(cs, COMPOSITE_LINEAR_LIGHT, i18n("Linear Light"), KoCompositeOp::categoryLight()); add<&cfColorBurn >(cs, COMPOSITE_BURN , i18n("Color Burn") , KoCompositeOp::categoryDark()); add<&cfLinearBurn >(cs, COMPOSITE_LINEAR_BURN , i18n("Linear Burn"), KoCompositeOp::categoryDark()); add<&cfDarkenOnly >(cs, COMPOSITE_DARKEN , i18n("Darken") , KoCompositeOp::categoryDark()); add<&cfGammaDark >(cs, COMPOSITE_GAMMA_DARK , i18n("Gamma Dark") , KoCompositeOp::categoryDark()); add<&cfAddition >(cs, COMPOSITE_ADD , i18n("Addition") , KoCompositeOp::categoryArithmetic()); add<&cfSubtract >(cs, COMPOSITE_SUBTRACT , i18n("Subtract") , KoCompositeOp::categoryArithmetic()); add<&cfInverseSubtract >(cs, COMPOSITE_INVERSE_SUBTRACT, i18n("Inversed-Subtract"), KoCompositeOp::categoryArithmetic()); add<&cfMultiply >(cs, COMPOSITE_MULT , i18n("Multiply") , KoCompositeOp::categoryArithmetic()); add<&cfDivide >(cs, COMPOSITE_DIVIDE , i18n("Divide") , KoCompositeOp::categoryArithmetic()); add<&cfArcTangent >(cs, COMPOSITE_ARC_TANGENT , i18n("Arcus Tangent") , KoCompositeOp::categoryNegative()); add<&cfDifference >(cs, COMPOSITE_DIFF , i18n("Difference") , KoCompositeOp::categoryNegative()); add<&cfExclusion >(cs, COMPOSITE_EXCLUSION , i18n("Exclusion") , KoCompositeOp::categoryNegative()); add<&cfEquivalence >(cs, COMPOSITE_EQUIVALENCE , i18n("Equivalence") , KoCompositeOp::categoryNegative()); add<&cfAdditiveSubtractive >(cs, COMPOSITE_ADDITIVE_SUBTRACTIVE , i18n("Additive-Subtractive") , KoCompositeOp::categoryNegative()); cs->addCompositeOp(new KoCompositeOpDissolve(cs, KoCompositeOp::categoryMisc())); } }; template struct AddRGBOps { static void add(KoColorSpace* cs) { Q_UNUSED(cs); } }; template struct AddRGBOps { typedef float Arg; - + static const qint32 red_pos = Traits::red_pos; static const qint32 green_pos = Traits::green_pos; static const qint32 blue_pos = Traits::blue_pos; - + template static void add(KoColorSpace* cs, const QString& id, const QString& description, const QString& category) { cs->addCompositeOp(new KoCompositeOpGenericHSL(cs, id, description, category)); } - + static void add(KoColorSpace* cs) { cs->addCompositeOp(new KoCompositeOpCopyChannel(cs, COMPOSITE_COPY_RED , i18n("Copy Red") , KoCompositeOp::categoryMisc())); cs->addCompositeOp(new KoCompositeOpCopyChannel(cs, COMPOSITE_COPY_GREEN, i18n("Copy Green"), KoCompositeOp::categoryMisc())); cs->addCompositeOp(new KoCompositeOpCopyChannel(cs, COMPOSITE_COPY_BLUE , i18n("Copy Blue") , KoCompositeOp::categoryMisc())); add<&cfTangentNormalmap >(cs, COMPOSITE_TANGENT_NORMALMAP , i18n("Tangent Normalmap") , KoCompositeOp::categoryMisc()); add<&cfReorientedNormalMapCombine >(cs, COMPOSITE_COMBINE_NORMAL, i18n("Combine Normal Maps"), KoCompositeOp::categoryMisc()); - + add<&cfColor >(cs, COMPOSITE_COLOR , i18n("Color") , KoCompositeOp::categoryHSY()); add<&cfHue >(cs, COMPOSITE_HUE , i18n("Hue") , KoCompositeOp::categoryHSY()); add<&cfSaturation >(cs, COMPOSITE_SATURATION , i18n("Saturation") , KoCompositeOp::categoryHSY()); add<&cfIncreaseSaturation >(cs, COMPOSITE_INC_SATURATION, i18n("Increase Saturation"), KoCompositeOp::categoryHSY()); add<&cfDecreaseSaturation >(cs, COMPOSITE_DEC_SATURATION, i18n("Decrease Saturation"), KoCompositeOp::categoryHSY()); add<&cfLightness >(cs, COMPOSITE_LUMINIZE , i18n("Luminosity") , KoCompositeOp::categoryHSY()); add<&cfIncreaseLightness >(cs, COMPOSITE_INC_LUMINOSITY, i18n("Increase Luminosity"), KoCompositeOp::categoryHSY()); add<&cfDecreaseLightness >(cs, COMPOSITE_DEC_LUMINOSITY, i18n("Decrease Luminosity"), KoCompositeOp::categoryHSY()); add<&cfDarkerColor >(cs, COMPOSITE_DARKER_COLOR, i18n("Darker Color"), KoCompositeOp::categoryDark());//darker color as PSD does it// add<&cfLighterColor >(cs, COMPOSITE_LIGHTER_COLOR, i18n("Lighter Color"), KoCompositeOp::categoryLight());//lighter color as PSD does it// - + add<&cfColor >(cs, COMPOSITE_COLOR_HSI , i18n("Color HSI") , KoCompositeOp::categoryHSI()); add<&cfHue >(cs, COMPOSITE_HUE_HSI , i18n("Hue HSI") , KoCompositeOp::categoryHSI()); add<&cfSaturation >(cs, COMPOSITE_SATURATION_HSI , i18n("Saturation HSI") , KoCompositeOp::categoryHSI()); add<&cfIncreaseSaturation >(cs, COMPOSITE_INC_SATURATION_HSI, i18n("Increase Saturation HSI"), KoCompositeOp::categoryHSI()); add<&cfDecreaseSaturation >(cs, COMPOSITE_DEC_SATURATION_HSI, i18n("Decrease Saturation HSI"), KoCompositeOp::categoryHSI()); add<&cfLightness >(cs, COMPOSITE_INTENSITY , i18n("Intensity") , KoCompositeOp::categoryHSI()); add<&cfIncreaseLightness >(cs, COMPOSITE_INC_INTENSITY , i18n("Increase Intensity") , KoCompositeOp::categoryHSI()); add<&cfDecreaseLightness >(cs, COMPOSITE_DEC_INTENSITY , i18n("Decrease Intensity") , KoCompositeOp::categoryHSI()); - + add<&cfColor >(cs, COMPOSITE_COLOR_HSL , i18n("Color HSL") , KoCompositeOp::categoryHSL()); add<&cfHue >(cs, COMPOSITE_HUE_HSL , i18n("Hue HSL") , KoCompositeOp::categoryHSL()); add<&cfSaturation >(cs, COMPOSITE_SATURATION_HSL , i18n("Saturation HSL") , KoCompositeOp::categoryHSL()); add<&cfIncreaseSaturation >(cs, COMPOSITE_INC_SATURATION_HSL, i18n("Increase Saturation HSL"), KoCompositeOp::categoryHSL()); add<&cfDecreaseSaturation >(cs, COMPOSITE_DEC_SATURATION_HSL, i18n("Decrease Saturation HSL"), KoCompositeOp::categoryHSL()); add<&cfLightness >(cs, COMPOSITE_LIGHTNESS , i18n("Lightness") , KoCompositeOp::categoryHSL()); add<&cfIncreaseLightness >(cs, COMPOSITE_INC_LIGHTNESS , i18n("Increase Lightness") , KoCompositeOp::categoryHSL()); add<&cfDecreaseLightness >(cs, COMPOSITE_DEC_LIGHTNESS , i18n("Decrease Lightness") , KoCompositeOp::categoryHSL()); - + add<&cfColor >(cs, COMPOSITE_COLOR_HSV , i18n("Color HSV") , KoCompositeOp::categoryHSV()); add<&cfHue >(cs, COMPOSITE_HUE_HSV , i18n("Hue HSV") , KoCompositeOp::categoryHSV()); add<&cfSaturation >(cs, COMPOSITE_SATURATION_HSV , i18n("Saturation HSV") , KoCompositeOp::categoryHSV()); add<&cfIncreaseSaturation >(cs, COMPOSITE_INC_SATURATION_HSV, i18n("Increase Saturation HSV"), KoCompositeOp::categoryHSV()); add<&cfDecreaseSaturation >(cs, COMPOSITE_DEC_SATURATION_HSV, i18n("Decrease Saturation HSV"), KoCompositeOp::categoryHSV()); - add<&cfLightness >(cs, COMPOSITE_VALUE , i18n("Value") , KoCompositeOp::categoryHSV()); + add<&cfLightness >(cs, COMPOSITE_VALUE , i18nc("HSV Value","Value") , KoCompositeOp::categoryHSV()); add<&cfIncreaseLightness >(cs, COMPOSITE_INC_VALUE , i18n("Increase Value") , KoCompositeOp::categoryHSV()); add<&cfDecreaseLightness >(cs, COMPOSITE_DEC_VALUE , i18n("Decrease Value") , KoCompositeOp::categoryHSV()); } }; } /** * This function add to the colorspace all the composite ops defined by * the pigment library. */ template void addStandardCompositeOps(KoColorSpace* cs) { typedef typename _Traits_::channels_type channels_type; - + static const bool useGeneralOps = true; static const bool useRGBOps = (boost::is_base_of, _Traits_>::value || boost::is_base_of, _Traits_>::value); - + _Private::AddGeneralOps<_Traits_, useGeneralOps>::add(cs); _Private::AddRGBOps <_Traits_, useRGBOps >::add(cs); } #endif diff --git a/libs/pigment/compositeops/KoVcMultiArchBuildSupport.h b/libs/pigment/compositeops/KoVcMultiArchBuildSupport.h index 2bdd069a74..f81fbea0f6 100644 --- a/libs/pigment/compositeops/KoVcMultiArchBuildSupport.h +++ b/libs/pigment/compositeops/KoVcMultiArchBuildSupport.h @@ -1,112 +1,122 @@ /* * Copyright (c) 2012 Dmitry Kazakov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; 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 __KOVCMULTIARCHBUILDSUPPORT_H #define __KOVCMULTIARCHBUILDSUPPORT_H #include "config-vc.h" #ifdef HAVE_VC #if defined(__clang__) #pragma GCC diagnostic ignored "-Wlocal-type-template-args" #endif #if defined _MSC_VER // Lets shut up the "possible loss of data" and "forcing value to bool 'true' or 'false' #pragma warning ( push ) #pragma warning ( disable : 4244 ) #pragma warning ( disable : 4800 ) #endif #include #include #include #if defined _MSC_VER #pragma warning ( pop ) #endif #else /* HAVE_VC */ namespace Vc { enum Implementation /*: std::uint_least32_t*/ { ScalarImpl, }; class CurrentImplementation { public: static constexpr Implementation current() { return static_cast(ScalarImpl); } }; } #endif /* HAVE_VC */ #include #include #include #include template typename FactoryType::ReturnType createOptimizedClass(typename FactoryType::ParamType param) { static bool isConfigInitialized = false; static bool useVectorization = true; if (!isConfigInitialized) { KConfigGroup cfg = KSharedConfig::openConfig()->group(""); useVectorization = !cfg.readEntry("amdDisableVectorWorkaround", false); isConfigInitialized = true; } if (!useVectorization) { qWarning() << "WARNING: vector instructions disabled by \'amdDisableVectorWorkaround\' option!"; return FactoryType::template create(param); } #ifdef HAVE_VC /** * We use SSE2, SSSE3, SSE4.1, AVX and AVX2. * The rest are integer and string instructions mostly. * * TODO: Add FMA3/4 when it is adopted by Vc */ if (Vc::isImplementationSupported(Vc::AVX2Impl)) { return FactoryType::template create(param); } else if (Vc::isImplementationSupported(Vc::AVXImpl)) { return FactoryType::template create(param); } else if (Vc::isImplementationSupported(Vc::SSE41Impl)) { return FactoryType::template create(param); } else if (Vc::isImplementationSupported(Vc::SSSE3Impl)) { return FactoryType::template create(param); } else if (Vc::isImplementationSupported(Vc::SSE2Impl)) { return FactoryType::template create(param); } else { #endif return FactoryType::template create(param); #ifdef HAVE_VC } #endif } +template +typename FactoryType::ReturnType +createOptimizedClass(typename FactoryType::ParamType param, bool forceScalarImplemetation) +{ + if(forceScalarImplemetation){ + return FactoryType::template create(param); + } + return createOptimizedClass(param); +} + #endif /* __KOVCMULTIARCHBUILDSUPPORT_H */ diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt index 7ca8b1bef2..c388f23b79 100644 --- a/libs/ui/CMakeLists.txt +++ b/libs/ui/CMakeLists.txt @@ -1,588 +1,592 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/qtlockedfile ${EXIV2_INCLUDE_DIR} ) include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR} ${OCIO_INCLUDE_DIR} ) add_subdirectory( tests ) if (APPLE) find_library(FOUNDATION_LIBRARY Foundation) find_library(APPKIT_LIBRARY AppKit) 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 canvas/KisSnapPointStrategy.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 dialogs/KisSessionManagerDialog.cpp dialogs/KisNewWindowLayoutDialog.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 + KisPaintopPropertiesBase.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_change_file_layer_command.h kis_safe_document_loader.cpp kis_splash_screen.cpp kis_filter_manager.cc kis_filters_model.cc kis_histogram_view.cc KisImageBarrierLockerWithFeedback.cpp 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 KisDecorationsManager.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 KisResourceServerProvider.cpp KisResourceBundleServerProvider.cpp KisSelectedShapesProxy.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 KisActionPlugin.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 opengl/kis_texture_tile_info_pool.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/KisStrokeSpeedMonitor.cpp tool/strokes/freehand_stroke.cpp tool/strokes/KisStrokeEfficiencyMeasurer.cpp tool/strokes/kis_painter_based_stroke_strategy.cpp tool/strokes/kis_filter_stroke_strategy.cpp tool/strokes/kis_color_picker_stroke_strategy.cpp tool/strokes/KisFreehandStrokeInfo.cpp tool/strokes/KisMaskedFreehandStrokePainter.cpp tool/strokes/KisMaskingBrushRenderer.cpp tool/strokes/KisMaskingBrushCompositeOpFactory.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_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_paintop_presets_save.cpp widgets/kis_paintop_preset_icon_library.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/KisSelectionPropertySlider.cpp widgets/kis_size_group.cpp widgets/kis_size_group_p.cpp widgets/kis_wdg_generator.cpp widgets/kis_workspace_chooser.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/kis_preset_live_preview_view.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 widgets/KoStrokeConfigWidget.cpp widgets/KoFillConfigWidget.cpp utils/kis_document_aware_spin_box_unit_manager.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_native_gesture_shortcut.cpp input/kis_single_action_shortcut.cpp input/kis_stroke_shortcut.cpp input/kis_shortcut_matcher.cpp input/kis_select_layer_action.cpp input/KisQtWidgetsTweaker.cpp input/KisInputActionGroup.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 actions/KisPasteActionFactory.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 + KisCloneDocumentStroke.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 KisImportExportUtils.cpp kis_async_action_feedback.cpp KisMainWindow.cpp KisOpenPane.cpp KisPart.cpp KisPrintJob.cpp KisTemplate.cpp KisTemplateCreateDia.cpp KisTemplateGroup.cpp KisTemplates.cpp KisTemplatesPane.cpp KisTemplateTree.cpp KisUndoActionsUpdateManager.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 KisWindowLayoutResource.cpp KisWindowLayoutManager.cpp KisSessionResource.cpp KisReferenceImagesDecoration.cpp KisReferenceImage.cpp flake/KisReferenceImagesLayer.cpp flake/KisReferenceImagesLayer.h ) if(WIN32) 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 input/wintab/kis_tablet_support_win8.cpp opengl/kis_opengl_win.cpp ) endif() set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} kis_animation_frame_cache.cpp kis_animation_cache_populator.cpp KisAsyncAnimationRendererBase.cpp KisAsyncAnimationCacheRenderer.cpp KisAsyncAnimationFramesSavingRenderer.cpp dialogs/KisAsyncAnimationRenderDialogBase.cpp dialogs/KisAsyncAnimationCacheRenderDialog.cpp dialogs/KisAsyncAnimationFramesSaveDialog.cpp canvas/kis_animation_player.cpp kis_animation_importer.cpp KisSyncedAudioPlayback.cpp + input/wintab/drawpile_tablettester/tablettester.cpp + input/wintab/drawpile_tablettester/tablettest.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/qxcbconnection_xi2.cpp input/wintab/qxcbconnection.cpp input/wintab/kis_xi2_event_filter.cpp ) endif() endif() if(APPLE) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} osx.mm ) endif() ki18n_wrap_ui(kritaui_LIB_SRCS widgets/KoFillConfigWidget.ui widgets/KoStrokeConfigWidget.ui 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/wdgsavebrushpreset.ui forms/wdgpreseticonlibrary.ui forms/wdgdlgblacklistcleanup.ui forms/wdgrectangleconstraints.ui forms/wdgimportimagesequence.ui forms/wdgstrokeselectionproperties.ui forms/KisDetailsPaneBase.ui forms/KisOpenPaneBase.ui forms/wdgstopgradienteditor.ui forms/wdgsessionmanager.ui forms/wdgnewwindowlayout.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 + input/wintab/drawpile_tablettester/tablettest.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}) target_link_libraries(kritaui ${APPKIT_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/KisApplication.cpp b/libs/ui/KisApplication.cpp index c97a622d24..f2c8c87dfc 100644 --- a/libs/ui/KisApplication.cpp +++ b/libs/ui/KisApplication.cpp @@ -1,865 +1,864 @@ /* * Copyright (C) 1998, 1999 Torben Weis * Copyright (C) 2012 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 "KisApplication.h" #include #ifdef Q_OS_WIN #include #include #endif #ifdef Q_OS_OSX #include "osx.h" #endif #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 "KoGlobal.h" #include "KoConfig.h" #include #include #include #include "thememanager.h" #include "KisPrintJob.h" #include "KisDocument.h" #include "KisMainWindow.h" #include "KisAutoSaveRecoveryDialog.h" #include "KisPart.h" #include #include "kis_md5_generator.h" #include "kis_splash_screen.h" #include "kis_config.h" #include "flake/kis_shape_selection.h" #include #include #include #include #include #include #include #include "kisexiv2/kis_exiv2.h" #include "KisApplicationArguments.h" #include #include "kis_action_registry.h" #include #include #include #include "kis_image_barrier_locker.h" #include "opengl/kis_opengl.h" #include "kis_spin_box_unit_manager.h" #include "kis_document_aware_spin_box_unit_manager.h" #include "KisViewManager.h" #include "kis_workspace_resource.h" +#include #include #include namespace { const QTime appStartTime(QTime::currentTime()); } -class KisApplicationPrivate +class KisApplication::Private { public: - KisApplicationPrivate() - : splashScreen(0) - {} + Private() {} QPointer splashScreen; + KisAutoSaveRecoveryDialog *autosaveDialog {0}; + QPointer mainWindow; // The first mainwindow we create on startup + bool batchRun {false}; + }; class KisApplication::ResetStarting { public: ResetStarting(KisSplashScreen *splash, int fileCount) : m_splash(splash) , m_fileCount(fileCount) { } ~ResetStarting() { if (m_splash) { KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen"); bool hideSplash = cfg.readEntry("HideSplashAfterStartup", false); if (m_fileCount > 0 || hideSplash) { m_splash->hide(); } else { m_splash->setWindowFlags(Qt::Dialog); QRect r(QPoint(), m_splash->size()); m_splash->move(QApplication::desktop()->availableGeometry().center() - r.center()); m_splash->setWindowTitle(qAppName()); m_splash->setParent(0); Q_FOREACH (QObject *o, m_splash->children()) { QWidget *w = qobject_cast(o); if (w && w->isHidden()) { w->setVisible(true); } } m_splash->show(); m_splash->activateWindow(); } } } QPointer m_splash; int m_fileCount; }; KisApplication::KisApplication(const QString &key, int &argc, char **argv) : QtSingleApplication(key, argc, argv) - , d(new KisApplicationPrivate) - , m_autosaveDialog(0) - , m_mainWindow(0) - , m_batchRun(false) + , d(new Private) { #ifdef Q_OS_OSX setMouseCoalescingEnabled(false); #endif QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath()); setApplicationDisplayName("Krita"); setApplicationName("krita"); // Note: Qt docs suggest we set this, but if we do, we get resource paths of the form of krita/krita, which is weird. // setOrganizationName("krita"); setOrganizationDomain("krita.org"); QString version = KritaVersionWrapper::versionString(true); setApplicationVersion(version); setWindowIcon(KisIconUtils::loadIcon("calligrakrita")); if (qgetenv("KRITA_NO_STYLE_OVERRIDE").isEmpty()) { QStringList styles = QStringList() << "breeze" << "fusion" << "plastique"; if (!styles.contains(style()->objectName().toLower())) { Q_FOREACH (const QString & style, styles) { if (!setStyle(style)) { qDebug() << "No" << style << "available."; } else { qDebug() << "Set style" << style; break; } } } } else { qDebug() << "Style override disabled, using" << style()->objectName(); } KisOpenGL::initialize(); qDebug() << "krita has opengl" << KisOpenGL::hasOpenGL(); } #if defined(Q_OS_WIN) && defined(ENV32BIT) typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); LPFN_ISWOW64PROCESS fnIsWow64Process; BOOL isWow64() { BOOL bIsWow64 = FALSE; //IsWow64Process is not available on all supported versions of Windows. //Use GetModuleHandle to get a handle to the DLL that contains the function //and GetProcAddress to get a pointer to the function if available. fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress( GetModuleHandle(TEXT("kernel32")),"IsWow64Process"); if(0 != fnIsWow64Process) { if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64)) { //handle error } } return bIsWow64; } #endif void KisApplication::initializeGlobals(const KisApplicationArguments &args) { int dpiX = args.dpiX(); int dpiY = args.dpiY(); if (dpiX > 0 && dpiY > 0) { KoDpi::setDPI(dpiX, dpiY); } } void KisApplication::addResourceTypes() { // qDebug() << "addResourceTypes();"; // All Krita's resource types KoResourcePaths::addResourceType("kis_pics", "data", "/pics/"); KoResourcePaths::addResourceType("kis_images", "data", "/images/"); KoResourcePaths::addResourceType("icc_profiles", "data", "/profiles/"); KoResourcePaths::addResourceType("metadata_schema", "data", "/metadata/schemas/"); KoResourcePaths::addResourceType("kis_brushes", "data", "/brushes/"); KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/"); KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/"); KoResourcePaths::addResourceType("gmic_definitions", "data", "/gmic/"); KoResourcePaths::addResourceType("kis_resourcebundles", "data", "/bundles/"); KoResourcePaths::addResourceType("kis_defaultpresets", "data", "/defaultpresets/"); KoResourcePaths::addResourceType("kis_paintoppresets", "data", "/paintoppresets/"); KoResourcePaths::addResourceType("kis_workspaces", "data", "/workspaces/"); KoResourcePaths::addResourceType("kis_windowlayouts", "data", "/windowlayouts/"); KoResourcePaths::addResourceType("kis_sessions", "data", "/sessions/"); KoResourcePaths::addResourceType("psd_layer_style_collections", "data", "/asl"); KoResourcePaths::addResourceType("ko_patterns", "data", "/patterns/", true); KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/"); KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/", true); KoResourcePaths::addResourceType("ko_palettes", "data", "/palettes/", true); KoResourcePaths::addResourceType("kis_shortcuts", "data", "/shortcuts/"); KoResourcePaths::addResourceType("kis_actions", "data", "/actions"); KoResourcePaths::addResourceType("icc_profiles", "data", "/color/icc"); KoResourcePaths::addResourceType("ko_effects", "data", "/effects/"); KoResourcePaths::addResourceType("tags", "data", "/tags/"); KoResourcePaths::addResourceType("templates", "data", "/templates"); KoResourcePaths::addResourceType("pythonscripts", "data", "/pykrita"); KoResourcePaths::addResourceType("symbols", "data", "/symbols"); KoResourcePaths::addResourceType("preset_icons", "data", "/preset_icons"); // // Extra directories to look for create resources. (Does anyone actually use that anymore?) // KoResourcePaths::addResourceDir("ko_gradients", "/usr/share/create/gradients/gimp"); // KoResourcePaths::addResourceDir("ko_gradients", QDir::homePath() + QString("/.create/gradients/gimp")); // KoResourcePaths::addResourceDir("ko_patterns", "/usr/share/create/patterns/gimp"); // KoResourcePaths::addResourceDir("ko_patterns", QDir::homePath() + QString("/.create/patterns/gimp")); // KoResourcePaths::addResourceDir("kis_brushes", "/usr/share/create/brushes/gimp"); // KoResourcePaths::addResourceDir("kis_brushes", QDir::homePath() + QString("/.create/brushes/gimp")); // KoResourcePaths::addResourceDir("ko_palettes", "/usr/share/create/swatches"); // KoResourcePaths::addResourceDir("ko_palettes", QDir::homePath() + QString("/.create/swatches")); // Make directories for all resources we can save, and tags QDir d; d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tags/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/asl/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/bundles/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/brushes/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/gradients/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/paintoppresets/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/palettes/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/patterns/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/taskset/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/workspaces/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/input/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/pykrita/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/symbols/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/color-schemes/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/preset_icons/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/preset_icons/tool_icons/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/preset_icons/emblem_icons/"); // Indicate that it is now safe for users of KoResourcePaths to load resources KoResourcePaths::setReady(); } void KisApplication::loadResources() { // qDebug() << "loadResources();"; setSplashScreenLoadingText(i18n("Loading Resources...")); processEvents(); KoResourceServerProvider::instance(); setSplashScreenLoadingText(i18n("Loading Brush Presets...")); processEvents(); KisResourceServerProvider::instance(); setSplashScreenLoadingText(i18n("Loading Brushes...")); processEvents(); KisBrushServer::instance()->brushServer(); setSplashScreenLoadingText(i18n("Loading Bundles...")); processEvents(); KisResourceBundleServerProvider::instance(); } void KisApplication::loadResourceTags() { // qDebug() << "loadResourceTags()"; KoResourceServerProvider::instance()->patternServer()->loadTags(); KoResourceServerProvider::instance()->gradientServer()->loadTags(); KoResourceServerProvider::instance()->paletteServer()->loadTags(); KoResourceServerProvider::instance()->svgSymbolCollectionServer()->loadTags(); KisBrushServer::instance()->brushServer()->loadTags(); KisResourceServerProvider::instance()->workspaceServer()->loadTags(); KisResourceServerProvider::instance()->layerStyleCollectionServer()->loadTags(); KisResourceBundleServerProvider::instance()->resourceBundleServer()->loadTags(); KisResourceServerProvider::instance()->paintOpPresetServer()->loadTags(); KisResourceServerProvider::instance()->paintOpPresetServer()->clearOldSystemTags(); } void KisApplication::loadPlugins() { // qDebug() << "loadPlugins();"; KoShapeRegistry* r = KoShapeRegistry::instance(); r->add(new KisShapeSelectionFactory()); KisActionRegistry::instance(); KisFilterRegistry::instance(); KisGeneratorRegistry::instance(); KisPaintOpRegistry::instance(); KoColorSpaceRegistry::instance(); } void KisApplication::loadGuiPlugins() { // qDebug() << "loadGuiPlugins();"; // Load the krita-specific tools setSplashScreenLoadingText(i18n("Loading Plugins for Krita/Tool...")); processEvents(); // qDebug() << "loading tools"; KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Tool"), QString::fromLatin1("[X-Krita-Version] == 28")); // Load dockers setSplashScreenLoadingText(i18n("Loading Plugins for Krita/Dock...")); processEvents(); // qDebug() << "loading dockers"; KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Dock"), QString::fromLatin1("[X-Krita-Version] == 28")); // XXX_EXIV: make the exiv io backends real plugins setSplashScreenLoadingText(i18n("Loading Plugins Exiv/IO...")); processEvents(); // qDebug() << "loading exiv2"; KisExiv2::initialize(); } bool KisApplication::start(const KisApplicationArguments &args) { KisConfig cfg; #if defined(Q_OS_WIN) #ifdef ENV32BIT if (isWow64() && !cfg.readEntry("WarnedAbout32Bits", false)) { QMessageBox::information(0, i18nc("@title:window", "Krita: Warning"), i18n("You are running a 32 bits build on a 64 bits Windows.\n" "This is not recommended.\n" "Please download and install the x64 build instead.")); cfg.writeEntry("WarnedAbout32Bits", true); } #endif #endif QString opengl = cfg.canvasState(); if (opengl == "OPENGL_NOT_TRIED" ) { cfg.setCanvasState("TRY_OPENGL"); } else if (opengl != "OPENGL_SUCCESS") { cfg.setCanvasState("OPENGL_FAILED"); } setSplashScreenLoadingText(i18n("Initializing Globals")); processEvents(); initializeGlobals(args); const bool doNewImage = args.doNewImage(); const bool doTemplate = args.doTemplate(); const bool print = args.print(); const bool exportAs = args.exportAs(); const bool exportAsPdf = args.exportAsPdf(); const QString exportFileName = args.exportFileName(); - m_batchRun = (print || exportAs || exportAsPdf || !exportFileName.isEmpty()); + d->batchRun = (print || exportAs || exportAsPdf || !exportFileName.isEmpty()); // print & exportAsPdf do user interaction ATM const bool needsMainWindow = !exportAs; // only show the mainWindow when no command-line mode option is passed // TODO: fix print & exportAsPdf to work without mainwindow shown bool showmainWindow = !exportAs; // would be !batchRun; - const bool showSplashScreen = !m_batchRun && qEnvironmentVariableIsEmpty("NOSPLASH"); + const bool showSplashScreen = !d->batchRun && qEnvironmentVariableIsEmpty("NOSPLASH"); if (showSplashScreen && d->splashScreen) { d->splashScreen->show(); d->splashScreen->repaint(); processEvents(); } KoHashGeneratorProvider::instance()->setGenerator("MD5", new KisMD5Generator()); // Initialize all Krita directories etc. KoGlobal::initialize(); KConfigGroup group(KSharedConfig::openConfig(), "theme"); Digikam::ThemeManager themeManager; themeManager.setCurrentTheme(group.readEntry("Theme", "Krita dark")); ResetStarting resetStarting(d->splashScreen, args.filenames().count()); // remove the splash when done Q_UNUSED(resetStarting); // Make sure we can save resources and tags setSplashScreenLoadingText(i18n("Adding resource types")); processEvents(); addResourceTypes(); // Load the plugins loadPlugins(); // Load all resources loadResources(); // Load all the tags loadResourceTags(); // Load the gui plugins loadGuiPlugins(); KisPart *kisPart = KisPart::instance(); if (needsMainWindow) { // show a mainWindow asap, if we want that setSplashScreenLoadingText(i18n("Loading Main Window...")); processEvents(); bool sessionNeeded = true; auto sessionMode = cfg.sessionOnStartup(); if (!args.session().isEmpty()) { sessionNeeded = !kisPart->restoreSession(args.session()); } else if (sessionMode == KisConfig::SOS_ShowSessionManager) { showmainWindow = false; sessionNeeded = false; kisPart->showSessionManager(); } else if (sessionMode == KisConfig::SOS_PreviousSession) { KConfigGroup sessionCfg = KSharedConfig::openConfig()->group("session"); const QString &sessionName = sessionCfg.readEntry("previousSession"); sessionNeeded = !kisPart->restoreSession(sessionName); } if (sessionNeeded) { kisPart->startBlankSession(); } if (!args.windowLayout().isEmpty()) { KoResourceServer * rserver = KisResourceServerProvider::instance()->windowLayoutServer(); KisWindowLayoutResource* windowLayout = rserver->resourceByName(args.windowLayout()); if (windowLayout) { windowLayout->applyLayout(); } } if (showmainWindow) { - m_mainWindow = kisPart->currentMainwindow(); + d->mainWindow = kisPart->currentMainwindow(); if (!args.workspace().isEmpty()) { KoResourceServer * rserver = KisResourceServerProvider::instance()->workspaceServer(); KisWorkspaceResource* workspace = rserver->resourceByName(args.workspace()); if (workspace) { - m_mainWindow->restoreWorkspace(workspace); + d->mainWindow->restoreWorkspace(workspace); } } if (args.canvasOnly()) { - m_mainWindow->viewManager()->switchCanvasOnly(true); + d->mainWindow->viewManager()->switchCanvasOnly(true); } if (args.fullScreen()) { - m_mainWindow->showFullScreen(); + d->mainWindow->showFullScreen(); } } else { - m_mainWindow = kisPart->createMainWindow(); + d->mainWindow = kisPart->createMainWindow(); } } short int numberOfOpenDocuments = 0; // number of documents open // Check for autosave files that can be restored, if we're not running a batchrun (test, print, export to pdf) - if (!m_batchRun) { + if (!d->batchRun) { checkAutosaveFiles(); } setSplashScreenLoadingText(QString()); // done loading, so clear out label processEvents(); //configure the unit manager KisSpinBoxUnitManagerFactory::setDefaultUnitManagerBuilder(new KisDocumentAwareSpinBoxUnitManagerBuilder()); connect(this, &KisApplication::aboutToQuit, &KisSpinBoxUnitManagerFactory::clearUnitManagerBuilder); //ensure the builder is destroyed when the application leave. //the new syntax slot syntax allow to connect to a non q_object static method. // Create a new image, if needed if (doNewImage) { KisDocument *doc = args.image(); if (doc) { kisPart->addDocument(doc); - m_mainWindow->addViewAndNotifyLoadingCompleted(doc); + d->mainWindow->addViewAndNotifyLoadingCompleted(doc); } } // Get the command line arguments which we have to parse int argsCount = args.filenames().count(); if (argsCount > 0) { // Loop through arguments short int nPrinted = 0; for (int argNumber = 0; argNumber < argsCount; argNumber++) { QString fileName = args.filenames().at(argNumber); // are we just trying to open a template? if (doTemplate) { // called in mix with batch options? ignore and silently skip - if (m_batchRun) { + if (d->batchRun) { continue; } - if (createNewDocFromTemplate(fileName, m_mainWindow)) { + if (createNewDocFromTemplate(fileName, d->mainWindow)) { ++numberOfOpenDocuments; } // now try to load } else { if (exportAs) { QString outputMimetype = KisMimeDatabase::mimeTypeForFile(exportFileName, false); if (outputMimetype == "application/octetstream") { dbgKrita << i18n("Mimetype not found, try using the -mimetype option") << endl; return 1; } KisDocument *doc = kisPart->createDocument(); - doc->setFileBatchMode(m_batchRun); + doc->setFileBatchMode(d->batchRun); doc->openUrl(QUrl::fromLocalFile(fileName)); qApp->processEvents(); // For vector layers to be updated doc->setFileBatchMode(true); if (!doc->exportDocumentSync(QUrl::fromLocalFile(exportFileName), outputMimetype.toLatin1())) { dbgKrita << "Could not export " << fileName << "to" << exportFileName << ":" << doc->errorMessage(); } nPrinted++; QTimer::singleShot(0, this, SLOT(quit())); } - else if (m_mainWindow) { - KisMainWindow::OpenFlags flags = m_batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; - if (m_mainWindow->openDocument(QUrl::fromLocalFile(fileName), flags)) { + else if (d->mainWindow) { + KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; + if (d->mainWindow->openDocument(QUrl::fromLocalFile(fileName), flags)) { if (print) { - m_mainWindow->slotFilePrint(); + d->mainWindow->slotFilePrint(); nPrinted++; // TODO: trigger closing of app once printing is done } else if (exportAsPdf) { - KisPrintJob *job = m_mainWindow->exportToPdf(exportFileName); + KisPrintJob *job = d->mainWindow->exportToPdf(exportFileName); if (job) - connect (job, SIGNAL(destroyed(QObject*)), m_mainWindow, + connect (job, SIGNAL(destroyed(QObject*)), d->mainWindow, SLOT(slotFileQuit()), Qt::QueuedConnection); nPrinted++; } else { // Normal case, success numberOfOpenDocuments++; } } else { // .... if failed // delete doc; done by openDocument } } } } - if (m_batchRun) { + if (d->batchRun) { return nPrinted > 0; } } // fixes BUG:369308 - Krita crashing on splash screen when loading. // trying to open a file before Krita has loaded can cause it to hang and crash if (d->splashScreen) { d->splashScreen->displayLinks(true); d->splashScreen->displayRecentFiles(true); } // not calling this before since the program will quit there. return true; } KisApplication::~KisApplication() { - delete d; } void KisApplication::setSplashScreen(QWidget *splashScreen) { d->splashScreen = qobject_cast(splashScreen); } void KisApplication::setSplashScreenLoadingText(QString textToLoad) { if (d->splashScreen) { //d->splashScreen->loadingLabel->setText(textToLoad); d->splashScreen->setLoadingText(textToLoad); d->splashScreen->repaint(); } } void KisApplication::hideSplashScreen() { if (d->splashScreen) { // hide the splashscreen to see the dialog d->splashScreen->hide(); } } bool KisApplication::notify(QObject *receiver, QEvent *event) { try { return QApplication::notify(receiver, event); } catch (std::exception &e) { qWarning("Error %s sending event %i to object %s", e.what(), event->type(), qPrintable(receiver->objectName())); } catch (...) { qWarning("Error sending event %i to object %s", event->type(), qPrintable(receiver->objectName())); } return false; } void KisApplication::remoteArguments(QByteArray message, QObject *socket) { Q_UNUSED(socket); // check if we have any mainwindow KisMainWindow *mw = qobject_cast(qApp->activeWindow()); if (!mw) { mw = KisPart::instance()->mainWindows().first(); } if (!mw) { return; } KisApplicationArguments args = KisApplicationArguments::deserialize(message); const bool doTemplate = args.doTemplate(); const int argsCount = args.filenames().count(); if (argsCount > 0) { // Loop through arguments for (int argNumber = 0; argNumber < argsCount; ++argNumber) { QString filename = args.filenames().at(argNumber); // are we just trying to open a template? if (doTemplate) { createNewDocFromTemplate(filename, mw); } else if (QFile(filename).exists()) { - KisMainWindow::OpenFlags flags = m_batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; + KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; mw->openDocument(QUrl::fromLocalFile(filename), flags); } } } } void KisApplication::fileOpenRequested(const QString &url) { KisMainWindow *mainWindow = KisPart::instance()->mainWindows().first(); if (mainWindow) { - KisMainWindow::OpenFlags flags = m_batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; + KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; mainWindow->openDocument(QUrl::fromLocalFile(url), flags); } } void KisApplication::checkAutosaveFiles() { - if (m_batchRun) return; + if (d->batchRun) return; // Check for autosave files from a previous run. There can be several, and // we want to offer a restore for every one. Including a nice thumbnail! QStringList filters; filters << QString(".krita-*-*-autosave.kra"); #ifdef Q_OS_WIN QDir dir = QDir::temp(); #else QDir dir = QDir::home(); #endif // all autosave files for our application QStringList autosaveFiles = dir.entryList(filters, QDir::Files | QDir::Hidden); // Allow the user to make their selection if (autosaveFiles.size() > 0) { if (d->splashScreen) { // hide the splashscreen to see the dialog d->splashScreen->hide(); } - m_autosaveDialog = new KisAutoSaveRecoveryDialog(autosaveFiles, activeWindow()); - QDialog::DialogCode result = (QDialog::DialogCode) m_autosaveDialog->exec(); + d->autosaveDialog = new KisAutoSaveRecoveryDialog(autosaveFiles, activeWindow()); + QDialog::DialogCode result = (QDialog::DialogCode) d->autosaveDialog->exec(); if (result == QDialog::Accepted) { - QStringList filesToRecover = m_autosaveDialog->recoverableFiles(); + QStringList filesToRecover = d->autosaveDialog->recoverableFiles(); Q_FOREACH (const QString &autosaveFile, autosaveFiles) { if (!filesToRecover.contains(autosaveFile)) { QFile::remove(dir.absolutePath() + "/" + autosaveFile); } } autosaveFiles = filesToRecover; } else { autosaveFiles.clear(); } if (autosaveFiles.size() > 0) { QList autosaveUrls; Q_FOREACH (const QString &autoSaveFile, autosaveFiles) { const QUrl url = QUrl::fromLocalFile(dir.absolutePath() + QLatin1Char('/') + autoSaveFile); autosaveUrls << url; } - if (m_mainWindow) { + if (d->mainWindow) { Q_FOREACH (const QUrl &url, autosaveUrls) { - KisMainWindow::OpenFlags flags = m_batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; - m_mainWindow->openDocument(url, flags | KisMainWindow::RecoveryFile); + KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; + d->mainWindow->openDocument(url, flags | KisMainWindow::RecoveryFile); } } } // cleanup - delete m_autosaveDialog; - m_autosaveDialog = nullptr; + delete d->autosaveDialog; + d->autosaveDialog = nullptr; } } bool KisApplication::createNewDocFromTemplate(const QString &fileName, KisMainWindow *mainWindow) { QString templatePath; const QUrl templateUrl = QUrl::fromLocalFile(fileName); if (QFile::exists(fileName)) { templatePath = templateUrl.toLocalFile(); dbgUI << "using full path..."; } else { QString desktopName(fileName); const QString templatesResourcePath = QStringLiteral("templates/"); QStringList paths = KoResourcePaths::findAllResources("data", templatesResourcePath + "*/" + desktopName); if (paths.isEmpty()) { paths = KoResourcePaths::findAllResources("data", templatesResourcePath + desktopName); } if (paths.isEmpty()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("No template found for: %1", desktopName)); } else if (paths.count() > 1) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Too many templates found for: %1", desktopName)); } else { templatePath = paths.at(0); } } if (!templatePath.isEmpty()) { QUrl templateBase; templateBase.setPath(templatePath); KDesktopFile templateInfo(templatePath); QString templateName = templateInfo.readUrl(); QUrl templateURL; templateURL.setPath(templateBase.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path() + '/' + templateName); - KisMainWindow::OpenFlags batchFlags = m_batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; + KisMainWindow::OpenFlags batchFlags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; if (mainWindow->openDocument(templateURL, KisMainWindow::Import | batchFlags)) { dbgUI << "Template loaded..."; return true; } else { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Template %1 failed to load.", templateURL.toDisplayString())); } } return false; } void KisApplication::clearConfig() { KIS_ASSERT_RECOVER_RETURN(qApp->thread() == QThread::currentThread()); KSharedConfigPtr config = KSharedConfig::openConfig(); // find user settings file bool createDir = false; QString kritarcPath = KoResourcePaths::locateLocal("config", "kritarc", createDir); QFile configFile(kritarcPath); if (configFile.exists()) { // clear file if (configFile.open(QFile::WriteOnly)) { configFile.close(); } else { QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Failed to clear %1\n\n" "Please make sure no other program is using the file and try again.", kritarcPath), QMessageBox::Ok, QMessageBox::Ok); } } // reload from disk; with the user file settings cleared, // this should load any default configuration files shipping with the program config->reparseConfiguration(); config->sync(); } void KisApplication::askClearConfig() { Qt::KeyboardModifiers mods = QApplication::queryKeyboardModifiers(); bool askClearConfig = (mods & Qt::ControlModifier) && (mods & Qt::ShiftModifier) && (mods & Qt::AltModifier); if (askClearConfig) { bool ok = QMessageBox::question(0, i18nc("@title:window", "Krita"), i18n("Do you want to clear the settings file?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes; if (ok) { clearConfig(); } } } diff --git a/libs/ui/KisApplication.h b/libs/ui/KisApplication.h index c1aa8310d4..54fed0058c 100644 --- a/libs/ui/KisApplication.h +++ b/libs/ui/KisApplication.h @@ -1,126 +1,124 @@ /* * Copyright (C) 1998, 1999 Torben Weis * Copyright (C) 2012 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_APPLICATION_H #define KIS_APPLICATION_H #include +#include #include #include "kritaui_export.h" -#include class KisMainWindow; class KisApplicationPrivate; class QWidget; class KisApplicationArguments; class KisAutoSaveRecoveryDialog; #include /** * @brief Base class for the %Krita app * * This class handles arguments given on the command line and * shows a generic about dialog for the Krita app. * * In addition it adds the standard directories where Krita * can find its images etc. * * If the last mainwindow becomes closed, KisApplication automatically * calls QApplication::quit. */ class KRITAUI_EXPORT KisApplication : public QtSingleApplication { Q_OBJECT public: /** * Creates an application object, adds some standard directories and * initializes kimgio. */ explicit KisApplication(const QString &key, int &argc, char **argv); /** * Destructor. */ ~KisApplication() override; /** * Call this to start the application. * * Parses command line arguments and creates the initial main windowss and docs * from them (or an empty doc if no cmd-line argument is specified ). * * You must call this method directly before calling QApplication::exec. * * It is valid behaviour not to call this method at all. In this case you * have to process your command line parameters by yourself. */ virtual bool start(const KisApplicationArguments &args); /** * Checks if user is holding ctrl+alt+shift keys and asks if the settings file should be cleared. * * Typically called during startup before reading the config. */ void askClearConfig(); /** * Tell KisApplication to show this splashscreen when you call start(); * when start returns, the splashscreen is hidden. Use KSplashScreen * to have the splash show correctly on Xinerama displays. */ void setSplashScreen(QWidget *splash); void setSplashScreenLoadingText(QString); void hideSplashScreen(); /// Overridden to handle exceptions from event handlers. bool notify(QObject *receiver, QEvent *event) override; void addResourceTypes(); void loadResources(); void loadResourceTags(); void loadPlugins(); void loadGuiPlugins(); void initializeGlobals(const KisApplicationArguments &args); public Q_SLOTS: void remoteArguments(QByteArray message, QObject*socket); void fileOpenRequested(const QString & url); private: /// @return the number of autosavefiles opened void checkAutosaveFiles(); bool createNewDocFromTemplate(const QString &fileName, KisMainWindow *m_mainWindow); void clearConfig(); private: - KisApplicationPrivate * const d; + class Private; + QScopedPointer d; class ResetStarting; friend class ResetStarting; - KisAutoSaveRecoveryDialog *m_autosaveDialog; - QPointer m_mainWindow; // The first mainwindow we create on startup - bool m_batchRun; }; #endif diff --git a/libs/ui/KisAsyncAnimationFramesSavingRenderer.cpp b/libs/ui/KisAsyncAnimationFramesSavingRenderer.cpp index 5478ba858e..442a9fbc6b 100644 --- a/libs/ui/KisAsyncAnimationFramesSavingRenderer.cpp +++ b/libs/ui/KisAsyncAnimationFramesSavingRenderer.cpp @@ -1,127 +1,127 @@ /* * Copyright (c) 2017 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 "KisAsyncAnimationFramesSavingRenderer.h" #include "kis_image.h" #include "kis_paint_device.h" #include "KisImportExportFilter.h" #include "KisPart.h" #include "KisDocument.h" #include "kis_time_range.h" #include "kis_paint_layer.h" struct KisAsyncAnimationFramesSavingRenderer::Private { Private(KisImageSP image, const KisTimeRange &_range, int _sequenceNumberingOffset, KisPropertiesConfigurationSP _exportConfiguration) : savingDoc(KisPart::instance()->createDocument()), range(_range), sequenceNumberingOffset(_sequenceNumberingOffset), exportConfiguration(_exportConfiguration) { - savingDoc->setAutoSaveDelay(0); + savingDoc->setInfiniteAutoSaveInterval(); savingDoc->setFileBatchMode(true); KisImageSP savingImage = new KisImage(savingDoc->createUndoStore(), image->bounds().width(), image->bounds().height(), image->colorSpace(), QString()); savingImage->setResolution(image->xRes(), image->yRes()); savingDoc->setCurrentImage(savingImage); KisPaintLayer* paintLayer = new KisPaintLayer(savingImage, "paint device", 255); savingImage->addNode(paintLayer, savingImage->root(), KisLayerSP(0)); savingDevice = paintLayer->paintDevice(); } QScopedPointer savingDoc; KisPaintDeviceSP savingDevice; KisTimeRange range; int sequenceNumberingOffset = 0; QString filenamePrefix; QString filenameSuffix; QByteArray outputMimeType; KisPropertiesConfigurationSP exportConfiguration; }; KisAsyncAnimationFramesSavingRenderer::KisAsyncAnimationFramesSavingRenderer(KisImageSP image, const QString &fileNamePrefix, const QString &fileNameSuffix, const QByteArray &outputMimeType, const KisTimeRange &range, const int sequenceNumberingOffset, KisPropertiesConfigurationSP exportConfiguration) : m_d(new Private(image, range, sequenceNumberingOffset, exportConfiguration)) { m_d->filenamePrefix = fileNamePrefix; m_d->filenameSuffix = fileNameSuffix; m_d->outputMimeType = outputMimeType; connect(this, SIGNAL(sigCompleteRegenerationInternal(int)), SLOT(notifyFrameCompleted(int))); connect(this, SIGNAL(sigCancelRegenerationInternal(int)), SLOT(notifyFrameCancelled(int))); } KisAsyncAnimationFramesSavingRenderer::~KisAsyncAnimationFramesSavingRenderer() { } void KisAsyncAnimationFramesSavingRenderer::frameCompletedCallback(int frame) { KisImageSP image = requestedImage(); if (!image) return; m_d->savingDevice->makeCloneFromRough(image->projection(), image->bounds()); KisTimeRange range(frame, 1); KisImportExportFilter::ConversionStatus status = KisImportExportFilter::OK; for (int i = range.start(); i <= range.end(); i++) { - QString frameNumber = QString("%1").arg(i - m_d->range.start() + m_d->sequenceNumberingOffset, 4, 10, QChar('0')); + QString frameNumber = QString("%1").arg(i + m_d->sequenceNumberingOffset, 4, 10, QChar('0')); QString filename = m_d->filenamePrefix + frameNumber + m_d->filenameSuffix; if (!m_d->savingDoc->exportDocumentSync(QUrl::fromLocalFile(filename), m_d->outputMimeType, m_d->exportConfiguration)) { status = KisImportExportFilter::InternalError; break; } } if (status == KisImportExportFilter::OK) { emit sigCompleteRegenerationInternal(frame); } else { emit sigCancelRegenerationInternal(frame); } } void KisAsyncAnimationFramesSavingRenderer::frameCancelledCallback(int frame) { notifyFrameCancelled(frame); } diff --git a/libs/ui/KisCloneDocumentStroke.cpp b/libs/ui/KisCloneDocumentStroke.cpp new file mode 100644 index 0000000000..a6290c91c9 --- /dev/null +++ b/libs/ui/KisCloneDocumentStroke.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018 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 "KisCloneDocumentStroke.h" + +#include "KisDocument.h" +#include "kis_layer_utils.h" + +struct KRITAIMAGE_NO_EXPORT KisCloneDocumentStroke::Private +{ + Private(KisDocument *_document) + : document(_document) + { + } + + KisDocument *document = 0; +}; + +KisCloneDocumentStroke::KisCloneDocumentStroke(KisDocument *document) + : KisSimpleStrokeStrategy("clone-document-stroke", kundo2_i18n("Clone Document")), + m_d(new Private(document)) +{ + setClearsRedoOnStart(false); + setRequestsOtherStrokesToEnd(false); // TODO: ??? + enableJob(JOB_INIT, true, KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE); + enableJob(JOB_FINISH, true, KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE); +} + +KisCloneDocumentStroke::~KisCloneDocumentStroke() +{ +} + +void KisCloneDocumentStroke::initStrokeCallback() +{ + KisLayerUtils::forceAllDelayedNodesUpdate(m_d->document->image()->root()); +} + +#include + +void KisCloneDocumentStroke::finishStrokeCallback() +{ + KisDocument *doc = m_d->document->clone(); + doc->moveToThread(qApp->thread()); + emit sigDocumentCloned(doc); +} diff --git a/libs/ui/dialogs/kis_about_application.h b/libs/ui/KisCloneDocumentStroke.h similarity index 55% copy from libs/ui/dialogs/kis_about_application.h copy to libs/ui/KisCloneDocumentStroke.h index 23a94d2dcb..d3d8d3ae25 100644 --- a/libs/ui/dialogs/kis_about_application.h +++ b/libs/ui/KisCloneDocumentStroke.h @@ -1,35 +1,45 @@ /* - * Copyright (c) 2014 Boudewijn Rempt + * Copyright (c) 2018 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_ABOUT_APPLICATION_H -#define KIS_ABOUT_APPLICATION_H +#ifndef KISCLONEDOCUMENTSTROKE_H +#define KISCLONEDOCUMENTSTROKE_H -#include +#include "kritaimage_export.h" +#include +#include "kis_simple_stroke_strategy.h" -class KisAboutApplication : public QDialog +class KisDocument; + +class KisCloneDocumentStroke : public QObject, public KisSimpleStrokeStrategy { Q_OBJECT public: - explicit KisAboutApplication(QWidget *parent = 0); + KisCloneDocumentStroke(KisDocument *document); + ~KisCloneDocumentStroke(); -Q_SIGNALS: + void initStrokeCallback() override; + void finishStrokeCallback() override; -public Q_SLOTS: +Q_SIGNALS: + void sigDocumentCloned(KisDocument *image); +private: + struct Private; + const QScopedPointer m_d; }; -#endif // KIS_ABOUT_APPLICATION_H +#endif // KISCLONEDOCUMENTSTROKE_H diff --git a/libs/ui/KisDocument.cpp b/libs/ui/KisDocument.cpp index 2b1a597315..e3946c0317 100644 --- a/libs/ui/KisDocument.cpp +++ b/libs/ui/KisDocument.cpp @@ -1,1732 +1,1791 @@ /* This file is part of the Krita project * * Copyright (C) 2014 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 "KisMainWindow.h" // XXX: remove #include // XXX: remove #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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Krita Image #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_layer_utils.h" // Local #include "KisViewManager.h" #include "kis_clipboard.h" #include "widgets/kis_custom_image_widget.h" #include "canvas/kis_canvas2.h" #include "flake/kis_shape_controller.h" #include "kis_statusbar.h" #include "widgets/kis_progress_widget.h" #include "kis_canvas_resource_provider.h" #include "KisResourceServerProvider.h" #include "kis_node_manager.h" #include "KisPart.h" #include "KisApplication.h" #include "KisDocument.h" #include "KisImportExportManager.h" #include "KisPart.h" #include "KisView.h" #include "kis_grid_config.h" #include "kis_guides_config.h" #include "kis_image_barrier_lock_adapter.h" #include "KisReferenceImagesLayer.h" #include #include "kis_config_notifier.h" #include "kis_async_action_feedback.h" +#include "KisCloneDocumentStroke.h" // Define the protocol used here for embedded documents' URL // This used to "store" but QUrl didn't like it, // so let's simply make it "tar" ! #define STORE_PROTOCOL "tar" // The internal path is a hack to make QUrl happy and for document children #define INTERNAL_PROTOCOL "intern" #define INTERNAL_PREFIX "intern:/" // Warning, keep it sync in koStore.cc #include using namespace std; namespace { constexpr int errorMessageTimeout = 5000; constexpr int successMessageTimeout = 1000; } /********************************************************** * * KisDocument * **********************************************************/ //static QString KisDocument::newObjectName() { static int s_docIFNumber = 0; QString name; name.setNum(s_docIFNumber++); name.prepend("document_"); return name; } class UndoStack : public KUndo2Stack { public: UndoStack(KisDocument *doc) : KUndo2Stack(doc), m_doc(doc) { } void setIndex(int idx) override { KisImageWSP image = this->image(); image->requestStrokeCancellation(); if(image->tryBarrierLock()) { KUndo2Stack::setIndex(idx); image->unlock(); } } void notifySetIndexChangedOneCommand() override { KisImageWSP image = this->image(); image->unlock(); /** * Some very weird commands may emit blocking signals to * the GUI (e.g. KisGuiContextCommand). Here is the best thing * we can do to avoid the deadlock */ while(!image->tryBarrierLock()) { QApplication::processEvents(); } } void undo() override { KisImageWSP image = this->image(); image->requestUndoDuringStroke(); if (image->tryUndoUnfinishedLod0Stroke() == UNDO_OK) { return; } if(image->tryBarrierLock()) { KUndo2Stack::undo(); image->unlock(); } } void redo() override { KisImageWSP image = this->image(); if(image->tryBarrierLock()) { KUndo2Stack::redo(); image->unlock(); } } private: KisImageWSP image() { KisImageWSP currentImage = m_doc->image(); Q_ASSERT(currentImage); return currentImage; } private: KisDocument *m_doc; }; class Q_DECL_HIDDEN KisDocument::Private { public: Private(KisDocument *q) : docInfo(new KoDocumentInfo(q)) // deleted by QObject , importExportManager(new KisImportExportManager(q)) // deleted manually + , autoSaveTimer(new QTimer(q)) , undoStack(new UndoStack(q)) // deleted by QObject , m_bAutoDetectedMime(false) , modified(false) , readwrite(true) , firstMod(QDateTime::currentDateTime()) , lastMod(firstMod) , nserver(new KisNameServer(1)) , imageIdleWatcher(2000 /*ms*/) , savingLock(&savingMutex) , batchMode(false) { if (QLocale().measurementSystem() == QLocale::ImperialSystem) { unit = KoUnit::Inch; } else { unit = KoUnit::Centimeter; } } Private(const Private &rhs, KisDocument *q) : docInfo(new KoDocumentInfo(*rhs.docInfo, q)) , unit(rhs.unit) , importExportManager(new KisImportExportManager(q)) , mimeType(rhs.mimeType) , outputMimeType(rhs.outputMimeType) + , autoSaveTimer(new QTimer(q)) , undoStack(new UndoStack(q)) , guidesConfig(rhs.guidesConfig) , m_bAutoDetectedMime(rhs.m_bAutoDetectedMime) , m_url(rhs.m_url) , m_file(rhs.m_file) , modified(rhs.modified) , readwrite(rhs.readwrite) , firstMod(rhs.firstMod) , lastMod(rhs.lastMod) , nserver(new KisNameServer(*rhs.nserver)) , preActivatedNode(0) // the node is from another hierarchy! , imageIdleWatcher(2000 /*ms*/) , assistants(rhs.assistants) // WARNING: assistants should not store pointers to the document! , gridConfig(rhs.gridConfig) , savingLock(&savingMutex) , batchMode(rhs.batchMode) { } ~Private() { // Don't delete m_d->shapeController because it's in a QObject hierarchy. delete nserver; } KoDocumentInfo *docInfo = 0; KoUnit unit; KisImportExportManager *importExportManager = 0; // The filter-manager to use when loading/saving [for the options] QByteArray mimeType; // The actual mimetype of the document QByteArray outputMimeType; // The mimetype to use when saving - QTimer autoSaveTimer; + QTimer *autoSaveTimer; QString lastErrorMessage; // see openFile() QString lastWarningMessage; int autoSaveDelay = 300; // in seconds, 0 to disable. bool modifiedAfterAutosave = false; bool isAutosaving = false; bool disregardAutosaveFailure = false; + int autoSaveFailureCount = 0; KUndo2Stack *undoStack = 0; KisGuidesConfig guidesConfig; bool m_bAutoDetectedMime = false; // whether the mimetype in the arguments was detected by the part itself QUrl m_url; // local url - the one displayed to the user. QString m_file; // Local file - the only one the part implementation should deal with. QMutex savingMutex; bool modified = false; bool readwrite = false; QDateTime firstMod; QDateTime lastMod; KisNameServer *nserver; KisImageSP image; KisImageSP savingImage; KisNodeWSP preActivatedNode; KisShapeController* shapeController = 0; KoShapeController* koShapeController = 0; KisIdleWatcher imageIdleWatcher; QScopedPointer imageIdleConnection; QList assistants; KisSharedPtr referenceImagesLayer; KisGridConfig gridConfig; StdLockableWrapper savingLock; bool modifiedWhileSaving = false; QScopedPointer backgroundSaveDocument; QPointer savingUpdater; QFuture childSavingFuture; KritaUtils::ExportFileJob backgroundSaveJob; bool isRecovered = false; bool batchMode { false }; void setImageAndInitIdleWatcher(KisImageSP _image) { image = _image; imageIdleWatcher.setTrackedImage(image); if (image) { imageIdleConnection.reset( new KisSignalAutoConnection( &imageIdleWatcher, SIGNAL(startedIdleMode()), image.data(), SLOT(explicitRegenerateLevelOfDetail()))); } } class StrippedSafeSavingLocker; }; class KisDocument::Private::StrippedSafeSavingLocker { public: StrippedSafeSavingLocker(QMutex *savingMutex, KisImageSP image) : m_locked(false) , m_image(image) , m_savingLock(savingMutex) , m_imageLock(image, true) { /** * Initial try to lock both objects. Locking the image guards * us from any image composition threads running in the * background, while savingMutex guards us from entering the * saving code twice by autosave and main threads. * * Since we are trying to lock multiple objects, so we should * do it in a safe manner. */ m_locked = std::try_lock(m_imageLock, m_savingLock) < 0; if (!m_locked) { m_image->requestStrokeEnd(); QApplication::processEvents(); // one more try... m_locked = std::try_lock(m_imageLock, m_savingLock) < 0; } } ~StrippedSafeSavingLocker() { if (m_locked) { m_imageLock.unlock(); m_savingLock.unlock(); } } bool successfullyLocked() const { return m_locked; } private: bool m_locked; KisImageSP m_image; StdLockableWrapper m_savingLock; KisImageBarrierLockAdapter m_imageLock; }; KisDocument::KisDocument() : d(new Private(this)) { connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); connect(d->undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(slotUndoStackCleanChanged(bool))); - connect(&d->autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave())); + connect(d->autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave())); setObjectName(newObjectName()); // preload the krita resources KisResourceServerProvider::instance(); d->shapeController = new KisShapeController(this, d->nserver), d->koShapeController = new KoShapeController(0, d->shapeController), slotConfigChanged(); } KisDocument::KisDocument(const KisDocument &rhs) : QObject(), d(new Private(*rhs.d, this)) { connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); connect(d->undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(slotUndoStackCleanChanged(bool))); - connect(&d->autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave())); + connect(d->autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave())); setObjectName(rhs.objectName()); d->shapeController = new KisShapeController(this, d->nserver), d->koShapeController = new KoShapeController(0, d->shapeController), slotConfigChanged(); // clone the image with keeping the GUIDs of the layers intact // NOTE: we expect the image to be locked! setCurrentImage(rhs.image()->clone(true), false); if (rhs.d->preActivatedNode) { // since we clone uuid's, we can use them for lacating new // nodes. Otherwise we would need to use findSymmetricClone() d->preActivatedNode = KisLayerUtils::findNodeByUuid(d->image->root(), rhs.d->preActivatedNode->uuid()); } } KisDocument::~KisDocument() { // wait until all the pending operations are in progress waitForSavingToComplete(); /** * Push a timebomb, which will try to release the memory after * the document has been deleted */ KisPaintDevice::createMemoryReleaseObject()->deleteLater(); - d->autoSaveTimer.disconnect(this); - d->autoSaveTimer.stop(); + d->autoSaveTimer->disconnect(this); + d->autoSaveTimer->stop(); delete d->importExportManager; // Despite being QObject they needs to be deleted before the image delete d->shapeController; delete d->koShapeController; if (d->image) { d->image->notifyAboutToBeDeleted(); /** * WARNING: We should wait for all the internal image jobs to * finish before entering KisImage's destructor. The problem is, * while execution of KisImage::~KisImage, all the weak shared * pointers pointing to the image enter an inconsistent * state(!). The shared counter is already zero and destruction * has started, but the weak reference doesn't know about it, * because KisShared::~KisShared hasn't been executed yet. So all * the threads running in background and having weak pointers will * enter the KisImage's destructor as well. */ d->image->requestStrokeCancellation(); d->image->waitForDone(); // clear undo commands that can still point to the image d->undoStack->clear(); d->image->waitForDone(); KisImageWSP sanityCheckPointer = d->image; Q_UNUSED(sanityCheckPointer); // The following line trigger the deletion of the image d->image.clear(); // check if the image has actually been deleted KIS_SAFE_ASSERT_RECOVER_NOOP(!sanityCheckPointer.isValid()); } delete d; } bool KisDocument::reload() { // XXX: reimplement! return false; } KisDocument *KisDocument::clone() { return new KisDocument(*this); } bool KisDocument::exportDocumentImpl(const KritaUtils::ExportFileJob &job, KisPropertiesConfigurationSP exportConfiguration) { QFileInfo filePathInfo(job.filePath); if (filePathInfo.exists() && !filePathInfo.isWritable()) { slotCompleteSavingDocument(job, KisImportExportFilter::CreationError, i18n("%1 cannot be written to. Please save under a different name.", job.filePath)); return false; } KisConfig cfg; if (cfg.backupFile() && filePathInfo.exists()) { KBackup::backupFile(job.filePath); } KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!job.mimeType.isEmpty(), false); const QString actionName = job.flags & KritaUtils::SaveIsExporting ? i18n("Exporting Document...") : i18n("Saving Document..."); bool started = initiateSavingInBackground(actionName, this, SLOT(slotCompleteSavingDocument(KritaUtils::ExportFileJob, KisImportExportFilter::ConversionStatus,QString)), job, exportConfiguration); if (!started) { emit canceled(QString()); } return started; } bool KisDocument::exportDocument(const QUrl &url, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration) { using namespace KritaUtils; SaveFlags flags = SaveIsExporting; if (showWarnings) { flags |= SaveShowWarnings; } return exportDocumentImpl(KritaUtils::ExportFileJob(url.toLocalFile(), mimeType, flags), exportConfiguration); } bool KisDocument::saveAs(const QUrl &url, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration) { using namespace KritaUtils; return exportDocumentImpl(ExportFileJob(url.toLocalFile(), mimeType, showWarnings ? SaveShowWarnings : SaveNone), exportConfiguration); } bool KisDocument::save(bool showWarnings, KisPropertiesConfigurationSP exportConfiguration) { return saveAs(url(), mimeType(), showWarnings, exportConfiguration); } QByteArray KisDocument::serializeToNativeByteArray() { QByteArray byteArray; QBuffer buffer(&byteArray); QScopedPointer filter(KisImportExportManager::filterForMimeType(nativeFormatMimeType(), KisImportExportManager::Export)); filter->setBatchMode(true); filter->setMimeType(nativeFormatMimeType()); Private::StrippedSafeSavingLocker locker(&d->savingMutex, d->image); if (!locker.successfullyLocked()) { return byteArray; } d->savingImage = d->image; if (filter->convert(this, &buffer) != KisImportExportFilter::OK) { qWarning() << "serializeToByteArray():: Could not export to our native format"; } return byteArray; } void KisDocument::slotCompleteSavingDocument(const KritaUtils::ExportFileJob &job, KisImportExportFilter::ConversionStatus status, const QString &errorMessage) { if (status == KisImportExportFilter::UserCancelled) return; const QString fileName = QFileInfo(job.filePath).fileName(); if (status != KisImportExportFilter::OK) { emit statusBarMessage(i18nc("%1 --- failing file name, %2 --- error message", "Error during saving %1: %2", fileName, exportErrorToUserMessage(status, errorMessage)), errorMessageTimeout); if (!fileBatchMode()) { const QString filePath = job.filePath; QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save %1\nReason: %2", filePath, errorMessage)); } } else { if (!(job.flags & KritaUtils::SaveIsExporting)) { setUrl(QUrl::fromLocalFile(job.filePath)); setLocalFilePath(job.filePath); setMimeType(job.mimeType); updateEditingTime(true); if (!d->modifiedWhileSaving) { d->undoStack->setClean(); } setRecovered(false); removeAutoSaveFiles(); } emit completed(); emit sigSavingFinished(); emit statusBarMessage(i18n("Finished saving %1", fileName), successMessageTimeout); } } QByteArray KisDocument::mimeType() const { return d->mimeType; } void KisDocument::setMimeType(const QByteArray & mimeType) { d->mimeType = mimeType; } bool KisDocument::fileBatchMode() const { return d->batchMode; } void KisDocument::setFileBatchMode(const bool batchMode) { d->batchMode = batchMode; } KisDocument* KisDocument::lockAndCloneForSaving() { // force update of all the asynchronous nodes before cloning QApplication::processEvents(); KisLayerUtils::forceAllDelayedNodesUpdate(d->image->root()); KisMainWindow *window = KisPart::instance()->currentMainwindow(); if (window) { if (window->viewManager()) { if (!window->viewManager()->blockUntilOperationsFinished(d->image)) { return 0; } } } Private::StrippedSafeSavingLocker locker(&d->savingMutex, d->image); if (!locker.successfullyLocked()) { return 0; } return new KisDocument(*this); } bool KisDocument::exportDocumentSync(const QUrl &url, const QByteArray &mimeType, KisPropertiesConfigurationSP exportConfiguration) { Private::StrippedSafeSavingLocker locker(&d->savingMutex, d->image); if (!locker.successfullyLocked()) { return false; } d->savingImage = d->image; const QString fileName = url.toLocalFile(); KisImportExportFilter::ConversionStatus status = d->importExportManager-> exportDocument(fileName, fileName, mimeType, false, exportConfiguration); d->savingImage = 0; return status == KisImportExportFilter::OK; } bool KisDocument::initiateSavingInBackground(const QString actionName, const QObject *receiverObject, const char *receiverMethod, const KritaUtils::ExportFileJob &job, KisPropertiesConfigurationSP exportConfiguration) +{ + return initiateSavingInBackground(actionName, receiverObject, receiverMethod, + job, exportConfiguration, std::unique_ptr()); +} + +bool KisDocument::initiateSavingInBackground(const QString actionName, + const QObject *receiverObject, const char *receiverMethod, + const KritaUtils::ExportFileJob &job, + KisPropertiesConfigurationSP exportConfiguration, + std::unique_ptr &&optionalClonedDocument) { KIS_ASSERT_RECOVER_RETURN_VALUE(job.isValid(), false); - QScopedPointer clonedDocument(lockAndCloneForSaving()); + QScopedPointer clonedDocument; + + if (!optionalClonedDocument) { + clonedDocument.reset(lockAndCloneForSaving()); + } else { + clonedDocument.reset(optionalClonedDocument.release()); + } // we block saving until the current saving is finished! if (!clonedDocument || !d->savingMutex.tryLock()) { return false; } KIS_ASSERT_RECOVER_RETURN_VALUE(!d->backgroundSaveDocument, false); KIS_ASSERT_RECOVER_RETURN_VALUE(!d->backgroundSaveJob.isValid(), false); d->backgroundSaveDocument.reset(clonedDocument.take()); d->backgroundSaveJob = job; d->modifiedWhileSaving = false; if (d->backgroundSaveJob.flags & KritaUtils::SaveInAutosaveMode) { d->backgroundSaveDocument->d->isAutosaving = true; } connect(d->backgroundSaveDocument.data(), SIGNAL(sigBackgroundSavingFinished(KisImportExportFilter::ConversionStatus, const QString&)), this, SLOT(slotChildCompletedSavingInBackground(KisImportExportFilter::ConversionStatus, const QString&))); connect(this, SIGNAL(sigCompleteBackgroundSaving(KritaUtils::ExportFileJob,KisImportExportFilter::ConversionStatus,QString)), receiverObject, receiverMethod, Qt::UniqueConnection); bool started = d->backgroundSaveDocument->startExportInBackground(actionName, job.filePath, job.filePath, job.mimeType, job.flags & KritaUtils::SaveShowWarnings, exportConfiguration); if (!started) { // the state should have been deinitialized in slotChildCompletedSavingInBackground() KIS_SAFE_ASSERT_RECOVER (!d->backgroundSaveDocument && !d->backgroundSaveJob.isValid()) { d->backgroundSaveDocument.take()->deleteLater(); d->savingMutex.unlock(); d->backgroundSaveJob = KritaUtils::ExportFileJob(); } } return started; } void KisDocument::slotChildCompletedSavingInBackground(KisImportExportFilter::ConversionStatus status, const QString &errorMessage) { KIS_SAFE_ASSERT_RECOVER(!d->savingMutex.tryLock()) { d->savingMutex.unlock(); return; } KIS_SAFE_ASSERT_RECOVER_RETURN(d->backgroundSaveDocument); if (d->backgroundSaveJob.flags & KritaUtils::SaveInAutosaveMode) { d->backgroundSaveDocument->d->isAutosaving = false; } d->backgroundSaveDocument.take()->deleteLater(); d->savingMutex.unlock(); KIS_SAFE_ASSERT_RECOVER_RETURN(d->backgroundSaveJob.isValid()); const KritaUtils::ExportFileJob job = d->backgroundSaveJob; d->backgroundSaveJob = KritaUtils::ExportFileJob(); emit sigCompleteBackgroundSaving(job, status, errorMessage); } -void KisDocument::slotAutoSave() +void KisDocument::slotAutoSaveImpl(std::unique_ptr &&optionalClonedDocument) { if (!d->modified || !d->modifiedAfterAutosave) return; const QString autoSaveFileName = generateAutoSaveFileName(localFilePath()); emit statusBarMessage(i18n("Autosaving... %1", autoSaveFileName), successMessageTimeout); + const bool hadClonedDocument = bool(optionalClonedDocument); bool started = false; - if (d->image->isIdle()) { + if (d->image->isIdle() || hadClonedDocument) { started = initiateSavingInBackground(i18n("Autosaving..."), this, SLOT(slotCompleteAutoSaving(KritaUtils::ExportFileJob, KisImportExportFilter::ConversionStatus, const QString&)), KritaUtils::ExportFileJob(autoSaveFileName, nativeFormatMimeType(), KritaUtils::SaveIsExporting | KritaUtils::SaveInAutosaveMode), - 0); + 0, + std::move(optionalClonedDocument)); } else { emit statusBarMessage(i18n("Autosaving postponed: document is busy..."), errorMessageTimeout); } - if (!started) { - const int emergencyAutoSaveInterval = 10; // sec - setAutoSaveDelay(emergencyAutoSaveInterval); + if (!started && !hadClonedDocument && d->autoSaveFailureCount >= 3) { + KisCloneDocumentStroke *stroke = new KisCloneDocumentStroke(this); + connect(stroke, SIGNAL(sigDocumentCloned(KisDocument*)), + this, SLOT(slotInitiateAsyncAutosaving(KisDocument*)), + Qt::BlockingQueuedConnection); + + KisStrokeId strokeId = d->image->startStroke(stroke); + d->image->endStroke(strokeId); + + setInfiniteAutoSaveInterval(); + + } else if (!started) { + setEmergencyAutoSaveInterval(); } else { d->modifiedAfterAutosave = false; } } +void KisDocument::slotAutoSave() +{ + slotAutoSaveImpl(std::unique_ptr()); +} + +void KisDocument::slotInitiateAsyncAutosaving(KisDocument *clonedDocument) +{ + slotAutoSaveImpl(std::unique_ptr(clonedDocument)); +} + void KisDocument::slotCompleteAutoSaving(const KritaUtils::ExportFileJob &job, KisImportExportFilter::ConversionStatus status, const QString &errorMessage) { Q_UNUSED(job); const QString fileName = QFileInfo(job.filePath).fileName(); if (status != KisImportExportFilter::OK) { - const int emergencyAutoSaveInterval = 10; // sec - setAutoSaveDelay(emergencyAutoSaveInterval); + setEmergencyAutoSaveInterval(); emit statusBarMessage(i18nc("%1 --- failing file name, %2 --- error message", "Error during autosaving %1: %2", fileName, exportErrorToUserMessage(status, errorMessage)), errorMessageTimeout); } else { KisConfig cfg; d->autoSaveDelay = cfg.autoSaveInterval(); if (!d->modifiedWhileSaving) { - d->autoSaveTimer.stop(); // until the next change + d->autoSaveTimer->stop(); // until the next change + d->autoSaveFailureCount = 0; } else { - setAutoSaveDelay(d->autoSaveDelay); // restart the timer + setNormalAutoSaveInterval(); } emit statusBarMessage(i18n("Finished autosaving %1", fileName), successMessageTimeout); } } bool KisDocument::startExportInBackground(const QString &actionName, const QString &location, const QString &realLocation, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration) { d->savingImage = d->image; KisMainWindow *window = KisPart::instance()->currentMainwindow(); if (window) { if (window->viewManager()) { d->savingUpdater = window->viewManager()->createThreadedUpdater(actionName); d->importExportManager->setUpdater(d->savingUpdater); } } KisImportExportFilter::ConversionStatus initializationStatus; d->childSavingFuture = d->importExportManager->exportDocumentAsyc(location, realLocation, mimeType, initializationStatus, showWarnings, exportConfiguration); if (initializationStatus != KisImportExportFilter::ConversionStatus::OK) { if (d->savingUpdater) { d->savingUpdater->cancel(); } d->savingImage.clear(); emit sigBackgroundSavingFinished(initializationStatus, this->errorMessage()); return false; } typedef QFutureWatcher StatusWatcher; StatusWatcher *watcher = new StatusWatcher(); watcher->setFuture(d->childSavingFuture); connect(watcher, SIGNAL(finished()), SLOT(finishExportInBackground())); connect(watcher, SIGNAL(finished()), watcher, SLOT(deleteLater())); return true; } void KisDocument::finishExportInBackground() { KIS_SAFE_ASSERT_RECOVER(d->childSavingFuture.isFinished()) { emit sigBackgroundSavingFinished(KisImportExportFilter::InternalError, ""); return; } KisImportExportFilter::ConversionStatus status = d->childSavingFuture.result(); const QString errorMessage = this->errorMessage(); d->savingImage.clear(); d->childSavingFuture = QFuture(); d->lastErrorMessage.clear(); if (d->savingUpdater) { d->savingUpdater->setProgress(100); } emit sigBackgroundSavingFinished(status, errorMessage); } void KisDocument::setReadWrite(bool readwrite) { d->readwrite = readwrite; - setAutoSaveDelay(d->autoSaveDelay); + setNormalAutoSaveInterval(); Q_FOREACH (KisMainWindow *mainWindow, KisPart::instance()->mainWindows()) { mainWindow->setReadWrite(readwrite); } } void KisDocument::setAutoSaveDelay(int delay) { - //qDebug() << "setting autosave delay from" << d->autoSaveDelay << "to" << delay; - d->autoSaveDelay = delay; - if (isReadWrite() && d->autoSaveDelay > 0) { - d->autoSaveTimer.start(d->autoSaveDelay * 1000); - } - else { - d->autoSaveTimer.stop(); + if (isReadWrite() && delay > 0) { + d->autoSaveTimer->start(delay * 1000); + } else { + d->autoSaveTimer->stop(); } } +void KisDocument::setNormalAutoSaveInterval() +{ + setAutoSaveDelay(d->autoSaveDelay); + d->autoSaveFailureCount = 0; +} + +void KisDocument::setEmergencyAutoSaveInterval() +{ + const int emergencyAutoSaveInterval = 10; /* sec */ + setAutoSaveDelay(emergencyAutoSaveInterval); + d->autoSaveFailureCount++; +} + +void KisDocument::setInfiniteAutoSaveInterval() +{ + setAutoSaveDelay(-1); +} + KoDocumentInfo *KisDocument::documentInfo() const { return d->docInfo; } bool KisDocument::isModified() const { return d->modified; } QPixmap KisDocument::generatePreview(const QSize& size) { KisImageSP image = d->image; if (d->savingImage) image = d->savingImage; if (image) { QRect bounds = image->bounds(); QSize newSize = bounds.size(); newSize.scale(size, Qt::KeepAspectRatio); QPixmap px = QPixmap::fromImage(image->convertToQImage(newSize, 0)); if (px.size() == QSize(0,0)) { px = QPixmap(newSize); QPainter gc(&px); QBrush checkBrush = QBrush(KisCanvasWidgetBase::createCheckersImage(newSize.width() / 5)); gc.fillRect(px.rect(), checkBrush); gc.end(); } return px; } return QPixmap(size); } QString KisDocument::generateAutoSaveFileName(const QString & path) const { QString retval; // Using the extension allows to avoid relying on the mime magic when opening const QString extension (".kra"); QRegularExpression autosavePattern("^\\..+-autosave.kra$"); QFileInfo fi(path); QString dir = fi.absolutePath(); QString filename = fi.fileName(); if (path.isEmpty() || autosavePattern.match(filename).hasMatch()) { // Never saved? #ifdef Q_OS_WIN // On Windows, use the temp location (https://bugs.kde.org/show_bug.cgi?id=314921) retval = QString("%1%2.%3-%4-%5-autosave%6").arg(QDir::tempPath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension); #else // On Linux, use a temp file in $HOME then. Mark it with the pid so two instances don't overwrite each other's autosave file retval = QString("%1%2.%3-%4-%5-autosave%6").arg(QDir::homePath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension); #endif } else { retval = QString("%1%2.%3-autosave%4").arg(dir).arg(QDir::separator()).arg(filename).arg(extension); } //qDebug() << "generateAutoSaveFileName() for path" << path << ":" << retval; return retval; } bool KisDocument::importDocument(const QUrl &_url) { bool ret; dbgUI << "url=" << _url.url(); // open... ret = openUrl(_url); // reset url & m_file (kindly? set by KisParts::openUrl()) to simulate a // File --> Import if (ret) { dbgUI << "success, resetting url"; resetURL(); setTitleModified(); } return ret; } bool KisDocument::openUrl(const QUrl &_url, OpenFlags flags) { if (!_url.isLocalFile()) { return false; } dbgUI << "url=" << _url.url(); d->lastErrorMessage.clear(); // Reimplemented, to add a check for autosave files and to improve error reporting if (!_url.isValid()) { d->lastErrorMessage = i18n("Malformed URL\n%1", _url.url()); // ## used anywhere ? return false; } QUrl url(_url); bool autosaveOpened = false; if (url.isLocalFile() && !fileBatchMode()) { QString file = url.toLocalFile(); QString asf = generateAutoSaveFileName(file); if (QFile::exists(asf)) { KisApplication *kisApp = static_cast(qApp); kisApp->hideSplashScreen(); //dbgUI <<"asf=" << asf; // ## TODO compare timestamps ? int res = QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("An autosaved file exists for this document.\nDo you want to open the autosaved file instead?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes); switch (res) { case QMessageBox::Yes : url.setPath(asf); autosaveOpened = true; break; case QMessageBox::No : QFile::remove(asf); break; default: // Cancel return false; } } } bool ret = openUrlInternal(url); if (autosaveOpened || flags & RecoveryFile) { setReadWrite(true); // enable save button setModified(true); setRecovered(true); } else { if (!(flags & DontAddToRecent)) { KisPart::instance()->addRecentURLToAllMainWindows(_url); } if (ret) { // Detect readonly local-files; remote files are assumed to be writable QFileInfo fi(url.toLocalFile()); setReadWrite(fi.isWritable()); } setRecovered(false); } return ret; } class DlgLoadMessages : public KoDialog { public: DlgLoadMessages(const QString &title, const QString &message, const QStringList &warnings) { setWindowTitle(title); setWindowIcon(KisIconUtils::loadIcon("warning")); QWidget *page = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout(page); QHBoxLayout *hlayout = new QHBoxLayout(); QLabel *labelWarning= new QLabel(); labelWarning->setPixmap(KisIconUtils::loadIcon("warning").pixmap(32, 32)); hlayout->addWidget(labelWarning); hlayout->addWidget(new QLabel(message)); layout->addLayout(hlayout); QTextBrowser *browser = new QTextBrowser(); QString warning = "

"; if (warnings.size() == 1) { warning += " Reason:

"; } else { warning += " Reasons:

"; } warning += "

    "; Q_FOREACH(const QString &w, warnings) { warning += "\n
  • " + w + "
  • "; } warning += "
"; browser->setHtml(warning); browser->setMinimumHeight(200); browser->setMinimumWidth(400); layout->addWidget(browser); setMainWidget(page); setButtons(KoDialog::Ok); resize(minimumSize()); } }; bool KisDocument::openFile() { //dbgUI <<"for" << localFilePath(); if (!QFile::exists(localFilePath())) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("File %1 does not exist.", localFilePath())); return false; } QString filename = localFilePath(); QString typeName = mimeType(); if (typeName.isEmpty()) { typeName = KisMimeDatabase::mimeTypeForFile(filename); } //qDebug() << "mimetypes 4:" << typeName; // Allow to open backup files, don't keep the mimetype application/x-trash. if (typeName == "application/x-trash") { QString path = filename; while (path.length() > 0) { path.chop(1); typeName = KisMimeDatabase::mimeTypeForFile(path); //qDebug() << "\t" << path << typeName; if (!typeName.isEmpty()) { break; } } //qDebug() << "chopped" << filename << "to" << path << "Was trash, is" << typeName; } dbgUI << localFilePath() << "type:" << typeName; KisMainWindow *window = KisPart::instance()->currentMainwindow(); if (window && window->viewManager()) { KoUpdaterPtr updater = window->viewManager()->createUnthreadedUpdater(i18n("Opening document")); d->importExportManager->setUpdater(updater); } KisImportExportFilter::ConversionStatus status; status = d->importExportManager->importDocument(localFilePath(), typeName); if (status != KisImportExportFilter::OK) { QString msg = KisImportExportFilter::conversionStatusString(status); if (!msg.isEmpty()) { DlgLoadMessages dlg(i18nc("@title:window", "Krita"), i18n("Could not open %2.\nReason: %1.", msg, prettyPathOrUrl()), errorMessage().split("\n") + warningMessage().split("\n")); dlg.exec(); } return false; } else if (!warningMessage().isEmpty()) { DlgLoadMessages dlg(i18nc("@title:window", "Krita"), i18n("There were problems opening %1.", prettyPathOrUrl()), warningMessage().split("\n")); dlg.exec(); setUrl(QUrl()); } setMimeTypeAfterLoading(typeName); emit sigLoadingFinished(); undoStack()->clear(); return true; } // shared between openFile and koMainWindow's "create new empty document" code void KisDocument::setMimeTypeAfterLoading(const QString& mimeType) { d->mimeType = mimeType.toLatin1(); d->outputMimeType = d->mimeType; } bool KisDocument::loadNativeFormat(const QString & file_) { return openUrl(QUrl::fromLocalFile(file_)); } void KisDocument::setModified() { d->modified = true; } void KisDocument::setModified(bool mod) { if (mod) { updateEditingTime(false); } if (d->isAutosaving) // ignore setModified calls due to autosaving return; if ( !d->readwrite && d->modified ) { errKrita << "Can't set a read-only document to 'modified' !" << endl; return; } //dbgUI<<" url:" << url.path(); //dbgUI<<" mod="<docInfo->aboutInfo("editing-time").toInt() + d->firstMod.secsTo(d->lastMod))); d->firstMod = now; } else if (firstModDelta > 60 || forceStoreElapsed) { d->docInfo->setAboutInfo("editing-time", QString::number(d->docInfo->aboutInfo("editing-time").toInt() + firstModDelta)); d->firstMod = now; } d->lastMod = now; } QString KisDocument::prettyPathOrUrl() const { QString _url(url().toDisplayString()); #ifdef Q_OS_WIN if (url().isLocalFile()) { _url = QDir::toNativeSeparators(_url); } #endif return _url; } // Get caption from document info (title(), in about page) QString KisDocument::caption() const { QString c; const QString _url(url().fileName()); // if URL is empty...it is probably an unsaved file if (_url.isEmpty()) { c = " [" + i18n("Not Saved") + "] "; } else { c = _url; // Fall back to document URL } return c; } void KisDocument::setTitleModified() { emit titleModified(caption(), isModified()); } QDomDocument KisDocument::createDomDocument(const QString& tagName, const QString& version) const { return createDomDocument("krita", tagName, version); } //static QDomDocument KisDocument::createDomDocument(const QString& appName, const QString& tagName, const QString& version) { QDomImplementation impl; QString url = QString("http://www.calligra.org/DTD/%1-%2.dtd").arg(appName).arg(version); QDomDocumentType dtype = impl.createDocumentType(tagName, QString("-//KDE//DTD %1 %2//EN").arg(appName).arg(version), url); // The namespace URN doesn't need to include the version number. QString namespaceURN = QString("http://www.calligra.org/DTD/%1").arg(appName); QDomDocument doc = impl.createDocument(namespaceURN, tagName, dtype); doc.insertBefore(doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""), doc.documentElement()); return doc; } bool KisDocument::isNativeFormat(const QByteArray& mimetype) const { if (mimetype == nativeFormatMimeType()) return true; return extraNativeMimeTypes().contains(mimetype); } void KisDocument::setErrorMessage(const QString& errMsg) { d->lastErrorMessage = errMsg; } QString KisDocument::errorMessage() const { return d->lastErrorMessage; } void KisDocument::setWarningMessage(const QString& warningMsg) { d->lastWarningMessage = warningMsg; } QString KisDocument::warningMessage() const { return d->lastWarningMessage; } void KisDocument::removeAutoSaveFiles() { //qDebug() << "removeAutoSaveFiles"; // Eliminate any auto-save file QString asf = generateAutoSaveFileName(localFilePath()); // the one in the current dir //qDebug() << "\tfilename:" << asf << "exists:" << QFile::exists(asf); if (QFile::exists(asf)) { //qDebug() << "\tremoving autosavefile" << asf; QFile::remove(asf); } asf = generateAutoSaveFileName(QString()); // and the one in $HOME //qDebug() << "Autsavefile in $home" << asf; if (QFile::exists(asf)) { //qDebug() << "\tremoving autsavefile 2" << asf; QFile::remove(asf); } } KoUnit KisDocument::unit() const { return d->unit; } void KisDocument::setUnit(const KoUnit &unit) { if (d->unit != unit) { d->unit = unit; emit unitChanged(unit); } } KUndo2Stack *KisDocument::undoStack() { return d->undoStack; } KisImportExportManager *KisDocument::importExportManager() const { return d->importExportManager; } void KisDocument::addCommand(KUndo2Command *command) { if (command) d->undoStack->push(command); } void KisDocument::beginMacro(const KUndo2MagicString & text) { d->undoStack->beginMacro(text); } void KisDocument::endMacro() { d->undoStack->endMacro(); } void KisDocument::slotUndoStackCleanChanged(bool value) { setModified(!value); } void KisDocument::slotConfigChanged() { KisConfig cfg; d->undoStack->setUndoLimit(cfg.undoStackLimit()); - setAutoSaveDelay(cfg.autoSaveInterval()); + d->autoSaveDelay = cfg.autoSaveInterval(); + setNormalAutoSaveInterval(); } void KisDocument::clearUndoHistory() { d->undoStack->clear(); } KisGridConfig KisDocument::gridConfig() const { return d->gridConfig; } void KisDocument::setGridConfig(const KisGridConfig &config) { d->gridConfig = config; } const KisGuidesConfig& KisDocument::guidesConfig() const { return d->guidesConfig; } void KisDocument::setGuidesConfig(const KisGuidesConfig &data) { if (d->guidesConfig == data) return; d->guidesConfig = data; emit sigGuidesConfigChanged(d->guidesConfig); } void KisDocument::resetURL() { setUrl(QUrl()); setLocalFilePath(QString()); } KoDocumentInfoDlg *KisDocument::createDocumentInfoDialog(QWidget *parent, KoDocumentInfo *docInfo) const { return new KoDocumentInfoDlg(parent, docInfo); } bool KisDocument::isReadWrite() const { return d->readwrite; } QUrl KisDocument::url() const { return d->m_url; } bool KisDocument::closeUrl(bool promptToSave) { if (promptToSave) { if ( isReadWrite() && isModified()) { Q_FOREACH (KisView *view, KisPart::instance()->views()) { if (view && view->document() == this) { if (!view->queryClose()) { return false; } } } } } // Not modified => ok and delete temp file. d->mimeType = QByteArray(); // It always succeeds for a read-only part, // but the return value exists for reimplementations // (e.g. pressing cancel for a modified read-write part) return true; } void KisDocument::setUrl(const QUrl &url) { d->m_url = url; } QString KisDocument::localFilePath() const { return d->m_file; } void KisDocument::setLocalFilePath( const QString &localFilePath ) { d->m_file = localFilePath; } bool KisDocument::openUrlInternal(const QUrl &url) { if ( !url.isValid() ) return false; if (d->m_bAutoDetectedMime) { d->mimeType = QByteArray(); d->m_bAutoDetectedMime = false; } QByteArray mimetype = d->mimeType; if ( !closeUrl() ) return false; d->mimeType = mimetype; setUrl(url); d->m_file.clear(); if (d->m_url.isLocalFile()) { d->m_file = d->m_url.toLocalFile(); bool ret; // set the mimetype only if it was not already set (for example, by the host application) if (d->mimeType.isEmpty()) { // get the mimetype of the file // using findByUrl() to avoid another string -> url conversion QString mime = KisMimeDatabase::mimeTypeForFile(d->m_url.toLocalFile()); d->mimeType = mime.toLocal8Bit(); d->m_bAutoDetectedMime = true; } setUrl(d->m_url); ret = openFile(); if (ret) { emit completed(); } else { emit canceled(QString()); } return ret; } return false; } bool KisDocument::newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace* cs, const KoColor &bgColor, bool backgroundAsLayer, int numberOfLayers, const QString &description, const double imageResolution) { Q_ASSERT(cs); KisConfig cfg; KisImageSP image; KisPaintLayerSP layer; if (!cs) return false; QApplication::setOverrideCursor(Qt::BusyCursor); image = new KisImage(createUndoStore(), width, height, cs, name); Q_CHECK_PTR(image); connect(image, SIGNAL(sigImageModified()), this, SLOT(setImageModified()), Qt::UniqueConnection); image->setResolution(imageResolution, imageResolution); image->assignImageProfile(cs->profile()); documentInfo()->setAboutInfo("title", name); documentInfo()->setAboutInfo("abstract", description); layer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, cs); Q_CHECK_PTR(layer); if (backgroundAsLayer) { image->setDefaultProjectionColor(KoColor(cs)); if (bgColor.opacityU8() == OPACITY_OPAQUE_U8) { layer->paintDevice()->setDefaultPixel(bgColor); } else { // Hack: with a semi-transparent background color, the projection isn't composited right if we just set the default pixel KisFillPainter painter; painter.begin(layer->paintDevice()); painter.fillRect(0, 0, width, height, bgColor, bgColor.opacityU8()); } } else { image->setDefaultProjectionColor(bgColor); } layer->setDirty(QRect(0, 0, width, height)); image->addNode(layer.data(), image->rootLayer().data()); setCurrentImage(image); for(int i = 1; i < numberOfLayers; ++i) { KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), OPACITY_OPAQUE_U8, cs); image->addNode(layer, image->root(), i); layer->setDirty(QRect(0, 0, width, height)); } cfg.defImageWidth(width); cfg.defImageHeight(height); cfg.defImageResolution(imageResolution); cfg.defColorModel(image->colorSpace()->colorModelId().id()); cfg.setDefaultColorDepth(image->colorSpace()->colorDepthId().id()); cfg.defColorProfile(image->colorSpace()->profile()->name()); QApplication::restoreOverrideCursor(); return true; } bool KisDocument::isSaving() const { const bool result = d->savingMutex.tryLock(); if (result) { d->savingMutex.unlock(); } return !result; } void KisDocument::waitForSavingToComplete() { if (isSaving()) { KisAsyncActionFeedback f(i18nc("progress dialog message when the user closes the document that is being saved", "Waiting for saving to complete..."), 0); f.waitForMutex(&d->savingMutex); } } KoShapeBasedDocumentBase *KisDocument::shapeController() const { return d->shapeController; } KoShapeLayer* KisDocument::shapeForNode(KisNodeSP layer) const { return d->shapeController->shapeForNode(layer); } QList KisDocument::assistants() const { return d->assistants; } void KisDocument::setAssistants(const QList &value) { d->assistants = value; } KisSharedPtr KisDocument::createReferenceImagesLayer(KisImageSP targetImage) { if (!d->referenceImagesLayer) { if (targetImage.isNull()) targetImage = d->image; d->referenceImagesLayer = new KisReferenceImagesLayer(shapeController(), targetImage); targetImage->addNode(d->referenceImagesLayer, targetImage->root()); } return d->referenceImagesLayer; } KisReferenceImagesLayer *KisDocument::referenceImagesLayer() const { return d->referenceImagesLayer.data(); } void KisDocument::setPreActivatedNode(KisNodeSP activatedNode) { d->preActivatedNode = activatedNode; } KisNodeSP KisDocument::preActivatedNode() const { return d->preActivatedNode; } KisImageWSP KisDocument::image() const { return d->image; } KisImageSP KisDocument::savingImage() const { return d->savingImage; } void KisDocument::setCurrentImage(KisImageSP image, bool forceInitialUpdate) { if (d->image) { // Disconnect existing sig/slot connections d->image->disconnect(this); d->shapeController->setImage(0); d->image = 0; } if (!image) return; d->setImageAndInitIdleWatcher(image); d->shapeController->setImage(image); setModified(false); connect(d->image, SIGNAL(sigImageModified()), this, SLOT(setImageModified()), Qt::UniqueConnection); if (forceInitialUpdate) { d->image->initialRefreshGraph(); } } void KisDocument::hackPreliminarySetImage(KisImageSP image) { KIS_SAFE_ASSERT_RECOVER_RETURN(!d->image); d->setImageAndInitIdleWatcher(image); d->shapeController->setImage(image); } void KisDocument::setImageModified() { setModified(true); } KisUndoStore* KisDocument::createUndoStore() { return new KisDocumentUndoStore(this); } bool KisDocument::isAutosaving() const { return d->isAutosaving; } QString KisDocument::exportErrorToUserMessage(KisImportExportFilter::ConversionStatus status, const QString &errorMessage) { return errorMessage.isEmpty() ? KisImportExportFilter::conversionStatusString(status) : errorMessage; } diff --git a/libs/ui/KisDocument.h b/libs/ui/KisDocument.h index 3f30864b87..22130bfb27 100644 --- a/libs/ui/KisDocument.h +++ b/libs/ui/KisDocument.h @@ -1,615 +1,644 @@ /* This file is part of the Krita project * * Copyright (C) 2014 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 KISDOCUMENT_H #define KISDOCUMENT_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kritaui_export.h" +#include + class QString; class KUndo2Command; class KoUnit; class KoColor; class KoColorSpace; class KoShapeBasedDocumentBase; class KoShapeLayer; class KoStore; class KoOdfReadStore; class KoDocumentInfo; class KoDocumentInfoDlg; class KisImportExportManager; class KisUndoStore; class KisPart; class KisGridConfig; class KisGuidesConfig; class QDomDocument; class KisReferenceImagesLayer; #define KIS_MIME_TYPE "application/x-krita" /** * The %Calligra document class * * This class provides some functionality each %Calligra document should have. * * @short The %Calligra document class */ class KRITAUI_EXPORT KisDocument : public QObject, public KoDocumentBase { Q_OBJECT protected: explicit KisDocument(); /** * @brief KisDocument makes a deep copy of the document \p rhs. * The caller *must* ensure that the image is properly * locked and is in consistent state before asking for * cloning. * @param rhs the source document to copy from */ explicit KisDocument(const KisDocument &rhs); public: enum OpenFlag { None = 0, DontAddToRecent = 0x1, RecoveryFile = 0x2 }; Q_DECLARE_FLAGS(OpenFlags, OpenFlag) /** * Destructor. * * The destructor does not delete any attached KisView objects and it does not * delete the attached widget as returned by widget(). */ ~KisDocument() override; /** * @brief reload Reloads the document from the original url * @return the result of loading the document */ bool reload(); /** * @brief creates a clone of the document and returns it. Please make sure that you * hold all the necessary locks on the image before asking for a clone! */ KisDocument* clone(); /** * @brief openUrl Open an URL * @param url The URL to open * @param flags Control specific behavior * @return success status */ bool openUrl(const QUrl &url, OpenFlags flags = None); /** * Opens the document given by @p url, without storing the URL * in the KisDocument. * Call this instead of openUrl() to implement KisMainWindow's * File --> Import feature. * * @note This will call openUrl(). To differentiate this from an ordinary * Open operation (in any reimplementation of openUrl() or openFile()) * call isImporting(). */ bool importDocument(const QUrl &url); /** * Saves the document as @p url without changing the state of the * KisDocument (URL, modified flag etc.). Call this instead of * KisParts::ReadWritePart::saveAs() to implement KisMainWindow's * File --> Export feature. */ bool exportDocument(const QUrl &url, const QByteArray &mimeType, bool showWarnings = false, KisPropertiesConfigurationSP exportConfiguration = 0); bool exportDocumentSync(const QUrl &url, const QByteArray &mimeType, KisPropertiesConfigurationSP exportConfiguration = 0); private: bool exportDocumentImpl(const KritaUtils::ExportFileJob &job, KisPropertiesConfigurationSP exportConfiguration); public: /** * @brief Sets whether the document can be edited or is read only. * * This recursively applied to all child documents and * KisView::updateReadWrite is called for every attached * view. */ void setReadWrite(bool readwrite = true); /** * To be preferred when a document exists. It is fast when calling * it multiple times since it caches the result that readNativeFormatMimeType() * delivers. * This comes from the X-KDE-NativeMimeType key in the .desktop file. */ static QByteArray nativeFormatMimeType() { return KIS_MIME_TYPE; } /// Checks whether a given mimetype can be handled natively. bool isNativeFormat(const QByteArray& mimetype) const; /// Returns a list of the mimetypes considered "native", i.e. which can /// be saved by KisDocument without a filter, in *addition* to the main one static QStringList extraNativeMimeTypes() { return QStringList() << KIS_MIME_TYPE; } /** * Returns the actual mimetype of the document */ QByteArray mimeType() const override; /** * @brief Sets the mime type for the document. * * When choosing "save as" this is also the mime type * selected by default. */ void setMimeType(const QByteArray & mimeType) override; /** * @return true if file operations should inhibit the option dialog */ bool fileBatchMode() const; /** * @param batchMode if true, do not show the option dialog for file operations. */ void setFileBatchMode(const bool batchMode); /** * Sets the error message to be shown to the user (use i18n()!) * when loading or saving fails. * If you asked the user about something and they chose "Cancel", */ void setErrorMessage(const QString& errMsg); /** * Return the last error message. Usually KisDocument takes care of * showing it; this method is mostly provided for non-interactive use. */ QString errorMessage() const; /** * Sets the warning message to be shown to the user (use i18n()!) * when loading or saving fails. */ void setWarningMessage(const QString& warningMsg); /** * Return the last warning message set by loading or saving. Warnings * mean that the document could not be completely loaded, but the errors * were not absolutely fatal. */ QString warningMessage() const; /** * @brief Generates a preview picture of the document * @note The preview is used in the File Dialog and also to create the Thumbnail */ QPixmap generatePreview(const QSize& size); /** * Tells the document that its title has been modified, either because * the modified status changes (this is done by setModified() ) or * because the URL or the document-info's title changed. */ void setTitleModified(); /** * @brief Sets the document to empty. * * Used after loading a template * (which is not empty, but not the user's input). * * @see isEmpty() */ void setEmpty(bool empty = true); /** * Return a correctly created QDomDocument for this KisDocument, * including processing instruction, complete DOCTYPE tag (with systemId and publicId), and root element. * @param tagName the name of the tag for the root element * @param version the DTD version (usually the application's version). */ QDomDocument createDomDocument(const QString& tagName, const QString& version) const; /** * Return a correctly created QDomDocument for an old (1.3-style) %Calligra document, * including processing instruction, complete DOCTYPE tag (with systemId and publicId), and root element. * This static method can be used e.g. by filters. * @param appName the app's instance name, e.g. words, kspread, kpresenter etc. * @param tagName the name of the tag for the root element, e.g. DOC for words/kpresenter. * @param version the DTD version (usually the application's version). */ static QDomDocument createDomDocument(const QString& appName, const QString& tagName, const QString& version); /** * Loads a document in the native format from a given URL. * Reimplement if your native format isn't XML. * * @param file the file to load - usually KReadOnlyPart::m_file or the result of a filter */ bool loadNativeFormat(const QString & file); /** - * Activate/deactivate/configure the autosave feature. - * @param delay in seconds, 0 to disable + * Set standard autosave interval that is set by a config file */ - void setAutoSaveDelay(int delay); + void setNormalAutoSaveInterval(); + + /** + * Set emergency interval that autosave uses when the image is busy, + * by default it is 10 sec + */ + void setEmergencyAutoSaveInterval(); + + /** + * Disable autosave + */ + void setInfiniteAutoSaveInterval(); /** * @return the information concerning this document. * @see KoDocumentInfo */ KoDocumentInfo *documentInfo() const; /** * Performs a cleanup of unneeded backup files */ void removeAutoSaveFiles(); /** * Returns true if this document or any of its internal child documents are modified. */ bool isModified() const override; /** * @return caption of the document * * Caption is of the form "[title] - [url]", * built out of the document info (title) and pretty-printed * document URL. * If the title is not present, only the URL it returned. */ QString caption() const; /** * Sets the document URL to empty URL * KParts doesn't allow this, but %Calligra apps have e.g. templates * After using loadNativeFormat on a template, one wants * to set the url to QUrl() */ void resetURL(); /** * @internal (public for KisMainWindow) */ void setMimeTypeAfterLoading(const QString& mimeType); /** * Returns the unit used to display all measures/distances. */ KoUnit unit() const; /** * Sets the unit used to display all measures/distances. */ void setUnit(const KoUnit &unit); KisGridConfig gridConfig() const; void setGridConfig(const KisGridConfig &config); /// returns the guides data for this document. const KisGuidesConfig& guidesConfig() const; void setGuidesConfig(const KisGuidesConfig &data); void clearUndoHistory(); /** * Sets the modified flag on the document. This means that it has * to be saved or not before deleting it. */ void setModified(bool _mod); void setRecovered(bool value); bool isRecovered() const; void updateEditingTime(bool forceStoreElapsed); /** * Returns the global undo stack */ KUndo2Stack *undoStack(); /** * @brief importExportManager gives access to the internal import/export manager * @return the document's import/export manager */ KisImportExportManager *importExportManager() const; /** * @brief serializeToNativeByteArray daves the document into a .kra file wtitten * to a memory-based byte-array * @return a byte array containing the .kra file */ QByteArray serializeToNativeByteArray(); /** * @brief isInSaving shown if the document has any (background) saving process or not * @return true if there is some saving in action */ bool isInSaving() const; public Q_SLOTS: /** * Adds a command to the undo stack and executes it by calling the redo() function. * @param command command to add to the undo stack */ void addCommand(KUndo2Command *command); /** * Begins recording of a macro command. At the end endMacro needs to be called. * @param text command description */ void beginMacro(const KUndo2MagicString &text); /** * Ends the recording of a macro command. */ void endMacro(); Q_SIGNALS: /** * This signal is emitted when the unit is changed by setUnit(). * It is common to connect views to it, in order to change the displayed units * (e.g. in the rulers) */ void unitChanged(const KoUnit &unit); /** * Emitted e.g. at the beginning of a save operation * This is emitted by KisDocument and used by KisView to display a statusbar message */ void statusBarMessage(const QString& text, int timeout = 0); /** * Emitted e.g. at the end of a save operation * This is emitted by KisDocument and used by KisView to clear the statusbar message */ void clearStatusBarMessage(); /** * Emitted when the document is modified */ void modified(bool); void titleModified(const QString &caption, bool isModified); void sigLoadingFinished(); void sigSavingFinished(); void sigGuidesConfigChanged(const KisGuidesConfig &config); void sigBackgroundSavingFinished(KisImportExportFilter::ConversionStatus status, const QString &errorMessage); void sigCompleteBackgroundSaving(const KritaUtils::ExportFileJob &job, KisImportExportFilter::ConversionStatus status, const QString &errorMessage); private Q_SLOTS: void finishExportInBackground(); void slotChildCompletedSavingInBackground(KisImportExportFilter::ConversionStatus status, const QString &errorMessage); void slotCompleteAutoSaving(const KritaUtils::ExportFileJob &job, KisImportExportFilter::ConversionStatus status, const QString &errorMessage); void slotCompleteSavingDocument(const KritaUtils::ExportFileJob &job, KisImportExportFilter::ConversionStatus status, const QString &errorMessage); + + void slotInitiateAsyncAutosaving(KisDocument *clonedDocument); + private: friend class KisPart; friend class SafeSavingLocker; + bool initiateSavingInBackground(const QString actionName, + const QObject *receiverObject, const char *receiverMethod, + const KritaUtils::ExportFileJob &job, + KisPropertiesConfigurationSP exportConfiguration, + std::unique_ptr &&optionalClonedDocument); + bool initiateSavingInBackground(const QString actionName, const QObject *receiverObject, const char *receiverMethod, const KritaUtils::ExportFileJob &job, KisPropertiesConfigurationSP exportConfiguration); bool startExportInBackground(const QString &actionName, const QString &location, const QString &realLocation, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration); + /** + * Activate/deactivate/configure the autosave feature. + * @param delay in seconds, 0 to disable + */ + void setAutoSaveDelay(int delay); + /** * Generate a name for the document. */ QString newObjectName(); QString generateAutoSaveFileName(const QString & path) const; /** * Loads a document * * Applies a filter if necessary, and calls loadNativeFormat in any case * You should not have to reimplement, except for very special cases. * * NOTE: this method also creates a new KisView instance! * * This method is called from the KReadOnlyPart::openUrl method. */ bool openFile(); /** @internal */ void setModified(); public: bool isAutosaving() const override; public: QString localFilePath() const override; void setLocalFilePath( const QString &localFilePath ); KoDocumentInfoDlg* createDocumentInfoDialog(QWidget *parent, KoDocumentInfo *docInfo) const; bool isReadWrite() const; QUrl url() const override; void setUrl(const QUrl &url) override; bool closeUrl(bool promptToSave = true); bool saveAs(const QUrl &url, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfigration = 0); /** * Create a new image that has this document as a parent and * replace the current image with this image. */ bool newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace * cs, const KoColor &bgColor, bool backgroundAsLayer, int numberOfLayers, const QString &imageDescription, const double imageResolution); bool isSaving() const; void waitForSavingToComplete(); KisImageWSP image() const; /** * @brief savingImage provides a detached, shallow copy of the original image that must be used when saving. * Any strokes in progress will not be applied to this image, so the result might be missing some data. On * the other hand, it won't block. * * @return a shallow copy of the original image, or 0 is saving is not in progress */ KisImageSP savingImage() const; /** * Set the current image to the specified image and turn undo on. */ void setCurrentImage(KisImageSP image, bool forceInitialUpdate = true); /** * Set the image of the document preliminary, before the document * has completed loading. Some of the document items (shapes) may want * to access image properties (bounds and resolution), so we should provide * it to them even before the entire image is loaded. * * Right now, the only use by KoShapeRegistry::createShapeFromOdf(), remove * after it is deprecated. */ void hackPreliminarySetImage(KisImageSP image); KisUndoStore* createUndoStore(); /** * The shape controller matches internal krita image layers with * the flake shape hierarchy. */ KoShapeBasedDocumentBase * shapeController() const; KoShapeLayer* shapeForNode(KisNodeSP layer) const; /** * Set the list of nodes that was marked as currently active. Used *only* * for saving loading. Never use it for tools or processing. */ void setPreActivatedNode(KisNodeSP activatedNode); /** * @return the node that was set as active during loading. Used *only* * for saving loading. Never use it for tools or processing. */ KisNodeSP preActivatedNode() const; /// @return the list of assistants associated with this document QList assistants() const; /// @replace the current list of assistants with @param value void setAssistants(const QList &value); /** * Get existing reference images layer or create new if none exists. */ KisSharedPtr createReferenceImagesLayer(KisImageSP targetImage = KisImageSP()); /** * Get existing reference images layer or null if none exists. */ KisReferenceImagesLayer *referenceImagesLayer() const; bool save(bool showWarnings, KisPropertiesConfigurationSP exportConfiguration); Q_SIGNALS: void completed(); void canceled(const QString &); private Q_SLOTS: void setImageModified(); void slotAutoSave(); void slotUndoStackCleanChanged(bool value); void slotConfigChanged(); private: /** * @brief try to clone the image. This method handles all the locking for you. If locking * has failed, no cloning happens * @return cloned document on success, null otherwise */ KisDocument *lockAndCloneForSaving(); QString exportErrorToUserMessage(KisImportExportFilter::ConversionStatus status, const QString &errorMessage); QString prettyPathOrUrl() const; bool openUrlInternal(const QUrl &url); + void slotAutoSaveImpl(std::unique_ptr &&optionalClonedDocument); + class Private; Private *const d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(KisDocument::OpenFlags) Q_DECLARE_METATYPE(KisDocument*) #endif diff --git a/libs/ui/kis_base_option.cpp b/libs/ui/KisPaintopPropertiesBase.cpp similarity index 69% rename from libs/ui/kis_base_option.cpp rename to libs/ui/KisPaintopPropertiesBase.cpp index 06d41b3525..b31df4386c 100644 --- a/libs/ui/kis_base_option.cpp +++ b/libs/ui/KisPaintopPropertiesBase.cpp @@ -1,46 +1,45 @@ /* * 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 "kis_base_option.h" +#include "KisPaintopPropertiesBase.h" #include "kis_properties_configuration.h" -KisBaseOption::~KisBaseOption() +KisPaintopPropertiesBase::~KisPaintopPropertiesBase() { } - -void KisBaseOption::readOptionSetting(KisPropertiesConfigurationSP settings) +void KisPaintopPropertiesBase::readOptionSetting(KisPropertiesConfigurationSP settings) { readOptionSettingImpl(settings.data()); } -void KisBaseOption::writeOptionSetting(KisPropertiesConfigurationSP settings) const +void KisPaintopPropertiesBase::writeOptionSetting(KisPropertiesConfigurationSP settings) const { writeOptionSettingImpl(settings.data()); } -void KisBaseOption::readOptionSetting(const KisPropertiesConfiguration *settings) +void KisPaintopPropertiesBase::readOptionSetting(const KisPropertiesConfiguration *settings) { readOptionSettingImpl(settings); } -void KisBaseOption::writeOptionSetting(KisPropertiesConfiguration *settings) const +void KisPaintopPropertiesBase::writeOptionSetting(KisPropertiesConfiguration *settings) const { writeOptionSettingImpl(settings); } diff --git a/libs/ui/kis_base_option.h b/libs/ui/KisPaintopPropertiesBase.h similarity index 87% rename from libs/ui/kis_base_option.h rename to libs/ui/KisPaintopPropertiesBase.h index 0ca874b57e..93a305a557 100644 --- a/libs/ui/kis_base_option.h +++ b/libs/ui/KisPaintopPropertiesBase.h @@ -1,56 +1,59 @@ /* * 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. */ #ifndef KISBASEOPTION_H #define KISBASEOPTION_H #include #include /** * This is a special base class for all the options that load/save * settings into a properties objects and do *not* store the properties - * themselves. This class adapts your option to allow its easy use with + * themselves. A KisPaintOpOption derived class generates a QWidget for + * its configuration page. This cannot be created from a KisPaintO[ + * + * This class adapts your option to allow its easy use with * both raw pointers and shared pointers. * * Motivation: * In quite a lot of places we call some options from the KisPaintOpSettings * class itself with patting 'this' as a parameter into * read/writeOptionSetting(). Conversion of 'this' into a shared pointer is * extremely dangerous, and, ideally, should be prohibited. We cannot prohibit * it atm, but we still can create a special interface for accepting raw pointers, * which will be used automatically, when 'this' is passed. */ -class KRITAUI_EXPORT KisBaseOption +class KRITAUI_EXPORT KisPaintopPropertiesBase { public: - virtual ~KisBaseOption(); + virtual ~KisPaintopPropertiesBase(); void readOptionSetting(KisPropertiesConfigurationSP settings); void writeOptionSetting(KisPropertiesConfigurationSP setting) const; void readOptionSetting(const KisPropertiesConfiguration *settings); void writeOptionSetting(KisPropertiesConfiguration *settings) const; protected: virtual void readOptionSettingImpl(const KisPropertiesConfiguration *settings) = 0; virtual void writeOptionSettingImpl(KisPropertiesConfiguration *settings) const = 0; }; #endif // KISBASEOPTION_H diff --git a/libs/ui/actions/KisPasteActionFactory.cpp b/libs/ui/actions/KisPasteActionFactory.cpp index d2e9dea045..8d9128b802 100644 --- a/libs/ui/actions/KisPasteActionFactory.cpp +++ b/libs/ui/actions/KisPasteActionFactory.cpp @@ -1,240 +1,241 @@ /* * Copyright (c) 2017 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 "KisPasteActionFactory.h" #include "kis_image.h" #include "KisViewManager.h" #include "kis_tool_proxy.h" #include "kis_canvas2.h" #include "kis_canvas_controller.h" #include "kis_paint_device.h" #include "kis_paint_layer.h" #include "kis_shape_layer.h" #include "kis_import_catcher.h" #include "kis_clipboard.h" #include "commands/kis_image_layer_add_command.h" #include "kis_processing_applicator.h" #include #include #include #include #include #include "kis_algebra_2d.h" #include #include #include "kis_time_range.h" #include "kis_keyframe_channel.h" #include "kis_raster_keyframe_channel.h" namespace { QPointF getFittingOffset(QList shapes, const QPointF &shapesOffset, const QRectF &documentRect, const qreal fitRatio) { QPointF accumulatedFitOffset; Q_FOREACH (KoShape *shape, shapes) { const QRectF bounds = shape->boundingRect(); const QPointF center = bounds.center() + shapesOffset; const qreal wMargin = (0.5 - fitRatio) * bounds.width(); const qreal hMargin = (0.5 - fitRatio) * bounds.height(); const QRectF allowedRect = documentRect.adjusted(-wMargin, -hMargin, wMargin, hMargin); const QPointF fittedCenter = KisAlgebra2D::clampPoint(center, allowedRect); accumulatedFitOffset += fittedCenter - center; } return accumulatedFitOffset; } bool tryPasteShapes(bool pasteAtCursorPosition, KisViewManager *view) { bool result = false; KoSvgPaste paste; if (paste.hasShapes()) { KoCanvasBase *canvas = view->canvasBase(); QSizeF fragmentSize; QList shapes = paste.fetchShapes(canvas->shapeController()->documentRectInPixels(), canvas->shapeController()->pixelsPerInch(), &fragmentSize); if (!shapes.isEmpty()) { KoShapeManager *shapeManager = canvas->shapeManager(); shapeManager->selection()->deselectAll(); // adjust z-index of the shapes so that they would be // pasted on the top of the stack QList topLevelShapes = shapeManager->topLevelShapes(); auto it = std::max_element(topLevelShapes.constBegin(), topLevelShapes.constEnd(), KoShape::compareShapeZIndex); if (it != topLevelShapes.constEnd()) { const int zIndexOffset = (*it)->zIndex(); std::stable_sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex); QList indexedShapes; std::transform(shapes.constBegin(), shapes.constEnd(), std::back_inserter(indexedShapes), [zIndexOffset] (KoShape *shape) { KoShapeReorderCommand::IndexedShape indexedShape(shape); indexedShape.zIndex += zIndexOffset; return indexedShape; }); indexedShapes = KoShapeReorderCommand::homogenizeZIndexesLazy(indexedShapes); KoShapeReorderCommand cmd(indexedShapes); cmd.redo(); } KUndo2Command *parentCommand = new KUndo2Command(kundo2_i18n("Paste shapes")); canvas->shapeController()->addShapesDirect(shapes, 0, parentCommand); QPointF finalShapesOffset; if (pasteAtCursorPosition) { QRectF boundingRect = KoShape::boundingRect(shapes); const QPointF cursorPos = canvas->canvasController()->currentCursorPosition(); finalShapesOffset = cursorPos - boundingRect.center(); } else { bool foundOverlapping = false; QRectF boundingRect = KoShape::boundingRect(shapes); const QPointF offsetStep = 0.1 * QPointF(boundingRect.width(), boundingRect.height()); QPointF offset; Q_FOREACH (KoShape *shape, shapes) { QRectF br1 = shape->boundingRect(); bool hasOverlappingShape = false; do { hasOverlappingShape = false; // we cannot use shapesAt() here, because the groups are not // handled in the shape manager's tree QList conflicts = shapeManager->shapes(); Q_FOREACH (KoShape *intersectedShape, conflicts) { if (intersectedShape == shape) continue; QRectF br2 = intersectedShape->boundingRect(); const qreal tolerance = 2.0; /* pt */ if (KisAlgebra2D::fuzzyCompareRects(br1, br2, tolerance)) { br1.translate(offsetStep.x(), offsetStep.y()); offset += offsetStep; hasOverlappingShape = true; foundOverlapping = true; break; } } } while (hasOverlappingShape); if (foundOverlapping) break; } if (foundOverlapping) { finalShapesOffset = offset; } } const QRectF documentRect = canvas->shapeController()->documentRect(); finalShapesOffset += getFittingOffset(shapes, finalShapesOffset, documentRect, 0.1); if (!finalShapesOffset.isNull()) { new KoShapeMoveCommand(shapes, finalShapesOffset, parentCommand); } canvas->addCommand(parentCommand); Q_FOREACH (KoShape *shape, shapes) { canvas->selectedShapesProxy()->selection()->select(shape); } result = true; } } return result; } } - +#include "kis_painter.h" void KisPasteActionFactory::run(bool pasteAtCursorPosition, KisViewManager *view) { KisImageSP image = view->image(); if (!image) return; if (tryPasteShapes(pasteAtCursorPosition, view)) { return; } KisTimeRange range; const QRect fittingBounds = pasteAtCursorPosition ? QRect() : image->bounds(); KisPaintDeviceSP clip = KisClipboard::instance()->clip(fittingBounds, true, &range); if (clip) { if (pasteAtCursorPosition) { const QPointF docPos = view->canvasBase()->canvasController()->currentCursorPosition(); const QPointF imagePos = view->canvasBase()->coordinatesConverter()->documentToImage(docPos); const QPointF offset = (imagePos - QRectF(clip->exactBounds()).center()).toPoint(); clip->setX(clip->x() + offset.x()); clip->setY(clip->y() + offset.y()); } KisImportCatcher::adaptClipToImageColorSpace(clip, image); KisPaintLayerSP newLayer = new KisPaintLayer(image.data(), image->nextLayerName() + i18n("(pasted)"), OPACITY_OPAQUE_U8); KisNodeSP aboveNode = view->activeLayer(); KisNodeSP parentNode = aboveNode ? aboveNode->parent() : image->root(); if (range.isValid()) { newLayer->enableAnimation(); KisKeyframeChannel *channel = newLayer->getKeyframeChannel(KisKeyframeChannel::Content.id(), true); KisRasterKeyframeChannel *rasterChannel = dynamic_cast(channel); rasterChannel->importFrame(range.start(), clip, 0); rasterChannel->addKeyframe(range.end() + 1, 0); } else { - newLayer->paintDevice()->makeCloneFromRough(clip, clip->extent()); + const QRect rc = clip->extent(); + KisPainter::copyAreaOptimized(rc.topLeft(), clip, newLayer->paintDevice(), rc); } KUndo2Command *cmd = new KisImageLayerAddCommand(image, newLayer, parentNode, aboveNode); KisProcessingApplicator *ap = beginAction(view, cmd->text()); ap->applyCommand(cmd, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL); endAction(ap, KisOperationConfiguration(id()).toXML()); } else { // XXX: "Add saving of XML data for Paste of shapes" view->canvasBase()->toolProxy()->paste(); } } diff --git a/libs/ui/dialogs/KisAsyncAnimationRenderDialogBase.cpp b/libs/ui/dialogs/KisAsyncAnimationRenderDialogBase.cpp index c12d5bc5e8..28059211fb 100644 --- a/libs/ui/dialogs/KisAsyncAnimationRenderDialogBase.cpp +++ b/libs/ui/dialogs/KisAsyncAnimationRenderDialogBase.cpp @@ -1,333 +1,333 @@ /* * Copyright (c) 2017 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 "KisAsyncAnimationRenderDialogBase.h" #include #include #include #include #include #include #include #include #include #include "KisViewManager.h" #include "KisAsyncAnimationRendererBase.h" #include "kis_time_range.h" #include "kis_image.h" #include "kis_image_config.h" #include "kis_memory_statistics_server.h" #include #include namespace { struct RendererPair { std::unique_ptr renderer; KisImageSP image; RendererPair() {} RendererPair(KisAsyncAnimationRendererBase *_renderer, KisImageSP _image) : renderer(_renderer), image(_image) { } RendererPair(RendererPair &&rhs) : renderer(std::move(rhs.renderer)), image(rhs.image) { } }; int calculateNumberMemoryAllowedClones(KisImageSP image) { KisMemoryStatisticsServer::Statistics stats = KisMemoryStatisticsServer::instance() ->fetchMemoryStatistics(image); const qint64 allowedMemory = 0.8 * stats.tilesHardLimit - stats.realMemorySize; const qint64 cloneSize = stats.projectionsSize; - return allowedMemory > 0 ? allowedMemory / cloneSize : 0; + return cloneSize > 0 ? allowedMemory / cloneSize : 0; } } struct KisAsyncAnimationRenderDialogBase::Private { Private(const QString &_actionTitle, KisImageSP _image, int _busyWait) : actionTitle(_actionTitle), image(_image), busyWait(_busyWait) { } QString actionTitle; KisImageSP image; int busyWait; bool isBatchMode = false; std::vector asyncRenderers; bool memoryLimitReached = false; QElapsedTimer processingTime; QScopedPointer progressDialog; QEventLoop waitLoop; QList stillDirtyFrames; QList framesInProgress; int dirtyFramesCount = 0; Result result = RenderComplete; int numDirtyFramesLeft() const { return stillDirtyFrames.size() + framesInProgress.size(); } }; KisAsyncAnimationRenderDialogBase::KisAsyncAnimationRenderDialogBase(const QString &actionTitle, KisImageSP image, int busyWait) : m_d(new Private(actionTitle, image, busyWait)) { } KisAsyncAnimationRenderDialogBase::~KisAsyncAnimationRenderDialogBase() { } KisAsyncAnimationRenderDialogBase::Result KisAsyncAnimationRenderDialogBase::regenerateRange(KisViewManager *viewManager) { { /** * Since this method can be called from the places where no * view manager is available, we need this manually crafted * ugly construction to "try-lock-cance" the image. */ bool imageIsIdle = true; if (viewManager) { imageIsIdle = viewManager->blockUntilOperationsFinished(m_d->image); } else { imageIsIdle = false; if (m_d->image->tryBarrierLock(true)) { m_d->image->unlock(); imageIsIdle = true; } } if (!imageIsIdle) { return RenderCancelled; } } m_d->stillDirtyFrames = calcDirtyFrames(); m_d->framesInProgress.clear(); m_d->result = RenderComplete; m_d->dirtyFramesCount = m_d->stillDirtyFrames.size(); if (!m_d->isBatchMode) { QWidget *parentWidget = viewManager ? viewManager->mainWindow() : 0; m_d->progressDialog.reset(new QProgressDialog(m_d->actionTitle, i18n("Cancel"), 0, 0, parentWidget)); m_d->progressDialog->setWindowModality(Qt::ApplicationModal); m_d->progressDialog->setMinimum(0); m_d->progressDialog->setMaximum(m_d->dirtyFramesCount); m_d->progressDialog->setMinimumDuration(m_d->busyWait); connect(m_d->progressDialog.data(), SIGNAL(canceled()), SLOT(slotCancelRegeneration())); } if (m_d->dirtyFramesCount <= 0) return m_d->result; m_d->processingTime.start(); KisImageConfig cfg; const int maxThreads = cfg.maxNumberOfThreads(); const int numAllowedWorker = 1 + calculateNumberMemoryAllowedClones(m_d->image); const int proposedNumWorkers = qMin(m_d->dirtyFramesCount, cfg.frameRenderingClones()); const int numWorkers = qMin(proposedNumWorkers, numAllowedWorker); const int numThreadsPerWorker = qMax(1, qCeil(qreal(maxThreads) / numWorkers)); m_d->memoryLimitReached = numWorkers < proposedNumWorkers; const int oldWorkingThreadsLimit = m_d->image->workingThreadsLimit(); ENTER_FUNCTION() << ppVar(numWorkers) << ppVar(numThreadsPerWorker); for (int i = 0; i < numWorkers; i++) { // reuse the image for one of the workers KisImageSP image = i == numWorkers - 1 ? m_d->image : m_d->image->clone(true); image->setWorkingThreadsLimit(numThreadsPerWorker); KisAsyncAnimationRendererBase *renderer = createRenderer(image); connect(renderer, SIGNAL(sigFrameCompleted(int)), SLOT(slotFrameCompleted(int))); connect(renderer, SIGNAL(sigFrameCancelled(int)), SLOT(slotFrameCancelled(int))); m_d->asyncRenderers.push_back(RendererPair(renderer, image)); } ENTER_FUNCTION() << "Copying done in" << m_d->processingTime.elapsed(); tryInitiateFrameRegeneration(); updateProgressLabel(); if (m_d->numDirtyFramesLeft() > 0) { m_d->waitLoop.exec(); } ENTER_FUNCTION() << "Full regeneration done in" << m_d->processingTime.elapsed(); for (auto &pair : m_d->asyncRenderers) { KIS_SAFE_ASSERT_RECOVER_NOOP(!pair.renderer->isActive()); if (viewManager) { viewManager->blockUntilOperationsFinishedForced(pair.image); } else { pair.image->barrierLock(true); pair.image->unlock(); } } m_d->asyncRenderers.clear(); if (viewManager) { viewManager->blockUntilOperationsFinishedForced(m_d->image); } else { m_d->image->barrierLock(true); m_d->image->unlock(); } m_d->image->setWorkingThreadsLimit(oldWorkingThreadsLimit); m_d->progressDialog.reset(); return m_d->result; } void KisAsyncAnimationRenderDialogBase::slotFrameCompleted(int frame) { Q_UNUSED(frame); m_d->framesInProgress.removeOne(frame); tryInitiateFrameRegeneration(); updateProgressLabel(); } void KisAsyncAnimationRenderDialogBase::slotFrameCancelled(int frame) { Q_UNUSED(frame); cancelProcessingImpl(false); } void KisAsyncAnimationRenderDialogBase::slotCancelRegeneration() { cancelProcessingImpl(true); } void KisAsyncAnimationRenderDialogBase::cancelProcessingImpl(bool isUserCancelled) { for (auto &pair : m_d->asyncRenderers) { if (pair.renderer->isActive()) { pair.renderer->cancelCurrentFrameRendering(); } KIS_SAFE_ASSERT_RECOVER_NOOP(!pair.renderer->isActive()); } m_d->stillDirtyFrames.clear(); m_d->framesInProgress.clear(); m_d->result = isUserCancelled ? RenderCancelled : RenderFailed; updateProgressLabel(); } void KisAsyncAnimationRenderDialogBase::tryInitiateFrameRegeneration() { bool hadWorkOnPreviousCycle = false; while (!m_d->stillDirtyFrames.isEmpty()) { for (auto &pair : m_d->asyncRenderers) { if (!pair.renderer->isActive()) { const int currentDirtyFrame = m_d->stillDirtyFrames.takeFirst(); initializeRendererForFrame(pair.renderer.get(), pair.image, currentDirtyFrame); pair.renderer->startFrameRegeneration(pair.image, currentDirtyFrame); hadWorkOnPreviousCycle = true; m_d->framesInProgress.append(currentDirtyFrame); break; } } if (!hadWorkOnPreviousCycle) break; hadWorkOnPreviousCycle = false; } } void KisAsyncAnimationRenderDialogBase::updateProgressLabel() { const int processedFramesCount = m_d->dirtyFramesCount - m_d->numDirtyFramesLeft(); const qint64 elapsedMSec = m_d->processingTime.elapsed(); const qint64 estimatedMSec = !processedFramesCount ? 0 : elapsedMSec * m_d->dirtyFramesCount / processedFramesCount; const QTime elapsedTime = QTime::fromMSecsSinceStartOfDay(elapsedMSec); const QTime estimatedTime = QTime::fromMSecsSinceStartOfDay(estimatedMSec); const QString timeFormat = estimatedTime.hour() > 0 ? "HH:mm:ss" : "mm:ss"; const QString elapsedTimeString = elapsedTime.toString(timeFormat); const QString estimatedTimeString = estimatedTime.toString(timeFormat); const QString memoryLimitMessage( i18n("\n\nMemory limit is reached!\nThe number of clones is limited to %1\n\n", m_d->asyncRenderers.size())); const QString progressLabel(i18n("%1\n\nElapsed: %2\nEstimated: %3\n\n%4", m_d->actionTitle, elapsedTimeString, estimatedTimeString, m_d->memoryLimitReached ? memoryLimitMessage : QString())); if (m_d->progressDialog) { m_d->progressDialog->setLabelText(progressLabel); m_d->progressDialog->setValue(processedFramesCount); } if (!m_d->numDirtyFramesLeft()) { m_d->waitLoop.quit(); } } void KisAsyncAnimationRenderDialogBase::setBatchMode(bool value) { m_d->isBatchMode = value; } bool KisAsyncAnimationRenderDialogBase::batchMode() const { return m_d->isBatchMode; } diff --git a/libs/ui/dialogs/kis_about_application.cpp b/libs/ui/dialogs/kis_about_application.cpp index 3ecda8822b..82e4366bdb 100644 --- a/libs/ui/dialogs/kis_about_application.cpp +++ b/libs/ui/dialogs/kis_about_application.cpp @@ -1,173 +1,202 @@ /* * Copyright (c) 2014 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 "kis_about_application.h" #include #include #include #include #include +#include #include #include #include #include #include #include +#include #include #include "../../krita/data/splash/splash_screen.xpm" #include "../../krita/data/splash/splash_screen_x2.xpm" #include "kis_splash_screen.h" KisAboutApplication::KisAboutApplication(QWidget *parent) : QDialog(parent) { setWindowTitle(i18n("About Krita")); QVBoxLayout *vlayout = new QVBoxLayout(this); vlayout->setMargin(0); - QTabWidget *wdg = new QTabWidget; - vlayout->addWidget(wdg); + QTabWidget *wdgTab = new QTabWidget; + vlayout->addWidget(wdgTab); KisSplashScreen *splash = new KisSplashScreen(qApp->applicationVersion(), QPixmap(splash_screen_xpm), QPixmap(splash_screen_x2_xpm), true); splash->setWindowFlags(Qt::Widget); splash->displayLinks(true); splash->setFixedSize(splash->sizeHint()); - wdg->addTab(splash, i18n("About")); - setMinimumSize(wdg->sizeHint()); + wdgTab->addTab(splash, i18n("About")); + setMinimumSize(wdgTab->sizeHint()); QTextEdit *lblAuthors = new QTextEdit(); lblAuthors->setReadOnly(true); QString authors = i18n("" "" "" "

Created By

" "

"); QFile fileDevelopers(":/developers.txt"); Q_ASSERT(fileDevelopers.exists()); fileDevelopers.open(QIODevice::ReadOnly); Q_FOREACH (const QByteArray &author, fileDevelopers.readAll().split('\n')) { authors.append(QString::fromUtf8(author)); authors.append(", "); } authors.chop(2); authors.append(".

"); lblAuthors->setText(authors); - wdg->addTab(lblAuthors, i18n("Authors")); + wdgTab->addTab(lblAuthors, i18n("Authors")); QTextEdit *lblKickstarter = new QTextEdit(); lblKickstarter->setReadOnly(true); QString backers = i18n("" "" "" "

Backed By

" "

"); QFile fileBackers(":/backers.txt"); Q_ASSERT(fileBackers.exists()); fileBackers.open(QIODevice::ReadOnly); Q_FOREACH (const QByteArray &backer, fileBackers.readAll().split('\n')) { backers.append(QString::fromUtf8(backer)); backers.append(", "); } backers.chop(2); backers.append(i18n(".

Thanks! You were all awesome!

")); lblKickstarter->setText(backers); - wdg->addTab(lblKickstarter, i18n("Backers")); + wdgTab->addTab(lblKickstarter, i18n("Backers")); QTextEdit *lblCredits = new QTextEdit(); lblCredits->setReadOnly(true); QString credits = i18n("" "" "" "

Thanks To

" "

"); QFile fileCredits(":/credits.txt"); Q_ASSERT(fileCredits.exists()); fileCredits.open(QIODevice::ReadOnly); Q_FOREACH (const QString &credit, QString::fromUtf8(fileCredits.readAll()).split('\n', QString::SkipEmptyParts)) { if (credit.contains(":")) { QList creditSplit = credit.split(':'); credits.append(creditSplit.at(0)); credits.append(" (" + creditSplit.at(1) + ")"); credits.append(", "); } } credits.chop(2); credits.append(i18n(".

For supporting Krita development with advice, icons, brush sets and more.

")); lblCredits->setText(credits); - wdg->addTab(lblCredits, i18n("Also Thanks To")); + wdgTab->addTab(lblCredits, i18n("Also Thanks To")); QTextEdit *lblLicense = new QTextEdit(); lblLicense->setReadOnly(true); QString license = i18n("" "" "" "

Your Rights

" - "

Krita is released under the GNU General Public License (version 2 or any later version).

" + "

Krita is released under the GNU General Public License (version 3 or any later version).

" "

This license grants people a number of freedoms:

" "
    " "
  • You are free to use Krita, for any purpose
  • " "
  • You are free to distribute Krita
  • " "
  • You can study how Krita works and change it
  • " "
  • You can distribute changed versions of Krita
  • " "
" "

The Krita Foundation and its projects on krita.org are committed to preserving Krita as free software.

" "

Your artwork

" "

What you create with Krita is your sole property. All your artwork is free for you to use as you like.

" "

That means that Krita can be used commercially, for any purpose. There are no restrictions whatsoever.

" "

Krita’s GNU GPL license guarantees you this freedom. Nobody is ever permitted to take it away, in contrast " "to trial or educational versions of commercial software that will forbid your work in commercial situations.

" "

");
 
     QFile licenseFile(":/LICENSE");
     Q_ASSERT(licenseFile.exists());
     licenseFile.open(QIODevice::ReadOnly);
     QByteArray ba = licenseFile.readAll();
     license.append(QString::fromUtf8(ba));
     license.append("
"); lblLicense->setText(license); - wdg->addTab(lblLicense, i18n("License")); + wdgTab->addTab(lblLicense, i18n("License")); + + QTextBrowser *lblThirdParty = new QTextBrowser(); + lblThirdParty->setOpenExternalLinks(true); + QFile thirdPartyFile(":/libraries.txt"); + if (thirdPartyFile.open(QIODevice::ReadOnly)) { + ba = thirdPartyFile.readAll(); + + QString thirdPartyHtml = i18n("" + "" + "" + "

Third-party Libraries used by Krita

" + "

Krita is built on the following free software libraries:

    "); + + + Q_FOREACH(const QString &lib, QString::fromUtf8(ba).split('\n')) { + if (!lib.startsWith("#")) { + QStringList parts = lib.split(','); + if (parts.size() >= 3) { + thirdPartyHtml.append(QString("
  • %1: %3
  • ").arg(parts[0], parts[1], parts[2])); + } + } + } + thirdPartyHtml.append("

      "); + lblThirdParty->setText(thirdPartyHtml); + } + wdgTab->addTab(lblThirdParty, i18n("Third-party libraries")); + QPushButton *bnClose = new QPushButton(i18n("Close")); connect(bnClose, SIGNAL(clicked()), SLOT(close())); QHBoxLayout *hlayout = new QHBoxLayout; hlayout->setMargin(10); hlayout->addStretch(10); hlayout->addWidget(bnClose); vlayout->addLayout(hlayout); } diff --git a/libs/ui/dialogs/kis_about_application.h b/libs/ui/dialogs/kis_about_application.h index 23a94d2dcb..6fd8a9e40b 100644 --- a/libs/ui/dialogs/kis_about_application.h +++ b/libs/ui/dialogs/kis_about_application.h @@ -1,35 +1,31 @@ /* * Copyright (c) 2014 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 KIS_ABOUT_APPLICATION_H #define KIS_ABOUT_APPLICATION_H #include class KisAboutApplication : public QDialog { Q_OBJECT public: explicit KisAboutApplication(QWidget *parent = 0); -Q_SIGNALS: - -public Q_SLOTS: - }; #endif // KIS_ABOUT_APPLICATION_H diff --git a/libs/ui/dialogs/kis_dlg_preferences.cc b/libs/ui/dialogs/kis_dlg_preferences.cc index f0fdcb30f0..045eedbe6d 100644 --- a/libs/ui/dialogs/kis_dlg_preferences.cc +++ b/libs/ui/dialogs/kis_dlg_preferences.cc @@ -1,1338 +1,1374 @@ /* * preferencesdlg.cc - part of KImageShop * * Copyright (c) 1999 Michael Koch * Copyright (c) 2003-2011 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 "kis_dlg_preferences.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 #include "KoID.h" #include #include #include #include #include #include "kis_action_registry.h" #include #include #include "kis_clipboard.h" #include "widgets/kis_cmb_idlist.h" #include "KoColorSpace.h" #include "KoColorSpaceRegistry.h" #include "KoColorConversionTransformation.h" #include "kis_cursor.h" #include "kis_config.h" #include "kis_canvas_resource_provider.h" #include "kis_preference_set_registry.h" #include "kis_color_manager.h" #include "KisProofingConfiguration.h" #include "kis_image_config.h" #include "slider_and_spin_box_sync.h" // for the performance update #include #include #include "input/config/kis_input_configuration_page.h" +#include "input/wintab/drawpile_tablettester/tablettester.h" #ifdef Q_OS_WIN # include #endif GeneralTab::GeneralTab(QWidget *_parent, const char *_name) : WdgGeneralSettings(_parent, _name) { KisConfig cfg; + // + // Cursor Tab + // m_cmbCursorShape->addItem(i18n("No Cursor")); m_cmbCursorShape->addItem(i18n("Tool Icon")); m_cmbCursorShape->addItem(i18n("Arrow")); m_cmbCursorShape->addItem(i18n("Small Circle")); m_cmbCursorShape->addItem(i18n("Crosshair")); m_cmbCursorShape->addItem(i18n("Triangle Righthanded")); m_cmbCursorShape->addItem(i18n("Triangle Lefthanded")); m_cmbCursorShape->addItem(i18n("Black Pixel")); m_cmbCursorShape->addItem(i18n("White Pixel")); + m_cmbCursorShape->setCurrentIndex(cfg.newCursorStyle()); + m_cmbOutlineShape->addItem(i18n("No Outline")); m_cmbOutlineShape->addItem(i18n("Circle Outline")); m_cmbOutlineShape->addItem(i18n("Preview Outline")); m_cmbOutlineShape->addItem(i18n("Tilt Outline")); + m_cmbOutlineShape->setCurrentIndex(cfg.newOutlineStyle()); + + m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting()); + + KoColor cursorColor(KoColorSpaceRegistry::instance()->rgb8()); + cursorColor.fromQColor(cfg.getCursorMainColor()); + cursorColorBtutton->setColor(cursorColor); + + // + // Window Tab + // + m_cmbMDIType->setCurrentIndex(cfg.readEntry("mdi_viewmode", (int)QMdiArea::TabbedView)); + + m_backgroundimage->setText(cfg.getMDIBackgroundImage()); + connect(m_bnFileName, SIGNAL(clicked()), SLOT(getBackgroundImage())); + connect(clearBgImageButton, SIGNAL(clicked()), SLOT(clearBackgroundImage())); + + KoColor mdiColor; + mdiColor.fromQColor(cfg.getMDIBackgroundColor()); + m_mdiColor->setColor(mdiColor); + + m_chkRubberBand->setChecked(cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); + + m_chkCanvasMessages->setChecked(cfg.showCanvasMessages()); + + const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); + QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); + m_chkHiDPI->setChecked(kritarc.value("EnableHiDPI", false).toBool()); + + m_chkSingleApplication->setChecked(kritarc.value("EnableSingleApplication", true).toBool()); + + // + // Tools tab + // + m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker()); + m_chkSwitchSelectionCtrlAlt->setChecked(cfg.switchSelectionCtrlAlt()); + chkEnableTouch->setChecked(!cfg.disableTouchOnCanvas()); + m_cmbKineticScrollingGesture->addItem(i18n("Disabled")); m_cmbKineticScrollingGesture->addItem(i18n("On Touch Drag")); m_cmbKineticScrollingGesture->addItem(i18n("On Click Drag")); - m_cmbCursorShape->setCurrentIndex(cfg.newCursorStyle()); - m_cmbOutlineShape->setCurrentIndex(cfg.newOutlineStyle()); + m_cmbKineticScrollingGesture->setCurrentIndex(cfg.kineticScrollingGesture()); + m_kineticScrollingSensitivity->setValue(cfg.kineticScrollingSensitivity()); + m_chkKineticScrollingScrollbar->setChecked(cfg.kineticScrollingScrollbar()); + // + // Miscellaneous + // cmbStartupSession->addItem(i18n("Open default window")); cmbStartupSession->addItem(i18n("Load previous session")); cmbStartupSession->addItem(i18n("Show session manager")); cmbStartupSession->setCurrentIndex(cfg.sessionOnStartup()); chkSaveSessionOnQuit->setChecked(cfg.saveSessionOnQuit(false)); - chkShowRootLayer->setChecked(cfg.showRootLayer()); - int autosaveInterval = cfg.autoSaveInterval(); //convert to minutes m_autosaveSpinBox->setValue(autosaveInterval / 60); m_autosaveCheckBox->setChecked(autosaveInterval > 0); - m_undoStackSize->setValue(cfg.undoStackLimit()); + + m_chkCompressKra->setChecked(cfg.compressKra()); + m_backupFileCheckBox->setChecked(cfg.backupFile()); - m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting()); + + m_chkConvertOnImport->setChecked(cfg.convertToImageColorspaceOnImport()); + + m_undoStackSize->setValue(cfg.undoStackLimit()); + + m_favoritePresetsSpinBox->setValue(cfg.favoritePresets()); + + chkShowRootLayer->setChecked(cfg.showRootLayer()); + m_hideSplashScreen->setChecked(cfg.hideSplashScreen()); KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); bool dontUseNative = true; #ifdef Q_OS_UNIX if (qgetenv("XDG_CURRENT_DESKTOP") == "KDE") { dontUseNative = false; } #endif #ifdef Q_OS_WIN dontUseNative = false; #endif m_chkNativeFileDialog->setChecked(!group.readEntry("DontUseNativeFileDialog", dontUseNative)); intMaxBrushSize->setValue(cfg.readEntry("maximumBrushSize", 1000)); - m_cmbMDIType->setCurrentIndex(cfg.readEntry("mdi_viewmode", (int)QMdiArea::TabbedView)); - m_chkRubberBand->setChecked(cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); - m_favoritePresetsSpinBox->setValue(cfg.favoritePresets()); - KoColor mdiColor; - mdiColor.fromQColor(cfg.getMDIBackgroundColor()); - m_mdiColor->setColor(mdiColor); - m_backgroundimage->setText(cfg.getMDIBackgroundImage()); - m_chkCanvasMessages->setChecked(cfg.showCanvasMessages()); - m_chkCompressKra->setChecked(cfg.compressKra()); - - const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); - QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); - m_chkHiDPI->setChecked(kritarc.value("EnableHiDPI", false).toBool()); - m_chkSingleApplication->setChecked(kritarc.value("EnableSingleApplication", true).toBool()); - - m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker()); - m_cmbKineticScrollingGesture->setCurrentIndex(cfg.kineticScrollingGesture()); - m_kineticScrollingSensitivity->setValue(cfg.kineticScrollingSensitivity()); - m_chkKineticScrollingScrollbar->setChecked(cfg.kineticScrollingScrollbar()); - m_chkSwitchSelectionCtrlAlt->setChecked(cfg.switchSelectionCtrlAlt()); - chkEnableTouch->setChecked(!cfg.disableTouchOnCanvas()); - m_chkConvertOnImport->setChecked(cfg.convertToImageColorspaceOnImport()); m_chkCacheAnimatioInBackground->setChecked(cfg.calculateAnimationCacheInBackground()); - KoColor cursorColor(KoColorSpaceRegistry::instance()->rgb8()); - cursorColor.fromQColor(cfg.getCursorMainColor()); - cursorColorBtutton->setColor(cursorColor); - connect(m_bnFileName, SIGNAL(clicked()), SLOT(getBackgroundImage())); - connect(clearBgImageButton, SIGNAL(clicked()), SLOT(clearBackgroundImage())); } void GeneralTab::setDefault() { KisConfig cfg; m_cmbCursorShape->setCurrentIndex(cfg.newCursorStyle(true)); m_cmbOutlineShape->setCurrentIndex(cfg.newOutlineStyle(true)); chkShowRootLayer->setChecked(cfg.showRootLayer(true)); m_autosaveCheckBox->setChecked(cfg.autoSaveInterval(true) > 0); //convert to minutes m_autosaveSpinBox->setValue(cfg.autoSaveInterval(true) / 60); m_undoStackSize->setValue(cfg.undoStackLimit(true)); m_backupFileCheckBox->setChecked(cfg.backupFile(true)); m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting(true)); m_hideSplashScreen->setChecked(cfg.hideSplashScreen(true)); m_chkNativeFileDialog->setChecked(false); intMaxBrushSize->setValue(1000); m_cmbMDIType->setCurrentIndex((int)QMdiArea::TabbedView); m_chkRubberBand->setChecked(cfg.useOpenGL(true)); m_favoritePresetsSpinBox->setValue(cfg.favoritePresets(true)); KoColor mdiColor; mdiColor.fromQColor(cfg.getMDIBackgroundColor(true)); m_mdiColor->setColor(mdiColor); m_backgroundimage->setText(cfg.getMDIBackgroundImage(true)); m_chkCanvasMessages->setChecked(cfg.showCanvasMessages(true)); m_chkCompressKra->setChecked(cfg.compressKra(true)); m_chkHiDPI->setChecked(false); m_chkSingleApplication->setChecked(true); m_chkHiDPI->setChecked(true); m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker(true)); m_cmbKineticScrollingGesture->setCurrentIndex(cfg.kineticScrollingGesture(true)); m_kineticScrollingSensitivity->setValue(cfg.kineticScrollingSensitivity(true)); m_chkKineticScrollingScrollbar->setChecked(cfg.kineticScrollingScrollbar(true)); m_chkSwitchSelectionCtrlAlt->setChecked(cfg.switchSelectionCtrlAlt(true)); chkEnableTouch->setChecked(!cfg.disableTouchOnCanvas(true)); m_chkConvertOnImport->setChecked(cfg.convertToImageColorspaceOnImport(true)); m_chkCacheAnimatioInBackground->setChecked(cfg.calculateAnimationCacheInBackground(true)); KoColor cursorColor(KoColorSpaceRegistry::instance()->rgb8()); cursorColor.fromQColor(cfg.getCursorMainColor(true)); cursorColorBtutton->setColor(cursorColor); } CursorStyle GeneralTab::cursorStyle() { return (CursorStyle)m_cmbCursorShape->currentIndex(); } OutlineStyle GeneralTab::outlineStyle() { return (OutlineStyle)m_cmbOutlineShape->currentIndex(); } KisConfig::SessionOnStartup GeneralTab::sessionOnStartup() const { return (KisConfig::SessionOnStartup)cmbStartupSession->currentIndex(); } bool GeneralTab::saveSessionOnQuit() const { return chkSaveSessionOnQuit->isChecked(); } bool GeneralTab::showRootLayer() { return chkShowRootLayer->isChecked(); } int GeneralTab::autoSaveInterval() { //convert to seconds return m_autosaveCheckBox->isChecked() ? m_autosaveSpinBox->value() * 60 : 0; } int GeneralTab::undoStackSize() { return m_undoStackSize->value(); } bool GeneralTab::showOutlineWhilePainting() { return m_showOutlinePainting->isChecked(); } bool GeneralTab::hideSplashScreen() { return m_hideSplashScreen->isChecked(); } int GeneralTab::mdiMode() { return m_cmbMDIType->currentIndex(); } int GeneralTab::favoritePresets() { return m_favoritePresetsSpinBox->value(); } bool GeneralTab::showCanvasMessages() { return m_chkCanvasMessages->isChecked(); } bool GeneralTab::compressKra() { return m_chkCompressKra->isChecked(); } bool GeneralTab::toolOptionsInDocker() { return m_radioToolOptionsInDocker->isChecked(); } int GeneralTab::kineticScrollingGesture() { return m_cmbKineticScrollingGesture->currentIndex(); } int GeneralTab::kineticScrollingSensitivity() { return m_kineticScrollingSensitivity->value(); } bool GeneralTab::kineticScrollingScrollbar() { return m_chkKineticScrollingScrollbar->isChecked(); } bool GeneralTab::switchSelectionCtrlAlt() { return m_chkSwitchSelectionCtrlAlt->isChecked(); } bool GeneralTab::convertToImageColorspaceOnImport() { return m_chkConvertOnImport->isChecked(); } bool GeneralTab::calculateAnimationCacheInBackground() { return m_chkCacheAnimatioInBackground->isChecked(); } void GeneralTab::getBackgroundImage() { KoFileDialog dialog(this, KoFileDialog::OpenFile, "BackgroundImages"); dialog.setCaption(i18n("Select a Background Image")); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setImageFilters(); QString fn = dialog.filename(); // dialog box was canceled or somehow no file was selected if (fn.isEmpty()) { return; } QImage image(fn); if (image.isNull()) { QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("%1 is not a valid image file!", fn)); } else { m_backgroundimage->setText(fn); } } void GeneralTab::clearBackgroundImage() { // clearing the background image text will implicitly make the background color be used m_backgroundimage->setText(""); } #include "kactioncollection.h" #include "KisActionsSnapshot.h" ShortcutSettingsTab::ShortcutSettingsTab(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(name); QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgShortcutSettings(this); l->addWidget(m_page, 0, 0); m_snapshot.reset(new KisActionsSnapshot); KActionCollection *collection = KisPart::instance()->currentMainwindow()->actionCollection(); Q_FOREACH (QAction *action, collection->actions()) { m_snapshot->addAction(action->objectName(), action); } QMap sortedCollections = m_snapshot->actionCollections(); for (auto it = sortedCollections.constBegin(); it != sortedCollections.constEnd(); ++it) { m_page->addCollection(it.value(), it.key()); } } ShortcutSettingsTab::~ShortcutSettingsTab() { } void ShortcutSettingsTab::setDefault() { m_page->allDefault(); } void ShortcutSettingsTab::saveChanges() { m_page->save(); KisActionRegistry::instance()->settingsPageSaved(); } void ShortcutSettingsTab::cancelChanges() { m_page->undo(); } ColorSettingsTab::ColorSettingsTab(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(name); // XXX: Make sure only profiles that fit the specified color model // are shown in the profile combos QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgColorSettings(this); l->addWidget(m_page, 0, 0); KisConfig cfg; m_page->chkUseSystemMonitorProfile->setChecked(cfg.useSystemMonitorProfile()); connect(m_page->chkUseSystemMonitorProfile, SIGNAL(toggled(bool)), this, SLOT(toggleAllowMonitorProfileSelection(bool))); m_page->cmbWorkingColorSpace->setIDList(KoColorSpaceRegistry::instance()->listKeys()); m_page->cmbWorkingColorSpace->setCurrent(cfg.workingColorSpace()); m_page->bnAddColorProfile->setIcon(KisIconUtils::loadIcon("document-open")); m_page->bnAddColorProfile->setToolTip( i18n("Open Color Profile") ); connect(m_page->bnAddColorProfile, SIGNAL(clicked()), SLOT(installProfile())); QFormLayout *monitorProfileGrid = new QFormLayout(m_page->monitorprofileholder); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { QLabel *lbl = new QLabel(i18nc("The number of the screen", "Screen %1:", i + 1)); m_monitorProfileLabels << lbl; SqueezedComboBox *cmb = new SqueezedComboBox(); cmb->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); monitorProfileGrid->addRow(lbl, cmb); m_monitorProfileWidgets << cmb; } refillMonitorProfiles(KoID("RGBA", "")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } m_page->chkBlackpoint->setChecked(cfg.useBlackPointCompensation()); m_page->chkAllowLCMSOptimization->setChecked(cfg.allowLCMSOptimization()); KisImageConfig cfgImage; KisProofingConfigurationSP proofingConfig = cfgImage.defaultProofingconfiguration(); m_page->sldAdaptationState->setMaximum(20); m_page->sldAdaptationState->setMinimum(0); m_page->sldAdaptationState->setValue((int)proofingConfig->adaptationState*20); //probably this should become the screenprofile? KoColor ga(KoColorSpaceRegistry::instance()->rgb8()); ga.fromKoColor(proofingConfig->warningColor); m_page->gamutAlarm->setColor(ga); const KoColorSpace *proofingSpace = KoColorSpaceRegistry::instance()->colorSpace(proofingConfig->proofingModel, proofingConfig->proofingDepth, proofingConfig->proofingProfile); if (proofingSpace) { m_page->proofingSpaceSelector->setCurrentColorSpace(proofingSpace); } m_page->cmbProofingIntent->setCurrentIndex((int)proofingConfig->intent); m_page->ckbProofBlackPoint->setChecked(proofingConfig->conversionFlags.testFlag(KoColorConversionTransformation::BlackpointCompensation)); m_pasteBehaviourGroup.addButton(m_page->radioPasteWeb, PASTE_ASSUME_WEB); m_pasteBehaviourGroup.addButton(m_page->radioPasteMonitor, PASTE_ASSUME_MONITOR); m_pasteBehaviourGroup.addButton(m_page->radioPasteAsk, PASTE_ASK); QAbstractButton *button = m_pasteBehaviourGroup.button(cfg.pasteBehaviour()); Q_ASSERT(button); if (button) { button->setChecked(true); } m_page->cmbMonitorIntent->setCurrentIndex(cfg.monitorRenderIntent()); toggleAllowMonitorProfileSelection(cfg.useSystemMonitorProfile()); } void ColorSettingsTab::installProfile() { KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocumentICC"); dialog.setCaption(i18n("Install Color Profiles")); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); dialog.setMimeTypeFilters(QStringList() << "application/vnd.iccprofile", "application/vnd.iccprofile"); QStringList profileNames = dialog.filenames(); KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); Q_ASSERT(iccEngine); QString saveLocation = KoResourcePaths::saveLocation("icc_profiles"); Q_FOREACH (const QString &profileName, profileNames) { if (!QFile::copy(profileName, saveLocation + QFileInfo(profileName).fileName())) { qWarning() << "Could not install profile!" << saveLocation + QFileInfo(profileName).fileName(); continue; } iccEngine->addProfile(saveLocation + QFileInfo(profileName).fileName()); } KisConfig cfg; refillMonitorProfiles(KoID("RGBA", "")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } } void ColorSettingsTab::toggleAllowMonitorProfileSelection(bool useSystemProfile) { if (useSystemProfile) { KisConfig cfg; QStringList devices = KisColorManager::instance()->devices(); if (devices.size() == QApplication::desktop()->screenCount()) { for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileWidgets[i]->clear(); QString monitorForScreen = cfg.monitorForScreen(i, devices[i]); Q_FOREACH (const QString &device, devices) { m_monitorProfileLabels[i]->setText(i18nc("The display/screen we got from Qt", "Screen %1:", i + 1)); m_monitorProfileWidgets[i]->addSqueezedItem(KisColorManager::instance()->deviceName(device), device); if (devices[i] == monitorForScreen) { m_monitorProfileWidgets[i]->setCurrentIndex(i); } } } } } else { KisConfig cfg; refillMonitorProfiles(KoID("RGBA", "")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } } } void ColorSettingsTab::setDefault() { m_page->cmbWorkingColorSpace->setCurrent("RGBA"); refillMonitorProfiles(KoID("RGBA", "")); KisConfig cfg; KisImageConfig cfgImage; KisProofingConfigurationSP proofingConfig = cfgImage.defaultProofingconfiguration(); const KoColorSpace *proofingSpace = KoColorSpaceRegistry::instance()->colorSpace(proofingConfig->proofingModel,proofingConfig->proofingDepth,proofingConfig->proofingProfile); if (proofingSpace) { m_page->proofingSpaceSelector->setCurrentColorSpace(proofingSpace); } m_page->cmbProofingIntent->setCurrentIndex((int)proofingConfig->intent); m_page->ckbProofBlackPoint->setChecked(proofingConfig->conversionFlags.testFlag(KoColorConversionTransformation::BlackpointCompensation)); m_page->sldAdaptationState->setValue(0); //probably this should become the screenprofile? KoColor ga(KoColorSpaceRegistry::instance()->rgb8()); ga.fromKoColor(proofingConfig->warningColor); m_page->gamutAlarm->setColor(ga); m_page->chkBlackpoint->setChecked(cfg.useBlackPointCompensation(true)); m_page->chkAllowLCMSOptimization->setChecked(cfg.allowLCMSOptimization(true)); m_page->cmbMonitorIntent->setCurrentIndex(cfg.monitorRenderIntent(true)); m_page->chkUseSystemMonitorProfile->setChecked(cfg.useSystemMonitorProfile(true)); QAbstractButton *button = m_pasteBehaviourGroup.button(cfg.pasteBehaviour(true)); Q_ASSERT(button); if (button) { button->setChecked(true); } } void ColorSettingsTab::refillMonitorProfiles(const KoID & colorSpaceId) { for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileWidgets[i]->clear(); } QMap profileList; Q_FOREACH(const KoColorProfile *profile, KoColorSpaceRegistry::instance()->profilesFor(colorSpaceId.id())) { profileList[profile->name()] = profile; } Q_FOREACH (const KoColorProfile *profile, profileList.values()) { //qDebug() << "Profile" << profile->name() << profile->isSuitableForDisplay() << csf->defaultProfile(); if (profile->isSuitableForDisplay()) { for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileWidgets[i]->addSqueezedItem(profile->name()); } } } for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileLabels[i]->setText(i18nc("The number of the screen", "Screen %1:", i + 1)); m_monitorProfileWidgets[i]->setCurrent(KoColorSpaceRegistry::instance()->defaultProfileForColorSpace(colorSpaceId.id())); } } //--------------------------------------------------------------------------------------------------- void TabletSettingsTab::setDefault() { KisCubicCurve curve; curve.fromString(DEFAULT_CURVE_STRING); m_page->pressureCurve->setCurve(curve); #ifdef Q_OS_WIN if (KisTabletSupportWin8::isAvailable()) { KisConfig cfg; m_page->radioWintab->setChecked(!cfg.useWin8PointerInput(true)); m_page->radioWin8PointerInput->setChecked(cfg.useWin8PointerInput(true)); } else { m_page->radioWintab->setChecked(true); m_page->radioWin8PointerInput->setChecked(false); } #endif } TabletSettingsTab::TabletSettingsTab(QWidget* parent, const char* name): QWidget(parent) { setObjectName(name); QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgTabletSettings(this); l->addWidget(m_page, 0, 0); KisConfig cfg; KisCubicCurve curve; curve.fromString( cfg.pressureTabletCurve() ); m_page->pressureCurve->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); m_page->pressureCurve->setCurve(curve); #ifdef Q_OS_WIN if (KisTabletSupportWin8::isAvailable()) { m_page->radioWintab->setChecked(!cfg.useWin8PointerInput()); m_page->radioWin8PointerInput->setChecked(cfg.useWin8PointerInput()); } else { m_page->radioWintab->setChecked(true); m_page->radioWin8PointerInput->setChecked(false); m_page->grpTabletApi->setVisible(false); } #else m_page->grpTabletApi->setVisible(false); #endif + connect(m_page->btnTabletTest, SIGNAL(clicked()), SLOT(slotTabletTest())); +} + +void TabletSettingsTab::slotTabletTest() +{ + TabletTestDialog tabletTestDialog(this); + tabletTestDialog.exec(); } //--------------------------------------------------------------------------------------------------- #include "kis_acyclic_signal_connector.h" int getTotalRAM() { KisImageConfig cfg; return cfg.totalRAM(); } int PerformanceTab::realTilesRAM() { return intMemoryLimit->value() - intPoolLimit->value(); } PerformanceTab::PerformanceTab(QWidget *parent, const char *name) : WdgPerformanceSettings(parent, name) { KisImageConfig cfg; const int totalRAM = cfg.totalRAM(); lblTotalMemory->setText(i18n("%1 MiB", totalRAM)); sliderMemoryLimit->setSuffix(i18n(" %")); sliderMemoryLimit->setRange(1, 100, 2); sliderMemoryLimit->setSingleStep(0.01); sliderPoolLimit->setSuffix(i18n(" %")); sliderPoolLimit->setRange(0, 20, 2); sliderMemoryLimit->setSingleStep(0.01); sliderUndoLimit->setSuffix(i18n(" %")); sliderUndoLimit->setRange(0, 50, 2); sliderMemoryLimit->setSingleStep(0.01); intMemoryLimit->setMinimumWidth(80); intPoolLimit->setMinimumWidth(80); intUndoLimit->setMinimumWidth(80); SliderAndSpinBoxSync *sync1 = new SliderAndSpinBoxSync(sliderMemoryLimit, intMemoryLimit, getTotalRAM); sync1->slotParentValueChanged(); m_syncs << sync1; SliderAndSpinBoxSync *sync2 = new SliderAndSpinBoxSync(sliderPoolLimit, intPoolLimit, std::bind(&KisIntParseSpinBox::value, intMemoryLimit)); connect(intMemoryLimit, SIGNAL(valueChanged(int)), sync2, SLOT(slotParentValueChanged())); sync2->slotParentValueChanged(); m_syncs << sync2; SliderAndSpinBoxSync *sync3 = new SliderAndSpinBoxSync(sliderUndoLimit, intUndoLimit, std::bind(&PerformanceTab::realTilesRAM, this)); connect(intPoolLimit, SIGNAL(valueChanged(int)), sync3, SLOT(slotParentValueChanged())); sync3->slotParentValueChanged(); m_syncs << sync3; sliderSwapSize->setSuffix(i18n(" GiB")); sliderSwapSize->setRange(1, 64); intSwapSize->setRange(1, 64); KisAcyclicSignalConnector *swapSizeConnector = new KisAcyclicSignalConnector(this); swapSizeConnector->connectForwardInt(sliderSwapSize, SIGNAL(valueChanged(int)), intSwapSize, SLOT(setValue(int))); swapSizeConnector->connectBackwardInt(intSwapSize, SIGNAL(valueChanged(int)), sliderSwapSize, SLOT(setValue(int))); lblSwapFileLocation->setText(cfg.swapDir()); connect(bnSwapFile, SIGNAL(clicked()), SLOT(selectSwapDir())); sliderThreadsLimit->setRange(1, QThread::idealThreadCount()); sliderFrameClonesLimit->setRange(1, QThread::idealThreadCount()); sliderFpsLimit->setRange(20, 100); sliderFpsLimit->setSuffix(i18n(" fps")); connect(sliderThreadsLimit, SIGNAL(valueChanged(int)), SLOT(slotThreadsLimitChanged(int))); connect(sliderFrameClonesLimit, SIGNAL(valueChanged(int)), SLOT(slotFrameClonesLimitChanged(int))); load(false); } PerformanceTab::~PerformanceTab() { qDeleteAll(m_syncs); } void PerformanceTab::load(bool requestDefault) { KisImageConfig cfg; sliderMemoryLimit->setValue(cfg.memoryHardLimitPercent(requestDefault)); sliderPoolLimit->setValue(cfg.memoryPoolLimitPercent(requestDefault)); sliderUndoLimit->setValue(cfg.memorySoftLimitPercent(requestDefault)); chkPerformanceLogging->setChecked(cfg.enablePerfLog(requestDefault)); chkProgressReporting->setChecked(cfg.enableProgressReporting(requestDefault)); sliderSwapSize->setValue(cfg.maxSwapSize(requestDefault) / 1024); lblSwapFileLocation->setText(cfg.swapDir(requestDefault)); m_lastUsedThreadsLimit = cfg.maxNumberOfThreads(requestDefault); m_lastUsedClonesLimit = cfg.frameRenderingClones(requestDefault); sliderThreadsLimit->setValue(m_lastUsedThreadsLimit); sliderFrameClonesLimit->setValue(m_lastUsedClonesLimit); sliderFpsLimit->setValue(cfg.fpsLimit(requestDefault)); { KisConfig cfg2; chkOpenGLFramerateLogging->setChecked(cfg2.enableOpenGLFramerateLogging(requestDefault)); chkBrushSpeedLogging->setChecked(cfg2.enableBrushSpeedLogging(requestDefault)); chkDisableVectorOptimizations->setChecked(cfg2.enableAmdVectorizationWorkaround(requestDefault)); } } void PerformanceTab::save() { KisImageConfig cfg; cfg.setMemoryHardLimitPercent(sliderMemoryLimit->value()); cfg.setMemorySoftLimitPercent(sliderUndoLimit->value()); cfg.setMemoryPoolLimitPercent(sliderPoolLimit->value()); cfg.setEnablePerfLog(chkPerformanceLogging->isChecked()); cfg.setEnableProgressReporting(chkProgressReporting->isChecked()); cfg.setMaxSwapSize(sliderSwapSize->value() * 1024); cfg.setSwapDir(lblSwapFileLocation->text()); cfg.setMaxNumberOfThreads(sliderThreadsLimit->value()); cfg.setFrameRenderingClones(sliderFrameClonesLimit->value()); cfg.setFpsLimit(sliderFpsLimit->value()); { KisConfig cfg2; cfg2.setEnableOpenGLFramerateLogging(chkOpenGLFramerateLogging->isChecked()); cfg2.setEnableBrushSpeedLogging(chkBrushSpeedLogging->isChecked()); cfg2.setEnableAmdVectorizationWorkaround(chkDisableVectorOptimizations->isChecked()); } } void PerformanceTab::selectSwapDir() { KisImageConfig cfg; QString swapDir = cfg.swapDir(); swapDir = QFileDialog::getExistingDirectory(0, i18nc("@title:window", "Select a swap directory"), swapDir); if (swapDir.isEmpty()) { return; } lblSwapFileLocation->setText(swapDir); } void PerformanceTab::slotThreadsLimitChanged(int value) { KisSignalsBlocker b(sliderFrameClonesLimit); sliderFrameClonesLimit->setValue(qMin(m_lastUsedClonesLimit, value)); m_lastUsedThreadsLimit = value; } void PerformanceTab::slotFrameClonesLimitChanged(int value) { KisSignalsBlocker b(sliderThreadsLimit); sliderThreadsLimit->setValue(qMax(m_lastUsedThreadsLimit, value)); m_lastUsedClonesLimit = value; } //--------------------------------------------------------------------------------------------------- #include "KoColor.h" DisplaySettingsTab::DisplaySettingsTab(QWidget *parent, const char *name) : WdgDisplaySettings(parent, name) { KisConfig cfg; const QString rendererOpenGLText = i18nc("canvas renderer", "OpenGL"); const QString rendererAngleText = i18nc("canvas renderer", "Direct3D 11 via ANGLE"); #ifdef Q_OS_WIN cmbRenderer->clear(); QString qtPreferredRendererText; if (KisOpenGL::getQtPreferredOpenGLRenderer() == KisOpenGL::RendererAngle) { qtPreferredRendererText = rendererAngleText; } else { qtPreferredRendererText = rendererOpenGLText; } cmbRenderer->addItem(i18nc("canvas renderer", "Auto (%1)", qtPreferredRendererText), KisOpenGL::RendererAuto); cmbRenderer->setCurrentIndex(0); if (KisOpenGL::getSupportedOpenGLRenderers() & KisOpenGL::RendererDesktopGL) { cmbRenderer->addItem(rendererOpenGLText, KisOpenGL::RendererDesktopGL); if (KisOpenGL::getNextUserOpenGLRendererConfig() == KisOpenGL::RendererDesktopGL) { cmbRenderer->setCurrentIndex(cmbRenderer->count() - 1); } } if (KisOpenGL::getSupportedOpenGLRenderers() & KisOpenGL::RendererAngle) { cmbRenderer->addItem(rendererAngleText, KisOpenGL::RendererAngle); if (KisOpenGL::getNextUserOpenGLRendererConfig() == KisOpenGL::RendererAngle) { cmbRenderer->setCurrentIndex(cmbRenderer->count() - 1); } } #else lblRenderer->setEnabled(false); cmbRenderer->setEnabled(false); cmbRenderer->clear(); cmbRenderer->addItem(rendererOpenGLText); cmbRenderer->setCurrentIndex(0); #endif #ifdef Q_OS_WIN if (!(KisOpenGL::getSupportedOpenGLRenderers() & (KisOpenGL::RendererDesktopGL | KisOpenGL::RendererAngle))) { #else if (!KisOpenGL::hasOpenGL()) { #endif grpOpenGL->setEnabled(false); grpOpenGL->setChecked(false); chkUseTextureBuffer->setEnabled(false); chkDisableVsync->setEnabled(false); cmbFilterMode->setEnabled(false); } else { grpOpenGL->setEnabled(true); grpOpenGL->setChecked(cfg.useOpenGL()); chkUseTextureBuffer->setEnabled(cfg.useOpenGL()); chkUseTextureBuffer->setChecked(cfg.useOpenGLTextureBuffer()); chkDisableVsync->setVisible(cfg.showAdvancedOpenGLSettings()); chkDisableVsync->setEnabled(cfg.useOpenGL()); chkDisableVsync->setChecked(cfg.disableVSync()); cmbFilterMode->setEnabled(cfg.useOpenGL()); cmbFilterMode->setCurrentIndex(cfg.openGLFilteringMode()); // Don't show the high quality filtering mode if it's not available if (!KisOpenGL::supportsLoD()) { cmbFilterMode->removeItem(3); } } const QStringList openglWarnings = KisOpenGL::getOpenGLWarnings(); if (openglWarnings.isEmpty()) { lblOpenGLWarnings->setVisible(false); } else { QString text(" "); text.append(i18n("Warning(s):")); text.append("
        "); Q_FOREACH (const QString &warning, openglWarnings) { text.append("
      • "); text.append(warning.toHtmlEscaped()); text.append("
      • "); } text.append("
      "); lblOpenGLWarnings->setText(text); lblOpenGLWarnings->setVisible(true); } if (qApp->applicationName() == "kritasketch" || qApp->applicationName() == "kritagemini") { grpOpenGL->setVisible(false); grpOpenGL->setMaximumHeight(0); } KoColor c; c.fromQColor(cfg.selectionOverlayMaskColor()); c.setOpacity(1.0); btnSelectionOverlayColor->setColor(c); sldSelectionOverlayOpacity->setRange(0.0, 1.0, 2); sldSelectionOverlayOpacity->setSingleStep(0.05); sldSelectionOverlayOpacity->setValue(cfg.selectionOverlayMaskColor().alphaF()); intCheckSize->setValue(cfg.checkSize()); chkMoving->setChecked(cfg.scrollCheckers()); KoColor ck1(KoColorSpaceRegistry::instance()->rgb8()); ck1.fromQColor(cfg.checkersColor1()); colorChecks1->setColor(ck1); KoColor ck2(KoColorSpaceRegistry::instance()->rgb8()); ck2.fromQColor(cfg.checkersColor2()); colorChecks2->setColor(ck2); KoColor cb(KoColorSpaceRegistry::instance()->rgb8()); cb.fromQColor(cfg.canvasBorderColor()); canvasBorder->setColor(cb); hideScrollbars->setChecked(cfg.hideScrollbars()); chkCurveAntialiasing->setChecked(cfg.antialiasCurves()); chkSelectionOutlineAntialiasing->setChecked(cfg.antialiasSelectionOutline()); chkChannelsAsColor->setChecked(cfg.showSingleChannelAsColor()); chkHidePopups->setChecked(cfg.hidePopups()); connect(grpOpenGL, SIGNAL(toggled(bool)), SLOT(slotUseOpenGLToggled(bool))); KoColor gridColor(KoColorSpaceRegistry::instance()->rgb8()); gridColor.fromQColor(cfg.getPixelGridColor()); pixelGridColorButton->setColor(gridColor); pixelGridDrawingThresholdBox->setValue(cfg.getPixelGridDrawingThreshold() * 100); } void DisplaySettingsTab::setDefault() { KisConfig cfg; cmbRenderer->setCurrentIndex(0); #ifdef Q_OS_WIN if (!(KisOpenGL::getSupportedOpenGLRenderers() & (KisOpenGL::RendererDesktopGL | KisOpenGL::RendererAngle))) { #else if (!KisOpenGL::hasOpenGL()) { #endif grpOpenGL->setEnabled(false); grpOpenGL->setChecked(false); chkUseTextureBuffer->setEnabled(false); chkDisableVsync->setEnabled(false); cmbFilterMode->setEnabled(false); } else { grpOpenGL->setEnabled(true); grpOpenGL->setChecked(cfg.useOpenGL(true)); chkUseTextureBuffer->setChecked(cfg.useOpenGLTextureBuffer(true)); chkUseTextureBuffer->setEnabled(true); chkDisableVsync->setEnabled(true); chkDisableVsync->setChecked(cfg.disableVSync(true)); cmbFilterMode->setEnabled(true); cmbFilterMode->setCurrentIndex(cfg.openGLFilteringMode(true)); } chkMoving->setChecked(cfg.scrollCheckers(true)); intCheckSize->setValue(cfg.checkSize(true)); KoColor ck1(KoColorSpaceRegistry::instance()->rgb8()); ck1.fromQColor(cfg.checkersColor1(true)); colorChecks1->setColor(ck1); KoColor ck2(KoColorSpaceRegistry::instance()->rgb8()); ck2.fromQColor(cfg.checkersColor2(true)); colorChecks2->setColor(ck2); KoColor cvb(KoColorSpaceRegistry::instance()->rgb8()); cvb.fromQColor(cfg.canvasBorderColor(true)); canvasBorder->setColor(cvb); hideScrollbars->setChecked(cfg.hideScrollbars(true)); chkCurveAntialiasing->setChecked(cfg.antialiasCurves(true)); chkSelectionOutlineAntialiasing->setChecked(cfg.antialiasSelectionOutline(true)); chkChannelsAsColor->setChecked(cfg.showSingleChannelAsColor(true)); chkHidePopups->setChecked(cfg.hidePopups(true)); KoColor gridColor(KoColorSpaceRegistry::instance()->rgb8()); gridColor.fromQColor(cfg.getPixelGridColor(true)); pixelGridColorButton->setColor(gridColor); pixelGridDrawingThresholdBox->setValue(cfg.getPixelGridDrawingThreshold(true) * 100); } void DisplaySettingsTab::slotUseOpenGLToggled(bool isChecked) { chkUseTextureBuffer->setEnabled(isChecked); chkDisableVsync->setEnabled(isChecked); cmbFilterMode->setEnabled(isChecked); } //--------------------------------------------------------------------------------------------------- FullscreenSettingsTab::FullscreenSettingsTab(QWidget* parent) : WdgFullscreenSettingsBase(parent) { KisConfig cfg; chkDockers->setChecked(cfg.hideDockersFullscreen()); chkMenu->setChecked(cfg.hideMenuFullscreen()); chkScrollbars->setChecked(cfg.hideScrollbarsFullscreen()); chkStatusbar->setChecked(cfg.hideStatusbarFullscreen()); chkTitlebar->setChecked(cfg.hideTitlebarFullscreen()); chkToolbar->setChecked(cfg.hideToolbarFullscreen()); } void FullscreenSettingsTab::setDefault() { KisConfig cfg; chkDockers->setChecked(cfg.hideDockersFullscreen(true)); chkMenu->setChecked(cfg.hideMenuFullscreen(true)); chkScrollbars->setChecked(cfg.hideScrollbarsFullscreen(true)); chkStatusbar->setChecked(cfg.hideStatusbarFullscreen(true)); chkTitlebar->setChecked(cfg.hideTitlebarFullscreen(true)); chkToolbar->setChecked(cfg.hideToolbarFullscreen(true)); } //--------------------------------------------------------------------------------------------------- KisDlgPreferences::KisDlgPreferences(QWidget* parent, const char* name) : KPageDialog(parent) { Q_UNUSED(name); setWindowTitle(i18n("Configure Krita")); setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::RestoreDefaults); button(QDialogButtonBox::Ok)->setDefault(true); setFaceType(KPageDialog::Tree); // General KoVBox *vbox = new KoVBox(); KPageWidgetItem *page = new KPageWidgetItem(vbox, i18n("General")); page->setObjectName("general"); page->setHeader(i18n("General")); page->setIcon(KisIconUtils::loadIcon("go-home")); addPage(page); m_general = new GeneralTab(vbox); // Shortcuts vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Keyboard Shortcuts")); page->setObjectName("shortcuts"); page->setHeader(i18n("Shortcuts")); page->setIcon(KisIconUtils::loadIcon("document-export")); addPage(page); m_shortcutSettings = new ShortcutSettingsTab(vbox); connect(this, SIGNAL(accepted()), m_shortcutSettings, SLOT(saveChanges())); connect(this, SIGNAL(rejected()), m_shortcutSettings, SLOT(cancelChanges())); // Canvas input settings m_inputConfiguration = new KisInputConfigurationPage(); page = addPage(m_inputConfiguration, i18n("Canvas Input Settings")); page->setHeader(i18n("Canvas Input")); page->setObjectName("canvasinput"); page->setIcon(KisIconUtils::loadIcon("configure")); // Display vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Display")); page->setObjectName("display"); page->setHeader(i18n("Display")); page->setIcon(KisIconUtils::loadIcon("preferences-desktop-display")); addPage(page); m_displaySettings = new DisplaySettingsTab(vbox); // Color vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Color Management")); page->setObjectName("colormanagement"); page->setHeader(i18n("Color")); page->setIcon(KisIconUtils::loadIcon("preferences-desktop-color")); addPage(page); m_colorSettings = new ColorSettingsTab(vbox); // Performance vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Performance")); page->setObjectName("performance"); page->setHeader(i18n("Performance")); page->setIcon(KisIconUtils::loadIcon("applications-system")); addPage(page); m_performanceSettings = new PerformanceTab(vbox); // Tablet vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Tablet settings")); page->setObjectName("tablet"); page->setHeader(i18n("Tablet")); page->setIcon(KisIconUtils::loadIcon("document-edit")); addPage(page); m_tabletSettings = new TabletSettingsTab(vbox); // full-screen mode vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Canvas-only settings")); page->setObjectName("canvasonly"); page->setHeader(i18n("Canvas-only")); page->setIcon(KisIconUtils::loadIcon("folder-pictures")); addPage(page); m_fullscreenSettings = new FullscreenSettingsTab(vbox); // Author profiles m_authorPage = new KoConfigAuthorPage(); page = addPage(m_authorPage, i18nc("@title:tab Author page", "Author" )); page->setObjectName("author"); page->setHeader(i18n("Author")); page->setIcon(KisIconUtils::loadIcon("im-user")); QPushButton *restoreDefaultsButton = button(QDialogButtonBox::RestoreDefaults); restoreDefaultsButton->setText("Restore Defaults"); connect(this, SIGNAL(accepted()), m_inputConfiguration, SLOT(saveChanges())); connect(this, SIGNAL(rejected()), m_inputConfiguration, SLOT(revertChanges())); KisPreferenceSetRegistry *preferenceSetRegistry = KisPreferenceSetRegistry::instance(); Q_FOREACH (KisAbstractPreferenceSetFactory *preferenceSetFactory, preferenceSetRegistry->values()) { KisPreferenceSet* preferenceSet = preferenceSetFactory->createPreferenceSet(); vbox = new KoVBox(); page = new KPageWidgetItem(vbox, preferenceSet->name()); page->setHeader(preferenceSet->header()); page->setIcon(preferenceSet->icon()); addPage(page); preferenceSet->setParent(vbox); preferenceSet->loadPreferences(); connect(restoreDefaultsButton, SIGNAL(clicked(bool)), preferenceSet, SLOT(loadDefaultPreferences()), Qt::UniqueConnection); connect(this, SIGNAL(accepted()), preferenceSet, SLOT(savePreferences()), Qt::UniqueConnection); } connect(restoreDefaultsButton, SIGNAL(clicked(bool)), this, SLOT(slotDefault())); } KisDlgPreferences::~KisDlgPreferences() { } void KisDlgPreferences::slotDefault() { if (currentPage()->objectName() == "general") { m_general->setDefault(); } else if (currentPage()->objectName() == "shortcuts") { m_shortcutSettings->setDefault(); } else if (currentPage()->objectName() == "display") { m_displaySettings->setDefault(); } else if (currentPage()->objectName() == "colormanagement") { m_colorSettings->setDefault(); } else if (currentPage()->objectName() == "performance") { m_performanceSettings->load(true); } else if (currentPage()->objectName() == "tablet") { m_tabletSettings->setDefault(); } else if (currentPage()->objectName() == "canvasonly") { m_fullscreenSettings->setDefault(); } else if (currentPage()->objectName() == "canvasinput") { m_inputConfiguration->setDefaults(); } } bool KisDlgPreferences::editPreferences() { KisDlgPreferences* dialog; dialog = new KisDlgPreferences(); bool baccept = (dialog->exec() == Accepted); if (baccept) { // General settings KisConfig cfg; cfg.setNewCursorStyle(dialog->m_general->cursorStyle()); cfg.setNewOutlineStyle(dialog->m_general->outlineStyle()); cfg.setShowRootLayer(dialog->m_general->showRootLayer()); cfg.setShowOutlineWhilePainting(dialog->m_general->showOutlineWhilePainting()); cfg.setHideSplashScreen(dialog->m_general->hideSplashScreen()); cfg.setSessionOnStartup(dialog->m_general->sessionOnStartup()); cfg.setSaveSessionOnQuit(dialog->m_general->saveSessionOnQuit()); cfg.setCalculateAnimationCacheInBackground(dialog->m_general->calculateAnimationCacheInBackground()); KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); group.writeEntry("DontUseNativeFileDialog", !dialog->m_general->m_chkNativeFileDialog->isChecked()); cfg.writeEntry("maximumBrushSize", dialog->m_general->intMaxBrushSize->value()); cfg.writeEntry("mdi_viewmode", dialog->m_general->mdiMode()); cfg.setMDIBackgroundColor(dialog->m_general->m_mdiColor->color().toQColor()); cfg.setMDIBackgroundImage(dialog->m_general->m_backgroundimage->text()); cfg.setAutoSaveInterval(dialog->m_general->autoSaveInterval()); cfg.setBackupFile(dialog->m_general->m_backupFileCheckBox->isChecked()); cfg.setShowCanvasMessages(dialog->m_general->showCanvasMessages()); cfg.setCompressKra(dialog->m_general->compressKra()); const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); kritarc.setValue("EnableHiDPI", dialog->m_general->m_chkHiDPI->isChecked()); kritarc.setValue("EnableSingleApplication", dialog->m_general->m_chkSingleApplication->isChecked()); cfg.setToolOptionsInDocker(dialog->m_general->toolOptionsInDocker()); cfg.setKineticScrollingGesture(dialog->m_general->kineticScrollingGesture()); cfg.setKineticScrollingSensitivity(dialog->m_general->kineticScrollingSensitivity()); cfg.setKineticScrollingScrollbar(dialog->m_general->kineticScrollingScrollbar()); cfg.setSwitchSelectionCtrlAlt(dialog->m_general->switchSelectionCtrlAlt()); cfg.setDisableTouchOnCanvas(!dialog->m_general->chkEnableTouch->isChecked()); cfg.setConvertToImageColorspaceOnImport(dialog->m_general->convertToImageColorspaceOnImport()); cfg.setUndoStackLimit(dialog->m_general->undoStackSize()); cfg.setFavoritePresets(dialog->m_general->favoritePresets()); // Color settings cfg.setUseSystemMonitorProfile(dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()); for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()) { int currentIndex = dialog->m_colorSettings->m_monitorProfileWidgets[i]->currentIndex(); QString monitorid = dialog->m_colorSettings->m_monitorProfileWidgets[i]->itemData(currentIndex).toString(); cfg.setMonitorForScreen(i, monitorid); } else { cfg.setMonitorProfile(i, dialog->m_colorSettings->m_monitorProfileWidgets[i]->itemHighlighted(), dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()); } } cfg.setWorkingColorSpace(dialog->m_colorSettings->m_page->cmbWorkingColorSpace->currentItem().id()); KisImageConfig cfgImage; cfgImage.setDefaultProofingConfig(dialog->m_colorSettings->m_page->proofingSpaceSelector->currentColorSpace(), dialog->m_colorSettings->m_page->cmbProofingIntent->currentIndex(), dialog->m_colorSettings->m_page->ckbProofBlackPoint->isChecked(), dialog->m_colorSettings->m_page->gamutAlarm->color(), (double)dialog->m_colorSettings->m_page->sldAdaptationState->value()/20); cfg.setUseBlackPointCompensation(dialog->m_colorSettings->m_page->chkBlackpoint->isChecked()); cfg.setAllowLCMSOptimization(dialog->m_colorSettings->m_page->chkAllowLCMSOptimization->isChecked()); cfg.setPasteBehaviour(dialog->m_colorSettings->m_pasteBehaviourGroup.checkedId()); cfg.setRenderIntent(dialog->m_colorSettings->m_page->cmbMonitorIntent->currentIndex()); // Tablet settings cfg.setPressureTabletCurve( dialog->m_tabletSettings->m_page->pressureCurve->curve().toString() ); #ifdef Q_OS_WIN if (KisTabletSupportWin8::isAvailable()) { cfg.setUseWin8PointerInput(dialog->m_tabletSettings->m_page->radioWin8PointerInput->isChecked()); } #endif dialog->m_performanceSettings->save(); #ifdef Q_OS_WIN { KisOpenGL::OpenGLRenderer renderer = static_cast( dialog->m_displaySettings->cmbRenderer->itemData( dialog->m_displaySettings->cmbRenderer->currentIndex()).toInt()); KisOpenGL::setNextUserOpenGLRendererConfig(renderer); const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); kritarc.setValue("OpenGLRenderer", KisOpenGL::convertOpenGLRendererToConfig(renderer)); } #endif if (!cfg.useOpenGL() && dialog->m_displaySettings->grpOpenGL->isChecked()) cfg.setCanvasState("TRY_OPENGL"); cfg.setUseOpenGL(dialog->m_displaySettings->grpOpenGL->isChecked()); cfg.setUseOpenGLTextureBuffer(dialog->m_displaySettings->chkUseTextureBuffer->isChecked()); cfg.setOpenGLFilteringMode(dialog->m_displaySettings->cmbFilterMode->currentIndex()); cfg.setDisableVSync(dialog->m_displaySettings->chkDisableVsync->isChecked()); cfg.setCheckSize(dialog->m_displaySettings->intCheckSize->value()); cfg.setScrollingCheckers(dialog->m_displaySettings->chkMoving->isChecked()); cfg.setCheckersColor1(dialog->m_displaySettings->colorChecks1->color().toQColor()); cfg.setCheckersColor2(dialog->m_displaySettings->colorChecks2->color().toQColor()); cfg.setCanvasBorderColor(dialog->m_displaySettings->canvasBorder->color().toQColor()); cfg.setHideScrollbars(dialog->m_displaySettings->hideScrollbars->isChecked()); KoColor c = dialog->m_displaySettings->btnSelectionOverlayColor->color(); c.setOpacity(dialog->m_displaySettings->sldSelectionOverlayOpacity->value()); cfg.setSelectionOverlayMaskColor(c.toQColor()); cfg.setAntialiasCurves(dialog->m_displaySettings->chkCurveAntialiasing->isChecked()); cfg.setAntialiasSelectionOutline(dialog->m_displaySettings->chkSelectionOutlineAntialiasing->isChecked()); cfg.setShowSingleChannelAsColor(dialog->m_displaySettings->chkChannelsAsColor->isChecked()); cfg.setHidePopups(dialog->m_displaySettings->chkHidePopups->isChecked()); cfg.setHideDockersFullscreen(dialog->m_fullscreenSettings->chkDockers->checkState()); cfg.setHideMenuFullscreen(dialog->m_fullscreenSettings->chkMenu->checkState()); cfg.setHideScrollbarsFullscreen(dialog->m_fullscreenSettings->chkScrollbars->checkState()); cfg.setHideStatusbarFullscreen(dialog->m_fullscreenSettings->chkStatusbar->checkState()); cfg.setHideTitlebarFullscreen(dialog->m_fullscreenSettings->chkTitlebar->checkState()); cfg.setHideToolbarFullscreen(dialog->m_fullscreenSettings->chkToolbar->checkState()); cfg.setCursorMainColor(dialog->m_general->cursorColorBtutton->color().toQColor()); cfg.setPixelGridColor(dialog->m_displaySettings->pixelGridColorButton->color().toQColor()); cfg.setPixelGridDrawingThreshold(dialog->m_displaySettings->pixelGridDrawingThresholdBox->value() / 100); dialog->m_authorPage->apply(); } delete dialog; return baccept; } diff --git a/libs/ui/dialogs/kis_dlg_preferences.h b/libs/ui/dialogs/kis_dlg_preferences.h index d5436acc51..25c7926f0b 100644 --- a/libs/ui/dialogs/kis_dlg_preferences.h +++ b/libs/ui/dialogs/kis_dlg_preferences.h @@ -1,347 +1,350 @@ /* * preferencesdlg.h - part of KImageShop^WKrita * * Copyright (c) 1999 Michael Koch * Copyright (c) 2003-2011 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 _KIS_DLG_PREFERENCES_H_ #define _KIS_DLG_PREFERENCES_H_ #include #include #include #include #include "kis_global.h" #include #include "ui_wdggeneralsettings.h" #include "ui_wdgdisplaysettings.h" #include "ui_wdgcolorsettings.h" #include "ui_wdgtabletsettings.h" #include "ui_wdgperformancesettings.h" #include "ui_wdgfullscreensettings.h" #include "KisShortcutsDialog.h" class KoID; class KisInputConfigurationPage; class KoConfigAuthorPage; /** * "General"-tab for preferences dialog */ class WdgGeneralSettings : public QWidget, public Ui::WdgGeneralSettings { Q_OBJECT public: WdgGeneralSettings(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(name); setupUi(this); chkShowRootLayer->setVisible(false); } }; class GeneralTab : public WdgGeneralSettings { Q_OBJECT public: GeneralTab(QWidget *parent = 0, const char *name = 0); CursorStyle cursorStyle(); OutlineStyle outlineStyle(); KisConfig::SessionOnStartup sessionOnStartup() const; bool saveSessionOnQuit() const; bool showRootLayer(); int autoSaveInterval(); void setDefault(); int undoStackSize(); bool showOutlineWhilePainting(); bool hideSplashScreen(); int mdiMode(); int favoritePresets(); bool showCanvasMessages(); bool compressKra(); bool toolOptionsInDocker(); int kineticScrollingGesture(); int kineticScrollingSensitivity(); bool kineticScrollingScrollbar(); bool switchSelectionCtrlAlt(); bool convertToImageColorspaceOnImport(); bool calculateAnimationCacheInBackground(); private Q_SLOTS: void getBackgroundImage(); void clearBackgroundImage(); }; /** * "Shortcuts" tab for preferences dialog */ class WdgShortcutSettings : public KisShortcutsDialog { Q_OBJECT public: WdgShortcutSettings(QWidget *parent) : KisShortcutsDialog(KisShortcutsEditor::AllActions, KisShortcutsEditor::LetterShortcutsAllowed, parent) { } }; class KisActionsSnapshot; class ShortcutSettingsTab : public QWidget { Q_OBJECT public: ShortcutSettingsTab(QWidget *parent = 0, const char *name = 0); ~ShortcutSettingsTab() override; public: void setDefault(); WdgShortcutSettings *m_page; QScopedPointer m_snapshot; public Q_SLOTS: void saveChanges(); void cancelChanges(); }; /** * "Color" tab for preferences dialog */ class WdgColorSettings : public QWidget, public Ui::WdgColorSettings { Q_OBJECT public: WdgColorSettings(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class ColorSettingsTab : public QWidget { Q_OBJECT public: ColorSettingsTab(QWidget *parent = 0, const char *name = 0); private Q_SLOTS: void refillMonitorProfiles(const KoID & s); void installProfile(); void toggleAllowMonitorProfileSelection(bool useSystemProfile); public: void setDefault(); WdgColorSettings *m_page; QButtonGroup m_pasteBehaviourGroup; QList m_monitorProfileLabels; QList m_monitorProfileWidgets; }; //======================= class WdgTabletSettings : public QWidget, public Ui::WdgTabletSettings { Q_OBJECT public: WdgTabletSettings(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class TabletSettingsTab : public QWidget { Q_OBJECT public: TabletSettingsTab(QWidget *parent = 0, const char *name = 0); +private Q_SLOTS: + void slotTabletTest(); + public: void setDefault(); WdgTabletSettings *m_page; }; //======================= /** * "Performance"-tab for preferences dialog */ class SliderAndSpinBoxSync; class WdgPerformanceSettings : public QWidget, public Ui::WdgPerformanceSettings { Q_OBJECT public: WdgPerformanceSettings(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(name); setupUi(this); } }; class PerformanceTab : public WdgPerformanceSettings { Q_OBJECT public: PerformanceTab(QWidget *parent = 0, const char *name = 0); ~PerformanceTab() override; void load(bool requestDefault); void save(); private Q_SLOTS: void selectSwapDir(); void slotThreadsLimitChanged(int value); void slotFrameClonesLimitChanged(int value); private: int realTilesRAM(); private: QVector m_syncs; int m_lastUsedThreadsLimit; int m_lastUsedClonesLimit; }; //======================= class WdgDisplaySettings : public QWidget, public Ui::WdgDisplaySettings { Q_OBJECT public: WdgDisplaySettings(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(name); setupUi(this); } }; /** * Display settings tab for preferences dialog */ class DisplaySettingsTab : public WdgDisplaySettings { Q_OBJECT public: DisplaySettingsTab(QWidget *parent = 0, const char *name = 0); public: void setDefault(); protected Q_SLOTS: void slotUseOpenGLToggled(bool isChecked); public: }; //======================= /** * Full screen settings tab for preferences dialog */ class WdgFullscreenSettingsBase : public QWidget, public Ui::WdgFullscreenSettings { Q_OBJECT public: WdgFullscreenSettingsBase(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class FullscreenSettingsTab : public WdgFullscreenSettingsBase { Q_OBJECT public: FullscreenSettingsTab(QWidget *parent); public: void setDefault(); }; //======================= /** * Preferences dialog of KImageShop^WKrayon^WKrita */ class KisDlgPreferences : public KPageDialog { Q_OBJECT public: static bool editPreferences(); protected: KisDlgPreferences(QWidget *parent = 0, const char *name = 0); ~KisDlgPreferences() override; protected: GeneralTab *m_general; ShortcutSettingsTab *m_shortcutSettings; ColorSettingsTab *m_colorSettings; PerformanceTab *m_performanceSettings; DisplaySettingsTab *m_displaySettings; TabletSettingsTab *m_tabletSettings; FullscreenSettingsTab *m_fullscreenSettings; KisInputConfigurationPage *m_inputConfiguration; KoConfigAuthorPage *m_authorPage; protected Q_SLOTS: void slotDefault(); }; #endif diff --git a/libs/ui/flake/KisReferenceImagesLayer.cpp b/libs/ui/flake/KisReferenceImagesLayer.cpp index 4bb9ad3694..1a4787f63b 100644 --- a/libs/ui/flake/KisReferenceImagesLayer.cpp +++ b/libs/ui/flake/KisReferenceImagesLayer.cpp @@ -1,129 +1,142 @@ /* * Copyright (C) 2017 Jouni Pentikäinen * * 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 #include #include #include #include "KisReferenceImagesLayer.h" #include "KisReferenceImage.h" struct AddReferenceImageCommand : KoShapeCreateCommand { AddReferenceImageCommand(KisReferenceImagesLayer *layer, KisReferenceImage* referenceImage) : KoShapeCreateCommand(layer->shapeController(), {referenceImage}, layer, nullptr, kundo2_i18n("Add reference image")) , m_layer(layer) {} void redo() override { KoShapeCreateCommand::redo(); } void undo() override { KoShapeCreateCommand::undo(); } private: KisReferenceImagesLayer *m_layer; }; class ReferenceImagesCanvas : public KisShapeLayerCanvasBase { public: ReferenceImagesCanvas(KisReferenceImagesLayer *parent, KisImageWSP image) : KisShapeLayerCanvasBase(parent, image) , m_layer(parent) {} void updateCanvas(const QRectF &rect) override { if (!m_layer->image() || m_isDestroying) { return; } QRectF r = m_viewConverter->documentToView(rect); m_layer->signalUpdate(r); } void forceRepaint() override { m_layer->signalUpdate(m_layer->extent()); } void rerenderAfterBeingInvisible() override {} void resetCache() override {} void setImage(KisImageWSP /*image*/) override {} private: KisReferenceImagesLayer *m_layer; }; KisReferenceImagesLayer::KisReferenceImagesLayer(KoShapeBasedDocumentBase* shapeController, KisImageWSP image) : KisShapeLayer(shapeController, image, i18n("Reference images"), OPACITY_OPAQUE_U8, new ReferenceImagesCanvas(this, image)) {} KisReferenceImagesLayer::KisReferenceImagesLayer(const KisReferenceImagesLayer &rhs) : KisShapeLayer(rhs) {} KUndo2Command * KisReferenceImagesLayer::addReferenceImage(KisReferenceImage *referenceImage) { return new AddReferenceImageCommand(this, referenceImage); } +QVector KisReferenceImagesLayer::referenceImages() const +{ + QVector references; + + Q_FOREACH(auto shape, shapes()) { + KisReferenceImage *referenceImage = dynamic_cast(shape); + if (referenceImage) { + references.append(referenceImage); + } + } + return references; +} + void KisReferenceImagesLayer::paintReferences(QPainter &painter) { shapeManager()->paint(painter, *converter(), false); } bool KisReferenceImagesLayer::allowAsChild(KisNodeSP) const { return false; } bool KisReferenceImagesLayer::accept(KisNodeVisitor &visitor) { return visitor.visit(this); } void KisReferenceImagesLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) { visitor.visit(this, undoAdapter); } void KisReferenceImagesLayer::signalUpdate(const QRectF &rect) { emit sigUpdateCanvas(rect); } QColor KisReferenceImagesLayer::getPixel(QPointF position) const { const QPointF docPoint = converter()->viewToDocument(position); KoShape *shape = shapeManager()->shapeAt(docPoint); if (shape) { auto *reference = dynamic_cast(shape); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(reference, QColor()); return reference->getPixel(docPoint); } return QColor(); } diff --git a/libs/ui/flake/KisReferenceImagesLayer.h b/libs/ui/flake/KisReferenceImagesLayer.h index fefa34ad1c..341bea56f2 100644 --- a/libs/ui/flake/KisReferenceImagesLayer.h +++ b/libs/ui/flake/KisReferenceImagesLayer.h @@ -1,64 +1,65 @@ /* * Copyright (C) 2017 Jouni Pentikäinen * * 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 KRITA_KISREFERENCEIMAGESLAYER_H #define KRITA_KISREFERENCEIMAGESLAYER_H #include "kis_shape_layer.h" #include class KRITAUI_EXPORT KisReferenceImagesLayer : public KisShapeLayer { Q_OBJECT public: KisReferenceImagesLayer(KoShapeBasedDocumentBase* shapeController, KisImageWSP image); KisReferenceImagesLayer(const KisReferenceImagesLayer &rhs); KUndo2Command * addReferenceImage(KisReferenceImage *referenceImage); + QVector referenceImages() const; QColor getPixel(QPointF position) const; void paintReferences(QPainter &painter); bool allowAsChild(KisNodeSP) const override; bool accept(KisNodeVisitor&) override; void accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) override; KisNodeSP clone() const override { return new KisReferenceImagesLayer(*this); } Q_SIGNALS: /** * The content of the layer has changed, and the canvas decoration * needs to update. */ void sigUpdateCanvas(const QRectF &rect); private: void signalUpdate(const QRectF &rect); friend struct AddReferenceImageCommand; friend struct ReferenceImagesCanvas; }; #endif //KRITA_KISREFERENCEIMAGESLAYER_H diff --git a/libs/ui/forms/wdgcolorsettings.ui b/libs/ui/forms/wdgcolorsettings.ui index a9e41265c2..7502b1cadc 100644 --- a/libs/ui/forms/wdgcolorsettings.ui +++ b/libs/ui/forms/wdgcolorsettings.ui @@ -1,423 +1,423 @@ WdgColorSettings 0 0 568 335 Color Settings 0 General Default color model for new images: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 0 20 When Pasting Into Krita From Other Applications Assume sRGB (like images from the web are supposed to be seen) - Assume &monitor profile (like you see it in the other application) + Assume &monitor profile (like you see it in other applications) As&k each time Note: When copying/pasting inside Krita color info is always preserved. Use Blackpoint Compensation true Allow Little CMS optimizations (uncheck when using linear light RGB or XYZ) true Qt::Vertical 20 40 Display Use system monitor profile false 50 50 0 0 0 0 The icm profile for your calibrated monitor &Rendering intent: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter cmbMonitorIntent 0 0 Perceptual Relative Colorimetric Saturation Absolute Colorimetric Add new color profile: 24 24 Qt::Horizontal 40 20 Qt::Vertical 20 40 Soft Proofing Perceptual Relative Colorimetric Saturation Absolute Colorimetric Adaptation State: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Proofing Rendering Intent: Gamut Warning: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Horizontal Black Point Compensation Qt::Vertical 20 40 100 100 Note: these are the default proofing settings for new images. Qt::Vertical 20 40 KComboBox QComboBox
      kcombobox.h
      + + KisCmbIDList + QComboBox +
      widgets/kis_cmb_idlist.h
      +
      KisColorButton QPushButton
      kis_color_button.h
      KisColorSpaceSelector QWidget
      widgets/kis_color_space_selector.h
      1
      - - KisCmbIDList - QComboBox -
      widgets/kis_cmb_idlist.h
      -
      diff --git a/libs/ui/forms/wdglayerproperties.ui b/libs/ui/forms/wdglayerproperties.ui index ec90f0d019..6173b40bbd 100644 --- a/libs/ui/forms/wdglayerproperties.ui +++ b/libs/ui/forms/wdglayerproperties.ui @@ -1,299 +1,299 @@ WdgLayerProperties 0 0 740 514 - + &Name: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editName 0 0 &Opacity: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter intOpacity 0 0 - Composite mode: + Blending mode: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 0 0 Color space: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 TextLabel 0 0 Profile: Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing 0 0 TextLabel Qt::PlainText false Color label: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + 0 0 Dimensions: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 TextLabel - Qt::Horizontal QSizePolicy::Fixed 10 20 Qt::Vertical Properties Qt::Vertical Select the set of active channels. Select the set of active channels. Only active channels will be visible, filtered or affected by painting. &Active Channels Qt::Vertical QSizePolicy::MinimumExpanding 20 40 + + KisSliderSpinBox + QWidget +
      kis_slider_spin_box.h
      + 1 +
      KisCompositeOpComboBox QComboBox
      widgets/kis_cmb_composite.h
      - - KisSliderSpinBox - QWidget -
      kis_slider_spin_box.h
      -
      KisColorLabelSelectorWidget QWidget
      widgets/kis_color_label_selector_widget.h
      1
      diff --git a/libs/ui/forms/wdgpaintopsettings.ui b/libs/ui/forms/wdgpaintopsettings.ui index 58cea59c91..2ceb4d3b82 100644 --- a/libs/ui/forms/wdgpaintopsettings.ui +++ b/libs/ui/forms/wdgpaintopsettings.ui @@ -1,949 +1,949 @@ WdgPaintOpSettings 0 0 - 1200 + 1203 431 Brush Editor 9 1 0 0 55 55 true 0 0 320 60 320 60 false Qt::DefaultContextMenu QFrame::StyledPanel QFrame::Raised 0 Qt::ScrollBarAlwaysOff Qt::ScrollBarAlwaysOff true Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop QPainter::Antialiasing|QPainter::HighQualityAntialiasing|QPainter::SmoothPixmapTransform QGraphicsView::AnchorViewCenter 0 10 0 0 10 75 true current brush 0 0 24 24 24 24 180 0 0 0 16777215 28 Save 16777215 28 Cancel Qt::Horizontal 20 20 6 0 0 26 26 Current Brush Engine 0 0 26 26 26 26 0 0 30 30 30 30 true Qt::Horizontal 40 20 Qt::Horizontal QSizePolicy::MinimumExpanding 0 0 0 0 0 16777215 28 Save New Brush Preset... 0 0 16777215 28 Overwrite Brush Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 24 24 24 24 12 12 0 0 Presets 0 0 Engine: 0 0 0 0 0 0 0 120 1677215 1677215 Qt::Vertical QSizePolicy::Preferred 5 5 22 22 Qt::Horizontal 5 10 0 0 0 0 22 22 false false Qt::Vertical 20 10 false 0 3 1 3 3 0 4 100 0 QFrame::NoFrame QFrame::Plain 0 5 5 0 5 5 0 0 Erase mode will use a separate brush size Eraser switch size false - + 0 0 + + Erase mode will use a separate brush opacity + - Temporarily Save Tweaks To Presets + Eraser switch opacity - true + false - + 0 0 - - Erase mode will use a separate brush opacity - - Eraser switch opacity + Temporarily Save Tweaks To Presets - false + true Qt::Horizontal 40 20 60 0 5 5 0 5 0 false 5 5 5 5 5 24 24 24 20 12 12 Scratchpad QFrame::NoFrame QFrame::Plain 0 0 0 0 0 0 0 0 250 0 Fill area with brush preset icon Fill area with current image Fill area with gradient Fill area with background color Reset area to white KisScratchPad QWidget
      kis_scratch_pad.h
      1
      KisPresetLivePreviewView QGraphicsView
      kis_preset_live_preview_view.h
      1
      KisPresetSelectorStrip QWidget
      kis_preset_selector_strip.h
      1
      KisLodAvailabilityWidget QWidget
      kis_lod_availability_widget.h
      1
      diff --git a/libs/ui/forms/wdgtabletsettings.ui b/libs/ui/forms/wdgtabletsettings.ui index 999b047c17..604c4d0744 100644 --- a/libs/ui/forms/wdgtabletsettings.ui +++ b/libs/ui/forms/wdgtabletsettings.ui @@ -1,203 +1,210 @@ WdgTabletSettings 0 0 569 - 366 + 433 0 0 Color Settings 10 10 10 10 10 0 0 200 250 false 0 0 Low Pressure Qt::Horizontal 40 20 0 0 High Pressure 1.0 Qt::Vertical 20 40 0.0 Input Pressure Global Curve Qt::Horizontal 40 20 - + Qt::Vertical 20 40 + + + + Open Tablet Tester... + + + Tablet Input API (changing this requires restarting Krita) WinTab Windows 8+ Pointer Input (depends on Windows Ink) (EXPERIMENTAL) KisCurveWidget QWidget
      widgets/kis_curve_widget.h
      1
      diff --git a/libs/ui/input/config/kis_input_configuration_page.ui b/libs/ui/input/config/kis_input_configuration_page.ui index 34091cf051..39a30c1f24 100644 --- a/libs/ui/input/config/kis_input_configuration_page.ui +++ b/libs/ui/input/config/kis_input_configuration_page.ui @@ -1,102 +1,102 @@ KisInputConfigurationPage 0 0 689 483 - Profile + Input Profile Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 false 0 0 Duplicate current profile Edit Profiles 18 20 QFrame::NoFrame QFrame::Plain true 0 0 - 677 - 446 + 671 + 429 KComboBox QComboBox
      kcombobox.h
      diff --git a/libs/ui/input/kis_abstract_input_action.cpp b/libs/ui/input/kis_abstract_input_action.cpp index 149233dd50..561d327436 100644 --- a/libs/ui/input/kis_abstract_input_action.cpp +++ b/libs/ui/input/kis_abstract_input_action.cpp @@ -1,223 +1,223 @@ /* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra * * 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_abstract_input_action.h" #include #include #include #include class Q_DECL_HIDDEN KisAbstractInputAction::Private { public: QString id; QString name; QString description; QHash indexes; QPointF lastCursorPosition; - static KisInputManager* inputManager; + static KisInputManager *inputManager; }; KisInputManager *KisAbstractInputAction::Private::inputManager = 0; -KisAbstractInputAction::KisAbstractInputAction(const QString & id) +KisAbstractInputAction::KisAbstractInputAction(const QString &id) : d(new Private) { d->id = id; d->indexes.insert(i18n("Activate"), 0); } KisAbstractInputAction::~KisAbstractInputAction() { delete d; } void KisAbstractInputAction::activate(int shortcut) { Q_UNUSED(shortcut); } void KisAbstractInputAction::deactivate(int shortcut) { Q_UNUSED(shortcut); } void KisAbstractInputAction::begin(int shortcut, QEvent *event) { Q_UNUSED(shortcut); if (event) { d->lastCursorPosition = eventPosF(event); } } -void KisAbstractInputAction::inputEvent(QEvent* event) +void KisAbstractInputAction::inputEvent(QEvent *event) { if (event) { QPointF newPosition = eventPosF(event); cursorMoved(d->lastCursorPosition, newPosition); d->lastCursorPosition = newPosition; } } void KisAbstractInputAction::end(QEvent *event) { Q_UNUSED(event); } void KisAbstractInputAction::cursorMoved(const QPointF &lastPos, const QPointF &pos) { Q_UNUSED(lastPos); Q_UNUSED(pos); } bool KisAbstractInputAction::supportsHiResInputEvents() const { return false; } KisInputActionGroup KisAbstractInputAction::inputActionGroup(int shortcut) const { Q_UNUSED(shortcut); return ModifyingActionGroup; } KisInputManager* KisAbstractInputAction::inputManager() const { return Private::inputManager; } QString KisAbstractInputAction::name() const { return d->name; } QString KisAbstractInputAction::description() const { return d->description; } int KisAbstractInputAction::priority() const { return 0; } bool KisAbstractInputAction::canIgnoreModifiers() const { return false; } QHash< QString, int > KisAbstractInputAction::shortcutIndexes() const { return d->indexes; } QString KisAbstractInputAction::id() const { return d->id; } -void KisAbstractInputAction::setName(const QString& name) +void KisAbstractInputAction::setName(const QString &name) { d->name = name; } -void KisAbstractInputAction::setDescription(const QString& description) +void KisAbstractInputAction::setDescription(const QString &description) { d->description = description; } -void KisAbstractInputAction::setShortcutIndexes(const QHash< QString, int >& indexes) +void KisAbstractInputAction::setShortcutIndexes(const QHash< QString, int > &indexes) { d->indexes = indexes; } void KisAbstractInputAction::setInputManager(KisInputManager *manager) { Private::inputManager = manager; } bool KisAbstractInputAction::isShortcutRequired(int shortcut) const { Q_UNUSED(shortcut); return false; } - -QPoint KisAbstractInputAction::eventPos(const QEvent *event) { - +QPoint KisAbstractInputAction::eventPos(const QEvent *event) +{ if(!event) { return QPoint(); } switch (event->type()) { case QEvent::MouseMove: case QEvent::MouseButtonPress: + case QEvent::MouseButtonDblClick: case QEvent::MouseButtonRelease: return static_cast(event)->pos(); case QEvent::TabletMove: case QEvent::TabletPress: case QEvent::TabletRelease: return static_cast(event)->pos(); case QEvent::Wheel: return static_cast(event)->pos(); case QEvent::NativeGesture: return static_cast(event)->pos(); default: warnInput << "KisAbstractInputAction" << d->name << "tried to process event data from an unhandled event type" << event->type(); return QPoint(); } } - QPointF KisAbstractInputAction::eventPosF(const QEvent *event) { switch (event->type()) { case QEvent::MouseMove: case QEvent::MouseButtonPress: + case QEvent::MouseButtonDblClick: case QEvent::MouseButtonRelease: return static_cast(event)->localPos(); case QEvent::TabletMove: case QEvent::TabletPress: case QEvent::TabletRelease: return static_cast(event)->posF(); case QEvent::Wheel: return static_cast(event)->posF(); case QEvent::NativeGesture: return QPointF(static_cast(event)->pos()); default: warnInput << "KisAbstractInputAction" << d->name << "tried to process event data from an unhandled event type" << event->type(); return QPointF(); } } bool KisAbstractInputAction::isAvailable() const { return true; } diff --git a/libs/ui/input/kis_alternate_invocation_action.cpp b/libs/ui/input/kis_alternate_invocation_action.cpp index 0cb11249c3..e4d0064989 100644 --- a/libs/ui/input/kis_alternate_invocation_action.cpp +++ b/libs/ui/input/kis_alternate_invocation_action.cpp @@ -1,163 +1,162 @@ /* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra * * 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_alternate_invocation_action.h" #include #include #include #include #include "kis_input_manager.h" #include "kis_cursor.h" struct KisAlternateInvocationAction::Private { KisTool::ToolAction savedAction; }; KisAlternateInvocationAction::KisAlternateInvocationAction() : KisAbstractInputAction("Alternate Invocation") , m_d(new Private) { setName(i18n("Alternate Invocation")); setDescription(i18n("The Alternate Invocation action performs an alternate action with the current tool. For example, using the brush tool it picks a color from the canvas.")); QHash shortcuts; shortcuts.insert(i18n("Primary Mode"), PrimaryAlternateModeShortcut); shortcuts.insert(i18n("Secondary Mode"), SecondaryAlternateModeShortcut); shortcuts.insert(i18n("Tertiary Mode"), TertiaryAlternateModeShortcut); shortcuts.insert(i18n("Pick Foreground Color from Current Layer"), PickColorFgLayerModeShortcut); shortcuts.insert(i18n("Pick Background Color from Current Layer"), PickColorBgLayerModeShortcut); shortcuts.insert(i18n("Pick Foreground Color from Merged Image"), PickColorFgImageModeShortcut); shortcuts.insert(i18n("Pick Background Color from Merged Image"), PickColorBgImageModeShortcut); setShortcutIndexes(shortcuts); } KisAlternateInvocationAction::~KisAlternateInvocationAction() { } KisTool::ToolAction KisAlternateInvocationAction::shortcutToToolAction(int shortcut) { KisTool::ToolAction action = KisTool::Alternate_NONE; switch ((Shortcut)shortcut) { case PickColorFgLayerModeShortcut: action = KisTool::AlternatePickFgNode; break; case PickColorBgLayerModeShortcut: action = KisTool::AlternatePickBgNode; break; case PickColorFgImageModeShortcut: action = KisTool::AlternatePickFgImage; break; case PickColorBgImageModeShortcut: action = KisTool::AlternatePickBgImage; break; case PrimaryAlternateModeShortcut: action = KisTool::AlternateSecondary; break; case SecondaryAlternateModeShortcut: action = KisTool::AlternateThird; break; case TertiaryAlternateModeShortcut: action = KisTool::AlternateFourth; break; } return action; } void KisAlternateInvocationAction::activate(int shortcut) { KisTool::ToolAction action = shortcutToToolAction(shortcut); inputManager()->toolProxy()->activateToolAction(action); } void KisAlternateInvocationAction::deactivate(int shortcut) { KisTool::ToolAction action = shortcutToToolAction(shortcut); inputManager()->toolProxy()->deactivateToolAction(action); } int KisAlternateInvocationAction::priority() const { return 9; } void KisAlternateInvocationAction::begin(int shortcut, QEvent *event) { if (!event) return; KisAbstractInputAction::begin(shortcut, event); QMouseEvent targetEvent(QEvent::MouseButtonPress, eventPosF(event), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier); // There must be a better way m_d->savedAction = shortcutToToolAction(shortcut); inputManager()->toolProxy()->forwardEvent(KisToolProxy::BEGIN, m_d->savedAction, &targetEvent, event); } void KisAlternateInvocationAction::end(QEvent *event) { if (!event) return; Qt::KeyboardModifiers modifiers; switch (m_d->savedAction) { case KisTool::AlternatePickFgNode: modifiers = Qt::ControlModifier; break; case KisTool::AlternateThird: modifiers = Qt::ControlModifier | Qt::AltModifier; break; default: ; } QMouseEvent targetEvent = QMouseEvent(QEvent::MouseButtonRelease, eventPosF(event), Qt::LeftButton, Qt::LeftButton, modifiers); inputManager()->toolProxy()->forwardEvent(KisToolProxy::END, m_d->savedAction, &targetEvent, event); KisAbstractInputAction::end(event); } void KisAlternateInvocationAction::inputEvent(QEvent* event) { if (event && ((event->type() == QEvent::MouseMove) || (event->type() == QEvent::TabletMove))) { Qt::KeyboardModifiers modifiers; switch (m_d->savedAction) { case KisTool::AlternatePickFgNode: modifiers = Qt::ControlModifier; break; case KisTool::AlternateThird: modifiers = Qt::ControlModifier | Qt::AltModifier; break; default: modifiers = Qt::ShiftModifier; } QMouseEvent targetEvent(QEvent::MouseMove, eventPosF(event), Qt::LeftButton, Qt::LeftButton, modifiers); inputManager()->toolProxy()->forwardEvent(KisToolProxy::CONTINUE, m_d->savedAction, &targetEvent, event); } - } diff --git a/libs/ui/input/kis_change_frame_action.cpp b/libs/ui/input/kis_change_frame_action.cpp index 1f5c0ba51b..818cdbac27 100644 --- a/libs/ui/input/kis_change_frame_action.cpp +++ b/libs/ui/input/kis_change_frame_action.cpp @@ -1,78 +1,78 @@ /* * 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_change_frame_action.h" #include #include "kis_action.h" #include "kis_input_manager.h" #include "kis_canvas2.h" #include "KisViewManager.h" #include "kis_action_manager.h" #include "kis_node.h" struct KisChangeFrameAction::Private { }; KisChangeFrameAction::KisChangeFrameAction() : KisAbstractInputAction("Switch Time"), m_d(new Private) { setName(i18n("Switch Time")); - setDescription(i18n("The Switch Time action changes the current time of the image.")); + setDescription(i18n("The Switch Time action changes the current time of the animation.")); QHash< QString, int > shortcuts; shortcuts.insert(i18n("Next Frame"), NextFrameShortcut); shortcuts.insert(i18n("Previous Frame"), PreviousFrameShortcut); setShortcutIndexes(shortcuts); } KisChangeFrameAction::~KisChangeFrameAction() { } bool KisChangeFrameAction::isAvailable() const { KisNodeSP node = inputManager()->canvas()->viewManager()->activeNode(); return node ? node->isAnimated() : false; } void KisChangeFrameAction::begin(int shortcut, QEvent *event) { KisAbstractInputAction::begin(shortcut, event); switch(shortcut) { case NextFrameShortcut: { KisAction *action = inputManager()->canvas()->viewManager()->actionManager()->actionByName("next_frame"); if (action) { action->trigger(); } break; } case PreviousFrameShortcut: { KisAction *action = inputManager()->canvas()->viewManager()->actionManager()->actionByName("previous_frame"); if (action) { action->trigger(); } break; } } } diff --git a/libs/ui/input/wintab/drawpile_tablettester/README.md b/libs/ui/input/wintab/drawpile_tablettester/README.md new file mode 100644 index 0000000000..40b9e97ee8 --- /dev/null +++ b/libs/ui/input/wintab/drawpile_tablettester/README.md @@ -0,0 +1,17 @@ +Notes on code originating from external source: + +Tablet Tester +============= + +Files are taken and possibly modified from the git repo of [Drawpile] on +commit bc06dceba83e8c758d25cfe81b986e18f38aae95. + +[Drawpile]: https://github.com/drawpile/Drawpile + +File | Original in repo of Drawpile +-------------------- | --- +tablettester.cpp | src/desktop/dialogs/tablettester.cpp +tablettester.h | src/desktop/dialogs/tablettester.h +tablettest.cpp | src/desktop/widgets/tablettest.cpp +tablettest.h | src/desktop/widgets/tablettest.h +tablettest.ui | src/desktop/ui/tablettest.ui diff --git a/libs/ui/input/wintab/drawpile_tablettester/tablettest.cpp b/libs/ui/input/wintab/drawpile_tablettester/tablettest.cpp new file mode 100644 index 0000000000..2b4e2cce13 --- /dev/null +++ b/libs/ui/input/wintab/drawpile_tablettester/tablettest.cpp @@ -0,0 +1,131 @@ +/* + Drawpile - a collaborative drawing program. + + Copyright (C) 2017 Calle Laakkonen + + Drawpile 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 3 of the License, or + (at your option) any later version. + + Drawpile 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 Drawpile. If not, see . +*/ + +#include "tablettest.h" + +#include +#include + +TabletTester::TabletTester(QWidget *parent) + : QWidget(parent), m_mouseDown(false), m_tabletDown(false) +{ +} + +void TabletTester::clear() +{ + m_mousePath.clear(); + m_tabletPath.clear(); + update(); +} + +void TabletTester::paintEvent(QPaintEvent *e) +{ + Q_UNUSED(e); + const int w = width(); + const int h = height(); + QPainter p(this); + p.fillRect(0, 0, w, h, QColor(200, 200, 200)); + p.setPen(QColor(128, 128, 128)); + + // Draw grid + for(int i=w/8;ix()).arg(e->y()).arg(e->button())); + m_mouseDown = true; + m_mousePath.clear(); + update(); +} + +void TabletTester::mouseMoveEvent(QMouseEvent *e) +{ + Q_EMIT eventReport(QString("Mouse move X=%1 Y=%2 B=%3").arg(e->x()).arg(e->y()).arg(e->buttons())); + m_mousePath << e->pos(); + update(); +} + +void TabletTester::mouseReleaseEvent(QMouseEvent *e) +{ + Q_UNUSED(e); + Q_EMIT eventReport(QString("Mouse release")); + m_mouseDown = false; +} + +void TabletTester::tabletEvent(QTabletEvent *e) +{ + e->accept(); + + QString msg; + switch(e->device()) { + case QTabletEvent::Stylus: msg = "Stylus"; break; + default: msg = QString("Device(%1)").arg(e->device()); break; + } + + switch(e->type()) { + case QEvent::TabletMove: + msg += " move"; + break; + case QEvent::TabletPress: + msg += " press"; + m_tabletPath.clear(); + m_tabletDown = true; + break; + case QEvent::TabletRelease: + msg += " release"; + m_tabletDown = false; + break; + default: + msg += QString(" event-%1").arg(e->type()); + break; + } + + msg += QString(" X=%1 Y=%2 B=%3 P=%4%") + .arg(e->posF().x(), 0, 'f', 2) + .arg(e->posF().y(), 0, 'f', 2) + .arg(e->buttons()) + .arg(e->pressure()*100, 0, 'f', 1) + ; + + if(e->type() == QEvent::TabletMove) { + if(m_tabletDown) { + msg += " (DRAW)"; + m_tabletPath << e->pos(); + update(); + } else { + msg += " (HOVER)"; + } + } + + Q_EMIT eventReport(msg); +} diff --git a/libs/ui/input/wintab/drawpile_tablettester/tablettest.h b/libs/ui/input/wintab/drawpile_tablettester/tablettest.h new file mode 100644 index 0000000000..9ad772c72c --- /dev/null +++ b/libs/ui/input/wintab/drawpile_tablettester/tablettest.h @@ -0,0 +1,51 @@ +/* + Drawpile - a collaborative drawing program. + + Copyright (C) 2017 Calle Laakkonen + + Drawpile 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 3 of the License, or + (at your option) any later version. + + Drawpile 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 Drawpile. If not, see . +*/ + +#ifndef TABLETTEST_WIDGET_H +#define TABLETTEST_WIDGET_H + +#include + +class TabletTester : public QWidget { + Q_OBJECT +public: + TabletTester(QWidget *parent=nullptr); + +public Q_SLOTS: + void clear(); + +Q_SIGNALS: + void eventReport(const QString &msg); + +protected: + void paintEvent(QPaintEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + void tabletEvent(QTabletEvent *e) override; + +private: + QPolygon m_mousePath; + QPolygon m_tabletPath; + + bool m_mouseDown; + bool m_tabletDown; +}; + +#endif diff --git a/libs/ui/input/wintab/drawpile_tablettester/tablettest.ui b/libs/ui/input/wintab/drawpile_tablettester/tablettest.ui new file mode 100644 index 0000000000..7baa698361 --- /dev/null +++ b/libs/ui/input/wintab/drawpile_tablettester/tablettest.ui @@ -0,0 +1,99 @@ + + + TabletTest + + + + 0 + 0 + 730 + 385 + + + + Tablet Tester + + + + + + + + + + + true + + + + + + + Clear + + + + + + + + + + TabletTester + QWidget +
      input/wintab/drawpile_tablettester/tablettest.h
      + 1 +
      +
      + + + + pushButton + clicked() + logView + clear() + + + 722 + 377 + + + 659 + 84 + + + + + pushButton + clicked() + tablettest + clear() + + + 419 + 359 + + + 142 + 242 + + + + + tablettest + eventReport(QString) + logView + appendPlainText(QString) + + + 287 + 187 + + + 546 + 207 + + + + +
      diff --git a/libs/ui/input/wintab/drawpile_tablettester/tablettester.cpp b/libs/ui/input/wintab/drawpile_tablettester/tablettester.cpp new file mode 100644 index 0000000000..1a803b09e3 --- /dev/null +++ b/libs/ui/input/wintab/drawpile_tablettester/tablettester.cpp @@ -0,0 +1,63 @@ +/* + Drawpile - a collaborative drawing program. + + Copyright (C) 2017 Calle Laakkonen + + Drawpile 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 3 of the License, or + (at your option) any later version. + + Drawpile 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 Drawpile. If not, see . +*/ + +#include "tablettester.h" +#include "tablettest.h" +#include "ui_tablettest.h" + +TabletTestDialog::TabletTestDialog( QWidget *parent) : + QDialog(parent, Qt::Window) +{ + m_ui = new Ui_TabletTest; + m_ui->setupUi(this); + + qApp->installEventFilter(this); +} + +TabletTestDialog::~TabletTestDialog() +{ + qApp->removeEventFilter(this); + delete m_ui; +} + +bool TabletTestDialog::eventFilter(QObject *watched, QEvent *e) { + Q_UNUSED(watched); + if(e->type() == QEvent::TabletEnterProximity || e->type() == QEvent::TabletLeaveProximity) { + QTabletEvent *te = static_cast(e); + bool isEraser = te->pointerType() == QTabletEvent::Eraser; + bool isNear = e->type() == QEvent::TabletEnterProximity; + QString msg; + if(isEraser) { + if (isNear) { + msg = QStringLiteral("Eraser brought near"); + } else { + msg = QStringLiteral("Eraser taken away"); + } + } else { + if (isNear) { + msg = QStringLiteral("Pen tip brought near"); + } else { + msg = QStringLiteral("Pen tip taken away"); + } + } + + m_ui->logView->appendPlainText(msg); + } + return QDialog::eventFilter(watched, e); +} diff --git a/libs/ui/input/wintab/drawpile_tablettester/tablettester.h b/libs/ui/input/wintab/drawpile_tablettester/tablettester.h new file mode 100644 index 0000000000..ec3923a5c3 --- /dev/null +++ b/libs/ui/input/wintab/drawpile_tablettester/tablettester.h @@ -0,0 +1,39 @@ +/* + Drawpile - a collaborative drawing program. + + Copyright (C) 2017 Calle Laakkonen + + Drawpile 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 3 of the License, or + (at your option) any later version. + + Drawpile 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 Drawpile. If not, see . +*/ +#ifndef TABLETTESTDIALOG_H +#define TABLETTESTDIALOG_H + +#include + +class Ui_TabletTest; + +class TabletTestDialog : public QDialog +{ + Q_OBJECT +public: + TabletTestDialog(QWidget *parent=nullptr); + ~TabletTestDialog(); + bool eventFilter(QObject *watched, QEvent *event) override; + +private: + Ui_TabletTest *m_ui; + +}; + +#endif diff --git a/libs/ui/kis_paintop_option.h b/libs/ui/kis_paintop_option.h index 62c84d7771..925e24c2cc 100644 --- a/libs/ui/kis_paintop_option.h +++ b/libs/ui/kis_paintop_option.h @@ -1,128 +1,133 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2008 * * 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_PAINTOP_OPTION_H #define KIS_PAINTOP_OPTION_H #include #include #include #include -#include +#include class QWidget; class QString; class KisPaintopLodLimitations; /** * Base interface for paintop options. A paintop option * can be enabled/disabled, has a configuration page * (for example, a curve), a user-visible name and can * be serialized and deserialized into KisPaintOpPresets * + * Because KisPaintOpOption classes create a QWidget in + * their constructor (the configuration page) you CANNOT + * create those objects in a KisPaintOp. KisPaintOps are + * created in non-gui threads. + * * Options are disabled by default. */ class KRITAUI_EXPORT KisPaintOpOption : public QObject { Q_OBJECT public: enum PaintopCategory { GENERAL, COLOR, TEXTURE, FILTER, MASKING_BRUSH }; KisPaintOpOption(KisPaintOpOption::PaintopCategory category, bool checked); ~KisPaintOpOption() override; KisPaintOpOption::PaintopCategory category() const; virtual bool isCheckable() const; virtual bool isChecked() const; virtual void setChecked(bool checked); void setLocked(bool value); bool isLocked() const; /** * Reimplement this to use the image in the option widget */ virtual void setImage(KisImageWSP image); virtual void setNode(KisNodeWSP node); void startReadOptionSetting(const KisPropertiesConfigurationSP setting); void startWriteOptionSetting(KisPropertiesConfigurationSP setting) const; - QWidget* configurationPage() const; + QWidget *configurationPage() const; virtual void lodLimitations(KisPaintopLodLimitations *l) const; protected: - void setConfigurationPage(QWidget * page); + void setConfigurationPage(QWidget *page); protected: /** * Re-implement this to save the configuration to the paint configuration. */ virtual void writeOptionSetting(KisPropertiesConfigurationSP setting) const { Q_UNUSED(setting); } /** * Re-implement this to set te widgets with the values in @param setting. */ virtual void readOptionSetting(const KisPropertiesConfigurationSP setting) { Q_UNUSED(setting); } protected Q_SLOTS: void emitSettingChanged(); void emitCheckedChanged(); Q_SIGNALS: /** * emit this whenever a setting has changed. It will update the preview */ void sigSettingChanged(); /** * emit this whenever a checked state of the option has changed. It as always * emitted *before* sigSettingChanged() */ void sigCheckedChanged(bool value); protected: bool m_checkable; bool m_locked; private: struct Private; Private* const m_d; }; #endif diff --git a/libs/ui/kis_palette_view.cpp b/libs/ui/kis_palette_view.cpp index dfaab179f0..04c70c32af 100644 --- a/libs/ui/kis_palette_view.cpp +++ b/libs/ui/kis_palette_view.cpp @@ -1,344 +1,344 @@ /* * 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 "kis_palette_view.h" #include #include #include "kis_palette_delegate.h" #include "KisPaletteModel.h" #include "kis_config.h" #include #include #include #include #include #include #include #include #include struct KisPaletteView::Private { KisPaletteModel *model = nullptr; bool allowPaletteModification = true; }; KisPaletteView::KisPaletteView(QWidget *parent) : KoTableView(parent) , m_d(new Private) { setShowGrid(false); horizontalHeader()->setVisible(false); verticalHeader()->setVisible(false); setItemDelegate(new KisPaletteDelegate()); // setDragEnabled(true); // setDragDropMode(QAbstractItemView::InternalMove); setDropIndicatorShown(true); KisConfig cfg; //QPalette pal(palette()); //pal.setColor(QPalette::Base, cfg.getMDIBackgroundColor()); //setAutoFillBackground(true); //setPalette(pal); int defaultSectionSize = cfg.paletteDockerPaletteViewSectionSize(); horizontalHeader()->setDefaultSectionSize(defaultSectionSize); verticalHeader()->setDefaultSectionSize(defaultSectionSize); connect(this, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(modifyEntry(QModelIndex))); } KisPaletteView::~KisPaletteView() { } void KisPaletteView::setCrossedKeyword(const QString &value) { KisPaletteDelegate *delegate = dynamic_cast(itemDelegate()); KIS_ASSERT_RECOVER_RETURN(delegate); delegate->setCrossedKeyword(value); } bool KisPaletteView::addEntryWithDialog(KoColor color) { KoDialog *window = new KoDialog(); window->setWindowTitle(i18nc("@title:window", "Add a new Colorset Entry")); QFormLayout *editableItems = new QFormLayout(); window->mainWidget()->setLayout(editableItems); QComboBox *cmbGroups = new QComboBox(); QString defaultGroupName = i18nc("Name for default group", "Default"); cmbGroups->addItem(defaultGroupName); cmbGroups->addItems(m_d->model->colorSet()->getGroupNames()); QLineEdit *lnIDName = new QLineEdit(); QLineEdit *lnName = new QLineEdit(); KisColorButton *bnColor = new KisColorButton(); bnColor->setPaletteViewEnabled(false); QCheckBox *chkSpot = new QCheckBox(); chkSpot->setToolTip(i18nc("@info:tooltip", "A spot color is a color that the printer is able to print without mixing the paints it has available to it. The opposite is called a process color.")); editableItems->addRow(i18n("Group"), cmbGroups); editableItems->addRow(i18n("ID"), lnIDName); editableItems->addRow(i18n("Name"), lnName); editableItems->addRow(i18n("Color"), bnColor); - editableItems->addRow(i18n("Spot"), chkSpot); + editableItems->addRow(i18nc("Spot color", "Spot"), chkSpot); cmbGroups->setCurrentIndex(0); lnName->setText(i18nc("Part of a default name for a color","Color")+" "+QString::number(m_d->model->colorSet()->nColors()+1)); lnIDName->setText(QString::number(m_d->model->colorSet()->nColors()+1)); bnColor->setColor(color); chkSpot->setChecked(false); // if (window->exec() == KoDialog::Accepted) { QString groupName = cmbGroups->currentText(); if (groupName == defaultGroupName) { groupName = QString(); } KoColorSetEntry newEntry; newEntry.setColor(bnColor->color()); newEntry.setName(lnName->text()); newEntry.setId(lnIDName->text()); newEntry.setSpotColor(chkSpot->isChecked()); m_d->model->addColorSetEntry(newEntry, groupName); m_d->model->colorSet()->save(); return true; } return false; } // should be move to colorSetChooser bool KisPaletteView::addGroupWithDialog() { KoDialog *window = new KoDialog(); window->setWindowTitle(i18nc("@title:window","Add a new group")); QFormLayout *editableItems = new QFormLayout(); window->mainWidget()->setLayout(editableItems); QLineEdit *lnName = new QLineEdit(); editableItems->addRow(i18nc("Name for a group", "Name"), lnName); lnName->setText(i18nc("Part of default name for a new group", "Color Group")+""+QString::number(m_d->model->colorSet()->getGroupNames().size()+1)); if (window->exec() == KoDialog::Accepted) { QString groupName = lnName->text(); m_d->model->addGroup(groupName); m_d->model->colorSet()->save(); return true; } return false; } bool KisPaletteView::removeEntryWithDialog(QModelIndex index) { bool keepColors = true; if (qvariant_cast(index.data(KisPaletteModel::IsHeaderRole))) { KoDialog *window = new KoDialog(); window->setWindowTitle(i18nc("@title:window","Removing Group")); QFormLayout *editableItems = new QFormLayout(); QCheckBox *chkKeep = new QCheckBox(); window->mainWidget()->setLayout(editableItems); editableItems->addRow(i18nc("Shows up when deleting a group","Keep the Colors"), chkKeep); chkKeep->setChecked(keepColors); if (window->exec() == KoDialog::Accepted) { keepColors = chkKeep->isChecked(); m_d->model->removeEntry(index, keepColors); m_d->model->colorSet()->save(); } } else { m_d->model->removeEntry(index, keepColors); m_d->model->colorSet()->save(); } return true; } void KisPaletteView::trySelectClosestColor(KoColor color) { KoColorSet* color_set = m_d->model->colorSet(); if (!color_set) return; //also don't select if the color is the same as the current selection if (selectedIndexes().size()>0) { QModelIndex currentI = currentIndex(); if (!currentI.isValid()) { currentI = selectedIndexes().last(); } if (!currentI.isValid()) { currentI = selectedIndexes().first(); } if (currentI.isValid()) { if (m_d->model->colorSetEntryFromIndex(currentI).color()==color) { return; } } } quint32 i = color_set->getIndexClosestColor(color); QModelIndex index = m_d->model->indexFromId(i); this->selectionModel()->clearSelection(); this->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select); } void KisPaletteView::mouseReleaseEvent(QMouseEvent *event) { bool foreground = false; if (event->button()== Qt::LeftButton) { foreground = true; } entrySelection(foreground); } void KisPaletteView::paletteModelChanged() { updateView(); updateRows(); } void KisPaletteView::setPaletteModel(KisPaletteModel *model) { if (m_d->model) { disconnect(m_d->model, 0, this, 0); } m_d->model = model; setModel(model); paletteModelChanged(); connect(m_d->model, SIGNAL(layoutChanged(QList,QAbstractItemModel::LayoutChangeHint)), this, SLOT(paletteModelChanged())); connect(m_d->model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(paletteModelChanged())); connect(m_d->model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(paletteModelChanged())); connect(m_d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(paletteModelChanged())); connect(m_d->model, SIGNAL(modelReset()), this, SLOT(paletteModelChanged())); } KisPaletteModel* KisPaletteView::paletteModel() const { return m_d->model; } void KisPaletteView::updateRows() { this->clearSpans(); if (m_d->model) { for (int r=0; r<=m_d->model->rowCount(); r++) { QModelIndex index = m_d->model->index(r, 0); if (qvariant_cast(index.data(KisPaletteModel::IsHeaderRole))) { setSpan(r, 0, 1, m_d->model->columnCount()); setRowHeight(r, this->fontMetrics().lineSpacing()+6); } else { this->setRowHeight(r, this->columnWidth(0)); } } } } void KisPaletteView::setAllowModification(bool allow) { m_d->allowPaletteModification = allow; } void KisPaletteView::wheelEvent(QWheelEvent *event) { if (event->modifiers() & Qt::ControlModifier) { int numDegrees = event->delta() / 8; int numSteps = numDegrees / 7; int curSize = horizontalHeader()->sectionSize(0); int setSize = numSteps + curSize; if ( (event->delta() <= 0) && (setSize <= 8) ) { // Ignore scroll-zooming down below a certain size } else { horizontalHeader()->setDefaultSectionSize(setSize); verticalHeader()->setDefaultSectionSize(setSize); KisConfig cfg; cfg.setPaletteDockerPaletteViewSectionSize(setSize); } event->accept(); } else { KoTableView::wheelEvent(event); } } void KisPaletteView::entrySelection(bool foreground) { QModelIndex index; if (selectedIndexes().size()<=0) { return; } if (selectedIndexes().last().isValid()) { index = selectedIndexes().last(); } else if (selectedIndexes().first().isValid()) { index = selectedIndexes().first(); } else { return; } if (qvariant_cast(index.data(KisPaletteModel::IsHeaderRole))==false) { KoColorSetEntry entry = m_d->model->colorSetEntryFromIndex(index); if (foreground) { emit(entrySelected(entry)); emit(indexEntrySelected(index)); } else { emit(entrySelectedBackGround(entry)); emit(indexEntrySelected(index)); } } } void KisPaletteView::modifyEntry(QModelIndex index) { if (m_d->allowPaletteModification) { KoDialog *group = new KoDialog(); QFormLayout *editableItems = new QFormLayout(); group->mainWidget()->setLayout(editableItems); QLineEdit *lnIDName = new QLineEdit(); QLineEdit *lnGroupName = new QLineEdit(); KisColorButton *bnColor = new KisColorButton(); bnColor->setPaletteViewEnabled(false); QCheckBox *chkSpot = new QCheckBox(); if (qvariant_cast(index.data(KisPaletteModel::IsHeaderRole))) { QString groupName = qvariant_cast(index.data(Qt::DisplayRole)); editableItems->addRow(i18nc("Name for a colorgroup","Name"), lnGroupName); lnGroupName->setText(groupName); if (group->exec() == KoDialog::Accepted) { m_d->model->colorSet()->changeGroupName(groupName, lnGroupName->text()); m_d->model->colorSet()->save(); updateRows(); } //rename the group. } else { KoColorSetEntry entry = m_d->model->colorSetEntryFromIndex(index); QStringList entryList = qvariant_cast(index.data(KisPaletteModel::RetrieveEntryRole)); chkSpot->setToolTip(i18nc("@info:tooltip", "A spot color is a color that the printer is able to print without mixing the paints it has available to it. The opposite is called a process color.")); editableItems->addRow(i18n("ID"), lnIDName); editableItems->addRow(i18n("Name"), lnGroupName); editableItems->addRow(i18n("Color"), bnColor); editableItems->addRow(i18n("Spot"), chkSpot); lnGroupName->setText(entry.name()); lnIDName->setText(entry.id()); bnColor->setColor(entry.color()); chkSpot->setChecked(entry.spotColor()); if (group->exec() == KoDialog::Accepted) { entry.setName(lnGroupName->text()); entry.setId(lnIDName->text()); entry.setColor(bnColor->color()); entry.setSpotColor(chkSpot->isChecked()); m_d->model->colorSet()->changeColorSetEntry(entry, entryList.at(0), entryList.at(1).toUInt()); m_d->model->colorSet()->save(); } } } } diff --git a/libs/ui/kis_selection_manager.cc b/libs/ui/kis_selection_manager.cc index f5c4e649ed..1f04c27376 100644 --- a/libs/ui/kis_selection_manager.cc +++ b/libs/ui/kis_selection_manager.cc @@ -1,609 +1,609 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2007 Sven Langkamp * * The outline algorithm uses the limn algorithm of fontutils by * Karl Berry and Kathryn Hargreaves * * 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_selection_manager.h" #include #include #include #include #include #include #include #include #include #include #include "KoCanvasController.h" #include "KoChannelInfo.h" #include "KoIntegerMaths.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_adjustment_layer.h" #include "kis_node_manager.h" #include "canvas/kis_canvas2.h" #include "kis_config.h" #include "kis_convolution_painter.h" #include "kis_convolution_kernel.h" #include "kis_debug.h" #include "KisDocument.h" #include "kis_fill_painter.h" #include "kis_group_layer.h" #include "kis_layer.h" #include "kis_statusbar.h" #include "kis_paint_device.h" #include "kis_paint_layer.h" #include "kis_painter.h" #include "kis_transaction.h" #include "kis_selection.h" #include "kis_types.h" #include "kis_canvas_resource_provider.h" #include "kis_undo_adapter.h" #include "kis_pixel_selection.h" #include "flake/kis_shape_selection.h" #include "commands/kis_selection_commands.h" #include "kis_selection_mask.h" #include "flake/kis_shape_layer.h" #include "kis_selection_decoration.h" #include "canvas/kis_canvas_decoration.h" #include "kis_node_commands_adapter.h" #include "kis_iterator_ng.h" #include "kis_clipboard.h" #include "KisViewManager.h" #include "kis_selection_filters.h" #include "kis_figure_painting_tool_helper.h" #include "KisView.h" #include "dialogs/kis_dlg_stroke_selection_properties.h" #include "actions/kis_selection_action_factories.h" #include "actions/KisPasteActionFactory.h" #include "kis_action.h" #include "kis_action_manager.h" #include "operations/kis_operation_configuration.h" //new #include "kis_recorded_path_paint_action.h" #include "kis_node_query_path.h" #include "kis_tool_shape.h" KisSelectionManager::KisSelectionManager(KisViewManager * view) : m_view(view), m_doc(0), m_imageView(0), m_adapter(new KisNodeCommandsAdapter(view)), m_copy(0), m_copyMerged(0), m_cut(0), m_paste(0), m_pasteNew(0), m_cutToNewLayer(0), m_selectAll(0), m_deselect(0), m_clear(0), m_reselect(0), m_invert(0), m_copyToNewLayer(0), m_fillForegroundColor(0), m_fillBackgroundColor(0), m_fillPattern(0), m_imageResizeToSelection(0), m_selectionDecoration(0) { m_clipboard = KisClipboard::instance(); } KisSelectionManager::~KisSelectionManager() { } void KisSelectionManager::setup(KisActionManager* actionManager) { m_cut = actionManager->createStandardAction(KStandardAction::Cut, this, SLOT(cut())); m_copy = actionManager->createStandardAction(KStandardAction::Copy, this, SLOT(copy())); m_paste = actionManager->createStandardAction(KStandardAction::Paste, this, SLOT(paste())); KisAction *action = actionManager->createAction("copy_sharp"); connect(action, SIGNAL(triggered()), this, SLOT(copySharp())); action = actionManager->createAction("cut_sharp"); connect(action, SIGNAL(triggered()), this, SLOT(cutSharp())); m_pasteNew = actionManager->createAction("paste_new"); connect(m_pasteNew, SIGNAL(triggered()), this, SLOT(pasteNew())); m_pasteAt = actionManager->createAction("paste_at"); connect(m_pasteAt, SIGNAL(triggered()), this, SLOT(pasteAt())); m_copyMerged = actionManager->createAction("copy_merged"); connect(m_copyMerged, SIGNAL(triggered()), this, SLOT(copyMerged())); m_selectAll = actionManager->createAction("select_all"); connect(m_selectAll, SIGNAL(triggered()), this, SLOT(selectAll())); m_deselect = actionManager->createAction("deselect"); connect(m_deselect, SIGNAL(triggered()), this, SLOT(deselect())); m_clear = actionManager->createAction("clear"); connect(m_clear, SIGNAL(triggered()), SLOT(clear())); m_reselect = actionManager->createAction("reselect"); connect(m_reselect, SIGNAL(triggered()), this, SLOT(reselect())); - m_invert = actionManager->createAction("invert"); + m_invert = actionManager->createAction("invert_selection"); m_invert->setOperationID("invertselection"); actionManager->registerOperation(new KisInvertSelectionOperation); m_copyToNewLayer = actionManager->createAction("copy_selection_to_new_layer"); connect(m_copyToNewLayer, SIGNAL(triggered()), this, SLOT(copySelectionToNewLayer())); m_cutToNewLayer = actionManager->createAction("cut_selection_to_new_layer"); connect(m_cutToNewLayer, SIGNAL(triggered()), this, SLOT(cutToNewLayer())); m_fillForegroundColor = actionManager->createAction("fill_selection_foreground_color"); connect(m_fillForegroundColor, SIGNAL(triggered()), this, SLOT(fillForegroundColor())); m_fillBackgroundColor = actionManager->createAction("fill_selection_background_color"); connect(m_fillBackgroundColor, SIGNAL(triggered()), this, SLOT(fillBackgroundColor())); m_fillPattern = actionManager->createAction("fill_selection_pattern"); connect(m_fillPattern, SIGNAL(triggered()), this, SLOT(fillPattern())); m_fillForegroundColorOpacity = actionManager->createAction("fill_selection_foreground_color_opacity"); connect(m_fillForegroundColorOpacity, SIGNAL(triggered()), this, SLOT(fillForegroundColorOpacity())); m_fillBackgroundColorOpacity = actionManager->createAction("fill_selection_background_color_opacity"); connect(m_fillBackgroundColorOpacity, SIGNAL(triggered()), this, SLOT(fillBackgroundColorOpacity())); m_fillPatternOpacity = actionManager->createAction("fill_selection_pattern_opacity"); connect(m_fillPatternOpacity, SIGNAL(triggered()), this, SLOT(fillPatternOpacity())); m_strokeShapes = actionManager->createAction("stroke_shapes"); connect(m_strokeShapes, SIGNAL(triggered()), this, SLOT(paintSelectedShapes())); m_toggleDisplaySelection = actionManager->createAction("toggle_display_selection"); connect(m_toggleDisplaySelection, SIGNAL(triggered()), this, SLOT(toggleDisplaySelection())); m_toggleDisplaySelection->setChecked(true); m_imageResizeToSelection = actionManager->createAction("resizeimagetoselection"); connect(m_imageResizeToSelection, SIGNAL(triggered()), this, SLOT(imageResizeToSelection())); action = actionManager->createAction("convert_to_vector_selection"); connect(action, SIGNAL(triggered()), SLOT(convertToVectorSelection())); action = actionManager->createAction("convert_shapes_to_vector_selection"); connect(action, SIGNAL(triggered()), SLOT(convertShapesToVectorSelection())); action = actionManager->createAction("convert_selection_to_shape"); connect(action, SIGNAL(triggered()), SLOT(convertToShape())); m_toggleSelectionOverlayMode = actionManager->createAction("toggle-selection-overlay-mode"); connect(m_toggleSelectionOverlayMode, SIGNAL(triggered()), SLOT(slotToggleSelectionDecoration())); m_strokeSelected = actionManager->createAction("stroke_selection"); connect(m_strokeSelected, SIGNAL(triggered()), SLOT(slotStrokeSelection())); QClipboard *cb = QApplication::clipboard(); connect(cb, SIGNAL(dataChanged()), SLOT(clipboardDataChanged())); } void KisSelectionManager::setView(QPointerimageView) { if (m_imageView && m_imageView->canvasBase()) { disconnect(m_imageView->canvasBase()->toolProxy(), SIGNAL(toolChanged(const QString&)), this, SLOT(clipboardDataChanged())); KoSelection *selection = m_imageView->canvasBase()->globalShapeManager()->selection(); selection->disconnect(this, SLOT(shapeSelectionChanged())); KisSelectionDecoration *decoration = qobject_cast(m_imageView->canvasBase()->decoration("selection").data()); if (decoration) { disconnect(SIGNAL(currentSelectionChanged()), decoration); } m_imageView->image()->undoAdapter()->disconnect(this); m_selectionDecoration = 0; } m_imageView = imageView; if (m_imageView) { connect(m_imageView->canvasBase()->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(shapeSelectionChanged())); KisSelectionDecoration* decoration = qobject_cast(m_imageView->canvasBase()->decoration("selection").data()); if (!decoration) { decoration = new KisSelectionDecoration(m_imageView); decoration->setVisible(true); m_imageView->canvasBase()->addDecoration(decoration); } m_selectionDecoration = decoration; connect(this, SIGNAL(currentSelectionChanged()), decoration, SLOT(selectionChanged())); connect(m_imageView->image()->undoAdapter(), SIGNAL(selectionChanged()), SLOT(selectionChanged())); connect(m_imageView->canvasBase()->toolProxy(), SIGNAL(toolChanged(const QString&)), SLOT(clipboardDataChanged())); } } void KisSelectionManager::clipboardDataChanged() { m_view->updateGUI(); } bool KisSelectionManager::havePixelsSelected() { KisSelectionSP activeSelection = m_view->selection(); return activeSelection && !activeSelection->selectedRect().isEmpty(); } bool KisSelectionManager::havePixelsInClipboard() { return m_clipboard->hasClip(); } bool KisSelectionManager::haveShapesSelected() { if (m_view && m_view->canvasBase()) { return m_view->canvasBase()->selectedShapesProxy()->selection()->count() > 0; } return false; } bool KisSelectionManager::haveShapesInClipboard() { KoSvgPaste paste; return paste.hasShapes(); } bool KisSelectionManager::havePixelSelectionWithPixels() { KisSelectionSP selection = m_view->selection(); if (selection && selection->hasPixelSelection()) { return !selection->pixelSelection()->selectedRect().isEmpty(); } return false; } void KisSelectionManager::updateGUI() { Q_ASSERT(m_view); Q_ASSERT(m_clipboard); if (!m_view || !m_clipboard) return; bool havePixelsSelected = this->havePixelsSelected(); bool havePixelsInClipboard = this->havePixelsInClipboard(); bool haveShapesSelected = this->haveShapesSelected(); bool haveShapesInClipboard = this->haveShapesInClipboard(); bool haveDevice = m_view->activeDevice(); KisLayerSP activeLayer = m_view->activeLayer(); KisImageWSP image = activeLayer ? activeLayer->image() : 0; bool canReselect = image && image->canReselectGlobalSelection(); bool canDeselect = image && image->globalSelection(); m_clear->setEnabled(haveDevice || havePixelsSelected || haveShapesSelected); m_cut->setEnabled(havePixelsSelected || haveShapesSelected); m_copy->setEnabled(havePixelsSelected || haveShapesSelected); m_paste->setEnabled(havePixelsInClipboard || haveShapesInClipboard); m_pasteAt->setEnabled(havePixelsInClipboard || haveShapesInClipboard); // FIXME: how about pasting shapes? m_pasteNew->setEnabled(havePixelsInClipboard); m_selectAll->setEnabled(true); m_deselect->setEnabled(canDeselect); m_reselect->setEnabled(canReselect); // m_load->setEnabled(true); // m_save->setEnabled(havePixelsSelected); updateStatusBar(); emit signalUpdateGUI(); } void KisSelectionManager::updateStatusBar() { if (m_view && m_view->statusBar()) { m_view->statusBar()->setSelection(m_view->image()); } } void KisSelectionManager::selectionChanged() { m_view->updateGUI(); emit currentSelectionChanged(); } void KisSelectionManager::cut() { KisCutCopyActionFactory factory; factory.run(true, false, m_view); } void KisSelectionManager::copy() { KisCutCopyActionFactory factory; factory.run(false, false, m_view); } void KisSelectionManager::cutSharp() { KisCutCopyActionFactory factory; factory.run(true, true, m_view); } void KisSelectionManager::copySharp() { KisCutCopyActionFactory factory; factory.run(false, true, m_view); } void KisSelectionManager::copyMerged() { KisCopyMergedActionFactory factory; factory.run(m_view); } void KisSelectionManager::paste() { KisPasteActionFactory factory; factory.run(false, m_view); } void KisSelectionManager::pasteAt() { KisPasteActionFactory factory; factory.run(true, m_view); } void KisSelectionManager::pasteNew() { KisPasteNewActionFactory factory; factory.run(m_view); } void KisSelectionManager::selectAll() { KisSelectAllActionFactory factory; factory.run(m_view); } void KisSelectionManager::deselect() { KisDeselectActionFactory factory; factory.run(m_view); } void KisSelectionManager::invert() { if(m_invert) m_invert->trigger(); } void KisSelectionManager::reselect() { KisReselectActionFactory factory; factory.run(m_view); } void KisSelectionManager::convertToVectorSelection() { KisSelectionToVectorActionFactory factory; factory.run(m_view); } void KisSelectionManager::convertShapesToVectorSelection() { KisShapesToVectorSelectionActionFactory factory; factory.run(m_view); } void KisSelectionManager::convertToShape() { KisSelectionToShapeActionFactory factory; factory.run(m_view); } void KisSelectionManager::clear() { KisClearActionFactory factory; factory.run(m_view); } void KisSelectionManager::fillForegroundColor() { KisFillActionFactory factory; factory.run("fg", m_view); } void KisSelectionManager::fillBackgroundColor() { KisFillActionFactory factory; factory.run("bg", m_view); } void KisSelectionManager::fillPattern() { KisFillActionFactory factory; factory.run("pattern", m_view); } void KisSelectionManager::fillForegroundColorOpacity() { KisFillActionFactory factory; factory.run("fg_opacity", m_view); } void KisSelectionManager::fillBackgroundColorOpacity() { KisFillActionFactory factory; factory.run("bg_opacity", m_view); } void KisSelectionManager::fillPatternOpacity() { KisFillActionFactory factory; factory.run("pattern_opacity", m_view); } void KisSelectionManager::copySelectionToNewLayer() { copy(); paste(); } void KisSelectionManager::cutToNewLayer() { cut(); paste(); } void KisSelectionManager::toggleDisplaySelection() { KIS_ASSERT_RECOVER_RETURN(m_selectionDecoration); m_selectionDecoration->toggleVisibility(); m_toggleDisplaySelection->blockSignals(true); m_toggleDisplaySelection->setChecked(m_selectionDecoration->visible()); m_toggleDisplaySelection->blockSignals(false); emit displaySelectionChanged(); } bool KisSelectionManager::displaySelection() { return m_toggleDisplaySelection->isChecked(); } void KisSelectionManager::shapeSelectionChanged() { KoShapeManager* shapeManager = m_view->canvasBase()->globalShapeManager(); KoSelection * selection = shapeManager->selection(); QList selectedShapes = selection->selectedShapes(); KoShapeStrokeSP border(new KoShapeStroke(0, Qt::lightGray)); Q_FOREACH (KoShape* shape, shapeManager->shapes()) { if (dynamic_cast(shape->parent())) { if (selectedShapes.contains(shape)) shape->setStroke(border); else shape->setStroke(KoShapeStrokeSP()); } } m_view->updateGUI(); } void KisSelectionManager::imageResizeToSelection() { KisImageResizeToSelectionActionFactory factory; factory.run(m_view); } void KisSelectionManager::paintSelectedShapes() { KisImageWSP image = m_view->image(); if (!image) return; KisLayerSP layer = m_view->activeLayer(); if (!layer) return; QList shapes = m_view->canvasBase()->shapeManager()->selection()->selectedShapes(); KisPaintLayerSP paintLayer = new KisPaintLayer(image, i18n("Stroked Shapes"), OPACITY_OPAQUE_U8); KUndo2MagicString actionName = kundo2_i18n("Stroke Shapes"); m_adapter->beginMacro(actionName); m_adapter->addNode(paintLayer.data(), layer->parent().data(), layer.data()); KisFigurePaintingToolHelper helper(actionName, image, paintLayer.data(), m_view->resourceProvider()->resourceManager(), KisPainter::StrokeStyleBrush, KisPainter::FillStyleNone); Q_FOREACH (KoShape* shape, shapes) { QTransform matrix = shape->absoluteTransformation(0) * QTransform::fromScale(image->xRes(), image->yRes()); QPainterPath mapedOutline = matrix.map(shape->outline()); helper.paintPainterPath(mapedOutline); } m_adapter->endMacro(); } void KisSelectionManager::slotToggleSelectionDecoration() { KIS_ASSERT_RECOVER_RETURN(m_selectionDecoration); KisSelectionDecoration::Mode mode = m_selectionDecoration->mode() ? KisSelectionDecoration::Ants : KisSelectionDecoration::Mask; m_selectionDecoration->setMode(mode); emit displaySelectionChanged(); } bool KisSelectionManager::showSelectionAsMask() const { if (m_selectionDecoration) { return m_selectionDecoration->mode() == KisSelectionDecoration::Mask; } return false; } void KisSelectionManager::slotStrokeSelection() { KisImageWSP image = m_view->image(); if (!image ) { return; } KisNodeSP currentNode = m_view->resourceProvider()->resourceManager()->resource(KisCanvasResourceProvider::CurrentKritaNode).value(); bool isVectorLayer = false; if (currentNode->inherits("KisShapeLayer")) { isVectorLayer = true; } QPointer dlg = new KisDlgStrokeSelection(image, m_view, isVectorLayer); if (dlg->exec() == QDialog::Accepted) { StrokeSelectionOptions params = dlg->getParams(); if (params.brushSelected){ KisStrokeBrushSelectionActionFactory factory; factory.run(m_view, params); } else { KisStrokeSelectionActionFactory factory; factory.run(m_view, params); } } delete dlg; } diff --git a/libs/ui/opengl/kis_opengl_image_textures.cpp b/libs/ui/opengl/kis_opengl_image_textures.cpp index 322f44d475..e609de58dd 100644 --- a/libs/ui/opengl/kis_opengl_image_textures.cpp +++ b/libs/ui/opengl/kis_opengl_image_textures.cpp @@ -1,684 +1,684 @@ /* * Copyright (c) 2005-2007 Adrian Page * * 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 "opengl/kis_opengl_image_textures.h" #include #include #include #include #include #include #include #include #include "kis_image.h" #include "kis_config.h" #include "KisPart.h" #ifdef HAVE_OPENEXR #include #endif #ifndef GL_CLAMP_TO_EDGE #define GL_CLAMP_TO_EDGE 0x812F #endif #ifndef GL_BGRA #define GL_BGRA 0x80E1 #endif // GL_EXT_texture_format_BGRA8888 #ifndef GL_BGRA_EXT #define GL_BGRA_EXT 0x80E1 #endif #ifndef GL_BGRA8_EXT #define GL_BGRA8_EXT 0x93A1 #endif KisOpenGLImageTextures::ImageTexturesMap KisOpenGLImageTextures::imageTexturesMap; KisOpenGLImageTextures::KisOpenGLImageTextures() : m_image(0) , m_monitorProfile(0) , m_proofingConfig(0) , m_createNewProofingTransform(true) , m_tilesDestinationColorSpace(0) , m_internalColorManagementActive(true) , m_checkerTexture(0) , m_glFuncs(0) , m_allChannelsSelected(true) , m_useOcio(false) , m_initialized(false) { KisConfig cfg; m_renderingIntent = (KoColorConversionTransformation::Intent)cfg.monitorRenderIntent(); m_conversionFlags = KoColorConversionTransformation::HighQuality; if (cfg.useBlackPointCompensation()) m_conversionFlags |= KoColorConversionTransformation::BlackpointCompensation; if (!cfg.allowLCMSOptimization()) m_conversionFlags |= KoColorConversionTransformation::NoOptimization; m_useOcio = cfg.useOcio(); } KisOpenGLImageTextures::KisOpenGLImageTextures(KisImageWSP image, const KoColorProfile *monitorProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) : m_image(image) , m_monitorProfile(monitorProfile) , m_renderingIntent(renderingIntent) , m_conversionFlags(conversionFlags) , m_createNewProofingTransform(true) , m_tilesDestinationColorSpace(0) , m_internalColorManagementActive(true) , m_checkerTexture(0) , m_glFuncs(0) , m_allChannelsSelected(true) , m_useOcio(false) , m_initialized(false) { Q_ASSERT(renderingIntent < 4); } void KisOpenGLImageTextures::initGL(QOpenGLFunctions *f) { if (f) { m_glFuncs = f; } else { errUI << "Tried to create OpenGLImageTextures with uninitialized QOpenGLFunctions"; } getTextureSize(&m_texturesInfo); // we use local static object for creating pools shared among // different images static KisTextureTileInfoPoolRegistry s_poolRegistry; m_infoChunksPool = s_poolRegistry.getPool(m_texturesInfo.width, m_texturesInfo.height); m_glFuncs->glGenTextures(1, &m_checkerTexture); createImageTextureTiles(); KisOpenGLUpdateInfoSP info = updateCache(m_image->bounds(), m_image); recalculateCache(info); } KisOpenGLImageTextures::~KisOpenGLImageTextures() { ImageTexturesMap::iterator it = imageTexturesMap.find(m_image); if (it != imageTexturesMap.end()) { KisOpenGLImageTextures *textures = it.value(); if (textures == this) { dbgUI << "Removing shared image context from map"; imageTexturesMap.erase(it); } } destroyImageTextureTiles(); m_glFuncs->glDeleteTextures(1, &m_checkerTexture); } KisImageSP KisOpenGLImageTextures::image() const { return m_image; } bool KisOpenGLImageTextures::imageCanShareTextures() { KisConfig cfg; if (cfg.useOcio()) return false; if (KisPart::instance()->mainwindowCount() == 1) return true; if (qApp->desktop()->screenCount() == 1) return true; for (int i = 1; i < qApp->desktop()->screenCount(); i++) { if (cfg.displayProfile(i) != cfg.displayProfile(i - 1)) { return false; } } return true; } KisOpenGLImageTexturesSP KisOpenGLImageTextures::getImageTextures(KisImageWSP image, const KoColorProfile *monitorProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { // Disabled until we figure out why we're deleting the shared textures on closing the second view on a single image if (false && imageCanShareTextures()) { ImageTexturesMap::iterator it = imageTexturesMap.find(image); if (it != imageTexturesMap.end()) { KisOpenGLImageTexturesSP textures = it.value(); textures->setMonitorProfile(monitorProfile, renderingIntent, conversionFlags); return textures; } else { KisOpenGLImageTextures *imageTextures = new KisOpenGLImageTextures(image, monitorProfile, renderingIntent, conversionFlags); imageTexturesMap[image] = imageTextures; dbgUI << "Added shareable textures to map"; return imageTextures; } } else { return new KisOpenGLImageTextures(image, monitorProfile, renderingIntent, conversionFlags); } } QRect KisOpenGLImageTextures::calculateTileRect(int col, int row) const { return m_image->bounds() & QRect(col * m_texturesInfo.effectiveWidth, row * m_texturesInfo.effectiveHeight, m_texturesInfo.effectiveWidth, m_texturesInfo.effectiveHeight); } void KisOpenGLImageTextures::createImageTextureTiles() { destroyImageTextureTiles(); updateTextureFormat(); if (!m_tilesDestinationColorSpace) { qDebug() << "No destination colorspace!!!!"; return; } m_storedImageBounds = m_image->bounds(); const int lastCol = xToCol(m_image->width()); const int lastRow = yToRow(m_image->height()); m_numCols = lastCol + 1; // Default color is transparent black const int pixelSize = m_tilesDestinationColorSpace->pixelSize(); QByteArray emptyTileData((m_texturesInfo.width) * (m_texturesInfo.height) * pixelSize, 0); KisConfig config; KisOpenGL::FilterMode mode = (KisOpenGL::FilterMode)config.openGLFilteringMode(); QOpenGLContext *ctx = QOpenGLContext::currentContext(); if (ctx) { QOpenGLFunctions *f = ctx->functions(); m_initialized = true; dbgUI << "OpenGL: creating texture tiles of size" << m_texturesInfo.height << "x" << m_texturesInfo.width; m_textureTiles.reserve((lastRow+1)*m_numCols); for (int row = 0; row <= lastRow; row++) { for (int col = 0; col <= lastCol; col++) { QRect tileRect = calculateTileRect(col, row); KisTextureTile *tile = new KisTextureTile(tileRect, &m_texturesInfo, emptyTileData, mode, config.useOpenGLTextureBuffer(), config.numMipmapLevels(), f); m_textureTiles.append(tile); } } } else { dbgUI << "Tried to init texture tiles without a current OpenGL Context."; } } void KisOpenGLImageTextures::destroyImageTextureTiles() { if (m_textureTiles.isEmpty()) return; Q_FOREACH (KisTextureTile *tile, m_textureTiles) { delete tile; } m_textureTiles.clear(); m_storedImageBounds = QRect(); } KisOpenGLUpdateInfoSP KisOpenGLImageTextures::updateCache(const QRect& rect, KisImageSP srcImage) { return updateCacheImpl(rect, srcImage, true); } KisOpenGLUpdateInfoSP KisOpenGLImageTextures::updateCacheNoConversion(const QRect& rect) { return updateCacheImpl(rect, m_image, false); } // TODO: add sanity checks about the conformance of the passed srcImage! KisOpenGLUpdateInfoSP KisOpenGLImageTextures::updateCacheImpl(const QRect& rect, KisImageSP srcImage, bool convertColorSpace) { const KoColorSpace *dstCS = m_tilesDestinationColorSpace; ConversionOptions options; if (convertColorSpace) { options = ConversionOptions(dstCS, m_renderingIntent, m_conversionFlags); } KisOpenGLUpdateInfoSP info = new KisOpenGLUpdateInfo(options); QRect updateRect = rect & srcImage->bounds(); if (updateRect.isEmpty() || !(m_initialized)) return info; /** * Why the rect is artificial? That's easy! * It does not represent any real piece of the image. It is * intentionally stretched to get through the overlappping * stripes of neutrality and poke neighbouring tiles. * Thanks to the rect we get the coordinates of all the tiles * involved into update process */ QRect artificialRect = stretchRect(updateRect, m_texturesInfo.border); artificialRect &= srcImage->bounds(); int firstColumn = xToCol(artificialRect.left()); int lastColumn = xToCol(artificialRect.right()); int firstRow = yToRow(artificialRect.top()); int lastRow = yToRow(artificialRect.bottom()); QBitArray channelFlags; // empty by default if (m_channelFlags.size() != srcImage->projection()->colorSpace()->channels().size()) { setChannelFlags(QBitArray()); } if (!m_useOcio) { // Ocio does its own channel flipping if (!m_allChannelsSelected) { // and we do it only if necessary channelFlags = m_channelFlags; } } qint32 numItems = (lastColumn - firstColumn + 1) * (lastRow - firstRow + 1); info->tileList.reserve(numItems); const QRect bounds = srcImage->bounds(); const int levelOfDetail = srcImage->currentLevelOfDetail(); QRect alignedUpdateRect = updateRect; QRect alignedBounds = bounds; if (levelOfDetail) { alignedUpdateRect = KisLodTransform::alignedRect(alignedUpdateRect, levelOfDetail); alignedBounds = KisLodTransform::alignedRect(alignedBounds, levelOfDetail); } for (int col = firstColumn; col <= lastColumn; col++) { for (int row = firstRow; row <= lastRow; row++) { const QRect tileRect = calculateTileRect(col, row); const QRect tileTextureRect = stretchRect(tileRect, m_texturesInfo.border); QRect alignedTileTextureRect = levelOfDetail ? KisLodTransform::alignedRect(tileTextureRect, levelOfDetail) : tileTextureRect; KisTextureTileUpdateInfoSP tileInfo( new KisTextureTileUpdateInfo(col, row, alignedTileTextureRect, alignedUpdateRect, alignedBounds, levelOfDetail, m_infoChunksPool)); // Don't update empty tiles if (tileInfo->valid()) { tileInfo->retrieveData(srcImage->projection(), channelFlags, m_onlyOneChannelSelected, m_selectedChannelIndex); //create transform if (m_createNewProofingTransform) { const KoColorSpace *proofingSpace = KoColorSpaceRegistry::instance()->colorSpace(m_proofingConfig->proofingModel,m_proofingConfig->proofingDepth,m_proofingConfig->proofingProfile); m_proofingTransform.reset(tileInfo->generateProofingTransform(dstCS, proofingSpace, m_renderingIntent, m_proofingConfig->intent, m_proofingConfig->conversionFlags, m_proofingConfig->warningColor, m_proofingConfig->adaptationState)); m_createNewProofingTransform = false; } if (convertColorSpace) { if (m_proofingConfig && m_proofingTransform && m_proofingConfig->conversionFlags.testFlag(KoColorConversionTransformation::SoftProofing)) { tileInfo->proofTo(dstCS, m_proofingConfig->conversionFlags, m_proofingTransform.data()); } else { tileInfo->convertTo(dstCS, m_renderingIntent, m_conversionFlags); } } info->tileList.append(tileInfo); } else { dbgUI << "Trying to create an empty tileinfo record" << col << row << tileTextureRect << updateRect << srcImage->bounds(); } } } info->assignDirtyImageRect(rect); info->assignLevelOfDetail(levelOfDetail); return info; } void KisOpenGLImageTextures::recalculateCache(KisUpdateInfoSP info) { if (!m_initialized) { dbgUI << "OpenGL: Tried to edit image texture cache before it was initialized."; return; } KisOpenGLUpdateInfoSP glInfo = dynamic_cast(info.data()); if(!glInfo) return; KisTextureTileUpdateInfoSP tileInfo; Q_FOREACH (tileInfo, glInfo->tileList) { KisTextureTile *tile = getTextureTileCR(tileInfo->tileCol(), tileInfo->tileRow()); KIS_ASSERT_RECOVER_RETURN(tile); tile->update(*tileInfo); } } void KisOpenGLImageTextures::generateCheckerTexture(const QImage &checkImage) { QOpenGLContext *ctx = QOpenGLContext::currentContext(); if (ctx) { QOpenGLFunctions *f = ctx->functions(); dbgUI << "Attaching checker texture" << checkerTexture(); f->glBindTexture(GL_TEXTURE_2D, checkerTexture()); f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); f->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); QImage img = checkImage; if (checkImage.width() != BACKGROUND_TEXTURE_SIZE || checkImage.height() != BACKGROUND_TEXTURE_SIZE) { img = checkImage.scaled(BACKGROUND_TEXTURE_SIZE, BACKGROUND_TEXTURE_SIZE); } GLint format = GL_BGRA, internalFormat = GL_RGBA8; if (KisOpenGL::hasOpenGLES()) { if (ctx->hasExtension(QByteArrayLiteral("GL_EXT_texture_format_BGRA8888"))) { format = GL_BGRA_EXT; internalFormat = GL_BGRA8_EXT; } else { format = GL_RGBA; } } f->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, BACKGROUND_TEXTURE_SIZE, BACKGROUND_TEXTURE_SIZE, 0, format, GL_UNSIGNED_BYTE, img.constBits()); } else { dbgUI << "OpenGL: Tried to generate checker texture before OpenGL was initialized."; } } GLuint KisOpenGLImageTextures::checkerTexture() { if (m_glFuncs) { if (m_checkerTexture == 0) { m_glFuncs->glGenTextures(1, &m_checkerTexture); } return m_checkerTexture; } else { dbgUI << "Tried to access checker texture before OpenGL was initialized"; return 0; } } void KisOpenGLImageTextures::updateConfig(bool useBuffer, int NumMipmapLevels) { if(m_textureTiles.isEmpty()) return; Q_FOREACH (KisTextureTile *tile, m_textureTiles) { tile->setUseBuffer(useBuffer); tile->setNumMipmapLevels(NumMipmapLevels); } } void KisOpenGLImageTextures::slotImageSizeChanged(qint32 /*w*/, qint32 /*h*/) { createImageTextureTiles(); } void KisOpenGLImageTextures::setMonitorProfile(const KoColorProfile *monitorProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { //dbgUI << "Setting monitor profile to" << monitorProfile->name() << renderingIntent << conversionFlags; m_monitorProfile = monitorProfile; m_renderingIntent = renderingIntent; m_conversionFlags = conversionFlags; createImageTextureTiles(); } void KisOpenGLImageTextures::setChannelFlags(const QBitArray &channelFlags) { m_channelFlags = channelFlags; int selectedChannels = 0; const KoColorSpace *projectionCs = m_image->projection()->colorSpace(); QList channelInfo = projectionCs->channels(); if (m_channelFlags.size() != channelInfo.size()) { m_channelFlags = QBitArray(); } for (int i = 0; i < m_channelFlags.size(); ++i) { if (m_channelFlags.testBit(i) && channelInfo[i]->channelType() == KoChannelInfo::COLOR) { selectedChannels++; m_selectedChannelIndex = i; } } m_allChannelsSelected = (selectedChannels == m_channelFlags.size()); m_onlyOneChannelSelected = (selectedChannels == 1); } void KisOpenGLImageTextures::setProofingConfig(KisProofingConfigurationSP proofingConfig) { m_proofingConfig = proofingConfig; m_createNewProofingTransform = true; } void KisOpenGLImageTextures::getTextureSize(KisGLTexturesInfo *texturesInfo) { KisConfig cfg; const GLint preferredTextureSize = cfg.openGLTextureSize(); GLint maxTextureSize; if (m_glFuncs) { m_glFuncs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); } else { dbgUI << "OpenGL: Tried to read texture size before OpenGL was initialized."; maxTextureSize = GL_MAX_TEXTURE_SIZE; } texturesInfo->width = qMin(preferredTextureSize, maxTextureSize); texturesInfo->height = qMin(preferredTextureSize, maxTextureSize); texturesInfo->border = cfg.textureOverlapBorder(); texturesInfo->effectiveWidth = texturesInfo->width - 2 * texturesInfo->border; texturesInfo->effectiveHeight = texturesInfo->height - 2 * texturesInfo->border; } bool KisOpenGLImageTextures::internalColorManagementActive() const { return m_internalColorManagementActive; } bool KisOpenGLImageTextures::setInternalColorManagementActive(bool value) { bool needsFinalRegeneration = m_internalColorManagementActive != value; if (needsFinalRegeneration) { m_internalColorManagementActive = value; createImageTextureTiles(); // at this point the value of m_internalColorManagementActive might // have been forcely reverted to 'false' in case of some problems } return needsFinalRegeneration; } void KisOpenGLImageTextures::updateTextureFormat() { QOpenGLContext *ctx = QOpenGLContext::currentContext(); if (!(m_image && ctx)) return; if (!KisOpenGL::hasOpenGLES()) { m_texturesInfo.internalFormat = GL_RGBA8; m_texturesInfo.type = GL_UNSIGNED_BYTE; m_texturesInfo.format = GL_BGRA; } else { m_texturesInfo.internalFormat = GL_BGRA8_EXT; m_texturesInfo.type = GL_UNSIGNED_BYTE; m_texturesInfo.format = GL_BGRA_EXT; if(!ctx->hasExtension(QByteArrayLiteral("GL_EXT_texture_format_BGRA8888"))) { // The red and blue channels are swapped, but it will be re-swapped // by texture swizzle mask set in KisTextureTile::setTextureParameters m_texturesInfo.internalFormat = GL_RGBA8; m_texturesInfo.type = GL_UNSIGNED_BYTE; m_texturesInfo.format = GL_RGBA; } } KoID colorModelId = m_image->colorSpace()->colorModelId(); KoID colorDepthId = m_image->colorSpace()->colorDepthId(); KoID destinationColorModelId = RGBAColorModelID; KoID destinationColorDepthId = Integer8BitsColorDepthID; dbgUI << "Choosing texture format:"; if (colorModelId == RGBAColorModelID) { if (colorDepthId == Float16BitsColorDepthID) { - if (KisOpenGL::hasOpenGLES()) { + if (KisOpenGL::hasOpenGLES() || KisOpenGL::hasOpenGL3()) { m_texturesInfo.internalFormat = GL_RGBA16F; - dbgUI << "Using half (GLES)"; + dbgUI << "Using half (GLES or GL3)"; } else if (ctx->hasExtension("GL_ARB_texture_float")) { m_texturesInfo.internalFormat = GL_RGBA16F_ARB; dbgUI << "Using ARB half"; } else if (ctx->hasExtension("GL_ATI_texture_float")) { m_texturesInfo.internalFormat = GL_RGBA_FLOAT16_ATI; dbgUI << "Using ATI half"; } bool haveBuiltInOpenExr = false; #ifdef HAVE_OPENEXR haveBuiltInOpenExr = true; #endif - if (haveBuiltInOpenExr && KisOpenGL::hasOpenGLES()) { + if (haveBuiltInOpenExr && (KisOpenGL::hasOpenGLES() || KisOpenGL::hasOpenGL3())) { m_texturesInfo.type = GL_HALF_FLOAT; destinationColorDepthId = Float16BitsColorDepthID; - dbgUI << "Pixel type half (GLES)"; + dbgUI << "Pixel type half (GLES or GL3)"; } else if (haveBuiltInOpenExr && ctx->hasExtension("GL_ARB_half_float_pixel")) { m_texturesInfo.type = GL_HALF_FLOAT_ARB; destinationColorDepthId = Float16BitsColorDepthID; dbgUI << "Pixel type half"; } else { m_texturesInfo.type = GL_FLOAT; destinationColorDepthId = Float32BitsColorDepthID; dbgUI << "Pixel type float"; } m_texturesInfo.format = GL_RGBA; } else if (colorDepthId == Float32BitsColorDepthID) { - if (KisOpenGL::hasOpenGLES()) { + if (KisOpenGL::hasOpenGLES() || KisOpenGL::hasOpenGL3()) { m_texturesInfo.internalFormat = GL_RGBA32F; - dbgUI << "Using float (GLES)"; + dbgUI << "Using float (GLES or GL3)"; } else if (ctx->hasExtension("GL_ARB_texture_float")) { m_texturesInfo.internalFormat = GL_RGBA32F_ARB; dbgUI << "Using ARB float"; } else if (ctx->hasExtension("GL_ATI_texture_float")) { m_texturesInfo.internalFormat = GL_RGBA_FLOAT32_ATI; dbgUI << "Using ATI float"; } m_texturesInfo.type = GL_FLOAT; m_texturesInfo.format = GL_RGBA; destinationColorDepthId = Float32BitsColorDepthID; } else if (colorDepthId == Integer16BitsColorDepthID) { if (!KisOpenGL::hasOpenGLES()) { m_texturesInfo.internalFormat = GL_RGBA16; m_texturesInfo.type = GL_UNSIGNED_SHORT; m_texturesInfo.format = GL_BGRA; destinationColorDepthId = Integer16BitsColorDepthID; dbgUI << "Using 16 bits rgba"; } // TODO: for ANGLE, see if we can convert to 16f to support 10-bit display } } else { // We will convert the colorspace to 16 bits rgba, instead of 8 bits if (colorDepthId == Integer16BitsColorDepthID && !KisOpenGL::hasOpenGLES()) { m_texturesInfo.internalFormat = GL_RGBA16; m_texturesInfo.type = GL_UNSIGNED_SHORT; m_texturesInfo.format = GL_BGRA; destinationColorDepthId = Integer16BitsColorDepthID; dbgUI << "Using conversion to 16 bits rgba"; } // TODO: for ANGLE, see if we can convert to 16f to support 10-bit display } if (!m_internalColorManagementActive && colorModelId != destinationColorModelId) { KisConfig cfg; KisConfig::OcioColorManagementMode cm = cfg.ocioColorManagementMode(); if (cm != KisConfig::INTERNAL) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("You enabled OpenColorIO based color management, but your image is not an RGB image.\n" "OpenColorIO-based color management only works with RGB images.\n" "Please check the settings in the LUT docker.\n" "OpenColorIO will now be deactivated.")); } warnUI << "WARNING: Internal color management was forcibly enabled"; warnUI << "Color Management Mode: " << cm; warnUI << ppVar(m_image->colorSpace()); warnUI << ppVar(destinationColorModelId); warnUI << ppVar(destinationColorDepthId); cfg.setOcioColorManagementMode(KisConfig::INTERNAL); m_internalColorManagementActive = true; } const KoColorProfile *profile = m_internalColorManagementActive || colorModelId != destinationColorModelId ? m_monitorProfile : m_image->colorSpace()->profile(); /** * TODO: add an optimization so that the tile->convertTo() method * would not be called when not needed (DK) */ m_tilesDestinationColorSpace = KoColorSpaceRegistry::instance()->colorSpace(destinationColorModelId.id(), destinationColorDepthId.id(), profile); } diff --git a/libs/ui/tool/kis_tool_paint.cc b/libs/ui/tool/kis_tool_paint.cc index 0d9463fced..ed5be2463b 100644 --- a/libs/ui/tool/kis_tool_paint.cc +++ b/libs/ui/tool/kis_tool_paint.cc @@ -1,815 +1,814 @@ /* * Copyright (c) 2003-2009 Boudewijn Rempt * Copyright (c) 2015 Moritz Molch * * 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_tool_paint.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 #include #include #include #include #include #include #include #include #include "kis_display_color_converter.h" #include #include #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_cursor.h" #include "widgets/kis_cmb_composite.h" #include "widgets/kis_slider_spin_box.h" #include "kis_canvas_resource_provider.h" #include #include "kis_tool_utils.h" #include #include #include #include #include "strokes/kis_color_picker_stroke_strategy.h" #include -KisToolPaint::KisToolPaint(KoCanvasBase * canvas, const QCursor & cursor) +KisToolPaint::KisToolPaint(KoCanvasBase *canvas, const QCursor &cursor) : KisTool(canvas, cursor), m_showColorPreview(false), m_colorPreviewShowComparePlate(false), m_colorPickerDelayTimer(), m_isOutlineEnabled(true) { m_specialHoverModifier = false; m_optionsWidgetLayout = 0; m_opacity = OPACITY_OPAQUE_U8; m_supportOutline = false; { int maxSize = KisConfig().readEntry("maximumBrushSize", 1000); int brushSize = 1; do { m_standardBrushSizes.push_back(brushSize); int increment = qMax(1, int(std::ceil(qreal(brushSize) / 15))); brushSize += increment; } while (brushSize < maxSize); m_standardBrushSizes.push_back(maxSize); } - KisCanvas2 * kiscanvas = dynamic_cast(canvas); + KisCanvas2 *kiscanvas = dynamic_cast(canvas); KisActionManager *actionManager = kiscanvas->viewManager()->actionManager(); // XXX: Perhaps a better place for these? if (!actionManager->actionByName("increase_brush_size")) { KisAction *increaseBrushSize = new KisAction(i18n("Increase Brush Size")); increaseBrushSize->setShortcut(Qt::Key_BracketRight); actionManager->addAction("increase_brush_size", increaseBrushSize); } if (!actionManager->actionByName("decrease_brush_size")) { KisAction *decreaseBrushSize = new KisAction(i18n("Decrease Brush Size")); decreaseBrushSize->setShortcut(Qt::Key_BracketLeft); actionManager->addAction("decrease_brush_size", decreaseBrushSize); } addAction("increase_brush_size", dynamic_cast(actionManager->actionByName("increase_brush_size"))); addAction("decrease_brush_size", dynamic_cast(actionManager->actionByName("decrease_brush_size"))); connect(this, SIGNAL(sigPaintingFinished()), kiscanvas->viewManager()->resourceProvider(), SLOT(slotPainting())); m_colorPickerDelayTimer.setSingleShot(true); connect(&m_colorPickerDelayTimer, SIGNAL(timeout()), this, SLOT(activatePickColorDelayed())); using namespace std::placeholders; // For _1 placeholder std::function callback = std::bind(&KisToolPaint::addPickerJob, this, _1); m_colorPickingCompressor.reset( new PickingCompressor(100, callback, KisSignalCompressor::FIRST_ACTIVE)); } KisToolPaint::~KisToolPaint() { } int KisToolPaint::flags() const { return KisTool::FLAG_USES_CUSTOM_COMPOSITEOP; } void KisToolPaint::canvasResourceChanged(int key, const QVariant& v) { KisTool::canvasResourceChanged(key, v); switch(key) { case(KisCanvasResourceProvider::Opacity): setOpacity(v.toDouble()); break; default: //nothing break; } connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(resetCursorStyle()), Qt::UniqueConnection); } void KisToolPaint::activate(ToolActivation toolActivation, const QSet &shapes) { if (currentPaintOpPreset()) { QString formattedBrushName = currentPaintOpPreset()->name().replace("_", " "); emit statusTextChanged(formattedBrushName); } KisTool::activate(toolActivation, shapes); if (flags() & KisTool::FLAG_USES_CUSTOM_SIZE) { connect(action("increase_brush_size"), SIGNAL(triggered()), SLOT(increaseBrushSize()), Qt::UniqueConnection); connect(action("decrease_brush_size"), SIGNAL(triggered()), SLOT(decreaseBrushSize()), Qt::UniqueConnection); } KisCanvasResourceProvider *provider = qobject_cast(canvas())->viewManager()->resourceProvider(); m_oldOpacity = provider->opacity(); provider->setOpacity(m_localOpacity); } void KisToolPaint::deactivate() { if (flags() & KisTool::FLAG_USES_CUSTOM_SIZE) { disconnect(action("increase_brush_size"), 0, this, 0); disconnect(action("decrease_brush_size"), 0, this, 0); } KisCanvasResourceProvider *provider = qobject_cast(canvas())->viewManager()->resourceProvider(); m_localOpacity = provider->opacity(); provider->setOpacity(m_oldOpacity); KisTool::deactivate(); } QPainterPath KisToolPaint::tryFixBrushOutline(const QPainterPath &originalOutline) { KisConfig cfg; if (cfg.newOutlineStyle() == OUTLINE_NONE) return originalOutline; const qreal minThresholdSize = cfg.outlineSizeMinimum(); /** * If the brush outline is bigger than the canvas itself (which * would make it invisible for a user in most of the cases) just * add a cross in the center of it */ QSize widgetSize = canvas()->canvasWidget()->size(); const int maxThresholdSum = widgetSize.width() + widgetSize.height(); QPainterPath outline = originalOutline; QRectF boundingRect = outline.boundingRect(); const qreal sum = boundingRect.width() + boundingRect.height(); QPointF center = boundingRect.center(); if (sum > maxThresholdSum) { const int hairOffset = 7; outline.moveTo(center.x(), center.y() - hairOffset); outline.lineTo(center.x(), center.y() + hairOffset); outline.moveTo(center.x() - hairOffset, center.y()); outline.lineTo(center.x() + hairOffset, center.y()); } else if (sum < minThresholdSize && !outline.isEmpty()) { outline = QPainterPath(); outline.addEllipse(center, 0.5 * minThresholdSize, 0.5 * minThresholdSize); } return outline; } void KisToolPaint::paint(QPainter &gc, const KoViewConverter &converter) { Q_UNUSED(converter); QPainterPath path = tryFixBrushOutline(pixelToView(m_currentOutline)); paintToolOutline(&gc, path); if (m_showColorPreview) { QRectF viewRect = converter.documentToView(m_oldColorPreviewRect); gc.fillRect(viewRect, m_colorPreviewCurrentColor); if (m_colorPreviewShowComparePlate) { QRectF baseColorRect = viewRect.translated(viewRect.width(), 0); gc.fillRect(baseColorRect, m_colorPreviewBaseColor); } } } void KisToolPaint::setMode(ToolMode mode) { if(this->mode() == KisTool::PAINT_MODE && mode != KisTool::PAINT_MODE) { // Let's add history information about recently used colors emit sigPaintingFinished(); } KisTool::setMode(mode); } void KisToolPaint::activatePickColor(AlternateAction action) { m_showColorPreview = true; requestUpdateOutline(m_outlineDocPoint, 0); int resource = colorPreviewResourceId(action); KoColor color = canvas()->resourceManager()->koColorResource(resource); KisCanvas2 * kisCanvas = dynamic_cast(canvas()); KIS_ASSERT_RECOVER_RETURN(kisCanvas); m_colorPreviewCurrentColor = kisCanvas->displayColorConverter()->toQColor(color); if (!m_colorPreviewBaseColor.isValid()) { m_colorPreviewBaseColor = m_colorPreviewCurrentColor; } } void KisToolPaint::deactivatePickColor(AlternateAction action) { Q_UNUSED(action); m_showColorPreview = false; m_oldColorPreviewRect = QRect(); m_oldColorPreviewUpdateRect = QRect(); m_colorPreviewCurrentColor = QColor(); } void KisToolPaint::pickColorWasOverridden() { m_colorPreviewShowComparePlate = false; m_colorPreviewBaseColor = QColor(); } void KisToolPaint::activateAlternateAction(AlternateAction action) { switch (action) { case PickFgNode: /* Falls through */ case PickBgNode: /* Falls through */ case PickFgImage: /* Falls through */ case PickBgImage: delayedAction = action; m_colorPickerDelayTimer.start(100); /* Falls through */ default: pickColorWasOverridden(); KisTool::activateAlternateAction(action); }; } void KisToolPaint::activatePickColorDelayed() { switch (delayedAction) { case PickFgNode: useCursor(KisCursor::pickerLayerForegroundCursor()); activatePickColor(delayedAction); break; case PickBgNode: useCursor(KisCursor::pickerLayerBackgroundCursor()); activatePickColor(delayedAction); break; case PickFgImage: useCursor(KisCursor::pickerImageForegroundCursor()); activatePickColor(delayedAction); break; case PickBgImage: useCursor(KisCursor::pickerImageBackgroundCursor()); activatePickColor(delayedAction); break; default: break; }; repaintDecorations(); - } bool KisToolPaint::isPickingAction(AlternateAction action) { return action == PickFgNode || action == PickBgNode || action == PickFgImage || action == PickBgImage; } void KisToolPaint::deactivateAlternateAction(AlternateAction action) { if (!isPickingAction(action)) { KisTool::deactivateAlternateAction(action); return; } delayedAction = KisTool::NONE; m_colorPickerDelayTimer.stop(); resetCursorStyle(); deactivatePickColor(action); } void KisToolPaint::addPickerJob(const PickingJob &pickingJob) { /** * The actual picking is delayed by a compressor, so we can get this * event when the stroke is already closed */ if (!m_pickerStrokeId) return; KIS_ASSERT_RECOVER_RETURN(isPickingAction(pickingJob.action)); const QPoint imagePoint = image()->documentToImagePixelFloored(pickingJob.documentPixel); const bool fromCurrentNode = pickingJob.action == PickFgNode || pickingJob.action == PickBgNode; m_pickingResource = colorPreviewResourceId(pickingJob.action); if (!fromCurrentNode) { auto *kisCanvas = dynamic_cast(canvas()); KIS_SAFE_ASSERT_RECOVER_RETURN(kisCanvas); auto *referencesLayer = kisCanvas->imageView()->document()->referenceImagesLayer(); if (referencesLayer) { QColor color = referencesLayer->getPixel(imagePoint); if (color.isValid() && !color.alpha() == 0) { slotColorPickingFinished(KoColor(color, image()->colorSpace())); return; } } } KisPaintDeviceSP device = fromCurrentNode ? currentNode()->colorPickSourceDevice() : image()->projection(); // Used for color picker blending. KoColor currentColor = canvas()->resourceManager()->foregroundColor(); if( pickingJob.action == PickBgNode || pickingJob.action == PickBgImage ){ currentColor = canvas()->resourceManager()->backgroundColor(); } image()->addJob(m_pickerStrokeId, new KisColorPickerStrokeStrategy::Data(device, imagePoint, currentColor)); } void KisToolPaint::beginAlternateAction(KoPointerEvent *event, AlternateAction action) { if (isPickingAction(action)) { KIS_ASSERT_RECOVER_RETURN(!m_pickerStrokeId); setMode(SECONDARY_PAINT_MODE); KisColorPickerStrokeStrategy *strategy = new KisColorPickerStrokeStrategy(); connect(strategy, &KisColorPickerStrokeStrategy::sigColorUpdated, this, &KisToolPaint::slotColorPickingFinished); m_pickerStrokeId = image()->startStroke(strategy); m_colorPickingCompressor->start(PickingJob(event->point, action)); requestUpdateOutline(event->point, event); } else { KisTool::beginAlternateAction(event, action); } } void KisToolPaint::continueAlternateAction(KoPointerEvent *event, AlternateAction action) { if (isPickingAction(action)) { KIS_ASSERT_RECOVER_RETURN(m_pickerStrokeId); m_colorPickingCompressor->start(PickingJob(event->point, action)); requestUpdateOutline(event->point, event); } else { KisTool::continueAlternateAction(event, action); } } void KisToolPaint::endAlternateAction(KoPointerEvent *event, AlternateAction action) { if (isPickingAction(action)) { KIS_ASSERT_RECOVER_RETURN(m_pickerStrokeId); image()->endStroke(m_pickerStrokeId); m_pickerStrokeId.clear(); requestUpdateOutline(event->point, event); setMode(HOVER_MODE); } else { KisTool::endAlternateAction(event, action); } } int KisToolPaint::colorPreviewResourceId(AlternateAction action) { bool toForegroundColor = action == PickFgNode || action == PickFgImage; int resource = toForegroundColor ? KoCanvasResourceManager::ForegroundColor : KoCanvasResourceManager::BackgroundColor; return resource; } void KisToolPaint::slotColorPickingFinished(const KoColor &color) { canvas()->resourceManager()->setResource(m_pickingResource, color); if (!m_showColorPreview) return; KisCanvas2 * kisCanvas = dynamic_cast(canvas()); KIS_ASSERT_RECOVER_RETURN(kisCanvas); QColor previewColor = kisCanvas->displayColorConverter()->toQColor(color); m_colorPreviewShowComparePlate = true; m_colorPreviewCurrentColor = previewColor; requestUpdateOutline(m_outlineDocPoint, 0); } void KisToolPaint::mousePressEvent(KoPointerEvent *event) { KisTool::mousePressEvent(event); if (mode() == KisTool::HOVER_MODE) { requestUpdateOutline(event->point, event); } } void KisToolPaint::mouseMoveEvent(KoPointerEvent *event) { KisTool::mouseMoveEvent(event); if (mode() == KisTool::HOVER_MODE) { requestUpdateOutline(event->point, event); } } void KisToolPaint::mouseReleaseEvent(KoPointerEvent *event) { KisTool::mouseReleaseEvent(event); if (mode() == KisTool::HOVER_MODE) { requestUpdateOutline(event->point, event); } } QWidget * KisToolPaint::createOptionWidget() { - QWidget * optionWidget = new QWidget(); + QWidget *optionWidget = new QWidget(); optionWidget->setObjectName(toolId()); - QVBoxLayout* verticalLayout = new QVBoxLayout(optionWidget); + QVBoxLayout *verticalLayout = new QVBoxLayout(optionWidget); verticalLayout->setObjectName("KisToolPaint::OptionWidget::VerticalLayout"); verticalLayout->setContentsMargins(0,0,0,0); verticalLayout->setSpacing(5); // See https://bugs.kde.org/show_bug.cgi?id=316896 QWidget *specialSpacer = new QWidget(optionWidget); specialSpacer->setObjectName("SpecialSpacer"); specialSpacer->setFixedSize(0, 0); verticalLayout->addWidget(specialSpacer); verticalLayout->addWidget(specialSpacer); m_optionsWidgetLayout = new QGridLayout(); m_optionsWidgetLayout->setColumnStretch(1, 1); verticalLayout->addLayout(m_optionsWidgetLayout); m_optionsWidgetLayout->setContentsMargins(0,0,0,0); m_optionsWidgetLayout->setSpacing(5); if (!quickHelp().isEmpty()) { - QPushButton* push = new QPushButton(KisIconUtils::loadIcon("help-contents"), QString(), optionWidget); + QPushButton *push = new QPushButton(KisIconUtils::loadIcon("help-contents"), QString(), optionWidget); connect(push, SIGNAL(clicked()), this, SLOT(slotPopupQuickHelp())); - QHBoxLayout* hLayout = new QHBoxLayout(optionWidget); + QHBoxLayout *hLayout = new QHBoxLayout(optionWidget); hLayout->addWidget(push); hLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed)); verticalLayout->addLayout(hLayout); } return optionWidget; } QWidget* findLabelWidget(QGridLayout *layout, QWidget *control) { QWidget *result = 0; int index = layout->indexOf(control); int row, col, rowSpan, colSpan; layout->getItemPosition(index, &row, &col, &rowSpan, &colSpan); if (col > 0) { QLayoutItem *item = layout->itemAtPosition(row, col - 1); if (item) { result = item->widget(); } } else { QLayoutItem *item = layout->itemAtPosition(row, col + 1); if (item) { result = item->widget(); } } return result; } void KisToolPaint::showControl(QWidget *control, bool value) { control->setVisible(value); QWidget *label = findLabelWidget(m_optionsWidgetLayout, control); if (label) { label->setVisible(value); } } void KisToolPaint::enableControl(QWidget *control, bool value) { control->setEnabled(value); QWidget *label = findLabelWidget(m_optionsWidgetLayout, control); if (label) { label->setEnabled(value); } } void KisToolPaint::addOptionWidgetLayout(QLayout *layout) { Q_ASSERT(m_optionsWidgetLayout != 0); int rowCount = m_optionsWidgetLayout->rowCount(); m_optionsWidgetLayout->addLayout(layout, rowCount, 0, 1, 2); } void KisToolPaint::addOptionWidgetOption(QWidget *control, QWidget *label) { Q_ASSERT(m_optionsWidgetLayout != 0); if (label) { m_optionsWidgetLayout->addWidget(label, m_optionsWidgetLayout->rowCount(), 0); m_optionsWidgetLayout->addWidget(control, m_optionsWidgetLayout->rowCount() - 1, 1); } else { m_optionsWidgetLayout->addWidget(control, m_optionsWidgetLayout->rowCount(), 0, 1, 2); } } void KisToolPaint::setOpacity(qreal opacity) { m_opacity = quint8(opacity * OPACITY_OPAQUE_U8); } const KoCompositeOp* KisToolPaint::compositeOp() { if (currentNode()) { KisPaintDeviceSP device = currentNode()->paintDevice(); if (device) { QString op = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentCompositeOp).toString(); return device->colorSpace()->compositeOp(op); } } return 0; } void KisToolPaint::slotPopupQuickHelp() { QWhatsThis::showText(QCursor::pos(), quickHelp()); } void KisToolPaint::setupPaintAction(KisRecordedPaintAction* action) { KisTool::setupPaintAction(action); action->setOpacity(m_opacity / qreal(255.0)); - const KoCompositeOp* op = compositeOp(); + const KoCompositeOp *op = compositeOp(); if (op) { action->setCompositeOp(op->id()); } } KisToolPaint::NodePaintAbility KisToolPaint::nodePaintAbility() { KisNodeSP node = currentNode(); if (!node) { return NONE; } if (node->inherits("KisShapeLayer")) { return VECTOR; } if (node->inherits("KisCloneLayer")) { return CLONE; } if (node->paintDevice()) { return PAINT; } return NONE; } void KisToolPaint::activatePrimaryAction() { pickColorWasOverridden(); setOutlineEnabled(true); KisTool::activatePrimaryAction(); } void KisToolPaint::deactivatePrimaryAction() { setOutlineEnabled(false); KisTool::deactivatePrimaryAction(); } bool KisToolPaint::isOutlineEnabled() const { return m_isOutlineEnabled; } void KisToolPaint::setOutlineEnabled(bool value) { m_isOutlineEnabled = value; requestUpdateOutline(m_outlineDocPoint, 0); } void KisToolPaint::increaseBrushSize() { qreal paintopSize = currentPaintOpPreset()->settings()->paintOpSize(); std::vector::iterator result = std::upper_bound(m_standardBrushSizes.begin(), m_standardBrushSizes.end(), qRound(paintopSize)); int newValue = result != m_standardBrushSizes.end() ? *result : m_standardBrushSizes.back(); currentPaintOpPreset()->settings()->setPaintOpSize(newValue); requestUpdateOutline(m_outlineDocPoint, 0); } void KisToolPaint::decreaseBrushSize() { qreal paintopSize = currentPaintOpPreset()->settings()->paintOpSize(); std::vector::reverse_iterator result = std::upper_bound(m_standardBrushSizes.rbegin(), m_standardBrushSizes.rend(), (int)paintopSize, std::greater()); int newValue = result != m_standardBrushSizes.rend() ? *result : m_standardBrushSizes.front(); currentPaintOpPreset()->settings()->setPaintOpSize(newValue); requestUpdateOutline(m_outlineDocPoint, 0); } QRectF KisToolPaint::colorPreviewDocRect(const QPointF &outlineDocPoint) { if (!m_showColorPreview) return QRect(); KisConfig cfg; const QRectF colorPreviewViewRect = cfg.colorPreviewRect(); const QRectF colorPreviewDocumentRect = canvas()->viewConverter()->viewToDocument(colorPreviewViewRect); return colorPreviewDocumentRect.translated(outlineDocPoint); } void KisToolPaint::requestUpdateOutline(const QPointF &outlineDocPoint, const KoPointerEvent *event) { if (!m_supportOutline) return; KisConfig cfg; KisPaintOpSettings::OutlineMode outlineMode; outlineMode = KisPaintOpSettings::CursorNoOutline; if (isOutlineEnabled() && (mode() == KisTool::GESTURE_MODE || ((cfg.newOutlineStyle() == OUTLINE_FULL || cfg.newOutlineStyle() == OUTLINE_CIRCLE || cfg.newOutlineStyle() == OUTLINE_TILT || cfg.newOutlineStyle() == OUTLINE_COLOR ) && ((mode() == HOVER_MODE) || (mode() == PAINT_MODE && cfg.showOutlineWhilePainting()))))) { // lisp forever! if(cfg.newOutlineStyle() == OUTLINE_CIRCLE) { outlineMode = KisPaintOpSettings::CursorIsCircleOutline; } else if(cfg.newOutlineStyle() == OUTLINE_TILT) { outlineMode = KisPaintOpSettings::CursorTiltOutline; } else if(cfg.newOutlineStyle() == OUTLINE_COLOR) { outlineMode = KisPaintOpSettings::CursorColorOutline; } else { outlineMode = KisPaintOpSettings::CursorIsOutline; } } m_outlineDocPoint = outlineDocPoint; m_currentOutline = getOutlinePath(m_outlineDocPoint, event, outlineMode); QRectF outlinePixelRect = m_currentOutline.boundingRect(); QRectF outlineDocRect = currentImage()->pixelToDocument(outlinePixelRect); // This adjusted call is needed as we paint with a 3 pixel wide brush and the pen is outside the bounds of the path // Pen uses view coordinates so we have to zoom the document value to match 2 pixel in view coordiates // See BUG 275829 qreal zoomX; qreal zoomY; canvas()->viewConverter()->zoom(&zoomX, &zoomY); qreal xoffset = 2.0/zoomX; qreal yoffset = 2.0/zoomY; if (!outlineDocRect.isEmpty()) { outlineDocRect.adjust(-xoffset,-yoffset,xoffset,yoffset); } QRectF colorPreviewDocRect = this->colorPreviewDocRect(m_outlineDocPoint); QRectF colorPreviewDocUpdateRect; if (!colorPreviewDocRect.isEmpty()) { colorPreviewDocUpdateRect.adjust(-xoffset,-yoffset,xoffset,yoffset); } // DIRTY HACK ALERT: we should fetch the assistant's dirty rect when requesting // the update, instead of just dumbly update the entire canvas! KisCanvas2 * kiscanvas = dynamic_cast(canvas()); KisPaintingAssistantsDecorationSP decoration = kiscanvas->paintingAssistantsDecoration(); if (decoration && decoration->visible()) { kiscanvas->updateCanvas(); } else { // TODO: only this branch should be present! if (!m_oldColorPreviewUpdateRect.isEmpty()) { canvas()->updateCanvas(m_oldColorPreviewUpdateRect); } if (!m_oldOutlineRect.isEmpty()) { canvas()->updateCanvas(m_oldOutlineRect); } if (!outlineDocRect.isEmpty()) { canvas()->updateCanvas(outlineDocRect); } if (!colorPreviewDocUpdateRect.isEmpty()) { canvas()->updateCanvas(colorPreviewDocUpdateRect); } } m_oldOutlineRect = outlineDocRect; m_oldColorPreviewRect = colorPreviewDocRect; m_oldColorPreviewUpdateRect = colorPreviewDocUpdateRect; } QPainterPath KisToolPaint::getOutlinePath(const QPointF &documentPos, const KoPointerEvent *event, KisPaintOpSettings::OutlineMode outlineMode) { Q_UNUSED(event); QPointF imagePos = currentImage()->documentToPixel(documentPos); QPainterPath path = currentPaintOpPreset()->settings()-> brushOutline(KisPaintInformation(imagePos), outlineMode); return path; } diff --git a/libs/ui/tool/kis_tool_paint.h b/libs/ui/tool/kis_tool_paint.h index d1c3a47556..93d1153d13 100644 --- a/libs/ui/tool/kis_tool_paint.h +++ b/libs/ui/tool/kis_tool_paint.h @@ -1,221 +1,212 @@ /* * Copyright (c) 2003 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 KIS_TOOL_PAINT_H_ #define KIS_TOOL_PAINT_H_ -#include +#include "kis_tool.h" -#include -#include #include -#include #include +#include #include #include #include #include #include #include #include "kis_signal_compressor_with_param.h" #include - #include -#include "kis_tool.h" -#include - class QGridLayout; class KoCompositeOp; - - class KoCanvasBase; - class KRITAUI_EXPORT KisToolPaint : public KisTool { Q_OBJECT public: - KisToolPaint(KoCanvasBase * canvas, const QCursor & cursor); + KisToolPaint(KoCanvasBase *canvas, const QCursor &cursor); ~KisToolPaint() override; int flags() const override; void mousePressEvent(KoPointerEvent *event) override; void mouseReleaseEvent(KoPointerEvent *event) override; void mouseMoveEvent(KoPointerEvent *event) override; protected: void setMode(ToolMode mode) override; - void canvasResourceChanged(int key, const QVariant & v) override; + void canvasResourceChanged(int key, const QVariant &v) override; - void paint(QPainter& gc, const KoViewConverter &converter) override; + void paint(QPainter &gc, const KoViewConverter &converter) override; void activatePrimaryAction() override; void deactivatePrimaryAction() override; void activateAlternateAction(AlternateAction action) override; void deactivateAlternateAction(AlternateAction action) override; void beginAlternateAction(KoPointerEvent *event, AlternateAction action) override; void continueAlternateAction(KoPointerEvent *event, AlternateAction action) override; void endAlternateAction(KoPointerEvent *event, AlternateAction action) override; virtual void requestUpdateOutline(const QPointF &outlineDocPoint, const KoPointerEvent *event); /** If the paint tool support outline like brushes, set to true. * If not (e.g. gradient tool), set to false. Default is false. */ void setSupportOutline(bool supportOutline) { m_supportOutline = supportOutline; } virtual QPainterPath getOutlinePath(const QPointF &documentPos, const KoPointerEvent *event, KisPaintOpSettings::OutlineMode outlineMode); protected: bool isOutlineEnabled() const; void setOutlineEnabled(bool enabled); bool pickColor(const QPointF &documentPixel, AlternateAction action); /// Add the tool-specific layout to the default option widget layout. void addOptionWidgetLayout(QLayout *layout); /// Add a widget and a label to the current option widget layout. virtual void addOptionWidgetOption(QWidget *control, QWidget *label = 0); void showControl(QWidget *control, bool value); void enableControl(QWidget *control, bool value); QWidget * createOptionWidget() override; /** * Quick help is a short help text about the way the tool functions. */ virtual QString quickHelp() const { return QString(); } void setupPaintAction(KisRecordedPaintAction* action) override; enum NodePaintAbility { NONE, PAINT, VECTOR, CLONE }; /// Checks if and how the tool can paint on the current node NodePaintAbility nodePaintAbility(); const KoCompositeOp* compositeOp(); public Q_SLOTS: void activate(ToolActivation toolActivation, const QSet &shapes) override; void deactivate() override; private Q_SLOTS: void slotPopupQuickHelp(); void increaseBrushSize(); void decreaseBrushSize(); void activatePickColorDelayed(); void slotColorPickingFinished(const KoColor &color); protected: quint8 m_opacity; bool m_paintOutline; QPointF m_outlineDocPoint; QPainterPath m_currentOutline; QRectF m_oldOutlineRect; bool m_showColorPreview; QRectF m_oldColorPreviewRect; QRectF m_oldColorPreviewUpdateRect; QColor m_colorPreviewCurrentColor; bool m_colorPreviewShowComparePlate; QColor m_colorPreviewBaseColor; private: QPainterPath tryFixBrushOutline(const QPainterPath &originalOutline); void setOpacity(qreal opacity); void activatePickColor(AlternateAction action); void deactivatePickColor(AlternateAction action); void pickColorWasOverridden(); int colorPreviewResourceId(AlternateAction action); QRectF colorPreviewDocRect(const QPointF &outlineDocPoint); bool isPickingAction(AlternateAction action); struct PickingJob { PickingJob() {} PickingJob(QPointF _documentPixel, AlternateAction _action) : documentPixel(_documentPixel), action(_action) {} QPointF documentPixel; AlternateAction action; }; void addPickerJob(const PickingJob &pickingJob); private: bool m_specialHoverModifier; QGridLayout *m_optionsWidgetLayout; bool m_supportOutline; /** * Used as a switch for pickColor */ // used to skip some of the tablet events and don't update the colour that often QTimer m_colorPickerDelayTimer; AlternateAction delayedAction; bool m_isOutlineEnabled; std::vector m_standardBrushSizes; KisStrokeId m_pickerStrokeId; int m_pickingResource; typedef KisSignalCompressorWithParam PickingCompressor; QScopedPointer m_colorPickingCompressor; qreal m_localOpacity {1.0}; qreal m_oldOpacity {1.0}; Q_SIGNALS: void sigPaintingFinished(); }; #endif // KIS_TOOL_PAINT_H_ diff --git a/libs/ui/tool/kis_tool_utils.cpp b/libs/ui/tool/kis_tool_utils.cpp index cfc8298b4d..7669791567 100644 --- a/libs/ui/tool/kis_tool_utils.cpp +++ b/libs/ui/tool/kis_tool_utils.cpp @@ -1,204 +1,203 @@ /* * Copyright (c) 2009 Boudewijn Rempt * Copyright (c) 2018 Emmet & Eoin O'Neill * * 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 #include #include #include #include #include #include #include namespace KisToolUtils { - bool pick(KisPaintDeviceSP dev, const QPoint &pos, KoColor *color, KoColor *previousColor, int radius, int blend) + bool pickColor(KoColor &out_color, KisPaintDeviceSP dev, const QPoint &pos, + KoColor const *const blendColor, int radius, int blend, bool pure) { KIS_ASSERT(dev); - const KoColorSpace* cs = dev->colorSpace(); + + const KoColorSpace *cs = dev->colorSpace(); KoColor pickedColor(Qt::transparent, cs); - // Ctrl picker sampling radius. - if (radius <= 1) { - dev->pixel(pos.x(), pos.y(), &pickedColor); - } else { + // Sampling radius. + if (!pure && radius > 1) { QVector pixels; - const int effectiveRadius = radius - 1; const QRect pickRect(pos.x() - effectiveRadius, pos.y() - effectiveRadius, 2 * effectiveRadius + 1, 2 * effectiveRadius + 1); KisSequentialConstIterator it(dev, pickRect); const int radiusSq = pow2(effectiveRadius); while (it.nextPixel()) { const QPoint realPos(it.x(), it.y()); const QPoint pt = realPos - pos; if (pow2(pt.x()) + pow2(pt.y()) < radiusSq) { pixels << it.oldRawData(); } } const quint8 **cpixels = const_cast(pixels.constData()); cs->mixColorsOp()->mixColors(cpixels, pixels.size(), pickedColor.data()); + } else { + dev->pixel(pos.x(), pos.y(), &pickedColor); } - // Ctrl picker color blending. - if (previousColor && blend < 100) { + // Color blending. + if (!pure && blendColor && blend < 100) { //Scale from 0..100% to 0..255 range for mixOp weights. quint8 blendScaled = static_cast(blend * 2.55f); const quint8 *colors[2]; - colors[0] = previousColor->data(); + colors[0] = blendColor->data(); colors[1] = pickedColor.data(); qint16 weights[2]; weights[0] = 255 - blendScaled; weights[1] = blendScaled; const KoMixColorsOp *mixOp = dev->colorSpace()->mixColorsOp(); mixOp->mixColors(colors, weights, 2, pickedColor.data()); } pickedColor.convertTo(dev->compositionSourceColorSpace()); - bool validColorPicked = - pickedColor.opacityU8() != OPACITY_TRANSPARENT_U8; + bool validColorPicked = pickedColor.opacityU8() != OPACITY_TRANSPARENT_U8; if (validColorPicked) { pickedColor.setOpacity(OPACITY_OPAQUE_U8); - *color = pickedColor; + out_color = pickedColor; } return validColorPicked; } KisNodeSP findNode(KisNodeSP node, const QPoint &point, bool wholeGroup, bool editableOnly) { KisNodeSP foundNode = 0; while (node) { KisLayerSP layer = qobject_cast(node.data()); if (!layer || !layer->isEditable()) { node = node->prevSibling(); continue; } KoColor color(layer->projection()->colorSpace()); layer->projection()->pixel(point.x(), point.y(), &color); KisGroupLayerSP group = dynamic_cast(layer.data()); if ((group && group->passThroughMode()) || color.opacityU8() != OPACITY_TRANSPARENT_U8) { if (layer->inherits("KisGroupLayer") && (!editableOnly || layer->isEditable())) { // if this is a group and the pixel is transparent, don't even enter it foundNode = findNode(node->lastChild(), point, wholeGroup, editableOnly); } else { foundNode = !wholeGroup ? node : node->parent(); } } if (foundNode) break; node = node->prevSibling(); } return foundNode; } bool clearImage(KisImageSP image, KisNodeSP node, KisSelectionSP selection) { if(node && node->hasEditablePaintDevice()) { KisPaintDeviceSP device = node->paintDevice(); image->barrierLock(); KisTransaction transaction(kundo2_i18n("Clear"), device); QRect dirtyRect; if (selection) { dirtyRect = selection->selectedRect(); device->clearSelection(selection); } else { dirtyRect = device->extent(); device->clear(); } transaction.commit(image->undoAdapter()); device->setDirty(dirtyRect); image->unlock(); return true; } return false; } const QString ColorPickerConfig::CONFIG_GROUP_NAME = "tool_color_picker"; ColorPickerConfig::ColorPickerConfig() : toForegroundColor(true) , updateColor(true) , addPalette(false) , normaliseValues(false) , sampleMerged(true) , radius(1) , blend(100) { } inline QString getConfigKey(bool defaultActivation) { return defaultActivation ? "ColorPickerDefaultActivation" : "ColorPickerTemporaryActivation"; } void ColorPickerConfig::save(bool defaultActivation) const { KisPropertiesConfiguration props; props.setProperty("toForegroundColor", toForegroundColor); props.setProperty("updateColor", updateColor); props.setProperty("addPalette", addPalette); props.setProperty("normaliseValues", normaliseValues); props.setProperty("sampleMerged", sampleMerged); props.setProperty("radius", radius); props.setProperty("blend", blend); KConfigGroup config = KSharedConfig::openConfig()->group(CONFIG_GROUP_NAME); config.writeEntry(getConfigKey(defaultActivation), props.toXML()); } void ColorPickerConfig::load(bool defaultActivation) { KisPropertiesConfiguration props; KConfigGroup config = KSharedConfig::openConfig()->group(CONFIG_GROUP_NAME); props.fromXML(config.readEntry(getConfigKey(defaultActivation))); toForegroundColor = props.getBool("toForegroundColor", true); updateColor = props.getBool("updateColor", true); addPalette = props.getBool("addPalette", false); normaliseValues = props.getBool("normaliseValues", false); sampleMerged = props.getBool("sampleMerged", !defaultActivation ? false : true); radius = props.getInt("radius", 1); blend = props.getInt("blend", 100); } - } diff --git a/libs/ui/tool/kis_tool_utils.h b/libs/ui/tool/kis_tool_utils.h index f1701f7290..c39dd180f6 100644 --- a/libs/ui/tool/kis_tool_utils.h +++ b/libs/ui/tool/kis_tool_utils.h @@ -1,66 +1,77 @@ /* * Copyright (c) 2009 Boudewijn Rempt * Copyright (c) 2018 Emmet & Eoin O'Neill * * 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_TOOL_UTILS_H #define KIS_TOOL_UTILS_H #include #include class QPoint; class KoColor; namespace KisToolUtils { struct KRITAUI_EXPORT ColorPickerConfig { ColorPickerConfig(); bool toForegroundColor; bool updateColor; bool addPalette; bool normaliseValues; bool sampleMerged; int radius; int blend; void save(bool defaultActivation = true) const; void load(bool defaultActivation = true); private: static const QString CONFIG_GROUP_NAME; }; /** - * return the color at the given position on the given paint device. + * Pick a color based on the given position on the given paint device. + * + * out_color - Output parameter returning newly picked color. + * dev - Paint device to pick from. + * pos - Position to pick from. + * blendColor - Optional color to be blended with. + * radius - Picking area radius in pixels. + * blend - Blend percentage. 100% all picked, 0% all blendColor. + * pure - Whether to bypass radius, blending, and active layer settings for pure picking. + * + * RETURN - Returns true if a valid color was picked. */ -bool KRITAUI_EXPORT pick(KisPaintDeviceSP dev, const QPoint &pos, KoColor *color, - KoColor *previousColor = nullptr, int radius = 1, int blend = 100); +bool KRITAUI_EXPORT pickColor(KoColor &out_color, KisPaintDeviceSP dev, const QPoint &pos, + KoColor const *const blendColor = nullptr, int radius = 1, + int blend = 100, bool pure = false); /** * Recursively search a node with a non-transparent pixel */ KisNodeSP KRITAUI_EXPORT findNode(KisNodeSP node, const QPoint &point, bool wholeGroup, bool editableOnly = true); /** * return true if success * Clears the image. Selection is optional, use 0 to clear everything. */ bool KRITAUI_EXPORT clearImage(KisImageSP image, KisNodeSP node, KisSelectionSP selection); } #endif // KIS_TOOL_UTILS_H diff --git a/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.cpp b/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.cpp index a197334748..816569093a 100644 --- a/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.cpp +++ b/libs/ui/tool/strokes/kis_color_picker_stroke_strategy.cpp @@ -1,74 +1,74 @@ /* * 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 "kis_color_picker_stroke_strategy.h" #include "kis_tool_utils.h" #include "kis_paint_device.h" struct KisColorPickerStrokeStrategy::Private { Private() : shouldSkipWork(false) {} bool shouldSkipWork; int radius = 1; int blend = 100; }; KisColorPickerStrokeStrategy::KisColorPickerStrokeStrategy(int lod) : m_d(new Private) { setSupportsWrapAroundMode(true); enableJob(KisSimpleStrokeStrategy::JOB_DOSTROKE); KisToolUtils::ColorPickerConfig config; config.load(); m_d->radius = qMax(1, qRound(config.radius * KisLodTransform::lodToScale(lod))); m_d->blend = config.blend; } KisColorPickerStrokeStrategy::~KisColorPickerStrokeStrategy() { } void KisColorPickerStrokeStrategy::doStrokeCallback(KisStrokeJobData *data) { if (m_d->shouldSkipWork) return; Data *d = dynamic_cast(data); KIS_ASSERT_RECOVER_RETURN(d); KoColor color; KoColor previous = d->currentColor; - bool result = KisToolUtils::pick(d->dev, d->pt, &color, &previous, m_d->radius, m_d->blend); + bool result = KisToolUtils::pickColor(color, d->dev, d->pt, &previous, m_d->radius, m_d->blend); Q_UNUSED(result); emit sigColorUpdated(color); } KisStrokeStrategy* KisColorPickerStrokeStrategy::createLodClone(int levelOfDetail) { m_d->shouldSkipWork = true; KisColorPickerStrokeStrategy *lodStrategy = new KisColorPickerStrokeStrategy(levelOfDetail); connect(lodStrategy, &KisColorPickerStrokeStrategy::sigColorUpdated, this, &KisColorPickerStrokeStrategy::sigColorUpdated, Qt::DirectConnection); return lodStrategy; } diff --git a/libs/ui/widgets/kis_advanced_color_space_selector.cc b/libs/ui/widgets/kis_advanced_color_space_selector.cc index c64684894c..adacb8a213 100644 --- a/libs/ui/widgets/kis_advanced_color_space_selector.cc +++ b/libs/ui/widgets/kis_advanced_color_space_selector.cc @@ -1,793 +1,793 @@ /* * Copyright (C) 2007 Cyrille Berger * Copyright (C) 2011 Boudewijn Rempt * Copyright (C) 2011 Srikanth Tiyyagura * Copyright (C) 2015 Wolthera van Hövell tot Westerflier * * 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_advanced_color_space_selector.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ui_wdgcolorspaceselectoradvanced.h" #include struct KisAdvancedColorSpaceSelector::Private { Ui_WdgColorSpaceSelectorAdvanced* colorSpaceSelector; QString knsrcFile; }; KisAdvancedColorSpaceSelector::KisAdvancedColorSpaceSelector(QWidget* parent, const QString &caption) : QDialog(parent) , d(new Private) { setWindowTitle(caption); d->colorSpaceSelector = new Ui_WdgColorSpaceSelectorAdvanced; d->colorSpaceSelector->setupUi(this); d->colorSpaceSelector->cmbColorModels->setIDList(KoColorSpaceRegistry::instance()->colorModelsList(KoColorSpaceRegistry::OnlyUserVisible)); fillCmbDepths(d->colorSpaceSelector->cmbColorModels->currentItem()); d->colorSpaceSelector->bnInstallProfile->setIcon(KisIconUtils::loadIcon("document-open")); d->colorSpaceSelector->bnInstallProfile->setToolTip( i18n("Open Color Profile") ); connect(d->colorSpaceSelector->cmbColorModels, SIGNAL(activated(const KoID &)), this, SLOT(fillCmbDepths(const KoID &))); connect(d->colorSpaceSelector->cmbColorDepth, SIGNAL(activated(const KoID &)), this, SLOT(fillLstProfiles())); connect(d->colorSpaceSelector->cmbColorModels, SIGNAL(activated(const KoID &)), this, SLOT(fillLstProfiles())); connect(d->colorSpaceSelector->lstProfile, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, SLOT(colorSpaceChanged())); connect(this, SIGNAL(selectionChanged(bool)), this, SLOT(fillDescription())); connect(this, SIGNAL(selectionChanged(bool)), d->colorSpaceSelector->TongueWidget, SLOT(repaint())); connect(this, SIGNAL(selectionChanged(bool)), d->colorSpaceSelector->TRCwidget, SLOT(repaint())); connect(d->colorSpaceSelector->bnInstallProfile, SIGNAL(clicked()), this, SLOT(installProfile())); connect(d->colorSpaceSelector->bnOK, SIGNAL(accepted()), this, SLOT(accept())); connect(d->colorSpaceSelector->bnOK, SIGNAL(rejected()), this, SLOT(reject())); fillLstProfiles(); } KisAdvancedColorSpaceSelector::~KisAdvancedColorSpaceSelector() { delete d->colorSpaceSelector; delete d; } void KisAdvancedColorSpaceSelector::fillLstProfiles() { d->colorSpaceSelector->lstProfile->blockSignals(true); const QString colorSpaceId = KoColorSpaceRegistry::instance()->colorSpaceId(d->colorSpaceSelector->cmbColorModels->currentItem(), d->colorSpaceSelector->cmbColorDepth->currentItem()); const QString defaultProfileName = KoColorSpaceRegistry::instance()->defaultProfileForColorSpace(colorSpaceId); d->colorSpaceSelector->lstProfile->clear(); QList profileList = KoColorSpaceRegistry::instance()->profilesFor(colorSpaceId); QStringList profileNames; Q_FOREACH (const KoColorProfile *profile, profileList) { profileNames.append(profile->name()); } std::sort(profileNames.begin(), profileNames.end()); QListWidgetItem *defaultProfile = new QListWidgetItem; defaultProfile->setText(defaultProfileName + " " + i18nc("This is appended to the color profile which is the default for the given colorspace and bit-depth","(Default)")); Q_FOREACH (QString stringName, profileNames) { if (stringName == defaultProfileName) { d->colorSpaceSelector->lstProfile->addItem(defaultProfile); } else { d->colorSpaceSelector->lstProfile->addItem(stringName); } } d->colorSpaceSelector->lstProfile->setCurrentItem(defaultProfile); d->colorSpaceSelector->lstProfile->blockSignals(false); colorSpaceChanged(); } void KisAdvancedColorSpaceSelector::fillCmbDepths(const KoID& id) { KoID activeDepth = d->colorSpaceSelector->cmbColorDepth->currentItem(); d->colorSpaceSelector->cmbColorDepth->clear(); QList depths = KoColorSpaceRegistry::instance()->colorDepthList(id, KoColorSpaceRegistry::OnlyUserVisible); QList sortedDepths; if (depths.contains(Integer8BitsColorDepthID)) { sortedDepths << Integer8BitsColorDepthID; } if (depths.contains(Integer16BitsColorDepthID)) { sortedDepths << Integer16BitsColorDepthID; } if (depths.contains(Float16BitsColorDepthID)) { sortedDepths << Float16BitsColorDepthID; } if (depths.contains(Float32BitsColorDepthID)) { sortedDepths << Float32BitsColorDepthID; } if (depths.contains(Float64BitsColorDepthID)) { sortedDepths << Float64BitsColorDepthID; } d->colorSpaceSelector->cmbColorDepth->setIDList(sortedDepths); if (sortedDepths.contains(activeDepth)) { d->colorSpaceSelector->cmbColorDepth->setCurrent(activeDepth); } } void KisAdvancedColorSpaceSelector::fillDescription() { QString notApplicable = i18nc("Not Applicable, used where there's no colorants or gamma curve found","N/A"); QString notApplicableTooltip = i18nc("@info:tooltip","This profile has no colorants."); QString profileName = i18nc("Shows up instead of the name when there's no profile","No Profile Found"); QString whatIsColorant = i18n("Colorant in d50-adapted xyY."); //set colorants const QString colorSpaceId = KoColorSpaceRegistry::instance()->colorSpaceId(d->colorSpaceSelector->cmbColorModels->currentItem(), d->colorSpaceSelector->cmbColorDepth->currentItem()); QList profileList = KoColorSpaceRegistry::instance()->profilesFor(colorSpaceId); if (!profileList.isEmpty()) { profileName = currentColorSpace()->profile()->name(); if (currentColorSpace()->profile()->hasColorants()){ QVector colorants = currentColorSpace()->profile()->getColorantsxyY(); QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); //QString text = currentColorSpace()->profile()->info() + " =" + d->colorSpaceSelector->lblXYZ_W->setText(nameWhitePoint(whitepoint)); d->colorSpaceSelector->lblXYZ_W->setToolTip(QString::number(whitepoint[0], 'f', 4) + ", " + QString::number(whitepoint[1], 'f', 4) + ", " + QString::number(whitepoint[2], 'f', 4)); d->colorSpaceSelector->TongueWidget->setToolTip("
      "+i18nc("@info:tooltip","This profile has the following xyY colorants:")+"
      "+ i18n("Red:") +""+QString::number(colorants[0], 'f', 4) + "" + QString::number(colorants[1], 'f', 4) + "" + QString::number(colorants[2], 'f', 4)+"
      "+ i18n("Green:")+""+QString::number(colorants[3], 'f', 4) + "" + QString::number(colorants[4], 'f', 4) + "" + QString::number(colorants[5], 'f', 4)+"
      "+ i18n("Blue:") +""+QString::number(colorants[6], 'f', 4) + "" + QString::number(colorants[7], 'f', 4) + "" + QString::number(colorants[8], 'f', 4)+"
      "); } else { QVector whitepoint2 = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->lblXYZ_W->setText(nameWhitePoint(whitepoint2)); d->colorSpaceSelector->lblXYZ_W->setToolTip(QString::number(whitepoint2[0], 'f', 4) + ", " + QString::number(whitepoint2[1], 'f', 4) + ", " + QString::number(whitepoint2[2], 'f', 4)); d->colorSpaceSelector->TongueWidget->setToolTip(notApplicableTooltip); } } else { d->colorSpaceSelector->lblXYZ_W->setText(notApplicable); d->colorSpaceSelector->lblXYZ_W->setToolTip(notApplicableTooltip); d->colorSpaceSelector->TongueWidget->setToolTip(notApplicableTooltip); } //set TRC QVector estimatedTRC(3); QString estimatedGamma = i18nc("Estimated Gamma indicates how the TRC (Tone Response Curve or Tone Reproduction Curve) is bent. A Gamma of 1.0 means linear.", "Estimated Gamma: "); QString estimatedsRGB = i18nc("This is for special Gamma types that LCMS cannot differentiate between", "Estimated Gamma: sRGB, L* or rec709 TRC"); QString whatissRGB = i18nc("@info:tooltip","The Tone Response Curve of this color space is either sRGB, L* or rec709 TRC."); QString currentModelStr = d->colorSpaceSelector->cmbColorModels->currentItem().id(); if (profileList.isEmpty()) { d->colorSpaceSelector->TongueWidget->setProfileDataAvailable(false); d->colorSpaceSelector->TRCwidget->setProfileDataAvailable(false); } else if (currentModelStr == "RGBA") { QVector colorants = currentColorSpace()->profile()->getColorantsxyY(); QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); if (currentColorSpace()->profile()->hasColorants()){ d->colorSpaceSelector->TongueWidget->setRGBData(whitepoint, colorants); } else { colorants.fill(0.0); d->colorSpaceSelector->TongueWidget->setRGBData(whitepoint, colorants); } d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); estimatedTRC = currentColorSpace()->profile()->getEstimatedTRC(); QString estimatedCurve = " Estimated curve: "; QPolygonF redcurve; QPolygonF greencurve; QPolygonF bluecurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); redcurve<colorSpaceSelector->TRCwidget->setRGBCurve(redcurve, greencurve, bluecurve); } else { QPolygonF curve = currentColorSpace()->estimatedTRCXYY(); redcurve << curve.at(0) << curve.at(1) << curve.at(2) << curve.at(3) << curve.at(4); greencurve << curve.at(5) << curve.at(6) << curve.at(7) << curve.at(8) << curve.at(9); bluecurve << curve.at(10) << curve.at(11) << curve.at(12) << curve.at(13) << curve.at(14); d->colorSpaceSelector->TRCwidget->setRGBCurve(redcurve, greencurve, bluecurve); } if (estimatedTRC[0] == -1) { d->colorSpaceSelector->TRCwidget->setToolTip(""+whatissRGB+"
      "+estimatedCurve+""); } else { d->colorSpaceSelector->TRCwidget->setToolTip(""+estimatedGamma + QString::number(estimatedTRC[0]) + "," + QString::number(estimatedTRC[1]) + "," + QString::number(estimatedTRC[2])+"
      "+estimatedCurve+""); } } else if (currentModelStr == "GRAYA") { QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->TongueWidget->setGrayData(whitepoint); d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); estimatedTRC = currentColorSpace()->profile()->getEstimatedTRC(); QString estimatedCurve = " Estimated curve: "; QPolygonF tonecurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); tonecurve<colorSpaceSelector->TRCwidget->setProfileDataAvailable(false); } d->colorSpaceSelector->TRCwidget->setGreyscaleCurve(tonecurve); if (estimatedTRC[0] == -1) { d->colorSpaceSelector->TRCwidget->setToolTip(""+whatissRGB+"
      "+estimatedCurve+""); } else { d->colorSpaceSelector->TRCwidget->setToolTip(""+estimatedGamma + QString::number(estimatedTRC[0])+"
      "+estimatedCurve+""); } } else if (currentModelStr == "CMYKA") { QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->TongueWidget->setCMYKData(whitepoint); d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); QString estimatedCurve = " Estimated curve: "; QPolygonF tonecurve; QPolygonF cyancurve; QPolygonF magentacurve; QPolygonF yellowcurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); tonecurve<colorSpaceSelector->TRCwidget->setGreyscaleCurve(tonecurve); } else { QPolygonF curve = currentColorSpace()->estimatedTRCXYY(); cyancurve << curve.at(0) << curve.at(1) << curve.at(2) << curve.at(3) << curve.at(4); magentacurve << curve.at(5) << curve.at(6) << curve.at(7) << curve.at(8) << curve.at(9); yellowcurve << curve.at(10) << curve.at(11) << curve.at(12) << curve.at(13) << curve.at(14); tonecurve << curve.at(15) << curve.at(16) << curve.at(17) << curve.at(18) << curve.at(19); d->colorSpaceSelector->TRCwidget->setCMYKCurve(cyancurve, magentacurve, yellowcurve, tonecurve); } d->colorSpaceSelector->TRCwidget->setToolTip(i18nc("@info:tooltip","Estimated Gamma cannot be retrieved for CMYK.")); } else if (currentModelStr == "XYZA") { QString estimatedCurve = " Estimated curve: "; estimatedTRC = currentColorSpace()->profile()->getEstimatedTRC(); QPolygonF tonecurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); tonecurve<colorSpaceSelector->TRCwidget->setGreyscaleCurve(tonecurve); } else { d->colorSpaceSelector->TRCwidget->setProfileDataAvailable(false); } QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->TongueWidget->setXYZData(whitepoint); d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); d->colorSpaceSelector->TRCwidget->setToolTip(""+estimatedGamma + QString::number(estimatedTRC[0])+"< br />"+estimatedCurve+""); } else if (currentModelStr == "LABA") { estimatedTRC = currentColorSpace()->profile()->getEstimatedTRC(); QString estimatedCurve = " Estimated curve: "; QPolygonF tonecurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); tonecurve<colorSpaceSelector->TRCwidget->setGreyscaleCurve(tonecurve); } else { d->colorSpaceSelector->TRCwidget->setProfileDataAvailable(false); } QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->TongueWidget->setLABData(whitepoint); d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); d->colorSpaceSelector->TRCwidget->setToolTip(""+i18nc("@info:tooltip","This is assumed to be the L * TRC. ")+"
      "+estimatedCurve+""); } else if (currentModelStr == "YCbCrA") { QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->TongueWidget->setYCbCrData(whitepoint); QString estimatedCurve = " Estimated curve: "; QPolygonF tonecurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); tonecurve<colorSpaceSelector->TRCwidget->setGreyscaleCurve(tonecurve); } else { d->colorSpaceSelector->TRCwidget->setProfileDataAvailable(false); } d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); d->colorSpaceSelector->TRCwidget->setToolTip(i18nc("@info:tooltip","Estimated Gamma cannot be retrieved for YCrCb.")); } d->colorSpaceSelector->textProfileDescription->clear(); if (profileList.isEmpty()==false) { d->colorSpaceSelector->textProfileDescription->append("

      "+i18nc("About ","About ") + currentColorSpace()->name() + "/" + profileName + "

      "); d->colorSpaceSelector->textProfileDescription->append("

      "+ i18nc("ICC profile version","ICC Version: ") + QString::number(currentColorSpace()->profile()->version()) + "

      "); //d->colorSpaceSelector->textProfileDescription->append("

      "+ i18nc("Who made the profile?","Manufacturer: ") + currentColorSpace()->profile()->manufacturer() + "

      "); //This would work if people actually wrote the manufacturer into the manufacturer fiedl... d->colorSpaceSelector->textProfileDescription->append("

      "+ i18nc("What is the copyright? These are from embedded strings from the icc profile, so they default to english.","Copyright: ") + currentColorSpace()->profile()->copyright() + "

      "); } else { d->colorSpaceSelector->textProfileDescription->append("

      " + profileName + "

      "); } if (currentModelStr == "RGBA") { d->colorSpaceSelector->textProfileDescription->append("

      "+i18nc("If the selected model is RGB", "RGB (Red, Green, Blue), is the color model used by screens and other light-based media.
      " "RGB is an additive color model: adding colors together makes them brighter. This color " "model is the most extensive of all color models, and is recommended as a model for painting," "that you can later convert to other spaces. RGB is also the recommended colorspace for HDR editing.")+"

      "); } else if (currentModelStr == "CMYKA") { d->colorSpaceSelector->textProfileDescription->append("

      "+i18nc("If the selected model is CMYK", "CMYK (Cyan, Magenta, Yellow, Key), " "is the model used by printers and other ink-based media.
      " "CMYK is a subtractive model, meaning that adding colors together will turn them darker. Because of CMYK " "profiles being very specific per printer, it is recommended to work in RGB space, and then later convert " "to a CMYK profile, preferably one delivered by your printer.
      " "CMYK is not recommended for painting." "Unfortunately, Krita cannot retrieve colorants or the TRC for this space.")+"

      "); } else if (currentModelStr == "XYZA") { d->colorSpaceSelector->textProfileDescription->append("

      "+i18nc("If the selected model is XYZ", "CIE XYZ" "is the space determined by the CIE as the space that encompasses all other colors, and used to " "convert colors between profiles. XYZ is an additive color model, meaning that adding colors together " "makes them brighter. XYZ is not recommended for painting, but can be useful to encode in. The Tone Response " "Curve is assumed to be linear.")+"

      "); } else if (currentModelStr == "GRAYA") { d->colorSpaceSelector->textProfileDescription->append("

      "+i18nc("If the selected model is Grayscale", "Grayscale only allows for " "gray values and transparent values. Grayscale images use half " "the memory and disk space compared to an RGB image of the same bit-depth.
      " "Grayscale is useful for inking and greyscale images. In " "Krita, you can mix Grayscale and RGB layers in the same image.")+"

      "); } else if (currentModelStr == "LABA") { d->colorSpaceSelector->textProfileDescription->append("

      "+i18nc("If the selected model is LAB", "L*a*b. L stands for Lightness, " "the a and b components represent color channels.
      " "L*a*b is a special model for color correction. It is based on human perception, meaning that it " "tries to encode the difference in lightness, red-green balance and yellow-blue balance. " "This makes it useful for color correction, but the vast majority of color maths in the blending " "modes do not work as expected here.
      " "Similarly, Krita does not support HDR in LAB, meaning that HDR images converted to LAB lose color " "information. This colorspace is not recommended for painting, nor for export, " "but best as a space to do post-processing in. The TRC is assumed to be the L* TRC.")+"

      "); } else if (currentModelStr == "YCbCrA") { d->colorSpaceSelector->textProfileDescription->append("

      "+i18nc("If the selected model is YCbCr", "YCbCr (Luma, Blue Chroma, Red Chroma), is a " "model designed for video encoding. It is based on human perception, meaning that it tries to " "encode the difference in lightness, red-green balance and yellow-blue balance. Chroma in " "this case is then a word indicating a special type of saturation, in these cases the saturation " "of Red and Blue, of which the desaturated equivalents are Green and Yellow respectively. It " "is available to open up certain images correctly, but Krita does not currently ship a profile for " "this due to lack of open source ICC profiles for YCrCb.")+"

      "); } QString currentDepthStr = d->colorSpaceSelector->cmbColorDepth->currentItem().id(); if (currentDepthStr == "U8") { d->colorSpaceSelector->textProfileDescription->append("

      "+i18nc("When the selected Bitdepth is 8", - "8 bit integer: The default amount of colors per channel. Each channel will have 256 values available, " - "leading to a total amount of 256*amount of channels. Recommended to use for images intended for the web, " + "8 bit integer: The default number of colors per channel. Each channel will have 256 values available, " + "leading to a total amount of colors of 256 to the power of the number of channels. Recommended to use for images intended for the web, " "or otherwise simple images.")+"

      "); } else if (currentDepthStr == "U16") { d->colorSpaceSelector->textProfileDescription->append("

      "+i18nc("When the selected Bitdepth is 16", "16 bit integer: Also known as 'deep color'. 16 bit is ideal for editing images with a linear TRC, large " "color space, or just when you need more precise color blending. This does take twice as much space on " "the RAM and hard-drive than any given 8 bit image of the same properties, and for some devices it " "takes much more processing power. We recommend watching the RAM usage of the file carefully, or " "otherwise use 8 bit if your computer slows down. Take care to disable conversion optimization " "when converting from 16 bit/channel to 8 bit/channel.")+"

      "); } else if (currentDepthStr == "F16") { d->colorSpaceSelector->textProfileDescription->append("

      "+i18nc("When the selected Bitdepth is 16 bit float", "16 bit floating point: Also known as 'Half Floating Point', and the standard in VFX industry images. " "16 bit float is ideal for editing images with a linear Tone Response Curve, large color space, or just when you need " "more precise color blending. It being floating point is an absolute requirement for Scene Referred " "(HDR) images. This does take twice as much space on the RAM and hard-drive than any given 8 bit image " "of the same properties, and for some devices it takes much more processing power. We recommend watching " "the RAM usage of the file carefully, or otherwise use 8 bit if your computer slows down.")+"

      "); } else if (currentDepthStr == "F32") { d->colorSpaceSelector->textProfileDescription->append("

      "+i18nc("When the selected Bitdepth is 32bit float", "32 bit float point: Also known as 'Full Floating Point'. 32 bit float is ideal for editing images " "with a linear TRC, large color space, or just when you need more precise color blending. It being " "floating point is an absolute requirement for Scene Referred (HDR) images. This does take four times " "as much space on the RAM and hard-drive than any given 8 bit image of the same properties, and for " "some devices it takes much more processing power. We recommend watching the RAM usage of the file " "carefully, or otherwise use 8 bit if your computer slows down.")+"

      "); } else if (currentDepthStr == "F64") { d->colorSpaceSelector->textProfileDescription->append("

      "+i18nc("When the selected Bitdepth is 64bit float, but this isn't actually available in Krita at the moment.",\ "64 bit float point: 64 bit float is as precise as it gets in current technology, and this depth is used " "most of the time for images that are generated or used as an input for software. It being floating point " "is an absolute requirement for Scene Referred (HDR) images. This does take eight times as much space on " "the RAM and hard-drive than any given 8 bit image of the same properties, and for some devices it takes " "much more processing power. We recommend watching the RAM usage of the file carefully, or otherwise use " "8 bit if your computer slows down.")+"

      "); } if (profileList.isEmpty()==false) { QString possibleConversionIntents = "

      "+i18n("The following conversion intents are possible: ")+"

        "; if (currentColorSpace()->profile()->supportsPerceptual()){ possibleConversionIntents += "
      • "+i18n("Perceptual")+"
      • "; } if (currentColorSpace()->profile()->supportsRelative()){ possibleConversionIntents += "
      • "+i18n("Relative Colorimetric")+"
      • "; } if (currentColorSpace()->profile()->supportsAbsolute()){ possibleConversionIntents += "
      • "+i18n("Absolute Colorimetric")+"
      • "; } if (currentColorSpace()->profile()->supportsSaturation()){ possibleConversionIntents += "
      • "+i18n("Saturation")+"
      • "; } possibleConversionIntents += "

    "; d->colorSpaceSelector->textProfileDescription->append(possibleConversionIntents); } if (profileName.contains("-elle-")) { d->colorSpaceSelector->textProfileDescription->append("

    "+i18nc("These are Elle Stone's notes on her profiles that we ship.", "

    Extra notes on profiles by Elle Stone:

    " "

    Krita comes with a number of high quality profiles created by " "Elle Stone. This is a summary. Please check " "the full documentation as well.

    ")); if (profileName.contains("ACES-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

    Quoting Wikipedia, 'Academy Color Encoding System (ACES) is a color image " "encoding system proposed by the Academy of Motion Picture Arts and Sciences that will allow for " "a fully encompassing color accurate workflow, with 'seamless interchange of high quality motion " "picture images regardless of source'.

    ")); } if (profileName.contains("ACEScg-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

    The ACEScg color space is smaller than the ACES color space, but large enough to contain the 'Rec-2020 gamut " "and the DCI-P3 gamut', unlike the ACES color space it has no negative values and contains only few colors " "that fall just barely outside the area of real colors humans can see

    ")); } if (profileName.contains("ClayRGB-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

    To avoid possible copyright infringement issues, I used 'ClayRGB' (following ArgyllCMS) as the base name " "for these profiles. As used below, 'Compatible with Adobe RGB 1998' is terminology suggested in the preamble " "to the AdobeRGB 1998 color space specifications.

    " "The Adobe RGB 1998 color gamut covers a higher " "percentage of real-world cyans, greens, and yellow-greens than sRGB, but still doesn't include all printable " "cyans, greens, yellow-greens, especially when printing using today's high-end, wider gamut, ink jet printers. " "BetaRGB (not included in the profile pack) and Rec.2020 are better matches for the color gamuts of today's " "wide gamut printers.

    " "The Adobe RGB 1998 color gamut is a reasonable approximation to some of today's " "high-end wide gamut monitors.

    ")); } if (profileName.contains("AllColorsRGB-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

    This profile's color gamut is roughly the same size and shape as the ACES color space gamut, " "and like the ACES color space, AllColorsRGB holds all possible real colors. But AllColorsRGB " "actually has a slightly larger color gamut (to capture some fringe colors that barely qualify " "as real when viewed by the standard observer) and uses the D50 white point.

    " "Just like the ACES color space, AllColorsRGB holds a high percentage of imaginary colors. See the Completely " "" "Painless Programmer's Guide to XYZ, RGB, ICC, xyY, and TRCs for more information about imaginary " "colors.

    " "There is no particular reason why anyone would want to use this profile " "for editing, unless one needs to make sure your color space really does hold all " "possible real colors.

    ")); } if (profileName.contains("CIERGB-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

    This profile is included mostly for its historical significance. " "It's the color space that was used in the original color matching experiments " "that led to the creation of the XYZ reference color space.

    " "The ASTM E white point " "is probably the right E white point to use when making the CIERGB color space profile. " "It's not clear to me what the correct CIERGB primaries really are. " "Lindbloom gives one set. The LCMS version 1 tutorial gives a different set. " "Experts in the field contend that the real primaries " "should be calculated from the spectral wavelengths, so I did.

    ")); } if (profileName.contains("IdentityRGB-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

    The IdentityRGB working space is included in the profile pack because it's a mathematically " "obvious way to include all possible visible colors, though it has a higher percentage of " "imaginary colors than the ACES and AllColorsRGB color spaces. I cannot think of any reason " "why you'd ever want to actually edit images in the IdentityRGB working space.

    ")); } if (profileName.contains("LargeRGB-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

    To avoid possible copyright infringement issues, I used 'LargeRGB' (following RawTherapee) " "as the base name for these profiles.

    " "Kodak designed the RIMM/ROMM (ProPhotoRGB) color " "gamut to include all printable and most real world colors. It includes some imaginary colors " "and excludes some of the real world blues and violet blues that can be captured by digital " "cameras. It also excludes some very saturated 'camera-captured' yellows as interpreted by " "some (and probably many) camera matrix input profiles.

    " "The ProPhotoRGB primaries are " "hard-coded into Adobe products such as Lightroom and the Dng-DCP camera 'profiles'. However, " "other than being large enough to hold a lot of colors, ProPhotoRGB has no particular merit " "as an RGB working space. Personally and for most editing purposes, I recommend BetaRGB, Rec2020, " "or the ACEScg profiles ProPhotoRGB.

    ")); } if (profileName.contains("Rec2020-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

    Rec.2020 is the up-and-coming replacement for the thoroughly outdated sRGB color space. As of " "June 2015, very few (if any) display devices (and certainly no affordable display devices) can " "display all of Rec.2020. However, display technology is closing in on Rec.2020, movies are " "already being made for Rec.2020, and various cameras offer support for Rec.2020. And in the " "digital darkroom Rec.2020 is much more suitable as a general RGB working space than the " "exceedingly small sRGB color space.

    ")); } if (profileName.contains("sRGB-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

    Hewlett-Packard and Microsoft designed sRGB to match the color gamut of consumer-grade CRTs " "from the 1990s. sRGB is the standard color space for the world wide web and is still the best " "choice for exporting images to the internet.

    " "The sRGB color gamut was a good match to " "calibrated decent quality CRTs. But sRGB is not a good match to many consumer-grade LCD monitors, " "which often cannot display the more saturated sRGB blues and magentas (the good news: as technology " "progresses, wider gamuts are trickling down to consumer grade monitors).

    " "Printer color gamuts can easily exceed the sRGB color gamut in cyans, greens, and yellow-greens. Colors from interpolated " "camera raw files also often exceed the sRGB color gamut.

    " "As a very relevant aside, using perceptual " "intent when converting to sRGB does not magically makes otherwise out of gamut colors fit inside the " "sRGB color gamut! The standard sRGB color space (along with all the other the RGB profiles provided " "in my profile pack) is a matrix profile, and matrix profiles don't have perceptual intent tables.

    ")); } if (profileName.contains("WideRGB-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

    To avoid possible copyright infringement issues, I used 'WideRGB' as the base name for these profiles.

    " "WideGamutRGB was designed by Adobe to be a wide gamut color space that uses spectral colors " "as its primaries. Pascale's primary values produce a profile that matches old V2 Widegamut profiles " "from Adobe and Canon. It is an interesting color space, but shortly after its introduction, Adobe " "switched their emphasis to the ProPhotoRGB color space.

    ")); } if (profileName.contains("Gray-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

    These profiles are for use with RGB images that have been converted to monotone gray (black and white). " "The main reason to convert from RGB to Gray is to save the file space needed to encode the image. " "Google places a premium on fast-loading web pages, and images are one of the slower-loading elements " "of a web page. So converting black and white images to Grayscale images does save some kilobytes. " " For grayscale images uploaded to the internet, convert the image to the V2 Gray profile with the sRGB TRC.

    ")); } if (profileName.contains("-g10")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

    The profiles that end in '-g10.icc' are linear gamma (gamma=1.0, 'linear light', etc) profiles and " "should only be used when editing at high bit depths (16-bit floating point, 16-bit integer, 32-bit " "floating point, 32-bit integer). Many editing operations produce better results in linear gamma color " "spaces.

    ")); } if (profileName.contains("-labl")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

    The profiles that end in '-labl.icc' have perceptually uniform TRCs. A few editing operations really " "should be done on perceptually uniform RGB. Make sure you use the V4 versions for editing high bit depth " "images.

    ")); } if (profileName.contains("-srgbtrc") || profileName.contains("-g22") || profileName.contains("-g18") || profileName.contains("-bt709")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

    The profiles that end in '-srgbtrc.icc', '-g22.icc', and '-bt709.icc' have approximately but not exactly " "perceptually uniform TRCs. ProPhotoRGB's gamma=1.8 TRC is not quite as close to being perceptually uniform.

    ")); } if (d->colorSpaceSelector->cmbColorDepth->currentItem().id()=="U8") { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

    When editing 8-bit images, you should use a profile with a small color gamut and an approximately or " "exactly perceptually uniform TRC. Of the profiles supplied in my profile pack, only the sRGB and AdobeRGB1998 " "(ClayRGB) color spaces are small enough for 8-bit editing. Even with the AdobeRGB1998 color space you need to " "be careful to not cause posterization. And of course you cannot use the linear gamma versions of these profiles " "for 8-bit editing.

    ")); } if (profileName.contains("-V4-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

    Use V4 profiles for editing images using high bit depth image editors that use LCMS as the Color Management Module. " "This includes Krita, digiKam/showFoto, and GIMP 2.9.

    ")); } if (profileName.contains("-V2-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

    Use V2 profiles for exporting finished images to be uploaded to the web or for use with imaging software that " "cannot read V4 profiles.

    ")); } } d->colorSpaceSelector->textProfileDescription->moveCursor(QTextCursor::Start); } QString KisAdvancedColorSpaceSelector::nameWhitePoint(QVector whitePoint) { QString name=(QString::number(whitePoint[0]) + ", " + QString::number(whitePoint[1], 'f', 4)); //A (0.451170, 0.40594) (2856K)(tungsten) if ((whitePoint[0]>0.451170-0.005 && whitePoint[0]<0.451170 + 0.005) && (whitePoint[1]>0.40594-0.005 && whitePoint[1]<0.40594 + 0.005)){ name="A"; return name; } //B (0.34980, 0.35270) (4874K) (Direct Sunlight at noon)(obsolete) //C (0.31039, 0.31905) (6774K) (avarage/north sky daylight)(obsolete) //D50 (0.34773, 0.35952) (5003K) (Horizon Light, default color of white paper, ICC profile standard illuminant) if ((whitePoint[0]>0.34773-0.005 && whitePoint[0]<0.34773 + 0.005) && (whitePoint[1]>0.35952-0.005 && whitePoint[1]<0.35952 + 0.005)){ name="D50"; return name; } //D55 (0.33411, 0.34877) (5503K) (Mid-morning / Mid-afternoon Daylight) if ((whitePoint[0]>0.33411-0.001 && whitePoint[0]<0.33411 + 0.001) && (whitePoint[1]>0.34877-0.005 && whitePoint[1]<0.34877 + 0.005)){ name="D55"; return name; } //D60 (0.3217, 0.3378) (~6000K) (ACES colorspace default) if ((whitePoint[0]>0.3217-0.001 && whitePoint[0]<0.3217 + 0.001) && (whitePoint[1]>0.3378-0.005 && whitePoint[1]<0.3378 + 0.005)){ name="D60"; return name; } //D65 (0.31382, 0.33100) (6504K) (Noon Daylight, default for computer and tv screens, sRGB default) //Elle's are old school with 0.3127 and 0.3289 if ((whitePoint[0]>0.31382-0.002 && whitePoint[0]<0.31382 + 0.002) && (whitePoint[1]>0.33100-0.005 && whitePoint[1]<0.33100 + 0.002)){ name="D65"; return name; } //D75 (0.29968, 0.31740) (7504K) (North sky Daylight) if ((whitePoint[0]>0.29968-0.001 && whitePoint[0]<0.29968 + 0.001) && (whitePoint[1]>0.31740-0.005 && whitePoint[1]<0.31740 + 0.005)){ name="D75"; return name; } //E (1/3, 1/3) (5454K) (Equal Energy. CIERGB default) if ((whitePoint[0]>(1.0/3.0)-0.001 && whitePoint[0]<(1.0/3.0) + 0.001) && (whitePoint[1]>(1.0/3.0)-0.001 && whitePoint[1]<(1.0/3.0) + 0.001)){ name="E"; return name; } //The F series seems to sorta overlap with the D series, so I'll just leave them in comment here.// //F1 (0.31811, 0.33559) (6430K) (Daylight Fluorescent) //F2 (0.37925, 0.36733) (4230K) (Cool White Fluorescent) //F3 (0.41761, 0.38324) (3450K) (White Fluorescent) //F4 (0.44920, 0.39074) (2940K) (Warm White Fluorescent) //F5 (0.31975, 0.34246) (6350K) (Daylight Fluorescent) //F6 (0.38660, 0.37847) (4150K) (Lite White Fluorescent) //F7 (0.31569, 0.32960) (6500K) (D65 simulator, Daylight simulator) //F8 (0.34902, 0.35939) (5000K) (D50 simulator) //F9 (0.37829, 0.37045) (4150K) (Cool White Deluxe Fluorescent) //F10 (0.35090, 0.35444) (5000K) (Philips TL85, Ultralume 50) //F11 (0.38541, 0.37123) (4000K) (Philips TL84, Ultralume 40) //F12 (0.44256, 0.39717) (3000K) (Philips TL83, Ultralume 30) return name; } const KoColorSpace* KisAdvancedColorSpaceSelector::currentColorSpace() { QString check = ""; if (d->colorSpaceSelector->lstProfile->currentItem()) { check = d->colorSpaceSelector->lstProfile->currentItem()->text(); } else if (d->colorSpaceSelector->lstProfile->item(0)) { check = d->colorSpaceSelector->lstProfile->item(0)->text(); } return KoColorSpaceRegistry::instance()->colorSpace(d->colorSpaceSelector->cmbColorModels->currentItem().id(), d->colorSpaceSelector->cmbColorDepth->currentItem().id(), check); } void KisAdvancedColorSpaceSelector::setCurrentColorModel(const KoID& id) { d->colorSpaceSelector->cmbColorModels->setCurrent(id); fillLstProfiles(); fillCmbDepths(id); } void KisAdvancedColorSpaceSelector::setCurrentColorDepth(const KoID& id) { d->colorSpaceSelector->cmbColorDepth->setCurrent(id); fillLstProfiles(); } void KisAdvancedColorSpaceSelector::setCurrentProfile(const QString& name) { QList Items= d->colorSpaceSelector->lstProfile->findItems(name, Qt::MatchStartsWith); d->colorSpaceSelector->lstProfile->setCurrentItem(Items.at(0)); } void KisAdvancedColorSpaceSelector::setCurrentColorSpace(const KoColorSpace* colorSpace) { if (!colorSpace) { return; } setCurrentColorModel(colorSpace->colorModelId()); setCurrentColorDepth(colorSpace->colorDepthId()); setCurrentProfile(colorSpace->profile()->name()); } void KisAdvancedColorSpaceSelector::colorSpaceChanged() { bool valid = d->colorSpaceSelector->lstProfile->count() != 0; emit(selectionChanged(valid)); if (valid) { emit colorSpaceChanged(currentColorSpace()); } } void KisAdvancedColorSpaceSelector::installProfile() { KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocumentICC"); dialog.setCaption(i18n("Install Color Profiles")); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); dialog.setMimeTypeFilters(QStringList() << "application/vnd.iccprofile", "application/vnd.iccprofile"); QStringList profileNames = dialog.filenames(); KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); Q_ASSERT(iccEngine); QString saveLocation = KoResourcePaths::saveLocation("icc_profiles"); Q_FOREACH (const QString &profileName, profileNames) { QUrl file(profileName); if (!QFile::copy(profileName, saveLocation + file.fileName())) { dbgKrita << "Could not install profile!"; return; } iccEngine->addProfile(saveLocation + file.fileName()); } fillLstProfiles(); } diff --git a/libs/ui/widgets/kis_custom_image_widget.cc b/libs/ui/widgets/kis_custom_image_widget.cc index 10cdafd5d9..22d0ebaa67 100644 --- a/libs/ui/widgets/kis_custom_image_widget.cc +++ b/libs/ui/widgets/kis_custom_image_widget.cc @@ -1,525 +1,525 @@ /* This file is part of the Calligra project * Copyright (C) 2005 Thomas Zander * Copyright (C) 2005 C. Boemann * Copyright (C) 2007 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 "widgets/kis_custom_image_widget.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 #include "kis_config.h" #include "KisPart.h" #include "kis_clipboard.h" #include "KisDocument.h" #include "widgets/kis_cmb_idlist.h" #include KisCustomImageWidget::KisCustomImageWidget(QWidget* parent, qint32 defWidth, qint32 defHeight, double resolution, const QString& defColorModel, const QString& defColorDepth, const QString& defColorProfile, const QString& imageName) : WdgNewImage(parent) { setObjectName("KisCustomImageWidget"); m_openPane = qobject_cast(parent); Q_ASSERT(m_openPane); txtName->setText(imageName); m_widthUnit = KoUnit(KoUnit::Pixel, resolution); doubleWidth->setValue(defWidth); doubleWidth->setDecimals(0); m_width = m_widthUnit.fromUserValue(defWidth); cmbWidthUnit->addItems(KoUnit::listOfUnitNameForUi(KoUnit::ListAll)); cmbWidthUnit->setCurrentIndex(m_widthUnit.indexInListForUi(KoUnit::ListAll)); m_heightUnit = KoUnit(KoUnit::Pixel, resolution); doubleHeight->setValue(defHeight); doubleHeight->setDecimals(0); m_height = m_heightUnit.fromUserValue(defHeight); cmbHeightUnit->addItems(KoUnit::listOfUnitNameForUi(KoUnit::ListAll)); cmbHeightUnit->setCurrentIndex(m_heightUnit.indexInListForUi(KoUnit::ListAll)); doubleResolution->setValue(72.0 * resolution); doubleResolution->setDecimals(0); imageGroupSpacer->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed); grpClipboard->hide(); sliderOpacity->setRange(0, 100, 0); sliderOpacity->setValue(100); sliderOpacity->setSuffix("%"); connect(cmbPredefined, SIGNAL(activated(int)), SLOT(predefinedClicked(int))); connect(doubleResolution, SIGNAL(valueChanged(double)), this, SLOT(resolutionChanged(double))); connect(cmbWidthUnit, SIGNAL(activated(int)), this, SLOT(widthUnitChanged(int))); connect(doubleWidth, SIGNAL(valueChanged(double)), this, SLOT(widthChanged(double))); connect(cmbHeightUnit, SIGNAL(activated(int)), this, SLOT(heightUnitChanged(int))); connect(doubleHeight, SIGNAL(valueChanged(double)), this, SLOT(heightChanged(double))); // Create image newDialogConfirmationButtonBox->button(QDialogButtonBox::Ok)->setText(i18n("Create")); connect(newDialogConfirmationButtonBox, SIGNAL(accepted()), this, SLOT(createImage())); // Cancel Create image button connect(newDialogConfirmationButtonBox, SIGNAL(rejected()), this->parentWidget(), SLOT(close())); connect(newDialogConfirmationButtonBox, SIGNAL(rejected()), this->parentWidget(), SLOT(deleteLater())); bnPortrait->setIcon(KisIconUtils::loadIcon("portrait")); connect(bnPortrait, SIGNAL(clicked()), SLOT(setPortrait())); connect(bnLandscape, SIGNAL(clicked()), SLOT(setLandscape())); bnLandscape->setIcon(KisIconUtils::loadIcon("landscape")); connect(doubleWidth, SIGNAL(valueChanged(double)), this, SLOT(switchPortraitLandscape())); connect(doubleHeight, SIGNAL(valueChanged(double)), this, SLOT(switchPortraitLandscape())); connect(bnSaveAsPredefined, SIGNAL(clicked()), this, SLOT(saveAsPredefined())); colorSpaceSelector->setCurrentColorModel(KoID(defColorModel)); colorSpaceSelector->setCurrentColorDepth(KoID(defColorDepth)); colorSpaceSelector->setCurrentProfile(defColorProfile); connect(colorSpaceSelector, SIGNAL(colorSpaceChanged(const KoColorSpace*)), this, SLOT(changeDocumentInfoLabel())); //connect(chkFromClipboard,SIGNAL(stateChanged(int)),this,SLOT(clipboardDataChanged())); connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(clipboardDataChanged())); connect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, SLOT(clipboardDataChanged())); connect(QApplication::clipboard(), SIGNAL(changed(QClipboard::Mode)), this, SLOT(clipboardDataChanged())); connect(colorSpaceSelector, SIGNAL(selectionChanged(bool)), newDialogConfirmationButtonBox->button(QDialogButtonBox::Ok), SLOT(setEnabled(bool))); KisConfig cfg; intNumLayers->setValue(cfg.numDefaultLayers()); KoColor bcol(KoColorSpaceRegistry::instance()->rgb8()); bcol.fromQColor(cfg.defaultBackgroundColor()); cmbColor->setColor(bcol); setBackgroundOpacity(cfg.defaultBackgroundOpacity()); KisConfig::BackgroundStyle bgStyle = cfg.defaultBackgroundStyle(); if (bgStyle == KisConfig::LAYER) { radioBackgroundAsLayer->setChecked(true); } else { radioBackgroundAsProjection->setChecked(true); } fillPredefined(); switchPortraitLandscape(); // this makes the portrait and landscape buttons more // obvious what is selected by changing the highlight color QPalette p = QApplication::palette(); QPalette palette_highlight(p ); QColor c = p.color(QPalette::Highlight); palette_highlight.setColor(QPalette::Button, c); bnLandscape->setPalette(palette_highlight); bnPortrait->setPalette(palette_highlight); changeDocumentInfoLabel(); } void KisCustomImageWidget::showEvent(QShowEvent *) { fillPredefined(); newDialogConfirmationButtonBox->button(QDialogButtonBox::Ok)->setFocus(); newDialogConfirmationButtonBox->button(QDialogButtonBox::Ok)->setEnabled(true); } KisCustomImageWidget::~KisCustomImageWidget() { m_predefined.clear(); } void KisCustomImageWidget::resolutionChanged(double res) { if (m_widthUnit.type() == KoUnit::Pixel) { m_widthUnit.setFactor(res / 72.0); m_width = m_widthUnit.fromUserValue(doubleWidth->value()); } if (m_heightUnit.type() == KoUnit::Pixel) { m_heightUnit.setFactor(res / 72.0); m_height = m_heightUnit.fromUserValue(doubleHeight->value()); } changeDocumentInfoLabel(); } void KisCustomImageWidget::widthUnitChanged(int index) { doubleWidth->blockSignals(true); m_widthUnit = KoUnit::fromListForUi(index, KoUnit::ListAll); if (m_widthUnit.type() == KoUnit::Pixel) { doubleWidth->setDecimals(0); m_widthUnit.setFactor(doubleResolution->value() / 72.0); } else { doubleWidth->setDecimals(2); } doubleWidth->setValue(KoUnit::ptToUnit(m_width, m_widthUnit)); doubleWidth->blockSignals(false); changeDocumentInfoLabel(); } void KisCustomImageWidget::widthChanged(double value) { m_width = m_widthUnit.fromUserValue(value); changeDocumentInfoLabel(); } void KisCustomImageWidget::heightUnitChanged(int index) { doubleHeight->blockSignals(true); m_heightUnit = KoUnit::fromListForUi(index, KoUnit::ListAll); if (m_heightUnit.type() == KoUnit::Pixel) { doubleHeight->setDecimals(0); m_heightUnit.setFactor(doubleResolution->value() / 72.0); } else { doubleHeight->setDecimals(2); } doubleHeight->setValue(KoUnit::ptToUnit(m_height, m_heightUnit)); doubleHeight->blockSignals(false); changeDocumentInfoLabel(); } void KisCustomImageWidget::heightChanged(double value) { m_height = m_heightUnit.fromUserValue(value); changeDocumentInfoLabel(); } void KisCustomImageWidget::createImage() { newDialogConfirmationButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false); KisDocument *doc = createNewImage(); if (doc) { doc->setModified(false); emit m_openPane->documentSelected(doc); } } KisDocument* KisCustomImageWidget::createNewImage() { const KoColorSpace * cs = colorSpaceSelector->currentColorSpace(); if (cs->colorModelId() == RGBAColorModelID && cs->colorDepthId() == Integer8BitsColorDepthID) { const KoColorProfile *profile = cs->profile(); if (profile->name().contains("linear") || profile->name().contains("scRGB") || profile->info().contains("linear") || profile->info().contains("scRGB")) { int result = QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("Linear gamma RGB color spaces are not supposed to be used " "in 8-bit integer modes. It is suggested to use 16-bit integer " "or any floating point colorspace for linear profiles.\n\n" - "Press \"Continue\" to create a 8-bit integer linear RGB color space " + "Press \"Ok\" to create a 8-bit integer linear RGB color space " "or \"Cancel\" to return to the settings dialog."), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); if (result == QMessageBox::Cancel) { dbgKrita << "Model RGB8" << "NOT SUPPORTED"; dbgKrita << ppVar(cs->name()); dbgKrita << ppVar(cs->profile()->name()); dbgKrita << ppVar(cs->profile()->info()); return 0; } } } KisDocument *doc = static_cast(KisPart::instance()->createDocument()); qint32 width, height; double resolution; resolution = doubleResolution->value() / 72.0; // internal resolution is in pixels per pt width = static_cast(0.5 + KoUnit::ptToUnit(m_width, KoUnit(KoUnit::Pixel, resolution))); height = static_cast(0.5 + KoUnit::ptToUnit(m_height, KoUnit(KoUnit::Pixel, resolution))); QColor qc = cmbColor->color().toQColor(); qc.setAlpha(backgroundOpacity()); KoColor bgColor(qc, cs); bool backgroundAsLayer = radioBackgroundAsLayer->isChecked(); doc->newImage(txtName->text(), width, height, cs, bgColor, backgroundAsLayer, intNumLayers->value(), txtDescription->toPlainText(), resolution); KisConfig cfg; cfg.setNumDefaultLayers(intNumLayers->value()); cfg.setDefaultBackgroundOpacity(backgroundOpacity()); cfg.setDefaultBackgroundColor(cmbColor->color().toQColor()); cfg.setDefaultBackgroundStyle(backgroundAsLayer ? KisConfig::LAYER : KisConfig::PROJECTION); return doc; } void KisCustomImageWidget::setNumberOfLayers(int layers) { intNumLayers->setValue(layers); } quint8 KisCustomImageWidget::backgroundOpacity() const { qint32 opacity = sliderOpacity->value(); if (!opacity) return 0; return (opacity * 255) / 100; } void KisCustomImageWidget::setBackgroundOpacity(quint8 value) { sliderOpacity->setValue((value * 100) / 255); } void KisCustomImageWidget::clipboardDataChanged() { } void KisCustomImageWidget::fillPredefined() { cmbPredefined->clear(); m_predefined.clear(); cmbPredefined->addItem(""); QStringList definitions = KoResourcePaths::findAllResources("data", "predefined_image_sizes/*.predefinedimage", KoResourcePaths::Recursive); definitions.sort(); if (!definitions.empty()) { Q_FOREACH (const QString &definition, definitions) { QFile f(definition); f.open(QIODevice::ReadOnly); if (f.exists()) { QString xml = QString::fromUtf8(f.readAll()); KisPropertiesConfigurationSP predefined = new KisPropertiesConfiguration; predefined->fromXML(xml); if (predefined->hasProperty("name") && predefined->hasProperty("width") && predefined->hasProperty("height") && predefined->hasProperty("resolution") && predefined->hasProperty("x-unit") && predefined->hasProperty("y-unit")) { m_predefined << predefined; cmbPredefined->addItem(predefined->getString("name")); } } } } cmbPredefined->setCurrentIndex(0); } void KisCustomImageWidget::predefinedClicked(int index) { if (index < 1 || index > m_predefined.size()) return; KisPropertiesConfigurationSP predefined = m_predefined[index - 1]; txtPredefinedName->setText(predefined->getString("name")); doubleResolution->setValue(predefined->getDouble("resolution")); cmbWidthUnit->setCurrentIndex(predefined->getInt("x-unit")); cmbHeightUnit->setCurrentIndex(predefined->getInt("y-unit")); widthUnitChanged(cmbWidthUnit->currentIndex()); heightUnitChanged(cmbHeightUnit->currentIndex()); doubleWidth->setValue(predefined->getDouble("width")); doubleHeight->setValue(predefined->getDouble("height")); changeDocumentInfoLabel(); } void KisCustomImageWidget::saveAsPredefined() { QString fileName = txtPredefinedName->text(); if (fileName.isEmpty()) { return; } QString saveLocation = KoResourcePaths::saveLocation("data", "predefined_image_sizes/", true); QFile f(saveLocation + '/' + fileName.replace(' ', '_').replace('(', '_').replace(')', '_').replace(':', '_') + ".predefinedimage"); f.open(QIODevice::WriteOnly | QIODevice::Truncate); KisPropertiesConfigurationSP predefined = new KisPropertiesConfiguration(); predefined->setProperty("name", txtPredefinedName->text()); predefined->setProperty("width", doubleWidth->value()); predefined->setProperty("height", doubleHeight->value()); predefined->setProperty("resolution", doubleResolution->value()); predefined->setProperty("x-unit", cmbWidthUnit->currentIndex()); predefined->setProperty("y-unit", cmbHeightUnit->currentIndex()); QString xml = predefined->toXML(); f.write(xml.toUtf8()); f.flush(); f.close(); int i = 0; bool found = false; Q_FOREACH (KisPropertiesConfigurationSP pr, m_predefined) { if (pr->getString("name") == txtPredefinedName->text()) { found = true; break; } ++i; } if (found) { m_predefined[i] = predefined; } else { m_predefined.append(predefined); cmbPredefined->addItem(txtPredefinedName->text()); } } void KisCustomImageWidget::setLandscape() { if (doubleWidth->value() < doubleHeight->value()) { switchWidthHeight(); } } void KisCustomImageWidget::setPortrait() { if (doubleWidth->value() > doubleHeight->value()) { switchWidthHeight(); } } void KisCustomImageWidget::switchWidthHeight() { double width = doubleWidth->value(); double height = doubleHeight->value(); doubleHeight->blockSignals(true); doubleWidth->blockSignals(true); cmbWidthUnit->blockSignals(true); cmbHeightUnit->blockSignals(true); doubleWidth->setValue(height); doubleHeight->setValue(width); cmbWidthUnit->setCurrentIndex(m_heightUnit.indexInListForUi(KoUnit::ListAll)); cmbHeightUnit->setCurrentIndex(m_widthUnit.indexInListForUi(KoUnit::ListAll)); doubleHeight->blockSignals(false); doubleWidth->blockSignals(false); cmbWidthUnit->blockSignals(false); cmbHeightUnit->blockSignals(false); switchPortraitLandscape(); widthChanged(doubleWidth->value()); heightChanged(doubleHeight->value()); changeDocumentInfoLabel(); } void KisCustomImageWidget::switchPortraitLandscape() { if(doubleWidth->value() > doubleHeight->value()) bnLandscape->setChecked(true); else bnPortrait->setChecked(true); } void KisCustomImageWidget::changeDocumentInfoLabel() { qint64 width, height; double resolution; resolution = doubleResolution->value() / 72.0; // internal resolution is in pixels per pt width = static_cast(0.5 + KoUnit::ptToUnit(m_width, KoUnit(KoUnit::Pixel, resolution))); height = static_cast(0.5 + KoUnit::ptToUnit(m_height, KoUnit(KoUnit::Pixel, resolution))); qint64 layerSize = width * height; const KoColorSpace *cs = colorSpaceSelector->currentColorSpace(); int bitSize = 8 * cs->pixelSize(); //pixelsize is in bytes. layerSize = layerSize * cs->pixelSize(); QString byte = i18n("bytes"); if (layerSize>1024) { layerSize/=1024; byte = i18nc("Abbreviation for kilobyte", "KB"); } if (layerSize>1024) { layerSize/=1024; byte = i18nc("Abbreviation for megabyte", "MB"); } if (layerSize>1024) { layerSize/=1024; byte = i18nc("Abbreviation for gigabyte", "GB"); } QString text = i18nc("arg1: width. arg2: height. arg3: colorspace name. arg4: size of a channel in bits. arg5: size in unites of arg6. Arg6: KB, MB or GB", "This document will be %1 pixels by %2 pixels in %3, which means the pixel size is %4 bit. A single paint layer will thus take up %5 %6 of RAM.", width, height, cs->name(), bitSize, layerSize, byte); lblDocumentInfo->setText(text); } diff --git a/libs/ui/widgets/kis_lod_availability_widget.cpp b/libs/ui/widgets/kis_lod_availability_widget.cpp index 0e9c645bcf..a4a220fd25 100644 --- a/libs/ui/widgets/kis_lod_availability_widget.cpp +++ b/libs/ui/widgets/kis_lod_availability_widget.cpp @@ -1,252 +1,252 @@ /* * 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_lod_availability_widget.h" #include #include #include #include #include #include #include "kis_config.h" #include #include #include "kis_signals_blocker.h" namespace { /** * These strings are added intentionally so we could relayout the threshold slider after * the string freeze for 4.0. Please translate them :) */ -static const QString stringForInstantPreviewThreshold1 = i18nc("@label:slider", "Threshold:"); -static const QString stringForInstantPreviewThreshold2 = i18nc("@label:slider", "Instant preview threshold:"); +static const KLocalizedString stringForInstantPreviewThreshold1 = ki18nc("@label:slider", "Threshold:"); +static const KLocalizedString stringForInstantPreviewThreshold2 = ki18nc("@label:slider", "Instant preview threshold:"); } struct KisLodAvailabilityWidget::Private { Private() : chkLod(0), resourceManager(0) {} QCheckBox *chkLod; QPushButton *btnLod; QScopedPointer thresholdMenu; KisDoubleSliderSpinBox *thresholdSlider = 0; KoCanvasResourceManager *resourceManager; KisPaintopLodLimitations limitations; bool thresholdSupported = true; bool sizeThresholdPassed(); }; KisLodAvailabilityWidget::KisLodAvailabilityWidget(QWidget *parent) : QWidget(parent), m_d(new Private) { m_d->chkLod = new QCheckBox(this); m_d->btnLod = new QPushButton(this); m_d->btnLod->setFlat(true); connect(m_d->btnLod, SIGNAL(clicked()), SLOT(showLodToolTip())); { m_d->thresholdMenu.reset(new QMenu()); m_d->thresholdMenu->addSection(i18n("Enable after:")); m_d->btnLod->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_d->btnLod, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showLodThresholdWidget(QPoint))); KisConfig cfg; m_d->thresholdSlider = new KisDoubleSliderSpinBox(m_d->thresholdMenu.data()); m_d->thresholdSlider->setRange(0, cfg.readEntry("maximumBrushSize", 1000), 2); m_d->thresholdSlider->setValue(100); m_d->thresholdSlider->setSingleStep(1); m_d->thresholdSlider->setExponentRatio(3.0); m_d->thresholdSlider->setSuffix(i18n(" px")); m_d->thresholdSlider->setBlockUpdateSignalOnDrag(true); QWidgetAction *sliderAction = new QWidgetAction(this); sliderAction->setDefaultWidget(m_d->thresholdSlider); m_d->thresholdMenu->addAction(sliderAction); } QHBoxLayout *layout = new QHBoxLayout; layout->addWidget(m_d->chkLod); layout->addWidget(m_d->btnLod); layout->setSpacing(0); setLayout(layout); // set no limitations setLimitations(m_d->limitations); connect(m_d->chkLod, SIGNAL(toggled(bool)), SIGNAL(sigUserChangedLodAvailability(bool))); connect(m_d->thresholdSlider, SIGNAL(valueChanged(qreal)), SIGNAL(sigUserChangedLodThreshold(qreal))); } KisLodAvailabilityWidget::~KisLodAvailabilityWidget() { } void KisLodAvailabilityWidget::showLodToolTip() { QToolTip::showText(QCursor::pos(), m_d->btnLod->toolTip(), m_d->btnLod); } void KisLodAvailabilityWidget::showLodThresholdWidget(const QPoint &pos) { Q_UNUSED(pos); if (m_d->thresholdSupported) { m_d->thresholdMenu->popup(QCursor::pos()); } } void KisLodAvailabilityWidget::setLimitations(const KisPaintopLodLimitations &l) { QString limitationsText; Q_FOREACH (const KoID &id, l.limitations) { limitationsText.append("
  • "); limitationsText.append(id.name()); limitationsText.append("
  • "); } QString blockersText; Q_FOREACH (const KoID &id, l.blockers) { blockersText.append("
  • "); blockersText.append(id.name()); blockersText.append("
  • "); } bool isBlocked = !l.blockers.isEmpty(); bool isLimited = !l.limitations.isEmpty(); m_d->thresholdSupported = m_d->resourceManager ? m_d->resourceManager->resource(KisCanvasResourceProvider::LodSizeThresholdSupported).toBool() : true; bool isBlockedByThreshold = !m_d->sizeThresholdPassed() && m_d->thresholdSupported; const QString text = !isBlocked && !isBlockedByThreshold && isLimited ? i18n("(Instant Preview)*") : i18n("Instant Preview"); QString toolTip; if (isBlocked) { toolTip = i18nc("@info:tooltip", "

    Instant Preview Mode is " "disabled by the following options:" "

      %1

    ", blockersText); } else if (isBlockedByThreshold) { const qreal lodThreshold = m_d->resourceManager->resource(KisCanvasResourceProvider::LodSizeThreshold).toDouble(); const qreal size = m_d->resourceManager->resource(KisCanvasResourceProvider::Size).toDouble(); toolTip = i18nc("@info:tooltip", "

    Instant Preview Mode is " "disabled by instant preview threshold. " "Please right-click here to change the threshold" "

    • Brush size %1
    • " "
    • Threshold: %2

    ", size, lodThreshold); } else if (isLimited) { toolTip = i18nc("@info:tooltip", "

    Instant Preview may look different " "from the final result. In case of troubles " "try disabling the following options:" "

      %1

    ", limitationsText); } else { toolTip = i18nc("@info:tooltip", "

    Instant Preview Mode is available

    "); } { QFont font; font.setStrikeOut(isBlocked || isBlockedByThreshold); m_d->chkLod->setEnabled(!isBlocked); m_d->btnLod->setEnabled(!isBlocked); m_d->btnLod->setFont(font); m_d->btnLod->setText(text); m_d->btnLod->setToolTip(toolTip); if (isBlocked) { /** * If LoD is really blocked by some limitation we sneakly reset * the checkbox to let the user know it is fully disabled. */ KisSignalsBlocker b(m_d->chkLod); m_d->chkLod->setChecked(false); } } m_d->limitations = l; if (m_d->resourceManager) { const bool lodAvailableForUse = !isBlocked && !isBlockedByThreshold && m_d->resourceManager->resource(KisCanvasResourceProvider::LodAvailability).toBool(); m_d->resourceManager->setResource(KisCanvasResourceProvider::EffectiveLodAvailablility, lodAvailableForUse); } } void KisLodAvailabilityWidget::slotUserChangedLodAvailability(bool value) { KisSignalsBlocker b(m_d->chkLod); m_d->chkLod->setChecked(value); } void KisLodAvailabilityWidget::slotUserChangedLodThreshold(qreal value) { KisSignalsBlocker b(m_d->thresholdSlider); m_d->thresholdSlider->setValue(value); setLimitations(m_d->limitations); } void KisLodAvailabilityWidget::slotUserChangedSize(qreal value) { Q_UNUSED(value); setLimitations(m_d->limitations); } void KisLodAvailabilityWidget::setCanvasResourceManager(KoCanvasResourceManager *resourceManager) { m_d->resourceManager = resourceManager; } bool KisLodAvailabilityWidget::Private::sizeThresholdPassed() { if (!resourceManager) return true; const qreal lodThreshold = resourceManager->resource(KisCanvasResourceProvider::LodSizeThreshold).toDouble(); const qreal size = resourceManager->resource(KisCanvasResourceProvider::Size).toDouble(); return size >= lodThreshold; } diff --git a/libs/ui/widgets/kis_scratch_pad.cpp b/libs/ui/widgets/kis_scratch_pad.cpp index e6f094fde8..a0a8231fb9 100644 --- a/libs/ui/widgets/kis_scratch_pad.cpp +++ b/libs/ui/widgets/kis_scratch_pad.cpp @@ -1,518 +1,518 @@ /* This file is part of the KDE project * Copyright 2010 (C) Boudewijn Rempt * Copyright 2011 (C) Dmitry Kazakov * * 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_scratch_pad.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_config.h" #include "kis_image.h" #include "kis_undo_stores.h" #include "kis_update_scheduler.h" #include "kis_post_execution_undo_adapter.h" #include "kis_scratch_pad_event_filter.h" #include "kis_painting_information_builder.h" #include "kis_tool_freehand_helper.h" #include "kis_image_patch.h" #include "kis_canvas_widget_base.h" #include "kis_layer_projection_plane.h" #include "kis_node_graph_listener.h" class KisScratchPadNodeListener : public KisNodeGraphListener { public: KisScratchPadNodeListener(KisScratchPad *scratchPad) : m_scratchPad(scratchPad) { } void requestProjectionUpdate(KisNode *node, const QVector &rects, bool resetAnimationCache) override { KisNodeGraphListener::requestProjectionUpdate(node, rects, resetAnimationCache); QMutexLocker locker(&m_lock); Q_FOREACH (const QRect &rc, rects) { m_scratchPad->imageUpdated(rc); } } private: KisScratchPad *m_scratchPad; QMutex m_lock; }; class KisScratchPadDefaultBounds : public KisDefaultBounds { public: KisScratchPadDefaultBounds(KisScratchPad *scratchPad) : m_scratchPad(scratchPad) { } ~KisScratchPadDefaultBounds() override {} QRect bounds() const override { return m_scratchPad->imageBounds(); } private: Q_DISABLE_COPY(KisScratchPadDefaultBounds) KisScratchPad *m_scratchPad; }; KisScratchPad::KisScratchPad(QWidget *parent) : QWidget(parent) , m_toolMode(HOVERING) , m_paintLayer(0) , m_displayProfile(0) , m_resourceProvider(0) { setAutoFillBackground(false); setMouseTracking(true); m_cursor = KisCursor::load("tool_freehand_cursor.png", 5, 5); setCursor(m_cursor); KisConfig cfg; QImage checkImage = KisCanvasWidgetBase::createCheckersImage(cfg.checkSize()); m_checkBrush = QBrush(checkImage); // We are not supposed to use updates here, // so just set the listener to null m_updateScheduler = new KisUpdateScheduler(0); m_undoStore = new KisSurrogateUndoStore(); m_undoAdapter = new KisPostExecutionUndoAdapter(m_undoStore, m_updateScheduler); m_nodeListener = new KisScratchPadNodeListener(this); connect(this, SIGNAL(sigUpdateCanvas(QRect)), SLOT(slotUpdateCanvas(QRect)), Qt::QueuedConnection); // filter will be deleted by the QObject hierarchy m_eventFilter = new KisScratchPadEventFilter(this); m_infoBuilder = new KisPaintingInformationBuilder(); m_helper = new KisToolFreehandHelper(m_infoBuilder); m_scaleBorderWidth = 1; } KisScratchPad::~KisScratchPad() { delete m_helper; delete m_infoBuilder; delete m_undoAdapter; delete m_undoStore; delete m_updateScheduler; delete m_nodeListener; } KisScratchPad::Mode KisScratchPad::modeFromButton(Qt::MouseButton button) const { return button == Qt::NoButton ? HOVERING : button == Qt::MidButton ? PANNING : button == Qt::RightButton ? PICKING : PAINTING; } void KisScratchPad::pointerPress(KoPointerEvent *event) { if (m_toolMode != HOVERING) return; m_toolMode = modeFromButton(event->button()); if (m_toolMode == PAINTING) { beginStroke(event); event->accept(); } else if (m_toolMode == PANNING) { beginPan(event); event->accept(); } else if (m_toolMode == PICKING) { pick(event); event->accept(); } } void KisScratchPad::pointerRelease(KoPointerEvent *event) { if (modeFromButton(event->button()) != m_toolMode) return; if (m_toolMode == PAINTING) { endStroke(event); m_toolMode = HOVERING; event->accept(); } else if (m_toolMode == PANNING) { endPan(event); m_toolMode = HOVERING; event->accept(); } else if (m_toolMode == PICKING) { event->accept(); m_toolMode = HOVERING; } } void KisScratchPad::pointerMove(KoPointerEvent *event) { m_helper->cursorMoved(documentToWidget().map(event->point)); if (m_toolMode == PAINTING) { doStroke(event); event->accept(); } else if (m_toolMode == PANNING) { doPan(event); event->accept(); } else if (m_toolMode == PICKING) { pick(event); event->accept(); } } void KisScratchPad::beginStroke(KoPointerEvent *event) { KoCanvasResourceManager *resourceManager = m_resourceProvider->resourceManager(); m_helper->initPaint(event, documentToWidget().map(event->point), resourceManager, 0, 0, m_updateScheduler, m_paintLayer, m_paintLayer->paintDevice()->defaultBounds()); } void KisScratchPad::doStroke(KoPointerEvent *event) { m_helper->paintEvent(event); } void KisScratchPad::endStroke(KoPointerEvent *event) { Q_UNUSED(event); m_helper->endPaint(); } void KisScratchPad::beginPan(KoPointerEvent *event) { setCursor(QCursor(Qt::ClosedHandCursor)); m_panDocPoint = event->point; } void KisScratchPad::doPan(KoPointerEvent *event) { QPointF docOffset = event->point - m_panDocPoint; m_translateTransform.translate(-docOffset.x(), -docOffset.y()); updateTransformations(); update(); } void KisScratchPad::endPan(KoPointerEvent *event) { Q_UNUSED(event); setCursor(m_cursor); } void KisScratchPad::pick(KoPointerEvent *event) { KoColor color; - if (KisToolUtils::pick(m_paintLayer->projection(), event->point.toPoint(), &color)) { + if (KisToolUtils::pickColor(color, m_paintLayer->projection(), event->point.toPoint())) { emit colorSelected(color); } } void KisScratchPad::setOnScreenResolution(qreal scaleX, qreal scaleY) { m_scaleBorderWidth = BORDER_SIZE(qMax(scaleX, scaleY)); m_scaleTransform = QTransform::fromScale(scaleX, scaleY); updateTransformations(); update(); } QTransform KisScratchPad::documentToWidget() const { return m_translateTransform.inverted() * m_scaleTransform; } QTransform KisScratchPad::widgetToDocument() const { return m_scaleTransform.inverted() * m_translateTransform; } void KisScratchPad::updateTransformations() { m_eventFilter->setWidgetToDocumentTransform(widgetToDocument()); } QRect KisScratchPad::imageBounds() const { return widgetToDocument().mapRect(rect()); } void KisScratchPad::imageUpdated(const QRect &rect) { emit sigUpdateCanvas(documentToWidget().mapRect(QRectF(rect)).toAlignedRect()); } void KisScratchPad::slotUpdateCanvas(const QRect &rect) { update(rect); } void KisScratchPad::paintEvent ( QPaintEvent * event ) { if(!m_paintLayer) return; QRectF imageRect = widgetToDocument().mapRect(QRectF(event->rect())); QRect alignedImageRect = imageRect.adjusted(-m_scaleBorderWidth, -m_scaleBorderWidth, m_scaleBorderWidth, m_scaleBorderWidth).toAlignedRect(); QPointF offset = alignedImageRect.topLeft(); m_paintLayer->projectionPlane()->recalculate(alignedImageRect, m_paintLayer); KisPaintDeviceSP projection = m_paintLayer->projection(); QImage image = projection->convertToQImage(m_displayProfile, alignedImageRect.x(), alignedImageRect.y(), alignedImageRect.width(), alignedImageRect.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); QPainter gc(this); gc.fillRect(event->rect(), m_checkBrush); gc.setRenderHints(QPainter::SmoothPixmapTransform); gc.drawImage(QRectF(event->rect()), image, imageRect.translated(-offset)); QBrush brush(Qt::lightGray); QPen pen(brush, 1, Qt::DotLine); gc.setPen(pen); if (m_cutoutOverlay.isValid()) { gc.drawRect(m_cutoutOverlay); } if(!isEnabled()) { QColor color(Qt::lightGray); color.setAlphaF(0.5); QBrush disabledBrush(color); gc.fillRect(event->rect(), disabledBrush); } gc.end(); } void KisScratchPad::setupScratchPad(KisCanvasResourceProvider* resourceProvider, const QColor &defaultColor) { m_resourceProvider = resourceProvider; KisConfig cfg; setDisplayProfile(cfg.displayProfile(QApplication::desktop()->screenNumber(this))); connect(m_resourceProvider, SIGNAL(sigDisplayProfileChanged(const KoColorProfile*)), SLOT(setDisplayProfile(const KoColorProfile*))); connect(m_resourceProvider, SIGNAL(sigOnScreenResolutionChanged(qreal,qreal)), SLOT(setOnScreenResolution(qreal,qreal))); connect(this, SIGNAL(colorSelected(const KoColor&)), m_resourceProvider, SLOT(slotSetFGColor(const KoColor&))); m_defaultColor = KoColor(defaultColor, KoColorSpaceRegistry::instance()->rgb8()); KisPaintDeviceSP paintDevice = new KisPaintDevice(m_defaultColor.colorSpace(), "scratchpad"); m_paintLayer = new KisPaintLayer(0, "ScratchPad", OPACITY_OPAQUE_U8, paintDevice); m_paintLayer->setGraphListener(m_nodeListener); m_paintLayer->paintDevice()->setDefaultBounds(new KisScratchPadDefaultBounds(this)); fillDefault(); } void KisScratchPad::setCutoutOverlayRect(const QRect& rc) { m_cutoutOverlay = rc; } QImage KisScratchPad::cutoutOverlay() const { if(!m_paintLayer) return QImage(); KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice(); QRect rc = widgetToDocument().mapRect(m_cutoutOverlay); QImage rawImage = paintDevice->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); QImage scaledImage = rawImage.scaled(m_cutoutOverlay.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); return scaledImage; } void KisScratchPad::setPresetImage(const QImage& image) { m_presetImage = image; } void KisScratchPad::paintCustomImage(const QImage& loadedImage) { // this is 99% copied from the normal paintPresetImage() // we don't want to save over the preset image, so we don't // want to store it in the m_presetImage if(!m_paintLayer) return; KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice(); QRect overlayRect = widgetToDocument().mapRect(m_cutoutOverlay); QRect imageRect(QPoint(), overlayRect.size()); QImage scaledImage = loadedImage.scaled(overlayRect.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); KisPaintDeviceSP device = new KisPaintDevice(paintDevice->colorSpace()); device->convertFromQImage(scaledImage, 0); KisPainter painter(paintDevice); painter.bitBlt(overlayRect.topLeft(), device, imageRect); update(); } void KisScratchPad::paintPresetImage() { if(!m_paintLayer) return; KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice(); QRect overlayRect = widgetToDocument().mapRect(m_cutoutOverlay); QRect imageRect(QPoint(), overlayRect.size()); QImage scaledImage = m_presetImage.scaled(overlayRect.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); KisPaintDeviceSP device = new KisPaintDevice(paintDevice->colorSpace()); device->convertFromQImage(scaledImage, 0); KisPainter painter(paintDevice); painter.bitBlt(overlayRect.topLeft(), device, imageRect); update(); } void KisScratchPad::setDisplayProfile(const KoColorProfile *colorProfile) { if (colorProfile) { m_displayProfile = colorProfile; QWidget::update(); } } void KisScratchPad::fillDefault() { if(!m_paintLayer) return; KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice(); paintDevice->setDefaultPixel(m_defaultColor); paintDevice->clear(); update(); } void KisScratchPad::fillTransparent() { if(!m_paintLayer) return; KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice(); QColor transQColor(0,0,0,0); KoColor transparentColor(transQColor, KoColorSpaceRegistry::instance()->rgb8()); transparentColor.setOpacity(0.0); paintDevice->setDefaultPixel(transparentColor); paintDevice->clear(); update(); } void KisScratchPad::fillGradient() { if(!m_paintLayer) return; KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice(); KoAbstractGradient* gradient = m_resourceProvider->currentGradient(); QRect gradientRect = widgetToDocument().mapRect(rect()); paintDevice->clear(); KisGradientPainter painter(paintDevice); painter.setGradient(gradient); painter.setGradientShape(KisGradientPainter::GradientShapeLinear); painter.paintGradient(gradientRect.topLeft(), gradientRect.bottomRight(), KisGradientPainter::GradientRepeatNone, 0.2, false, gradientRect.left(), gradientRect.top(), gradientRect.width(), gradientRect.height()); update(); } void KisScratchPad::fillBackground() { if(!m_paintLayer) return; KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice(); paintDevice->setDefaultPixel(m_resourceProvider->bgColor()); paintDevice->clear(); update(); } void KisScratchPad::fillLayer() { if(!m_paintLayer) return; KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice(); KisPainter painter(paintDevice); QRect sourceRect(0, 0, paintDevice->exactBounds().width(), paintDevice->exactBounds().height()); painter.bitBlt(QPoint(0, 0), m_resourceProvider->currentImage()->projection(), sourceRect); update(); } diff --git a/libs/widgets/kis_file_name_requester.cpp b/libs/widgets/kis_file_name_requester.cpp index 5aa79b34d7..835b482f01 100644 --- a/libs/widgets/kis_file_name_requester.cpp +++ b/libs/widgets/kis_file_name_requester.cpp @@ -1,110 +1,110 @@ /* * 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_name("OpenDocument") { 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::setConfiguratioName(const QString &name) +void KisFileNameRequester::setConfigurationName(const QString &name) { m_name = name; } void KisFileNameRequester::setFileName(const QString &path) { m_ui->txtFileName->setText(path); m_basePath = 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, m_name); 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(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); } else { dialog.setDefaultDir(m_basePath); } Q_ASSERT(!m_mime_filter_list.isEmpty()); dialog.setMimeTypeFilters(m_mime_filter_list, m_mime_default_filter); QString newFileName = dialog.filename(); if (!newFileName.isEmpty()) { setFileName(newFileName); } } diff --git a/libs/widgets/kis_file_name_requester.h b/libs/widgets/kis_file_name_requester.h index 42a9914779..0c77fe74ab 100644 --- a/libs/widgets/kis_file_name_requester.h +++ b/libs/widgets/kis_file_name_requester.h @@ -1,83 +1,83 @@ /* * 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_FILE_NAME_REQUESTER_H #define KIS_FILE_NAME_REQUESTER_H #include "kritawidgets_export.h" #include #include #include #include namespace Ui { class WdgFileNameRequester; } /** * This represents an editable file name. - * Visual it presents a QLineEdit + a buton that pops up + * Visual it presents a QLineEdit + a button that pops up * a file chooser. * * Signals are fired when the user changes the text * or selects a new file via the button/file chooser. */ class KRITAWIDGETS_EXPORT KisFileNameRequester : public QWidget { Q_OBJECT public: explicit KisFileNameRequester(QWidget *parent = 0); ~KisFileNameRequester() override; void setStartDir(const QString &path); /// Set the name used to store the last-used directory in the settings - void setConfiguratioName(const QString &name); + void setConfigurationName(const QString &name); QString fileName() const; void setFileName(const QString &path); void setMode(KoFileDialog::DialogType mode); KoFileDialog::DialogType mode() const; /** * Sets the mime type filters to use, same format as KoFileDialog::setMimeTypeFilters. * If this is not called, the default list is used, which simply selects all the image * file formats Krita can load. */ void setMimeTypeFilters(const QStringList &filterList, QString defaultFilter = QString()); public Q_SLOTS: void slotSelectFile(); Q_SIGNALS: void textChanged(const QString &fileName); void fileSelected(const QString &fileName); private: QScopedPointer m_ui; QString m_basePath; KoFileDialog::DialogType m_mode; QStringList m_mime_filter_list; QString m_mime_default_filter; QString m_name; }; #endif // KIS_FILE_NAME_REQUESTER_H diff --git a/packaging/linux/snap/setup/gui/krita.desktop b/packaging/linux/snap/setup/gui/krita.desktop index a0385abada..f2b69e0e3c 100755 --- a/packaging/linux/snap/setup/gui/krita.desktop +++ b/packaging/linux/snap/setup/gui/krita.desktop @@ -1,136 +1,136 @@ [Desktop Entry] Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita Exec=krita %U GenericName=Digital Painting GenericName[ar]=رسم رقميّ GenericName[bs]=Digitalno Bojenje GenericName[ca]=Dibuix digital GenericName[ca@valencia]=Dibuix digital GenericName[cs]=Digitální malování GenericName[da]=Digital tegning GenericName[de]=Digitales Malen GenericName[el]=Ψηφιακή ζωγραφική GenericName[en_GB]=Digital Painting GenericName[es]=Pintura digital GenericName[et]=Digitaalne joonistamine GenericName[eu]=Pintura digitala GenericName[fi]=Digitaalimaalaus GenericName[fr]=Peinture numérique GenericName[gl]=Debuxo dixital GenericName[hu]=Digitális festészet GenericName[ia]=Pintura Digital GenericName[is]=Stafræn málun GenericName[it]=Pittura digitale GenericName[ja]=デジタルペインティング GenericName[kk]=Цифрлық сурет салу GenericName[lt]=Skaitmeninis piešimas GenericName[mr]=डिजिटल पेंटिंग GenericName[nb]=Digital maling GenericName[nl]=Digitaal schilderen GenericName[pl]=Cyfrowe malowanie GenericName[pt]=Pintura Digital GenericName[pt_BR]=Pintura digital GenericName[ru]=Цифровая живопись GenericName[sk]=Digitálne maľovanie GenericName[sl]=Digitalno slikanje GenericName[sv]=Digital målning GenericName[tr]=Sayısal Boyama GenericName[ug]=سىفىرلىق رەسىم سىزغۇ GenericName[uk]=Цифрове малювання GenericName[x-test]=xxDigital Paintingxx GenericName[zh_CN]=数字绘画 GenericName[zh_TW]=數位繪畫 MimeType=application/x-krita;image/openraster;application/x-krita-paintoppreset; Comment=Pixel-based image manipulation program for the Calligra Suite Comment[ar]=برنامج لتعديل الصّور البكسليّة لطقم «كاليغرا» Comment[ca]=Programa de manipulació d'imatges basades en píxels per a la Suite Calligra Comment[ca@valencia]=Programa de manipulació d'imatges basades en píxels per a la Suite Calligra Comment[de]=Pixelbasiertes Bildbearbeitungsprogramm für die Calligra-Suite Comment[el]=Πρόγραμμα επεξεργασίας εικόνας με βάση εικονοστοιχεία για το Calligra Stage Comment[en_GB]=Pixel-based image manipulation program for the Calligra Suite Comment[es]=Programa de manipulación de imágenes basado en píxeles para la suite Calligra Comment[et]=Calligra pikslipõhine pilditöötluse rakendus Comment[gl]=Programa da colección de Calligra para a manipulación de imaxes baseadas en píxeles. Comment[is]=Myndvinnsluforrit fyrir Calligra-forritavöndulinn Comment[it]=Programma di manipolazione delle immagini basato su pixel per Calligra Suite Comment[nl]=Afbeeldingsbewerkingsprogramma gebaseerd op pixels voor de Calligra Suite Comment[pl]=Program do obróbki obrazów na poziomie pikseli dla Pakietu Calligra Comment[pt]='Plugin' de manipulação de imagens em pixels para o Calligra Stage Comment[pt_BR]=Programa de manipulação de imagens baseado em pixels para o Calligra Suite Comment[ru]=Программа редактирования пиксельной анимации для the Calligra Suite Comment[sk]=Program na manipuláciu s pixelmi pre Calligra Suite Comment[sv]=Bildpunktsbaserat bildbehandlingsprogram för Calligra-sviten Comment[tr]=Calligra Suite için Pixel tabanlı görüntü düzenleme programı Comment[uk]=Програма для роботи із растровими зображеннями для комплексу програм Calligra Comment[x-test]=xxPixel-based image manipulation program for the Calligra Suitexx -Comment[zh_CN]=Calligra 套件的位图图像处理程序 +Comment[zh_CN]=Calligra 套件的像素图像处理程序 Comment[zh_TW]=Calligra 套件中基於像素的影像處理程式 Type=Application Icon=${SNAP}/meta/gui/calligrakrita.png Categories=Qt;KDE;Graphics; X-KDE-NativeMimeType=application/x-krita X-KDE-ExtraNativeMimeTypes= StartupNotify=true X-Krita-Version=28 diff --git a/packaging/windows/ConfigureInstallerNsis.cmake b/packaging/windows/installer/ConfigureInstallerNsis.cmake similarity index 51% rename from packaging/windows/ConfigureInstallerNsis.cmake rename to packaging/windows/installer/ConfigureInstallerNsis.cmake index 1c04b4e022..45085c4069 100644 --- a/packaging/windows/ConfigureInstallerNsis.cmake +++ b/packaging/windows/installer/ConfigureInstallerNsis.cmake @@ -1,15 +1,25 @@ if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") set(INSTALLER_NSIS_IS_32_BIT NO) else() set(INSTALLER_NSIS_IS_32_BIT YES) endif() configure_file( ${CMAKE_CURRENT_LIST_DIR}/MakeInstallerNsis.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/MakeinstallerNsis.cmake @ONLY ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/MakeinstallerNsis.cmake DESTINATION ${CMAKE_INSTALL_PREFIX} ) + +install(FILES ${CMAKE_CURRENT_LIST_DIR}/installer_krita.nsi + ${CMAKE_CURRENT_LIST_DIR}/license_gpl-3.0.rtf + DESTINATION ${CMAKE_INSTALL_PREFIX}/installer +) + +install(FILES ${CMAKE_CURRENT_LIST_DIR}/include/FileExists2.nsh + ${CMAKE_CURRENT_LIST_DIR}/include/IsFileInUse.nsh + DESTINATION ${CMAKE_INSTALL_PREFIX}/installer/include +) diff --git a/packaging/windows/MakeInstallerNsis.cmake.in b/packaging/windows/installer/MakeInstallerNsis.cmake.in similarity index 91% rename from packaging/windows/MakeInstallerNsis.cmake.in rename to packaging/windows/installer/MakeInstallerNsis.cmake.in index e37ba40583..a4603609dc 100644 --- a/packaging/windows/MakeInstallerNsis.cmake.in +++ b/packaging/windows/installer/MakeInstallerNsis.cmake.in @@ -1,147 +1,166 @@ cmake_minimum_required(VERSION 3.0 FATAL_ERROR) set(ARCH_IS_32_BIT @INSTALLER_NSIS_IS_32_BIT@) if(ARCH_IS_32_BIT) set(ARG_ARCH "/DKRITA_INSTALLER_32") set(FILENAME_ARCH "-x86") else() set(ARG_ARCH "/DKRITA_INSTALLER_64") set(FILENAME_ARCH "-x64") endif() if(NOT IS_DIRECTORY "${KRITA_PACKAGE_ROOT}") message(FATAL_ERROR "KRITA_PACKAGE_ROOT not set") endif() if(NOT DEFINED OUTPUT_FILEPATH) set(OUTPUT_FILEPATH "${CMAKE_CURRENT_BINARY_DIR}/krita-setup${FILENAME_ARCH}.exe") endif() if(NOT DEFINED DOWNLOAD_DIR) set(DOWNLOAD_DIR "${CMAKE_CURRENT_BINARY_DIR}") endif() # Download and find NSIS if(NOT DEFINED NO_DOWNLOAD_NSIS) set(DOWNLOAD_NSIS_VERSION "3.02.1") message(STATUS "Downloading NSIS...") if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/nsis-${DOWNLOAD_NSIS_VERSION}") file(REMOVE_RECURSE "${CMAKE_CURRENT_BINARY_DIR}/nsis-${DOWNLOAD_NSIS_VERSION}") endif() file(DOWNLOAD "https://files.kde.org/krita/build/dependencies/nsis-${DOWNLOAD_NSIS_VERSION}.zip" "${DOWNLOAD_DIR}/nsis-${DOWNLOAD_NSIS_VERSION}.zip" EXPECTED_HASH SHA1=06c791f9cf668d945564316c9c947ebb4cc469e9 ) execute_process(COMMAND ${CMAKE_COMMAND} -E tar xfz "${DOWNLOAD_DIR}/nsis-${DOWNLOAD_NSIS_VERSION}.zip" WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" RESULT_VARIABLE result_extract ) if(NOT result_extract EQUAL 0) message(FATAL_ERROR "Failed to extract NSIS") endif() if(NOT IS_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/nsis-${DOWNLOAD_NSIS_VERSION}") message(FATAL_ERROR "Failed to find NSIS after extracting") endif() find_program(TOOL_MAKENSIS NAMES makensis.exe PATHS "${CMAKE_CURRENT_BINARY_DIR}/nsis-${DOWNLOAD_NSIS_VERSION}" NO_DEFAULT_PATH ) else() find_program(TOOL_MAKENSIS NAMES makensis.exe HINTS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\NSIS]" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS]" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\NSIS\\Unicode]" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS\\Unicode]" ) endif() if(NOT TOOL_MAKENSIS) message(FATAL_ERROR "Failed to find makensis.exe") endif() execute_process(COMMAND "${TOOL_MAKENSIS}" "/version" OUTPUT_VARIABLE MAKENSIS_VERSION ) # Check version is v3.* if(NOT MAKENSIS_VERSION MATCHES "^v3\\.") message(FATAL_ERROR "Expected NSIS version v3.*, got ${MAKENSIS_VERSION}") endif() message(STATUS "NSIS version ${MAKENSIS_VERSION}") # Check if package contains debug symbols file(TO_CMAKE_PATH "${KRITA_PACKAGE_ROOT}" KRITA_PACKAGE_ROOT_PATCHED) file(GLOB_RECURSE globForDebugFiles "${KRITA_PACKAGE_ROOT_PATCHED}/*.debug" ) if(globForDebugFiles) if(REMOVE_DEBUG) message(STATUS "Removing debug symbols") foreach(debugFileItem ${globForDebugFiles}) get_filename_component(debugFileDir "${debugFileItem}" DIRECTORY) get_filename_component(debugDirName "${debugFileDir}" NAME) if(debugDirName STREQUAL ".debug") if(EXISTS "${debugFileDir}") message(STATUS "Deleting ${debugFileDir}") file(REMOVE_RECURSE "${debugFileDir}") endif() else() message(STATUS "Deleting ${debugFileItem}") file(REMOVE "${debugFileItem}") endif() endforeach() else() message(FATAL_ERROR "${KRITA_PACKAGE_ROOT} seems to contain debug symbols. Set REMOVE_DEBUG to true if you want to remove them.") endif() endif() # Download installer script package message(STATUS "Downloading NSIS script package...") if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/krita-nsis") file(REMOVE_RECURSE "${CMAKE_CURRENT_BINARY_DIR}/krita-nsis") endif() file(DOWNLOAD "https://github.com/alvinhochun/KritaShellExtension/releases/download/v1.2.4b/krita-nsis-v1.2.4b.zip" "${DOWNLOAD_DIR}/krita-nsis.zip" EXPECTED_HASH SHA1=43c5bade13fb885e8546dac4486b8e2df62dc692 ) execute_process(COMMAND ${CMAKE_COMMAND} -E tar xfz "${DOWNLOAD_DIR}/krita-nsis.zip" WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" RESULT_VARIABLE result_extract ) if(NOT result_extract EQUAL 0) message(FATAL_ERROR "Failed to extract krita-nsis package") endif() if(NOT IS_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/krita-nsis") message(FATAL_ERROR "Failed to find krita-nsis after extracting") endif() +# Place Krita installer files in the right place +file(COPY "${CMAKE_CURRENT_LIST_DIR}/installer/" + DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/krita-nsis/" + FILES_MATCHING PATTERN "*" +) + + +# Detect FFmpeg (currently optional) +if(EXISTS "${KRITA_PACKAGE_ROOT}/bin/ffmpeg.exe" + AND EXISTS "${KRITA_PACKAGE_ROOT}/bin/ffmpeg_LICENSE.txt" + AND EXISTS "${KRITA_PACKAGE_ROOT}/bin/ffmpeg_README.txt" +) + set(ARG_HAS_FFMPEG "/DHAS_FFMPEG") +else() + set(ARG_HAS_FFMPEG "/DNO_HAS_FFMPEG") +endif() + + # Build installer message(STATUS "Building installer...") set(KRITA_VERSION_NUMBER "@KRITA_STABLE_VERSION_MAJOR@.@KRITA_STABLE_VERSION_MINOR@.@KRITA_VERSION_RELEASE@.@KRITA_VERSION_REVISION@") set(KRITA_VERSION_STRING "@KRITA_VERSION_STRING@") set(KRITA_GIT_SHA1_STRING "@KRITA_GIT_SHA1_STRING@") if(KRITA_GIT_SHA1_STRING) set(KRITA_VERSION_STRING "${KRITA_VERSION_STRING} (git ${KRITA_GIT_SHA1_STRING})") endif() execute_process(COMMAND "${TOOL_MAKENSIS}" "${ARG_ARCH}" "/DKRITA_VERSION=${KRITA_VERSION_NUMBER}" "/DKRITA_VERSION_DISPLAY=${KRITA_VERSION_STRING}" "/DKRITA_INSTALLER_OUTPUT_DIR=" "/DKRITA_INSTALLER_OUTPUT_NAME=${OUTPUT_FILEPATH}" "/DKRITA_PACKAGE_ROOT=${KRITA_PACKAGE_ROOT}" + "${ARG_HAS_FFMPEG}" "/XSetCompressor /SOLID lzma" "/V3" "${CMAKE_CURRENT_BINARY_DIR}/krita-nsis/installer_krita.nsi" RESULT_VARIABLE result_makensis ) if(NOT result_makensis EQUAL 0) message(FATAL_ERROR "Failed to build installer") endif() message(STATUS "Built installer") diff --git a/packaging/windows/installer/include/FileExists2.nsh b/packaging/windows/installer/include/FileExists2.nsh new file mode 100644 index 0000000000..0111785481 --- /dev/null +++ b/packaging/windows/installer/include/FileExists2.nsh @@ -0,0 +1,22 @@ +;FileExists is already part of LogicLib, but returns true for directories as well as files +!macro _FileExists2 _a _b _t _f + !insertmacro _LOGICLIB_TEMP + StrCpy $_LOGICLIB_TEMP "0" + StrCmp `${_b}` `` +4 0 ;if path is not blank, continue to next check + IfFileExists `${_b}` `0` +3 ;if path exists, continue to next check (IfFileExists returns true if this is a directory) + IfFileExists `${_b}\*.*` +2 0 ;if path is not a directory, continue to confirm exists + StrCpy $_LOGICLIB_TEMP "1" ;file exists + ;now we have a definitive value - the file exists or it does not + StrCmp $_LOGICLIB_TEMP "1" `${_t}` `${_f}` +!macroend +!undef FileExists +!define FileExists `"" FileExists2` +!macro _DirExists _a _b _t _f + !insertmacro _LOGICLIB_TEMP + StrCpy $_LOGICLIB_TEMP "0" + StrCmp `${_b}` `` +3 0 ;if path is not blank, continue to next check + IfFileExists `${_b}\*.*` 0 +2 ;if directory exists, continue to confirm exists + StrCpy $_LOGICLIB_TEMP "1" + StrCmp $_LOGICLIB_TEMP "1" `${_t}` `${_f}` +!macroend +!define DirExists `"" DirExists` diff --git a/packaging/windows/installer/include/IsFileInUse.nsh b/packaging/windows/installer/include/IsFileInUse.nsh new file mode 100644 index 0000000000..ac2cdb9f6d --- /dev/null +++ b/packaging/windows/installer/include/IsFileInUse.nsh @@ -0,0 +1,12 @@ +; Checks whether a file is in use by trying to open it in Append mode + +!macro _IsFileInUse _a _b _t _f + !insertmacro _LOGICLIB_TEMP + StrCpy $_LOGICLIB_TEMP "0" + IfFileExists `${_b}` `0` +4 ; If file not exist then not in use + FileOpen $_LOGICLIB_TEMP ${_b} a ; If file open failed then file is in use + IfErrors +2 + FileClose $_LOGICLIB_TEMP + StrCmp $_LOGICLIB_TEMP `` `${_t}` `${_f}` ; Var is empty when file in use +!macroend +!define IsFileInUse `"" IsFileInUse` diff --git a/packaging/windows/installer/installer_krita.nsi b/packaging/windows/installer/installer_krita.nsi new file mode 100644 index 0000000000..41140f640e --- /dev/null +++ b/packaging/windows/installer/installer_krita.nsi @@ -0,0 +1,662 @@ +!ifndef KRITA_INSTALLER_32 & KRITA_INSTALLER_64 + !error "Either one of KRITA_INSTALLER_32 or KRITA_INSTALLER_64 must be defined." +!endif +!ifdef KRITA_INSTALLER_32 & KRITA_INSTALLER_64 + !error "Only one of KRITA_INSTALLER_32 or KRITA_INSTALLER_64 should be defined." +!endif + +!ifndef KRITA_PACKAGE_ROOT + !error "KRITA_PACKAGE_ROOT should be defined and point to the root of the package files." +!endif + +!ifdef KRITA_INSTALLER_64 + !define KRITA_INSTALLER_BITNESS 64 +!else + !define KRITA_INSTALLER_BITNESS 32 +!endif + +Unicode true +ManifestDPIAware true + +# Krita constants (can be overridden in command line params) +!define /ifndef KRITA_VERSION "0.0.0.0" +!define /ifndef KRITA_VERSION_DISPLAY "test-version" +#!define /ifndef KRITA_VERSION_GIT "" +!define /ifndef KRITA_INSTALLER_OUTPUT_DIR "" +!ifdef KRITA_INSTALLER_64 + !define /ifndef KRITA_INSTALLER_OUTPUT_NAME "krita_x64_setup.exe" +!else + !define /ifndef KRITA_INSTALLER_OUTPUT_NAME "krita_x86_setup.exe" +!endif + +# Krita constants (fixed) +!if "${KRITA_INSTALLER_OUTPUT_DIR}" == "" + !define KRITA_INSTALLER_OUTPUT "${KRITA_INSTALLER_OUTPUT_NAME}" +!else + !define KRITA_INSTALLER_OUTPUT "${KRITA_INSTALLER_OUTPUT_DIR}\${KRITA_INSTALLER_OUTPUT_NAME}" +!endif +!define KRTIA_PUBLISHER "Krita Foundation" +!ifdef KRITA_INSTALLER_64 + !define KRITA_PRODUCTNAME "Krita (x64)" + !define KRITA_UNINSTALL_REGKEY "Krita_x64" +!else + !define KRITA_PRODUCTNAME "Krita (x86)" + !define KRITA_UNINSTALL_REGKEY "Krita_x86" +!endif + +VIProductVersion "${KRITA_VERSION}" +VIAddVersionKey "CompanyName" "${KRTIA_PUBLISHER}" +VIAddVersionKey "FileDescription" "${KRITA_PRODUCTNAME} ${KRITA_VERSION_DISPLAY} Setup" +VIAddVersionKey "FileVersion" "${KRITA_VERSION}" +VIAddVersionKey "InternalName" "${KRITA_INSTALLER_OUTPUT_NAME}" +VIAddVersionKey "LegalCopyright" "${KRTIA_PUBLISHER}" +VIAddVersionKey "OriginalFileName" "${KRITA_INSTALLER_OUTPUT_NAME}" +VIAddVersionKey "ProductName" "${KRITA_PRODUCTNAME} ${KRITA_VERSION_DISPLAY} Setup" +VIAddVersionKey "ProductVersion" "${KRITA_VERSION}" + +BrandingText "[NSIS ${NSIS_VERSION}] ${KRITA_PRODUCTNAME} ${KRITA_VERSION}" + +Name "${KRITA_PRODUCTNAME} ${KRITA_VERSION_DISPLAY}" +OutFile ${KRITA_INSTALLER_OUTPUT} +!ifdef KRITA_INSTALLER_64 + InstallDir "$PROGRAMFILES64\Krita (x64)" +!else + InstallDir "$PROGRAMFILES32\Krita (x86)" +!endif +XPstyle on + +ShowInstDetails show +ShowUninstDetails show + +Var KritaStartMenuFolder +Var CreateDesktopIcon + +!include MUI2.nsh + +!define MUI_FINISHPAGE_NOAUTOCLOSE + +# Installer Pages +!insertmacro MUI_PAGE_WELCOME +!define MUI_LICENSEPAGE_CHECKBOX +!insertmacro MUI_PAGE_LICENSE "license_gpl-3.0.rtf" +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_COMPONENTS +!define MUI_PAGE_CUSTOMFUNCTION_PRE func_ShellExLicensePage_Init +!define MUI_PAGE_HEADER_TEXT "License Agreement (Krita Shell Extension)" +!insertmacro MUI_PAGE_LICENSE "license.rtf" +!define MUI_STARTMENUPAGE_DEFAULTFOLDER "Krita" +!define MUI_STARTMENUPAGE_REGISTRY_ROOT HKLM +!define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\Krita" +!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "StartMenuFolder" +!define MUI_STARTMENUPAGE_NODISABLE +!insertmacro MUI_PAGE_STARTMENU Krita $KritaStartMenuFolder +Page Custom func_DesktopShortcutPage_Init +Page Custom func_BeforeInstallPage_Init +!insertmacro MUI_PAGE_INSTFILES +!insertmacro MUI_PAGE_FINISH + +# Uninstaller Pages +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES + +!insertmacro MUI_LANGUAGE "English" + +!include Sections.nsh +!include LogicLib.nsh +!include x64.nsh +!include WinVer.nsh +!include WordFunc.nsh + +!define KRITA_SHELLEX_DIR "$INSTDIR\shellex" + +!include "include\FileExists2.nsh" +!include "include\IsFileInUse.nsh" +!include "krita_versions_detect.nsh" +!include "krita_shell_integration.nsh" + +Var KritaMsiProductX86 +Var KritaMsiProductX64 +Var KritaNsisVersion +Var KritaNsisBitness +Var KritaNsisInstallLocation + +Var PrevShellExInstallLocation +Var PrevShellExStandalone + +Var UninstallShellExStandalone + +Section "-Remove_shellex" SEC_remove_shellex + ${If} $PrevShellExInstallLocation != "" + ${AndIf} $PrevShellExStandalone == 1 + ${AndIf} $KritaNsisVersion == "" + ${AndIf} ${FileExists} "$PrevShellExInstallLocation\uninstall.exe" + push $R0 + DetailPrint "Removing Krita Shell Integration..." + SetDetailsPrint listonly + ExecWait "$PrevShellExInstallLocation\uninstall.exe /S _?=$PrevShellExInstallLocation" $R0 + ${If} $R0 != 0 + ${IfNot} ${Silent} + MessageBox MB_OK|MB_ICONSTOP "Failed to remove Krita Shell Integration." + ${EndIf} + SetDetailsPrint both + DetailPrint "Failed to remove Krita Shell Integration." + Abort + ${EndIf} + Delete "$PrevShellExInstallLocation\uninstall.exe" + RMDir /REBOOTOK "$PrevShellExInstallLocation" + SetRebootFlag false + SetDetailsPrint lastused + DetailPrint "Krita Shell Integration removed." + pop $R0 + ${EndIf} +SectionEnd + +Section "Remove Old Version" SEC_remove_old_version + ${If} $KritaNsisInstallLocation != "" + ${AndIf} ${FileExists} "$KritaNsisInstallLocation\uninstall.exe" + push $R0 + DetailPrint "Removing previous version..." + SetDetailsPrint listonly + ExecWait "$KritaNsisInstallLocation\uninstall.exe /S _?=$KritaNsisInstallLocation" $R0 + ${If} $R0 != 0 + ${IfNot} ${Silent} + MessageBox MB_OK|MB_ICONSTOP "Failed to remove previous version of Krita." + ${EndIf} + SetDetailsPrint both + DetailPrint "Failed to remove previous version of Krita." + Abort + ${EndIf} + Delete "$KritaNsisInstallLocation\uninstall.exe" + RMDir /REBOOTOK "$KritaNsisInstallLocation" + SetRebootFlag false + SetDetailsPrint lastused + DetailPrint "Previous version removed." + pop $R0 + ${EndIf} +SectionEnd + +Section "-Thing" + SetOutPath $INSTDIR + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${KRITA_UNINSTALL_REGKEY}" \ + "DisplayName" "${KRITA_PRODUCTNAME} ${KRITA_VERSION_DISPLAY}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${KRITA_UNINSTALL_REGKEY}" \ + "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" + WriteUninstaller $INSTDIR\uninstall.exe + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${KRITA_UNINSTALL_REGKEY}" \ + "DisplayVersion" "${KRITA_VERSION}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${KRITA_UNINSTALL_REGKEY}" \ + "DisplayIcon" "$\"$INSTDIR\shellex\krita.ico$\",0" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${KRITA_UNINSTALL_REGKEY}" \ + "URLInfoAbout" "https://krita.org/" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${KRITA_UNINSTALL_REGKEY}" \ + "InstallLocation" "$INSTDIR" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${KRITA_UNINSTALL_REGKEY}" \ + "Publisher" "${KRTIA_PUBLISHER}" + #WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${KRITA_UNINSTALL_REGKEY}" \ + # "EstimatedSize" 250000 + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${KRITA_UNINSTALL_REGKEY}" \ + "NoModify" 1 + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${KRITA_UNINSTALL_REGKEY}" \ + "NoRepair" 1 + # Registry entries for version recognition + # InstallLocation: + # Where krita is installed + WriteRegStr HKLM "Software\Krita" \ + "InstallLocation" "$INSTDIR" + # Version: + # Version of Krita + WriteRegStr HKLM "Software\Krita" \ + "Version" "${KRITA_VERSION}" + # x64: + # Set to 1 for 64-bit Krita, can be missing for 32-bit Krita +!ifdef KRITA_INSTALLER_64 + WriteRegDWORD HKLM "Software\Krita" \ + "x64" 1 +!else + DeleteRegValue HKLM "Software\Krita" "x64" +!endif + # StartMenuFolder: + # Start Menu Folder + # Handled by Modern UI 2.0 MUI_PAGE_STARTMENU +SectionEnd + +Section "${KRITA_PRODUCTNAME}" SEC_product_main + # TODO: Maybe switch to explicit file list? + File /r /x ffmpeg.exe /x ffmpeg_README.txt /x ffmpeg_LICENSE.txt ${KRITA_PACKAGE_ROOT}\bin + File /r ${KRITA_PACKAGE_ROOT}\lib + File /r ${KRITA_PACKAGE_ROOT}\share + File /r ${KRITA_PACKAGE_ROOT}\python +SectionEnd + +Section "-Main_associate" + CreateDirectory ${KRITA_SHELLEX_DIR} + ${Krita_RegisterFileAssociation} "$INSTDIR\bin\krita.exe" +SectionEnd + +Section "-Main_Shortcuts" + # Placing this after Krita_RegisterFileAssociation to get the icon + !insertmacro MUI_STARTMENU_WRITE_BEGIN Krita + CreateDirectory "$SMPROGRAMS\$KritaStartMenuFolder" + CreateShortcut "$SMPROGRAMS\$KritaStartMenuFolder\${KRITA_PRODUCTNAME}.lnk" "$INSTDIR\bin\krita.exe" "" "$INSTDIR\shellex\krita.ico" 0 + CreateDirectory "$SMPROGRAMS\$KritaStartMenuFolder\Tools" + CreateShortcut "$SMPROGRAMS\$KritaStartMenuFolder\Tools\Uninstall ${KRITA_PRODUCTNAME}.lnk" "$INSTDIR\Uninstall.exe" + !insertmacro MUI_STARTMENU_WRITE_END + ${If} $CreateDesktopIcon == 1 + # For the desktop icon, keep the name short and omit version info + CreateShortcut "$DESKTOP\Krita.lnk" "$INSTDIR\bin\krita.exe" "" "$INSTDIR\shellex\krita.ico" 0 + ${EndIf} +SectionEnd + +Section "Shell Integration" SEC_shellex + ${If} ${RunningX64} + ${Krita_RegisterComComonents} 64 + ${EndIf} + ${Krita_RegisterComComonents} 32 + + ${Krita_RegisterShellExtension} + + # ShellExtension\InstallLocation: + # Where the shell extension is installed + # If installed by Krita installer, this must point to shellex sub-dir + WriteRegStr HKLM "Software\Krita\ShellExtension" \ + "InstallLocation" "$INSTDIR\shellex" + # ShellExtension\Version: + # Version of the shell extension + WriteRegStr HKLM "Software\Krita\ShellExtension" \ + "Version" "${KRITASHELLEX_VERSION}" + # ShellExtension\Standalone: + # 0 = Installed by Krita installer + # 1 = Standalone installer + WriteRegDWORD HKLM "Software\Krita\ShellExtension" \ + "Standalone" 0 + # ShellExtension\KritaExePath: + # Path to krita.exe as specified by user or by Krita installer + # Empty if not specified + WriteRegStr HKLM "Software\Krita\ShellExtension" \ + "KritaExePath" "$INSTDIR\bin\krita.exe" +SectionEnd + +!ifdef HAS_FFMPEG +Section "Bundled FFmpeg" SEC_ffmpeg + File /oname=bin\ffmpeg.exe ${KRITA_PACKAGE_ROOT}\bin\ffmpeg.exe + File /oname=bin\ffmpeg_LICENSE.txt ${KRITA_PACKAGE_ROOT}\bin\ffmpeg_LICENSE.txt + File /oname=bin\ffmpeg_README.txt ${KRITA_PACKAGE_ROOT}\bin\ffmpeg_README.txt +SectionEnd +!endif + +Section "-Main_refreshShell" + ${RefreshShell} +SectionEnd + +!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN + !insertmacro MUI_DESCRIPTION_TEXT ${SEC_remove_shellex} "Remove previously installed Krita Shell Integration." + !insertmacro MUI_DESCRIPTION_TEXT ${SEC_remove_old_version} "Remove previously installed Krita $KritaNsisVersion ($KritaNsisBitness-bit)." + !insertmacro MUI_DESCRIPTION_TEXT ${SEC_product_main} "${KRITA_PRODUCTNAME} ${KRITA_VERSION_DISPLAY}$\r$\n$\r$\nVersion: ${KRITA_VERSION}" + !insertmacro MUI_DESCRIPTION_TEXT ${SEC_shellex} "Shell Extension component to provide thumbnails and file properties display for Krita files.$\r$\n$\r$\nVersion: ${KRITASHELLEX_VERSION}" +!ifdef HAS_FFMPEG + !insertmacro MUI_DESCRIPTION_TEXT ${SEC_ffmpeg} "Install a bundled version of FFmpeg for exporting animations." +!endif +!insertmacro MUI_FUNCTION_DESCRIPTION_END + +Section "un.Shell Integration" + ${If} $UninstallShellExStandalone == 1 + push $R0 + DetailPrint "Removing Krita Shell Integration..." + SetDetailsPrint listonly + ExecWait "$INSTDIR\shellex\uninstall.exe /S _?=$INSTDIR\shellex" $R0 + ${If} $R0 != 0 + ${IfNot} ${Silent} + MessageBox MB_OK|MB_ICONSTOP "Failed to remove Krita Shell Integration. Please report this bug!" + ${EndIf} + SetDetailsPrint lastused + SetDetailsPrint both + DetailPrint "Failed to remove Krita Shell Integration." + ${EndIf} + Delete "$INSTDIR\shellex\uninstall.exe" + RMDir /REBOOTOK "$INSTDIR\shellex" + SetDetailsPrint lastused + DetailPrint "Krita Shell Integration removed." + pop $R0 + ${Else} + ${Krita_UnregisterShellExtension} + + ${If} ${RunningX64} + ${Krita_UnregisterComComonents} 64 + ${EndIf} + ${Krita_UnregisterComComonents} 32 + ${EndIf} +SectionEnd + +Section "un.Main_associate" + # TODO: Conditional, use install log + ${If} $UninstallShellExStandalone != 1 + ${Krita_UnregisterFileAssociation} + ${EndIf} +SectionEnd + +Section "un.Main_Shortcuts" + Delete "$DESKTOP\Krita.lnk" + !insertmacro MUI_STARTMENU_GETFOLDER Krita $KritaStartMenuFolder + Delete "$SMPROGRAMS\$KritaStartMenuFolder\Tools\Uninstall ${KRITA_PRODUCTNAME}.lnk" + RMDir "$SMPROGRAMS\$KritaStartMenuFolder\Tools" + Delete "$SMPROGRAMS\$KritaStartMenuFolder\${KRITA_PRODUCTNAME}.lnk" + RMDir "$SMPROGRAMS\$KritaStartMenuFolder" +SectionEnd + +Section "un.${KRITA_PRODUCTNAME}" + # TODO: Maybe switch to explicit file list or some sort of install log? + RMDir /r $INSTDIR\bin + RMDir /r $INSTDIR\lib + RMDir /r $INSTDIR\share + RMDir /r $INSTDIR\python +SectionEnd + +Section "un.Thing" + RMDir /REBOOTOK $INSTDIR\shellex + DeleteRegKey HKLM "Software\Krita" + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${KRITA_UNINSTALL_REGKEY}" + Delete $INSTDIR\uninstall.exe + RMDir /REBOOTOK $INSTDIR +SectionEnd + +Section "un.Main_refreshShell" + ${RefreshShell} +SectionEnd + +Function .onInit + SetShellVarContext all + !insertmacro SetSectionFlag ${SEC_product_main} ${SF_RO} + !insertmacro SetSectionFlag ${SEC_product_main} ${SF_BOLD} + !insertmacro SetSectionFlag ${SEC_remove_old_version} ${SF_RO} +!ifdef HAS_FFMPEG + !insertmacro SetSectionFlag ${SEC_ffmpeg} ${SF_RO} +!endif + StrCpy $CreateDesktopIcon 1 # Create desktop icon by default + ${IfNot} ${AtLeastWin7} + ${IfNot} ${Silent} + MessageBox MB_OK|MB_ICONSTOP "${KRITA_PRODUCTNAME} ${KRITA_VERSION_DISPLAY} requires Windows 7 or above." + ${EndIf} + Abort + ${EndIf} +!ifdef KRITA_INSTALLER_64 + ${If} ${RunningX64} + SetRegView 64 + ${Else} + ${IfNot} ${Silent} + MessageBox MB_OK|MB_ICONSTOP "You are running 32-bit Windows, but this installer installs Krita 64-bit which can only be installed on 64-bit Windows. Please download the 32-bit version on https://krita.org/" + ${EndIf} + Abort + ${Endif} +!else + ${If} ${RunningX64} + SetRegView 64 + ${IfNot} ${Silent} + MessageBox MB_YESNO|MB_ICONEXCLAMATION "You are trying to install 32-bit Krita on 64-bit Windows. You are strongly recommended to install the 64-bit version of Krita instead since it offers better performance.$\nIf you want to use the 32-bit version for testing, you should consider using the zip package instead.$\n$\nDo you still wish to install the 32-bit version of Krita?" \ + /SD IDYES \ + IDYES lbl_allow32on64 + Abort + ${EndIf} + lbl_allow32on64: + ${Endif} +!endif + # Detect other Krita versions + ${DetectKritaMsi32bit} $KritaMsiProductX86 + ${If} ${RunningX64} + ${DetectKritaMsi64bit} $KritaMsiProductX64 + ${IfKritaMsi3Alpha} $KritaMsiProductX64 + ${IfNot} ${Silent} + MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Krita 3.0 Alpha 1 is installed. It must be removed before ${KRITA_PRODUCTNAME} ${KRITA_VERSION_DISPLAY} can be installed.$\nDo you wish to remove it now?" \ + /SD IDYES \ + IDYES lbl_removeKrita3alpha + Abort + ${EndIf} + lbl_removeKrita3alpha: + push $R0 + ${MsiUninstall} $KritaMsiProductX64 $R0 + ${If} $R0 != 0 + ${IfNot} ${Silent} + MessageBox MB_OK|MB_ICONSTOP "Failed to remove Krita 3.0 Alpha 1." + ${EndIf} + Abort + ${EndIf} + pop $R0 + StrCpy $KritaMsiProductX64 "" + ${ElseIf} $KritaMsiProductX64 != "" + ${If} $KritaMsiProductX86 != "" + ${IfNot} ${Silent} + MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Both 32-bit and 64-bit editions of Krita 2.9 or below are installed.$\nBoth must be removed before ${KRITA_PRODUCTNAME} ${KRITA_VERSION_DISPLAY} can be installed.$\nDo you want to remove them now?" \ + /SD IDYES \ + IDYES lbl_removeKritaBoth + Abort + ${EndIf} + lbl_removeKritaBoth: + push $R0 + ${MsiUninstall} $KritaMsiProductX86 $R0 + ${If} $R0 != 0 + ${IfNot} ${Silent} + MessageBox MB_OK|MB_ICONSTOP "Failed to remove Krita (32-bit)." + ${EndIf} + Abort + ${EndIf} + ${MsiUninstall} $KritaMsiProductX64 $R0 + ${If} $R0 != 0 + ${IfNot} ${Silent} + MessageBox MB_OK|MB_ICONSTOP "Failed to remove Krita (64-bit)." + ${EndIf} + Abort + ${EndIf} + pop $R0 + StrCpy $KritaMsiProductX86 "" + StrCpy $KritaMsiProductX64 "" + ${Else} + ${IfNot} ${Silent} + MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Krita (64-bit) 2.9 or below is installed.$\nIt must be removed before ${KRITA_PRODUCTNAME} ${KRITA_VERSION_DISPLAY} can be installed.$\nDo you wish to remove it now?" \ + /SD IDYES \ + IDYES lbl_removeKritaX64 + Abort + ${EndIf} + lbl_removeKritaX64: + push $R0 + ${MsiUninstall} $KritaMsiProductX64 $R0 + ${If} $R0 != 0 + ${IfNot} ${Silent} + MessageBox MB_OK|MB_ICONSTOP "Failed to remove Krita (64-bit)." + ${EndIf} + Abort + ${EndIf} + pop $R0 + StrCpy $KritaMsiProductX64 "" + ${EndIf} + ${EndIf} + ${Endif} + ${If} $KritaMsiProductX86 != "" + ${IfNot} ${Silent} + MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Krita (32-bit) 2.9 or below is installed.$\nIt must be removed before ${KRITA_PRODUCTNAME} ${KRITA_VERSION_DISPLAY} can be installed.$\nDo you wish to remove it now?" \ + /SD IDYES \ + IDYES lbl_removeKritaX86 + Abort + ${EndIf} + lbl_removeKritaX86: + push $R0 + ${MsiUninstall} $KritaMsiProductX86 $R0 + ${If} $R0 != 0 + ${IfNot} ${Silent} + MessageBox MB_OK|MB_ICONSTOP "Failed to remove Krita (32-bit)." + ${EndIf} + Abort + ${EndIf} + pop $R0 + StrCpy $KritaMsiProductX86 "" + ${EndIf} + + ${DetectKritaNsis} $KritaNsisVersion $KritaNsisBitness $KritaNsisInstallLocation + ${If} $KritaNsisVersion != "" + push $R0 + ${VersionCompare} "${KRITA_VERSION}" "$KritaNsisVersion" $R0 + ${If} $R0 == 0 + # Same version installed... probably + ${If} $KritaNsisBitness == ${KRITA_INSTALLER_BITNESS} + # Very likely the same version + ${IfNot} ${Silent} + MessageBox MB_OK|MB_ICONINFORMATION "It appears that ${KRITA_PRODUCTNAME} ${KRITA_VERSION_DISPLAY} is already installed.$\nThis setup will reinstall it." + ${EndIf} + ${Else} + # Very likely the same version but different arch + ${IfNot} ${Silent} +!ifdef KRITA_INSTALLER_64 + MessageBox MB_OK|MB_ICONINFORMATION "It appears that Krita 32-bit ${KRITA_VERSION_DISPLAY} is currently installed. This setup will replace it with the 64-bit version." +!else + MessageBox MB_OK|MB_ICONEXCLAMATION "It appears that Krita 64-bit ${KRITA_VERSION_DISPLAY} is currently installed. This setup will replace it with the 32-bit version." +!endif + ${EndIf} + ${EndIf} + ${ElseIf} $R0 == 1 + # Upgrade + ${If} $KritaNsisBitness == ${KRITA_INSTALLER_BITNESS} + # Slient about upgrade + ${Else} + # Upgrade but different arch + ${IfNot} ${Silent} +!ifdef KRITA_INSTALLER_64 + MessageBox MB_OK|MB_ICONINFORMATION "It appears that Krita 32-bit ($KritaNsisVersion) is currently installed. This setup will replace it with the 64-bit version of Krita ${KRITA_VERSION_DISPLAY}." +!else + MessageBox MB_OK|MB_ICONEXCLAMATION "It appears that Krita 64-bit ($KritaNsisVersion) is currently installed. This setup will replace it with the 32-bit version of Krita ${KRITA_VERSION_DISPLAY}." +!endif + ${EndIf} + ${EndIf} + ${ElseIf} $R0 == 2 + ${IfNot} ${Silent} + MessageBox MB_OK|MB_ICONSTOP "It appears that a newer version of Krita $KritaNsisBitness-bit ($KritaNsisVersion) is currently installed. If you want to downgrade Krita to ${KRITA_VERSION_DISPLAY}, please uninstall the newer version manually before running this setup." + ${EndIf} + Abort + ${Else} + ${IfNot} ${Silent} + MessageBox MB_OK|MB_ICONSTOP "Unexpected state" + ${EndIf} + Abort + ${EndIf} + !insertmacro SetSectionFlag ${SEC_remove_old_version} ${SF_SELECTED} + # Detect if Krita is running... + ${If} ${IsFileinUse} "$KritaNsisInstallLocation\bin\krita.exe" + ${IfNot} ${Silent} + MessageBox MB_OK|MB_ICONEXCLAMATION "Krita appears to be running. Please close Krita before running this installer." + ${EndIf} + Abort + ${EndIf} + pop $R0 + ${Else} + !insertmacro ClearSectionFlag ${SEC_remove_old_version} ${SF_SELECTED} + SectionSetText ${SEC_remove_old_version} "" + ${EndIf} + + # Detect standalone shell extension + # TODO: Would it be possible to update Krita without replacing the standalone shellex? + ClearErrors + ReadRegStr $PrevShellExInstallLocation HKLM "Software\Krita\ShellExtension" "InstallLocation" + #ReadRegStr $PrevShellExVersion HKLM "Software\Krita\ShellExtension" "Version" + ReadRegDWORD $PrevShellExStandalone HKLM "Software\Krita\ShellExtension" "Standalone" + #ReadRegStr $PrevShellExKritaExePath HKLM "Software\Krita\ShellExtension" "KritaExePath" + ${If} ${Errors} + # TODO: Assume no previous version installed or what? + ${EndIf} + ${If} $PrevShellExStandalone == 1 + ${IfNot} ${Silent} + MessageBox MB_YESNO|MB_ICONQUESTION "Krita Shell Integration was installed separately. It will be uninstalled automatically when installing Krita.$\nDo you want to continue?" \ + /SD IDYES \ + IDYES lbl_allowremoveshellex + Abort + ${EndIf} + lbl_allowremoveshellex: + #!insertmacro SetSectionFlag ${SEC_remove_shellex} ${SF_SELECTED} + ${Else} + #!insertmacro ClearSectionFlag ${SEC_remove_shellex} ${SF_SELECTED} + #SectionSetText ${SEC_remove_shellex} "" + ${EndIf} +FunctionEnd + +Function un.onInit + SetShellVarContext all +!ifdef KRITA_INSTALLER_64 + ${If} ${RunningX64} + SetRegView 64 + ${Else} + Abort + ${Endif} +!else + ${If} ${RunningX64} + SetRegView 64 + ${Endif} +!endif + ReadRegDWORD $UninstallShellExStandalone HKLM "Software\Krita\ShellExtension" "Standalone" + ${If} ${IsFileinUse} "$INSTDIR\bin\krita.exe" + ${IfNot} ${Silent} + MessageBox MB_OK|MB_ICONEXCLAMATION "Krita appears to be running. Please close Krita before uninstalling." + ${EndIf} + Abort + ${EndIf} +FunctionEnd + +Function func_ShellExLicensePage_Init + ${IfNot} ${SectionIsSelected} ${SEC_shellex} + # Skip ShellEx license page if not selected + Abort + ${EndIf} +FunctionEnd + +Var hwndChkDesktopIcon + +Function func_DesktopShortcutPage_Init + push $R0 + + nsDialogs::Create 1018 + pop $R0 + ${If} $R0 == error + Abort + ${EndIf} + !insertmacro MUI_HEADER_TEXT "Desktop Icon" "Configure desktop shortcut icon." + + ${NSD_CreateLabel} 0u 0u 300u 20u "You can choose to create a shortcut icon on the desktop for launching Krita." + pop $R0 + + ${NSD_CreateCheckbox} 0u 20u 300u 10u "Create a desktop icon" + pop $hwndChkDesktopIcon + ${If} $CreateDesktopIcon == 1 + ${NSD_Check} $hwndChkDesktopIcon + ${Else} + ${NSD_Uncheck} $hwndChkDesktopIcon + ${EndIf} + ${NSD_OnClick} $hwndChkDesktopIcon func_DesktopShortcutPage_CheckChange + + nsDialogs::Show + + pop $R0 +FunctionEnd + +Function func_DesktopShortcutPage_CheckChange + ${NSD_GetState} $hwndChkDesktopIcon $CreateDesktopIcon + ${If} $CreateDesktopIcon == ${BST_CHECKED} + StrCpy $CreateDesktopIcon 1 + ${Else} + StrCpy $CreateDesktopIcon 0 + ${EndIf} +FunctionEnd + +Function func_BeforeInstallPage_Init + push $R0 + + nsDialogs::Create 1018 + pop $R0 + ${If} $R0 == error + Abort + ${EndIf} + !insertmacro MUI_HEADER_TEXT "Confirm Installation" "Confirm installation of ${KRITA_PRODUCTNAME} ${KRITA_VERSION_DISPLAY}." + + ${NSD_CreateLabel} 0u 0u 300u 140u "Setup is ready to install ${KRITA_PRODUCTNAME} ${KRITA_VERSION_DISPLAY}. You may review the install options before you continue.$\r$\n$\r$\n$_CLICK" + pop $R0 + + # TODO: Add install option summary for review? + + nsDialogs::Show + + pop $R0 +FunctionEnd diff --git a/packaging/windows/license_gpl-3.0.rtf b/packaging/windows/installer/license_gpl-3.0.rtf similarity index 100% rename from packaging/windows/license_gpl-3.0.rtf rename to packaging/windows/installer/license_gpl-3.0.rtf diff --git a/packaging/windows/krita_installer/CPL.TXT b/packaging/windows/krita_installer/CPL.TXT deleted file mode 100644 index 5207f810bd..0000000000 --- a/packaging/windows/krita_installer/CPL.TXT +++ /dev/null @@ -1,94 +0,0 @@ -Common Public License Version 1.0 - -THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. - - -1. DEFINITIONS - -"Contribution" means: - -a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and - -b) in the case of each subsequent Contributor: - -i) changes to the Program, and - -ii) additions to the Program; - -where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. - -"Contributor" means any person or entity that distributes the Program. - -"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. - -"Program" means the Contributions distributed in accordance with this Agreement. - -"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. - - -2. GRANT OF RIGHTS - -a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. - -b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. - -c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. - -d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. - - -3. REQUIREMENTS - -A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: - -a) it complies with the terms and conditions of this Agreement; and - -b) its license agreement: - -i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; - -ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; - -iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and - -iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. - -When the Program is made available in source code form: - -a) it must be made available under this Agreement; and - -b) a copy of this Agreement must be included with each copy of the Program. - -Contributors may not remove or alter any copyright notices contained within the Program. - -Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. - - -4. COMMERCIAL DISTRIBUTION - -Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. - -For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. - - -5. NO WARRANTY - -EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. - - -6. DISCLAIMER OF LIABILITY - -EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - - -7. GENERAL - -If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. - -If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed. In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. - -All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. - -Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward. IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. - -This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. diff --git a/packaging/windows/krita_installer/PreserveGUIDs.xslt b/packaging/windows/krita_installer/PreserveGUIDs.xslt deleted file mode 100644 index 2f868d75f8..0000000000 --- a/packaging/windows/krita_installer/PreserveGUIDs.xslt +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packaging/windows/krita_installer/README.txt b/packaging/windows/krita_installer/README.txt deleted file mode 100644 index 09dbb905e9..0000000000 --- a/packaging/windows/krita_installer/README.txt +++ /dev/null @@ -1,89 +0,0 @@ -Krita installer README -------------------- - -This includes instructions for preparing your environment to build an MSI installer for Krita 3 on Windows. - -Preparation ------------ - -You will need to have a working KDE Windows environment which lets you build Calligra on the Windows platform. - -The default folder structure is: -parent ($PACKAGER_ROOT) -+-krita -+-installer-input (receives a packaged version of Krita + KDE, output from package.ps1) -+-installer-output (intermediate and complete MSI written by build_msi.ps1 process) -+-installer-temp (temp folder used by the package.ps1 process) -+-deps (stores 3rd party dependencies - Visual C++ runtime components for the installer) - +-vcredist - +-DLLs - +-MergeModules (stores .msm files distrubuted with Visual Studio) -+-wix310 (install wix 3.10 here) - -You will require the Wix toolset. Currently tested with 3.10. -This may be downloaded from http://wixtoolset.org/ -Extract the .zip into the installation folder - -Setting up the environment ------------------------- - -The following script sets the necessary shell variables. - -> .\env.ps1 - -You will need to customize the script to, at a minimum, update the version -number. You should also ensure that the variables inside match your directory -structure. Check whether it worked correctly with the command: - -> dir variables: - -To choose how/whether to distribute the VC++ runtime files, you must set -KRITA_VC2015_DISTRIBUTE to point to the process you intend to use. - - DLL - informs wix that you want to distribute DLLs with it - MSM - informs wix that you want to use the MergeModule - -Any other value is ignored and causes an error message to be displayed if it is -not found. - -If wix has been installed anywhere other than ..\wix36, then you must set -KRITA_WIX_BIN to point to it. - -KRITA_VERSION - the version number of the package. This must consist of four -sets of digits separate by periods (e.g. 2.3.87.1). Currently the numbering -convention for KRITA_VERSION uses the first three to identify the last main -tagged release of calligra, and the fourth for subsequent builds from master. - -KRITA_GITREV - the intention is for this to take a git revision identifier. -This needs to be set in advance, and would normally be set by the build process -which created the Calligra installation. - -PACKAGER_ROOT - working path for package.ps1 and build_msi.ps1 - -You must set CALLIGRA_INST to point to the inst folder of the Calligra -installation. KDEROOT is set by kdeenv, so should not need to be explicitly -declared. - - - -Packaging Calligra ------------------- - -Now you can run: -.\package.ps1 - -This will copy files from KRITA_INST and KDEROOT in to KRITA_INPUT, -packaging the Windows distribution of Calligra with the required KDE files. The -KRITA_TEMP folder contains some files that package touches, but it should clean -this up on completion. This should complete without errors (although you may -need to prompt it to overwrite files ...may need to review this behaviour) - -Once this has completed, you are ready to run: -.\build_msi.ps1 - -This will run all the required steps: - heat to read the KRITA_INPUT directory in order to generate HeatFragment.wxs - candle to build the intermediate obj files fromm the xml sources - light to build the msi file. - - diff --git a/packaging/windows/krita_installer/build_msi.bat b/packaging/windows/krita_installer/build_msi.bat deleted file mode 100644 index 0e86d0a62c..0000000000 --- a/packaging/windows/krita_installer/build_msi.bat +++ /dev/null @@ -1,38 +0,0 @@ -:: Copyright (c) 2011-2012 KO GmbH. All rights reserved. -:: Copyright (c) 2011-2012 Stuart Dickson -:: -:: The use and distribution terms for this software are covered by the -:: Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) -:: which can be found in the file CPL.TXT at the root of this distribution. -:: By using this software in any fashion, you are agreeing to be bound by -:: the terms of this license. -:: -:: You must not remove this notice, or any other, from this software. -:: ------------------------------------------------------------------------ -:: -:: build_msi.bat -:: -:: - -@echo off -pushd %~dp0 -set PATH=%WIX_BIN%;%PATH% -del "%C2WINSTALL_OUTPUT%\*.wixobj" - -set C2WINSTALL_TEMP_INSTALLER=%C2WINSTALL_OUTPUT%\krita_%C2WINSTALL_VERSION% - -:: call %~dp0run_heat.bat -m:\package\wix36\candle.exe %~dp0HeatFragment.wxs %~dp0krita.wxs %~dp0res\wix_snippets\BundleVC2010x86.wxs %~dp0res\UIExtension\CustomUI_Mondo.wxs %~dp0res\UIExtension\CustomProgressDlg.wxs %~dp0res\UIExtension\CustomWelcomeDlg.wxs %~dp0res\UIExtension\CustomMaintenanceWelcomeDlg.wxs %~dp0res\UIExtension\CustomResumeDlg.wxs -out %C2WINSTALL_OUTPUT%\\ - -echo. -echo Running light... -m:\package\wix36\light.exe -nologo "%C2WINSTALL_OUTPUT%\*.wixobj" -out "%C2WINSTALL_TEMP_INSTALLER%.msi" -ext WixUIExtension -shasum "%C2WINSTALL_TEMP_INSTALLER%.msi" > "%C2WINSTALL_TEMP_INSTALLER%.sh1" -:: Delete intermediate file -:: It is possible for candle to fail without an error and hence -:: we should delete the wixobj file in case light.exe picks up -:: an old version -:: -echo Complete. -echo. -popd diff --git a/packaging/windows/krita_installer/build_msi.ps1 b/packaging/windows/krita_installer/build_msi.ps1 deleted file mode 100644 index 4e9b168d49..0000000000 --- a/packaging/windows/krita_installer/build_msi.ps1 +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) 2011-2012 KO GmbH. All rights reserved. -# Copyright (c) 2011-2012 Stuart Dickson -# Copyright (c) 2015 Michael Abrahams - - -# The use and distribution terms for this software are covered by the -# Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) -# which can be found in the file CPL.TXT at the root of this distribution. -# By using this software in any fashion, you are agreeing to be bound by -# the terms of this license. - -# You must not remove this notice, or any other, from this software. -# ------------------------------------------------------------------------ - -# build_msi.bat - - -# TODO: disable UpdateMimeDB and KBuildSycoca in krita.wxs -# TODO: look into xdg and etc directories to see what's needed - -# Clean up from last time. -del "$env:KRITA_OUTPUT\*.wixobj" - -$KRITA_INSTALLER_FILENAME="$env:KRITA_OUTPUT\krita_$env:KRITA_VERSION" - -# Collects contents of $KRITA_INPUT. "KRITADIR" is a tag for the installation directory used inside the wxs file -echo "Locating files..." -."$env:KRITA_WIX_BIN\heat.exe" dir "$env:KRITA_INPUT" -cg CalligraBaseFiles -gg -scom -sreg -sfrag -srd -dr KRITADIR -var env.KRITA_INPUT -out "HeatFragment.wxs" - - -# Configures installation instructions & metadata. x64 only, for now. This uses -# the XML configuration files contained in in res\, and bakes in paths using -# res\package\env.bat - -# This uses x64 architecture. -# To make it work for 32 bits, I believe you want to change these things: -# -Remove "-arch x64" and change "ProgramFiles64Folder" - -$KRITA_ARCH = "x64" - -echo "Building metadata..." -."$env:KRITA_WIX_BIN\candle.exe" -arch x64 ` - HeatFragment.wxs krita.wxs ` - res\wix_snippets\BundleVC2015x64.wxs ` - res\UIExtension\CustomUI_Mondo.wxs ` - res\UIExtension\CustomProgressDlg.wxs ` - res\UIExtension\CustomWelcomeDlg.wxs ` - res\UIExtension\CustomMaintenanceWelcomeDlg.wxs ` - res\UIExtension\CustomResumeDlg.wxs -out $env:KRITA_OUTPUT\\ - - -echo "Creating executable..." -."$env:KRITA_WIX_BIN\light.exe" -nologo "$env:KRITA_OUTPUT\*.wixobj" ` - -out "$KRITA_INSTALLER_FILENAME.msi" -ext WixUIExtension - - -Get-FileHash -Algorithm SHA1 "$KRITA_INSTALLER_FILENAME.msi" > "$KRITA_INSTALLER_FILENAME.sha1" - - -echo "Complete." diff --git a/packaging/windows/krita_installer/env.bat b/packaging/windows/krita_installer/env.bat deleted file mode 100644 index 6a4f994bd3..0000000000 --- a/packaging/windows/krita_installer/env.bat +++ /dev/null @@ -1,100 +0,0 @@ -:: Copyright (c) 2011-2012 KO GmbH. All rights reserved. -:: Copyright (c) 2011-2012 Stuart Dickson -:: -:: The use and distribution terms for this software are covered by the -:: Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) -:: which can be found in the file CPL.TXT at the root of this distribution. -:: By using this software in any fashion, you are agreeing to be bound by -:: the terms of this license. -:: -:: You must not remove this notice, or any other, from this software. -:: ------------------------------------------------------------------------ -:: -:: C2W Installer -:: Environment settings -:: -:: This file checks for the presence of environment variables -:: and sets default values if they do not exist. -:: -:: wix process -:: ----------- -:: C2WINSTALL_WIX_BIN - location of the Wix binaries -:: C2WINSTALL_OUTPUT - destination for generated installers -:: C2WINSTALL_INPUT - source directory of packaged KDEROOT+CALLIGRA_INST -:: C2WINSTALL_MERGEMODS - directory where we should look for Merge Modules -:: -:: package process -:: --------------- -:: CALLIGRA_INST - the Calligra installation directory -:: KDEROOT - the root of the KDE on Windows emerge based distribution -:: -:: Build options (defined elsewhere) -:: --------------------------------- -:: C2WINSTALL_VC2010_DISTRIBUTE: -:: DLL - bundle DLLs -:: MSM - use Merge Modules -:: - display error message -:: -:: ...USE_DLL has priority, if neither are defined, an error is displayed -:: -:: ----------------------------------------------------------------------- -:: -set C2WINSTALL_GITREV=last -set C2WINSTALL_VERSION=2.6.8.0 -set C2WINSTALL_VERSIONSTRING="2.6.8.0" -set CALLIGRA_INST=u: -set KDEROOT=u: -set C2WINSTALL_VC2010_DISTRIBUTE=MSM - -IF "%C2WINSTALL_VERSION%"=="" ( - :: We must define this, if it hasn't already been - echo WARNING: You should set C2WINSTALL_VERSION - set C2WINSTALL_VERSION=0.0.0.0 -) - -IF "%C2WINSTALL_GITREV%"=="" ( - :: We must define this, if it hasn't already been - echo WARNING: You should set C2WINSTALL_GITREV - set C2WINSTALL_GITREV=last -) - -IF "%C2WINSTALL_VC2010_DISTRIBUTE%"=="" ( - :: If no distribute set, then need to set to - :: something in order to display warning dialog. - set C2WINSTALL_VC2010_DISTRIBUTE=ERROR -) - -IF "%C2WINSTALL_WIX_BIN%"=="" ( - set C2WINSTALL_WIX_BIN=%~dp0..\wix36 -) - -IF "%C2WINSTALL_MERGEMODULES%"=="" ( - set C2WINSTALL_MERGEMODULES=%~dp0..\deps -) - -IF "%C2WINSTALL_VC2010_DLLS%"=="" ( - set C2WINSTALL_VC2010_DLLS=%~dp0..\deps\vcredist\DLLs\Microsoft.VC100.CRT -) - -IF "%C2WINSTALL_TEMP%"=="" ( - set C2WINSTALL_TEMP=%~dp0..\c2winstaller-temp -) - -IF "%C2WINSTALL_INPUT%"=="" ( - set C2WINSTALL_INPUT=%~dp0..\c2winstaller-input -) - -IF "%C2WINSTALL_OUTPUT%"=="" ( - set C2WINSTALL_OUTPUT=%~dp0..\c2winstaller-output -) - -IF "%CALLIGRA_INST%"=="" ( - set CALLIGRA_INST=%~dp0..\kde4\inst -) - -IF "%KDEROOT%"=="" ( - set KDEROOT=%~dp0..\kderoot -) - -SET PATH=%C2WINSTALL_WIX_BIN%;%PATH% - diff --git a/packaging/windows/krita_installer/env.ps1 b/packaging/windows/krita_installer/env.ps1 deleted file mode 100644 index 8769f534c3..0000000000 --- a/packaging/windows/krita_installer/env.ps1 +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright (c) 2011-2012 KO GmbH. All rights reserved. -# Copyright (c) 2011-2012 Stuart Dickson -# Copyright (c) 2015 Michael Abrahams -# -# The use and distribution terms for this software are covered by the -# Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) -# which can be found in the file CPL.TXT at the root of this distribution. -# By using this software in any fashion, you are agreeing to be bound by -# the terms of this license. -# -# You must not remove this notice, or any other, from this software. -# ------------------------------------------------------------------------ -# -# Krita Windows Installer -# Environment settings -# -# This file checks for the presence of environment variables -# and sets default values if they do not exist. -# -# See README.txt for more details. -# -# wix process -# ----------- -# KRITA_WIX_BIN - location of the Wix binaries -# KRITA_OUTPUT - destination for generated installers -# KRITA_INPUT - source directory of packaged KDEROOT+CALLIGRA_INST -# KRITA_MERGEMODS - directory where we should look for Merge Modules -# -# package process -# --------------- -# CALLIGRA_INST - the Krita installation directory (TODO: rename) -# KDEROOT - the root of the KDE on Windows emerge based distribution -# -# Build options (defined elsewhere) -# --------------------------------- -# -# ...USE_DLL has priority, if neither are defined, an error is displayed -# -# ----------------------------------------------------------------------- -# - -# WIX reads environment variables, but PowerShell variables are local by -# default, so we have to prefix all variables with $env: -$env:KRITA_VERSION="3.0.0.1" -$env:KRITA_VERSIONSTRING="3.0.0.1" # This could be set to a cute name if you wanted. -$env:KRITA_GITREV="last" - - -## Base installation directories -$env:KDEROOT="r:" -$env:CALLIGRA_INST="r:" -$PACKAGER_ROOT="r:\inst" # Working directory for package.ps1 (script-local) -$env:KRITA_WIX_BIN="r:\inst\wix310" # Location of WIX binaries - - - -# Method of distributing MSVC 2015 -# DLL - bundle DLLs -# MSM - use Merge Modules -# - error -$env:KRITA_VC2015_DISTRIBUTE="MSM" - -# See MSDN: "Redistributing Components By Using Merge Modules" -# https://msdn.microsoft.com/en-us/library/ms235290.aspx -$env:KRITA_MERGEMODULES="C:\Program Files (x86)\Common Files\Merge Modules" - - -function warn-unset($v) { - # Use this function only when there is no acceptable default. - $val = (Get-Variable $v).value - iex "echo ""Warning: variable $v was not set. Please check your configuration." - Exit -} - -if ($env:CALLIGRA_INST -eq $null) { - warn-unset "CALLIGRA_INST" -} - -if ($env:KDEROOT -eq $null) { - warn-unset "$env:KDEROOT" -} - -if ($env:KRITA_VERSION -eq $null) { - warn-unset "KRITA_VERSION" - Exit -} - -if ($env:KRITA_GITREV -eq $null) { - warn-unset "KRITA_GITREV" -} - - -$env:KRITA_VC2015_DLLS="$PWD\..\deps\vcredist\DLLs\Microsoft.VC140.CRT" -$env:KRITA_TEMP="$PACKAGER_ROOT\installer-temp" -$env:KRITA_INPUT="$PACKAGER_ROOT\installer-input" -$env:KRITA_OUTPUT="$PACKAGER_ROOT\installer-output" - - - -# set $env:Path "$($env:Path);$KRITA_WIX_BIN" diff --git a/packaging/windows/krita_installer/krita-files.json b/packaging/windows/krita_installer/krita-files.json deleted file mode 100644 index b3502c95a7..0000000000 --- a/packaging/windows/krita_installer/krita-files.json +++ /dev/null @@ -1,344 +0,0 @@ -{"InstallationInfo" : [ - - { - "dir": "lib\\plugins", - "msg": "Copying plugins", - "comment": "Hmm", - "files": [ - "lib\\plugins\\kritathumbnail.dll", - "lib\\plugins\\kstyle_oxygen_config.dll" - ] - }, - - { - "dir": "lib\\plugins\\imageformats", - "msg": "Copying image format plugins", - "files": [ - "lib\\plugins\\imageformats\\kimg_kra.dll", - "lib\\plugins\\imageformats\\kimg_ora.dll" - ] - }, - - { - "dir": "lib\\kritaplugins", - "msg": "Copying lib\\kritaplugins", - "comment": "Double check these, this list was compiled without all modules built.", - "files": [ - "lib\\kritaplugins\\calligra_tool_basicflakes.dll", - "lib\\kritaplugins\\kritaanimationdocker.dll", - "lib\\kritaplugins\\kritaartisticcolorselector.dll", - "lib\\kritaplugins\\kritabigbrother.dll", - "lib\\kritaplugins\\kritablurfilter.dll", - "lib\\kritaplugins\\kritabmpexport.dll", - "lib\\kritaplugins\\kritabmpimport.dll", - "lib\\kritaplugins\\kritachanneldocker.dll", - "lib\\kritaplugins\\kritaclonesarray.dll", - "lib\\kritaplugins\\kritacolorgenerator.dll", - "lib\\kritaplugins\\kritacolorrange.dll", - "lib\\kritaplugins\\kritacolorselectorng.dll", - "lib\\kritaplugins\\kritacolorsfilters.dll", - "lib\\kritaplugins\\kritacolorslider.dll", - "lib\\kritaplugins\\kritacolorsmudgepaintop.dll", - "lib\\kritaplugins\\kritacolorspaceconversion.dll", - "lib\\kritaplugins\\kritacompositiondocker.dll", - "lib\\kritaplugins\\kritaconvolutionfilters.dll", - "lib\\kritaplugins\\kritacurvepaintop.dll", - "lib\\kritaplugins\\kritadefaultdockers.dll", - "lib\\kritaplugins\\kritadefaultpaintops.dll", - "lib\\kritaplugins\\kritadefaulttools.dll", - "lib\\kritaplugins\\kritadeformpaintop.dll", - "lib\\kritaplugins\\kritadigitalmixer.dll", - "lib\\kritaplugins\\kritadodgeburn.dll", - "lib\\kritaplugins\\kritadynapaintop.dll", - "lib\\kritaplugins\\kritaembossfilter.dll", - "lib\\kritaplugins\\kritaexample.dll", - "lib\\kritaplugins\\kritaexperimentpaintop.dll", - "lib\\kritaplugins\\kritaextensioncolorsfilters.dll", - "lib\\kritaplugins\\kritafastcolortransferfilter.dll", - "lib\\kritaplugins\\kritafilterop.dll", - "lib\\kritaplugins\\kritagridpaintop.dll", - "lib\\kritaplugins\\kritahairypaintop.dll", - "lib\\kritaplugins\\kritahatchingpaintop.dll", - "lib\\kritaplugins\\kritaheightmapexport.dll", - "lib\\kritaplugins\\kritaheightmapimport.dll", - "lib\\kritaplugins\\kritahistogram.dll", - "lib\\kritaplugins\\kritahistorydocker.dll", - "lib\\kritaplugins\\kritaimagedocker.dll", - "lib\\kritaplugins\\kritaimageenhancement.dll", - "lib\\kritaplugins\\kritaimagesize.dll", - "lib\\kritaplugins\\kritaimagesplit.dll", - "lib\\kritaplugins\\kritaindexcolors.dll", - "lib\\kritaplugins\\kritajpegexport.dll", - "lib\\kritaplugins\\kritajpegimport.dll", - "lib\\kritaplugins\\kritalayergroupswitcher.dll", - "lib\\kritaplugins\\kritalayersplit.dll", - "lib\\kritaplugins\\kritalcmsengine.dll", - "lib\\kritaplugins\\kritalevelfilter.dll", - "lib\\kritaplugins\\kritametadataeditor.dll", - "lib\\kritaplugins\\kritamodifyselection.dll", - "lib\\kritaplugins\\kritanoisefilter.dll", - "lib\\kritaplugins\\kritanormalize.dll", - "lib\\kritaplugins\\kritaodgimport.dll", - "lib\\kritaplugins\\kritaoffsetimage.dll", - "lib\\kritaplugins\\kritaoilpaintfilter.dll", - "lib\\kritaplugins\\kritaoraexport.dll", - "lib\\kritaplugins\\kritaoraimport.dll", - "lib\\kritaplugins\\kritaoverviewdocker.dll", - "lib\\kritaplugins\\kritapalettedocker.dll", - "lib\\kritaplugins\\kritaparticlepaintop.dll", - "lib\\kritaplugins\\kritapatterndocker.dll", - "lib\\kritaplugins\\kritapatterngenerator.dll", - "lib\\kritaplugins\\kritaphongbumpmap.dll", - "lib\\kritaplugins\\kritapixelizefilter.dll", - "lib\\kritaplugins\\kritapngexport.dll", - "lib\\kritaplugins\\kritapngimport.dll", - "lib\\kritaplugins\\kritaposterize.dll", - "lib\\kritaplugins\\kritappmexport.dll", - "lib\\kritaplugins\\kritappmimport.dll", - "lib\\kritaplugins\\kritapresetdocker.dll", - "lib\\kritaplugins\\kritapresethistory.dll", - "lib\\kritaplugins\\kritapsdexport.dll", - "lib\\kritaplugins\\kritapsdimport.dll", - "lib\\kritaplugins\\kritaqmlexport.dll", - "lib\\kritaplugins\\kritaraindropsfilter.dll", - "lib\\kritaplugins\\kritarandompickfilter.dll", - "lib\\kritaplugins\\kritaresourcemanager.dll", - "lib\\kritaplugins\\kritarotateimage.dll", - "lib\\kritaplugins\\kritaroundcornersfilter.dll", - "lib\\kritaplugins\\kritarulerassistanttool.dll", - "lib\\kritaplugins\\kritaselectiontools.dll", - "lib\\kritaplugins\\kritaseparatechannels.dll", - "lib\\kritaplugins\\kritashearimage.dll", - "lib\\kritaplugins\\kritasketchpaintop.dll", - "lib\\kritaplugins\\kritasmallcolorselector.dll", - "lib\\kritaplugins\\kritasmalltilesfilter.dll", - "lib\\kritaplugins\\kritasobelfilter.dll", - "lib\\kritaplugins\\kritaspecificcolorselector.dll", - "lib\\kritaplugins\\kritaspraypaintop.dll", - "lib\\kritaplugins\\kritatangentnormalpaintop.dll", - "lib\\kritaplugins\\kritatasksetdocker.dll", - "lib\\kritaplugins\\kritatgaexport.dll", - "lib\\kritaplugins\\kritatgaimport.dll", - "lib\\kritaplugins\\kritatiffexport.dll", - "lib\\kritaplugins\\kritatiffimport.dll", - "lib\\kritaplugins\\kritatoolcrop.dll", - "lib\\kritaplugins\\kritatooldyna.dll", - "lib\\kritaplugins\\kritatoolgrid.dll", - "lib\\kritaplugins\\kritatoolperspectivegrid.dll", - "lib\\kritaplugins\\kritatoolpolygon.dll", - "lib\\kritaplugins\\kritatoolpolyline.dll", - "lib\\kritaplugins\\kritatooltext.dll", - "lib\\kritaplugins\\kritatooltransform.dll", - "lib\\kritaplugins\\kritaunsharpfilter.dll", - "lib\\kritaplugins\\kritawavefilter.dll", - "lib\\kritaplugins\\kritaxcfimport.dll", - "lib\\kritaplugins\\krita_colorspaces_extensions.dll", - "lib\\kritaplugins\\krita_docker_defaults.dll", - "lib\\kritaplugins\\krita_filtereffects.dll", - "lib\\kritaplugins\\krita_flaketools.dll", - "lib\\kritaplugins\\krita_karbontools.dll", - "lib\\kritaplugins\\krita_shape_artistictext.dll", - "lib\\kritaplugins\\krita_shape_paths.dll", - "lib\\kritaplugins\\krita_shape_text.dll", - "lib\\kritaplugins\\krita_shape_vector.dll" - ] - - }, - - { - "dir" : "lib\\plugins\\styles", - "files" : [ - "lib\\plugins\\styles\\oxygen.dll", - "lib\\plugins\\styles\\breeze.dll" - ] - }, - - - - - { - "msg" : "Copying Shared Libraries", - "dir" : "bin\\", - "files" : ["bin\\boost_system-vc150-mt-1_59.dll", - "bin\\dbus-1.dll", - "bin\\exiv2.dll", - "bin\\expat.dll", - "bin\\iconv.dll", - "bin\\icudt55.dll", - "bin\\icuin55.dll", - "bin\\icuuc55.dll", - "bin\\lcms2.dll", - "bin\\libeay32.dll", - "bin\\libintl.dll", - "bin\\libpng15.dll", - "bin\\lzma.dll", - "bin\\oxygenstyle5.dll", - "bin\\oxygenstyleconfig5.dll", - "bin\\ssleay32.dll", - "bin\\tiff3.dll", - "bin\\zlib.dll" - ] - }, - - { - "msg" : "Copying Qt and KDE Libraries", - "dir" : "bin\\", - "files" : ["bin\\KF5Archive.dll", - "bin\\KF5Completion.dll", - "bin\\KF5ConfigCore.dll", - "bin\\KF5ConfigGui.dll", - "bin\\KF5CoreAddons.dll", - "bin\\KF5DbusAddons.dll", - "bin\\KF5Crash.dll", - "bin\\KF5GuiAddons.dll", - "bin\\KF5I18n.dll", - "bin\\KF5ItemModels.dll", - "bin\\KF5ItemViews.dll", - "bin\\KF5KIOCore.dll", - "bin\\KF5Service.dll", - "bin\\KF5WidgetsAddons.dll", - "bin\\KF5WindowSystem.dll", - "bin\\Qt5Concurrent.dll", - "bin\\Qt5Core.dll", - "bin\\Qt5DBus.dll", - "bin\\Qt5Gui.dll", - "bin\\Qt5Network.dll", - "bin\\Qt5OpenGL.dll", - "bin\\Qt5PrintSupport.dll", - "bin\\Qt5Svg.dll", - "bin\\Qt5Widgets.dll", - "bin\\Qt5WinExtras.dll", - "bin\\Qt5Xml.dll" - ] - }, - - { - "msg" : "Copying Krita Libraries", - "dir" : "bin\\", - "files" : ["bin\\kritabasicflakes.dll", - "bin\\kritacolor.dll", - "bin\\kritaflake.dll", - "bin\\kritaglobal.dll", - "bin\\kritaimage.dll", - "bin\\kritalibbrush.dll", - "bin\\kritalibpaintop.dll", - "bin\\kritaodf.dll", - "bin\\kritapigment.dll", - "bin\\kritaplugin.dll", - "bin\\kritapsd.dll", - "bin\\kritasketchlib.dll", - "bin\\kritastore.dll", - "bin\\kritatext.dll", - "bin\\kritatextlayout.dll", - "bin\\kritaui.dll", - "bin\\kritaundo2.dll", - "bin\\kritavectorimage.dll", - "bin\\kritaversion.dll", - "bin\\kritawidgets.dll", - "bin\\kritawidgetutils.dll" - ] - }, - - { - "msg" : "Copying Executables", - "dir" : "bin\\", - "files" : [ - "bin\\gmicparser.exe", - "bin\\krita.exe" - ] - }, - - { - "msg" : "Copying directory /etc/dbus-1", - "dir": "etc\\dbus-1", - "files": ["etc\\"] - }, - - { - "msg" : "Copying Qt plugins", - "dir" : "bin", - "files" : [ - "plugins\\imageformats", - "plugins\\platforms", - "plugins\\iconengines"] - }, - - - - { - "msg": "Copying directory /share", - "dir" : "share", - "files" : [ - "share\\calligra", - "share\\color-schemes", - "share\\apps\\desktoptheme", - "share\\krita", - "share\\kritasketch", - "share\\kritaanimation", - "share\\kritaplugins", - "share\\kstyle", - "share\\mime", - "share\\xdg", - "share\\color"] - }, - - { - "msg" : "Copying directory /etc/xdg", - "dir" : "etc\\xdg", - "files" : [ - "etc\\xdg\\colors", - "etc\\xdg\\ui", - "etc\\xdg\\krita*", - "etc\\xdg\\kdebug*", - "etc\\xdg\\khot*", - "etc\\xdg\\kdebugrc" - ] - }, - - { - "msg" : "Copying dbus interfaces", - "dir" : "share\\dbus-1\\interfaces", - "files" : [ - "share\\dbus-1\\interfaces\\kf5_org.kde.*", - "share\\dbus-1\\services\\kf5_org.kde.*"] - }, - - { - "msg": "Copying directory share/kservices5", - "dir" : "share\\kservices5", - "files" : [ - "share\\kservices5\\krita_kra_thumbnail.desktop", - "share\\kservices5\\kshorturifilter.desktop", - "share\\kservices5\\kuriikwsfilter.desktop", - "share\\kservices5\\kurisearchfilter.desktop", - "share\\kservices5\\localdomainurifilter.desktop", - "share\\kservices5\\*.protocol"] - }, - - { - "msg": "Copying directory share/kservicetypes5", - "dir" : "share\\kservicetypes5", - "files" : [ - "share\\kservicetypes5\\application.desktop", - "share\\kservicetypes5\\calligra*.desktop", - "share\\kservicetypes5\\filtereffect.desktop", - "share\\kservicetypes5\\flake.desktop", - "share\\kservicetypes5\\flakedevice.desktop", - "share\\kservicetypes5\\flakeshape.desktop", - "share\\kservicetypes5\\flaketool.desktop", - "share\\kservicetypes5\\inlinetextobject.desktop", - "share\\kservicetypes5\\kfileitemactionplugin.desktop", - "share\\kservicetypes5\\kfilewrite.desktop", - "share\\kservicetypes5\\knotificationplugin.desktop", - "share\\kservicetypes5\\kpart.desktop", - "share\\kservicetypes5\\kplugininfo.desktop", - "share\\kservicetypes5\\krita*.desktop", - "share\\kservicetypes5\\kurifilterplugin.desktop", - "share\\kservicetypes5\\pigment.desktop", - "share\\kservicetypes5\\pigmentextension.desktop", - "share\\kservicetypes5\\qimageio_plugin.desktop", - "share\\kservicetypes5\\texteditingplugin.desktop" - ] - } - -]} diff --git a/packaging/windows/krita_installer/krita.wxi b/packaging/windows/krita_installer/krita.wxi deleted file mode 100644 index 8b708b88b5..0000000000 --- a/packaging/windows/krita_installer/krita.wxi +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - diff --git a/packaging/windows/krita_installer/krita.wxs b/packaging/windows/krita_installer/krita.wxs deleted file mode 100644 index 3379ab1f03..0000000000 --- a/packaging/windows/krita_installer/krita.wxs +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NOT Installed - NOT Installed - - - - diff --git a/packaging/windows/krita_installer/package.bat b/packaging/windows/krita_installer/package.bat deleted file mode 100644 index 6969278219..0000000000 --- a/packaging/windows/krita_installer/package.bat +++ /dev/null @@ -1,383 +0,0 @@ -:: package_calligra.bat -:: -:: Copies relevant files from the calligra inst and kderoot folders -:: -@echo off - -:: Safety check: -:: Make sure key variables are defined - -IF "%C2WINSTALL_INPUT%"=="" ( - echo !!! C2WINSTALL VARIABLES NOT PROPERLY DEFINED !!! - echo Operation cancelled. - goto :eof - pause -) -IF "%C2WINSTALL_OUTPUT%"=="" ( - echo !!! C2WINSTALL VARIABLES NOT PROPERLY DEFINED !!! - echo Operation cancelled. - goto :eof - pause -) - -:: Create redistribution directories -rm -rf %C2WINSTALL_INPUT% -mkdir %C2WINSTALL_INPUT% -mkdir %C2WINSTALL_OUTPUT% - -mkdir %C2WINSTALL_INPUT%\bin -mkdir %C2WINSTALL_INPUT%\etc -mkdir %C2WINSTALL_INPUT%\lib -mkdir %C2WINSTALL_INPUT%\lib\kde4 -mkdir %C2WINSTALL_INPUT%\plugins -mkdir %C2WINSTALL_INPUT%\share - -echo Copying plugins -cp %CALLIGRA_INST%\lib\kde4/artistictextshape.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/autocorrect.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/basicflakesplugin.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/calligradocinfopropspage.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/calligradockers.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/calligraimagethumbnail.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/calligrathumbnail.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/changecase.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/comicbookthumbnail.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/defaulttools.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/htmlthumbnail.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/imagethumbnail.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/jpegthumbnail.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/karbon_flattenpathplugin.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/karbon_refinepathplugin.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/karbon_roundcornersplugin.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/karbon_whirlpinchplugin.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/karbon1ximport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/karbonfiltereffects.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/karbonimageexport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/karbonpart.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/karbonsvgexport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/karbonsvgimport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/karbontools.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/karbonxfigimport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kded*.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kio*.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kfile*.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kolcmsengine.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/krita_colorspaces_extensions.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/krita_raw_import.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaartisticcolorselector.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritabigbrother.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritablurfilter.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritabmpexport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritabmpimport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritachanneldocker.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritacolorgenerator.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritacolorrange.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritacolorselectorng.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritacolorsfilters.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritacolorsmudgepaintop.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritacolorspaceconversion.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritacompositiondocker.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaconvolutionfilters.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritacurvepaintop.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritadefaultdockers.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritadefaultpaintops.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritadefaulttools.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritadeformpaintop.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritadigitalmixer.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritadodgeburn.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritadynapaintop.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaembossfilter.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaexample.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaexperimentpaintop.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaextensioncolorsfilters.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritafastcolortransferfilter.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritafilterop.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaflipbookdocker.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaflipbookimport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritagridpaintop.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritahairypaintop.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritahatchingpaintop.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritahistogram.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritahistorydocker.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaimagedocker.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaimageenhancement.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaimagesize.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaimagesplit.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritajp2export.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritajp2import.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritajpegexport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritajpegimport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritalayercompose.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritalevelfilter.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritametadataeditor.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritamodifyselection.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritanoisefilter.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaodgimport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaoilpaintfilter.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaoraexport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaoraimport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritapart.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaparticlepaintop.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritapatterndocker.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritapdfimport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaphongbumpmap.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritapixelizefilter.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritapngexport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritapngimport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritappmexport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritappmimport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritapresetdocker.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritapsdexport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritapsdimport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaqmlexport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaraindropsfilter.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritarandompickfilter.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritarotateimage.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaroundcornersfilter.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritarulerassistanttool.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaselectiontools.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaseparatechannels.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritashearimage.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritasketchpaintop.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritasmallcolorselector.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritasmalltilesfilter.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritasobelfilter.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaspecificcolorselector.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaspraypaintop.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritatasksetdocker.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritatiffexport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritatiffimport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritatoolcrop.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritatooldyna.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritatoolgrid.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritatoolperspectivegrid.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritatoolpolygon.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritatoolpolyline.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritatooltext.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritatooltransform.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaunsharpfilter.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritawavefilter.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kritaxcfimport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/kstyle_oxygen_config.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/pathshapes.dll %C2WINSTALL_INPUT%\lib\kde4 - -mkdir %C2WINSTALL_INPUT%\lib\kde4\styles -cp %CALLIGRA_INST%\lib\kde4/plugins\styles\oxygen.dll %C2WINSTALL_INPUT%\lib\kde4\styles - -mkdir %C2WINSTALL_INPUT%\lib\kde4\styles\imageformats -cp %CALLIGRA_INST%\lib\kde4/plugins\imageformats\kimg_dds.dll %C2WINSTALL_INPUT%\lib\kde4\imageformats -cp %CALLIGRA_INST%\lib\kde4/plugins\imageformats\kimg_pcx.dll %C2WINSTALL_INPUT%\lib\kde4\imageformats -cp %CALLIGRA_INST%\lib\kde4/plugins\imageformats\kimg_ras.dll %C2WINSTALL_INPUT%\lib\kde4\imageformats -cp %CALLIGRA_INST%\lib\kde4/plugins\imageformats\kimg_xcf.dll %C2WINSTALL_INPUT%\lib\kde4\imageformats -cp %CALLIGRA_INST%\lib\kde4/plugins\imageformats\kimg_eps.dll %C2WINSTALL_INPUT%\lib\kde4\imageformats -cp %CALLIGRA_INST%\lib\kde4/plugins\imageformats\kimg_pic.dll %C2WINSTALL_INPUT%\lib\kde4\imageformats -cp %CALLIGRA_INST%\lib\kde4/plugins\imageformats\kimg_rgb.dll %C2WINSTALL_INPUT%\lib\kde4\imageformats -cp %CALLIGRA_INST%\lib\kde4/plugins\imageformats\kimg_xview.dll %C2WINSTALL_INPUT%\lib\kde4\imageformats -cp %CALLIGRA_INST%\lib\kde4/plugins\imageformats\kimg_jp2.dll %C2WINSTALL_INPUT%\lib\kde4\imageformats -cp %CALLIGRA_INST%\lib\kde4/plugins\imageformats\kimg_psd.dll %C2WINSTALL_INPUT%\lib\kde4\imageformats -cp %CALLIGRA_INST%\lib\kde4/plugins\imageformats\kimg_tga.dll %C2WINSTALL_INPUT%\lib\kde4\imageformats - -cp %CALLIGRA_INST%\lib\kde4/kurisearchfilter.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/localdomainurifilter.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/svgthumbnail.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/textshape.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/textthumbnail.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/windowsexethumbnail.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/windowsimagethumbnail.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/wmfexport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/wmfimport.dll %C2WINSTALL_INPUT%\lib\kde4 -cp %CALLIGRA_INST%\lib\kde4/wpgimport.dll %C2WINSTALL_INPUT%\lib\kde4 - -echo Copying Libraries - -cp %CALLIGRA_INST%\bin\*dll %C2WINSTALL_INPUT%\bin\ - -echo Copying Exes - -cp %CALLIGRA_INST%\bin\krita.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\calligraconverter.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\kwalletd.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\dbus-daemon.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\dbus-launch.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\kbuildsycoca4.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\kconf_update.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\kconfig_compiler.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\kde4-config.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\kdebugdialog.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\kded4.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\kdeinit4.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\kioexec.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\kioclient.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\kioslave.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\kquitapp.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\ktraderclient.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\kwalletd.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\kwrapper4.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\klauncher.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\knotify4.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\drkonqi.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\update-mime-database.exe %C2WINSTALL_INPUT%\bin\ -cp %CALLIGRA_INST%\bin\khelpcenter.exe %C2WINSTALL_INPUT%\bin\ - -echo Copying /etc directory for dbus - -cp -r %CALLIGRA_INST%\etc\dbus-1 %C2WINSTALL_INPUT%\etc\ - -echo Copying Qt plugins - -cp -r %CALLIGRA_INST%\plugins\imageformats %C2WINSTALL_INPUT%\plugins -cp -r %CALLIGRA_INST%\plugins\iconengines %C2WINSTALL_INPUT%\plugins - -echo Copying the /share/applications directory -mkdir %C2WINSTALL_INPUT%\share\applications\kde4 -cp %CALLIGRA_INST%\share\applications\kde4\calligra.desktop %C2WINSTALL_INPUT%\share\applications\kde4 -cp %CALLIGRA_INST%\share\applications\kde4\krita.desktop %C2WINSTALL_INPUT%\share\applications\kde4 -cp %CALLIGRA_INST%\share\applications\kde4\krita_bmp.desktop %C2WINSTALL_INPUT%\share\applications\kde4 -cp %CALLIGRA_INST%\share\applications\kde4\krita_flipbook.desktop %C2WINSTALL_INPUT%\share\applications\kde4 -cp %CALLIGRA_INST%\share\applications\kde4\krita_jp2.desktop %C2WINSTALL_INPUT%\share\applications\kde4 -cp %CALLIGRA_INST%\share\applications\kde4\krita_jpeg.desktop %C2WINSTALL_INPUT%\share\applications\kde4 -cp %CALLIGRA_INST%\share\applications\kde4\krita_odg.desktop %C2WINSTALL_INPUT%\share\applications\kde4 -cp %CALLIGRA_INST%\share\applications\kde4\krita_ora.desktop %C2WINSTALL_INPUT%\share\applications\kde4 -cp %CALLIGRA_INST%\share\applications\kde4\krita_pdf.desktop %C2WINSTALL_INPUT%\share\applications\kde4 -cp %CALLIGRA_INST%\share\applications\kde4\krita_png.desktop %C2WINSTALL_INPUT%\share\applications\kde4 -cp %CALLIGRA_INST%\share\applications\kde4\krita_ppm.desktop %C2WINSTALL_INPUT%\share\applications\kde4 -cp %CALLIGRA_INST%\share\applications\kde4\krita_psd.desktop %C2WINSTALL_INPUT%\share\applications\kde4 -cp %CALLIGRA_INST%\share\applications\kde4\krita_qml.desktop %C2WINSTALL_INPUT%\share\applications\kde4 -cp %CALLIGRA_INST%\share\applications\kde4\krita_raw.desktop %C2WINSTALL_INPUT%\share\applications\kde4 -cp %CALLIGRA_INST%\share\applications\kde4\krita_tiff.desktop %C2WINSTALL_INPUT%\share\applications\kde4 -cp %CALLIGRA_INST%\share\applications\kde4\krita_xcf.desktop %C2WINSTALL_INPUT%\share\applications\kde4 - -echo Copying the /share/apps directory - -mkdir %C2WINSTALL_INPUT%\share\applications\kde4\apps - -cp -r %CALLIGRA_INST%\share\apps\calligra %C2WINSTALL_INPUT%\share\apps -cp -r %CALLIGRA_INST%\share\apps\color-schemes %C2WINSTALL_INPUT%\share\apps -cp -r %CALLIGRA_INST%\share\apps\desktoptheme %C2WINSTALL_INPUT%\share\apps -cp -r %CALLIGRA_INST%\share\apps\hardwarenotifications %C2WINSTALL_INPUT%\share\apps -cp -r %CALLIGRA_INST%\share\apps\kauth %C2WINSTALL_INPUT%\share\apps -cp -r %CALLIGRA_INST%\share\apps\kde %C2WINSTALL_INPUT%\share\apps\ -cp -r %CALLIGRA_INST%\share\apps\kdeui %C2WINSTALL_INPUT%\share\apps\ -cp -r %CALLIGRA_INST%\share\apps\kdewidgets %C2WINSTALL_INPUT%\share\apps\ -cp -r %CALLIGRA_INST%\share\apps\kio* %C2WINSTALL_INPUT%\share\apps\ -cp -r %CALLIGRA_INST%\share\apps\knewstuff %C2WINSTALL_INPUT%\share\apps\ -cp -r %CALLIGRA_INST%\share\apps\kritaplugins %C2WINSTALL_INPUT%\share\apps\ -cp -r %CALLIGRA_INST%\share\apps\krita %C2WINSTALL_INPUT%\share\apps\ -cp -r %CALLIGRA_INST%\share\apps\kssl %C2WINSTALL_INPUT%\share\apps\ -cp -r %CALLIGRA_INST%\share\apps\kstyle %C2WINSTALL_INPUT%\share\apps\ -cp -r %CALLIGRA_INST%\share\apps\kwalletd %C2WINSTALL_INPUT%\share\apps\ -cp -r %CALLIGRA_INST%\share\apps\mime %C2WINSTALL_INPUT%\share\apps\ - -echo Copying the /share/color directory -cp -r %CALLIGRA_INST%\share\color %C2WINSTALL_INPUT%\share\ - -echo Copying the /share/config directory -mkdir %C2WINSTALL_INPUT%\share\config -cp -r %CALLIGRA_INST%\share\config\colors %C2WINSTALL_INPUT%\share\config -cp -r %CALLIGRA_INST%\share\config\ui %C2WINSTALL_INPUT%\share\config -cp -r %CALLIGRA_INST%\share\config\krita* %C2WINSTALL_INPUT%\share\config -cp -r %CALLIGRA_INST%\share\config\kdebug* %C2WINSTALL_INPUT%\share\config -cp -r %CALLIGRA_INST%\share\config\khot* %C2WINSTALL_INPUT%\share\config -cp -r %CALLIGRA_INST%\share\config\kdebugrc %C2WINSTALL_INPUT%\share\config -cp -r %CALLIGRA_INST%\share\config\kdeglobals %C2WINSTALL_INPUT%\share\config - -echo Copying the /share/dbus-1 directory -mkdir %C2WINSTALL_INPUT%\share\dbus-1\interfaces -mkdir %C2WINSTALL_INPUT%\share\dbus-1\services -mkdir %C2WINSTALL_INPUT%\share\dbus-1\system-services - -cp -r %CALLIGRA_INST%\share\dbus-1\interfaces\org.kde.* %C2WINSTALL_INPUT%\share\dbus-1\interfaces -cp -r %CALLIGRA_INST%\share\dbus-1\services\org.kde.* %C2WINSTALL_INPUT%\share\dbus-1\services - -echo Copying the /share/icons directory (need to be cleaned out!) -cp -r %CALLIGRA_INST%\share\icons %C2WINSTALL_INPUT%\share\ - -echo Copying the /share/mime directory -cp -r %CALLIGRA_INST%\share\mime %C2WINSTALL_INPUT%\share\ - -echo Copying the /share/kde4 directory -mkdir %C2WINSTALL_INPUT%\share\kde4\services - -cp %CALLIGRA_INST%\share\kde4\services\*.protocol %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\krita*.desktop %C2WINSTALL_INPUT%\share\kde4\services - -cp %CALLIGRA_INST%\share\kde4\services\artistictextshape.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\autocorrect.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\basicflakesplugin.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\calligra_odg_thumbnail.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\calligradocinfopropspage.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\calligradockers.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\changecase.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\comicbookthumbnail.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\componentchooser.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\defaulttools.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\desktopthumbnail.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\directorythumbnail.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\djvuthumbnail.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\htmlthumbnail.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\imagethumbnail.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\jpegthumbnail.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\kolcmsengine.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\kwalletd.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\pathshapes.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\textshape.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\textthumbnail.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\thumbnail.protocol %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\filetypes.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\fixhosturifilter.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\htmlthumbnail.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\icons.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\karbonfiltereffects.desktop %C2WINSTALL_INPUT%\share\kde4\service -cp %CALLIGRA_INST%\share\kde4\services\karbontools.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\kfilemodule.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\kglobalaccel.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\knotify4.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\kshorturifilter.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\kuiserver.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\kuriikwsfilter.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\kurisearchfilter.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\localdomainurifilter.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\platform.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\svgthumbnail.desktop %C2WINSTALL_INPUT%\share\kde4\services -cp %CALLIGRA_INST%\share\kde4\services\windowsimagethumbnail.desktop %C2WINSTALL_INPUT%\share\kde4\services - -cp -r %CALLIGRA_INST%\share\kde4\services\qimageioplugins %C2WINSTALL_INPUT%\share\kde4\services - -mkdir %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\application.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\calligra*.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\filtereffect.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\flake.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\flakedevice.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\flakeshape.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\flaketool.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\inlinetextobject.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\karbon_module.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\kconfigbackend.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\kdedmodule.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\kfileitemactionplugin.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\kfileplugin.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\kfilewrite.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\kiofilemodule.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\knotifynotifymethod.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\kofilter.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\kofilterwrapper.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\koplugin.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\kpart.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\kplugininfo.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\krita_brush.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\krita_dock.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\krita_filter.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\krita_generator.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\krita_paintop.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\krita_plugin.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\krita_tool.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\kurifilterplugin.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\pigment.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\pigmentextension.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\qimageio_plugin.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\texteditingplugin.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\textvariableplugin.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes -cp %CALLIGRA_INST%\share\kde4\servicetypes\thumbcreator.desktop %C2WINSTALL_INPUT%\share\kde4\servicetypes - -cp %C2WINSTALL_INPUT%\..\c2winstaller\res\package\env.bat %C2WINSTALL_INPUT% diff --git a/packaging/windows/krita_installer/package.ps1 b/packaging/windows/krita_installer/package.ps1 deleted file mode 100644 index 6b068e5fbb..0000000000 --- a/packaging/windows/krita_installer/package.ps1 +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (c) 2015 Michael Abrahams -# package_calligra.ps1 -# -# Copies relevant files from the calligra inst and kderoot folders. -# -# -# Information about the files to copy is contained in krita-files.json. The -# directory structure is specified here. -# - -# Safety check: -# Make sure key variables are defined - -if (($env:KRITA_INPUT -eq $null) -or ($env:KRITA_OUTPUT -eq $null)) { - echo "!!! ENVIRONMENT VARIABLES NOT PROPERLY DEFINED !!!" - echo "(Did you run env.ps1?)" - Exit -} - -# Create directories for the installer. -mkdir "$env:KRITA_INPUT" -ea SilentlyContinue -mkdir "$env:KRITA_INPUT\bin" -ea SilentlyContinue -mkdir "$env:KRITA_INPUT\etc" -ea SilentlyContinue -mkdir "$env:KRITA_INPUT\lib" -ea SilentlyContinue -mkdir "$env:KRITA_INPUT\lib\kritaplugins" -ea SilentlyContinue -mkdir "$env:KRITA_INPUT\lib\plugins\styles" -ea SilentlyContinue -mkdir "$env:KRITA_INPUT\lib\plugins\imageformats" -ea SilentlyContinue -mkdir "$env:KRITA_INPUT\share" -ea SilentlyContinue -mkdir "$env:KRITA_INPUT\share\appdata" -ea SilentlyContinue -mkdir "$env:KRITA_INPUT\share\applications" -ea SilentlyContinue -mkdir "$env:KRITA_INPUT\share\krita" -ea SilentlyContinue -mkdir "$env:KRITA_INPUT\share\config" -ea SilentlyContinue -mkdir "$env:KRITA_INPUT\etc\dbus-1\" -ea SilentlyContinue -mkdir "$env:KRITA_INPUT\etc\xdg\" -ea SilentlyContinue -mkdir "$env:KRITA_INPUT\share\dbus-1\" -ea SilentlyContinue -mkdir "$env:KRITA_INPUT\share\dbus-1\interfaces" -ea SilentlyContinue -mkdir "$env:KRITA_INPUT\share\dbus-1\services" -ea SilentlyContinue -mkdir "$env:KRITA_INPUT\share\dbus-1\system-services"-ea SilentlyContinue -mkdir "$env:KRITA_INPUT\share\kservices5" -ea SilentlyContinue -mkdir "$env:KRITA_INPUT\share\kservicetypes5" -ea SilentlyContinue - -mkdir "$env:KRITA_OUTPUT" -ea SilentlyContinue - -# The format of the JSON is as follows. -# Each data item has four elements. -# -# "dir" is the destination directory, relative to $KRITA_INPUT. It is posssible -# to have several different groups of files copied to the same destination -# directory. DIR must exist already, created in the "mkdir" list above. -# "files" is a list of files copied into DIR. -# "msg" will be displayed during the copying procedure. -# "comment" is ignored by this script. -$installinfo = ConvertFrom-Json ((Get-Content "krita-files.json") -join "`n") - - -# Helper function - basically an rsync kinda thing. -# Copy items only if they are newer, return error string on failure. -function copy-newer($src, $dst) { - Try { - $src_item = Get-Item $src -ErrorAction SilentlyContinue - if (-not $?) { - return "Could not locate $src" - } - $dst_item = Get-Item $dst -ErrorAction SilentlyContinue - if (-not $?) { - $src_update = [datetime](Get-ItemProperty -Path $src -Name LastWriteTime)[0].LastWriteTime - $dst_update = [datetime](Get-ItemProperty -Path $dst -Name LastWriteTime)[0].LastWriteTime - if ($src_update -le $dst_update) { - # Source file is not newer, skip this file - return "" - } - } - copy -Recurse $src $dst -ErrorAction SilentlyContinue - return "" - } - Catch [System.Management.Automation.RuntimeException] - { - return "File $src - $($Error[0].Exception.Message)" - } - Catch [System.InvalidCastException] - { - return "Copying $src failed - $($Error[0].Exception.Message)" - } - Catch - { - return "File $src - $($Error[0].Exception.Message)" - } -} - - -foreach ($i in $installinfo.InstallationInfo) { - - # Print messages from JSON data - if ($i.msg) { - echo "$($i.msg)..." - } - - # One directory per group in JSON data - $dst = "$env:KRITA_INPUT\$($i.dir)" - - #Loop through files - foreach ($f in $i.files) { - $src = "$env:CALLIGRA_INST\$f" - echo "Copying - $src" - $errstr = copy-newer $src $dst - - # If we got an error when copying, print something helpful - if ( $errstr ) { - if ($Error[0].Exception.GetType() -eq [System.IO.IOException]) { - echo "INFO: file $dst already exists." - } - elseif ($i.optional) { - echo $errstr - } - else { - Write-Warning $errstr - } - } - } -} diff --git a/packaging/windows/krita_installer/res/UIExtension/Bitmaps/Billboards/billboard.bmp b/packaging/windows/krita_installer/res/UIExtension/Bitmaps/Billboards/billboard.bmp deleted file mode 100644 index b5a7811016..0000000000 Binary files a/packaging/windows/krita_installer/res/UIExtension/Bitmaps/Billboards/billboard.bmp and /dev/null differ diff --git a/packaging/windows/krita_installer/res/UIExtension/Bitmaps/Billboards/template.bmp b/packaging/windows/krita_installer/res/UIExtension/Bitmaps/Billboards/template.bmp deleted file mode 100644 index c2c8a4eed2..0000000000 Binary files a/packaging/windows/krita_installer/res/UIExtension/Bitmaps/Billboards/template.bmp and /dev/null differ diff --git a/packaging/windows/krita_installer/res/UIExtension/Bitmaps/New.ico b/packaging/windows/krita_installer/res/UIExtension/Bitmaps/New.ico deleted file mode 100644 index 09746fd3e4..0000000000 Binary files a/packaging/windows/krita_installer/res/UIExtension/Bitmaps/New.ico and /dev/null differ diff --git a/packaging/windows/krita_installer/res/UIExtension/Bitmaps/Up.ico b/packaging/windows/krita_installer/res/UIExtension/Bitmaps/Up.ico deleted file mode 100644 index d435630c3b..0000000000 Binary files a/packaging/windows/krita_installer/res/UIExtension/Bitmaps/Up.ico and /dev/null differ diff --git a/packaging/windows/krita_installer/res/UIExtension/Bitmaps/bannrbmp.bmp b/packaging/windows/krita_installer/res/UIExtension/Bitmaps/bannrbmp.bmp deleted file mode 100644 index 6baff46aac..0000000000 Binary files a/packaging/windows/krita_installer/res/UIExtension/Bitmaps/bannrbmp.bmp and /dev/null differ diff --git a/packaging/windows/krita_installer/res/UIExtension/Bitmaps/billboard03.bmp b/packaging/windows/krita_installer/res/UIExtension/Bitmaps/billboard03.bmp deleted file mode 100644 index b9b529c92f..0000000000 Binary files a/packaging/windows/krita_installer/res/UIExtension/Bitmaps/billboard03.bmp and /dev/null differ diff --git a/packaging/windows/krita_installer/res/UIExtension/Bitmaps/dlgbmp.bmp b/packaging/windows/krita_installer/res/UIExtension/Bitmaps/dlgbmp.bmp deleted file mode 100644 index b84bd5c70b..0000000000 Binary files a/packaging/windows/krita_installer/res/UIExtension/Bitmaps/dlgbmp.bmp and /dev/null differ diff --git a/packaging/windows/krita_installer/res/UIExtension/Bitmaps/dlgbmp.bmp.bmp b/packaging/windows/krita_installer/res/UIExtension/Bitmaps/dlgbmp.bmp.bmp deleted file mode 100644 index fe1d64d740..0000000000 Binary files a/packaging/windows/krita_installer/res/UIExtension/Bitmaps/dlgbmp.bmp.bmp and /dev/null differ diff --git a/packaging/windows/krita_installer/res/UIExtension/Bitmaps/exclamic.ico b/packaging/windows/krita_installer/res/UIExtension/Bitmaps/exclamic.ico deleted file mode 100644 index 15075116dc..0000000000 Binary files a/packaging/windows/krita_installer/res/UIExtension/Bitmaps/exclamic.ico and /dev/null differ diff --git a/packaging/windows/krita_installer/res/UIExtension/Bitmaps/info.ico b/packaging/windows/krita_installer/res/UIExtension/Bitmaps/info.ico deleted file mode 100644 index 07aa95c892..0000000000 Binary files a/packaging/windows/krita_installer/res/UIExtension/Bitmaps/info.ico and /dev/null differ diff --git a/packaging/windows/krita_installer/res/UIExtension/CustomExitDialog.wxs b/packaging/windows/krita_installer/res/UIExtension/CustomExitDialog.wxs deleted file mode 100644 index 3e4d7fd5d6..0000000000 --- a/packaging/windows/krita_installer/res/UIExtension/CustomExitDialog.wxs +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packaging/windows/krita_installer/res/UIExtension/CustomMaintenanceWelcomeDlg.wxs b/packaging/windows/krita_installer/res/UIExtension/CustomMaintenanceWelcomeDlg.wxs deleted file mode 100644 index 59061f1e49..0000000000 --- a/packaging/windows/krita_installer/res/UIExtension/CustomMaintenanceWelcomeDlg.wxs +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - !(wix.WixUICostingPopupOptOut) OR CostingComplete = 1 - - - 1 - - - - - - - - - - Installed AND NOT RESUME AND NOT Preselected AND NOT PATCH - - - - \ No newline at end of file diff --git a/packaging/windows/krita_installer/res/UIExtension/CustomProgressDlg.wxs b/packaging/windows/krita_installer/res/UIExtension/CustomProgressDlg.wxs deleted file mode 100644 index 9a8b2caa9d..0000000000 --- a/packaging/windows/krita_installer/res/UIExtension/CustomProgressDlg.wxs +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packaging/windows/krita_installer/res/UIExtension/CustomResumeDlg.wxs b/packaging/windows/krita_installer/res/UIExtension/CustomResumeDlg.wxs deleted file mode 100644 index dbe751ce3e..0000000000 --- a/packaging/windows/krita_installer/res/UIExtension/CustomResumeDlg.wxs +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - 1 - - - - - - - - - - Installed AND (RESUME OR Preselected) - - - - \ No newline at end of file diff --git a/packaging/windows/krita_installer/res/UIExtension/CustomUI_Mondo.wxs b/packaging/windows/krita_installer/res/UIExtension/CustomUI_Mondo.wxs deleted file mode 100644 index 6fd946729f..0000000000 --- a/packaging/windows/krita_installer/res/UIExtension/CustomUI_Mondo.wxs +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - 1 - - NOT Installed - Installed AND PATCH - - 1 - LicenseAccepted = "1" - - Installed - NOT Installed - 1 - - NOT Installed OR WixUI_InstallMode = "Change" - Installed AND NOT PATCH - Installed AND PATCH - - 1 - - 1 - 1 - 1 - 1 - - - - - \ No newline at end of file diff --git a/packaging/windows/krita_installer/res/UIExtension/CustomWelcomeDlg.wxs b/packaging/windows/krita_installer/res/UIExtension/CustomWelcomeDlg.wxs deleted file mode 100644 index 0f7ee2e943..0000000000 --- a/packaging/windows/krita_installer/res/UIExtension/CustomWelcomeDlg.wxs +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - Installed AND PATCH - - - 1 - - - - - - NOT Installed OR NOT PATCH - Installed AND PATCH - - - Installed AND PATCH - NOT Installed OR NOT PATCH - - - - - - NOT Installed OR PATCH - - - - \ No newline at end of file diff --git a/packaging/windows/krita_installer/res/UIExtension/VSError.wxs b/packaging/windows/krita_installer/res/UIExtension/VSError.wxs deleted file mode 100644 index f42c7eaab1..0000000000 --- a/packaging/windows/krita_installer/res/UIExtension/VSError.wxs +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - 1 - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packaging/windows/krita_installer/res/UIExtension/gpl-2.0.rtf b/packaging/windows/krita_installer/res/UIExtension/gpl-2.0.rtf deleted file mode 100644 index 902e47edb5..0000000000 --- a/packaging/windows/krita_installer/res/UIExtension/gpl-2.0.rtf +++ /dev/null @@ -1,89 +0,0 @@ -{\rtf1\ansi\ansicpg1252\deff0\deflang2057{\fonttbl{\f0\fnil\fcharset0 Times New Roman;}{\f1\fnil\fprq1\fcharset0 Courier New,courier;}{\f2\fnil\fcharset0 Calibri;}} -{\colortbl ;\red0\green0\blue0;\red0\green0\blue255;} -{\*\generator Msftedit 5.41.21.2510;}\viewkind4\uc1\pard\sb280\sa240\cf1\lang9\b\f0\fs22 GNU GENERAL PUBLIC LICENSE\par -\pard\sb240\sa240\b0 Version 2, June 1991\par -\pard\sb240\f1 Copyright (C) 1989, 1991 Free Software Foundation, Inc. \par -\pard 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\par -\cf0\f2\par -\cf1\f1 Everyone is permitted to copy and distribute verbatim copies\par -\pard\sa240 of this license document, but changing it is not allowed.\par -\pard\b\f0 Preamble\par -\pard\sb240\sa240\b0 The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too.\par -When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.\par -To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.\par -For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.\par -We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.\par -Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.\par -Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.\par -The precise terms and conditions for copying, distribution and modification follow.\par -\pard\b TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\par -\pard\sb240\sa240 0.\b0\~This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".\par -Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.\par -\b 1.\b0\~You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.\par -You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.\par -\b 2.\b0\~You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:\par -\pard\li600\b a)\b0\~You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.\par -\b b)\b0\~You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.\par -\pard\li600\sa160\b c)\b0\~If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)\par -\pard\sb240\sa240 These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.\par -Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.\par -In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.\par -\b 3.\b0\~You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:\par -\pard\li600\b a)\b0\~Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,\par -\b b)\b0\~Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,\par -\pard\li600\sa160\b c)\b0\~Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)\par -\pard\sb240\sa240 The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.\par -If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.\par -\b 4.\b0\~You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.\par -\b 5.\b0\~You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.\par -\b 6.\b0\~Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.\par -\b 7.\b0\~If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.\par -If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.\par -It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.\par -This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.\par -\b 8.\b0\~If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.\par -\b 9.\b0\~The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.\par -Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.\par -\b 10.\b0\~If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.\par -\pard\sb240\sa240\b NO WARRANTY\par -\pard\sb240\sa240 11.\b0\~BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\par -\b 12.\b0\~IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\par -\pard\sb280\sa240\b END OF TERMS AND CONDITIONS\par -\pard How to Apply These Terms to Your New Programs\par -\pard\sb240\sa240\b0 If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.\par -To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.\par -\pard\sb240\i\f1 one line to give the program's name and an idea of what it does.\par -\pard\i0 Copyright (C) \i yyyy\i0 \i name of author\par -\cf0\i0\f2\par -\pard\cf1\f1 This program is free software; you can redistribute it and/or\par -\pard modify it under the terms of the GNU General Public License\par -as published by the Free Software Foundation; either version 2\par -of the License, or (at your option) any later version.\par -\cf0\f2\par -\cf1\f1 This program is distributed in the hope that it will be useful,\par -but WITHOUT ANY WARRANTY; without even the implied warranty of\par -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\par -GNU General Public License for more details.\par -\cf0\f2\par -\cf1\f1 You should have received a copy of the GNU General Public License\par -along with this program; if not, write to the Free Software\par -\pard\sa240 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\par -\pard\sb240\sa240\f0 Also add information on how to contact you by electronic and paper mail.\par -If the program is interactive, make it output a short notice like this when it starts in an interactive mode:\par -\pard\sb240\f1 Gnomovision version 69, Copyright (C) \i year\i0 \i name of author\par -\pard\i0 Gnomovision comes with ABSOLUTELY NO WARRANTY; for details\par -\pard type `show w'. This is free software, and you are welcome\par -to redistribute it under certain conditions; type `show c' \par -\pard\sa240 for details.\par -\pard\sb240\sa240\f0 The hypothetical commands\~\f1 `show w'\f0\~and\~\f1 `show c'\f0\~should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than\~\f1 `show w'\f0\~and\~\f1 `show c'\f0 ; they could even be mouse-clicks or menu items--whatever suits your program.\par -You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:\par -\pard\sb240\f1 Yoyodyne, Inc., hereby disclaims all copyright\par -\pard interest in the program `Gnomovision'\par -(which makes passes at compilers) written \par -by James Hacker.\par -\cf0\f2\par -\cf1\i\f1 signature of Ty Coon\i0 , 1 April 1989\par -\pard\sa240 Ty Coon, President of Vice\par -\pard\sb240\sa240\f0 This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the\~{\field{\*\fldinst{HYPERLINK "http://www.gnu.org/licenses/lgpl.html"}}{\fldrslt{\ul GNU Lesser General Public License}}}\ulnone\f0\fs22\~instead of this License.\par -} - diff --git a/packaging/windows/krita_installer/res/package/env.bat b/packaging/windows/krita_installer/res/package/env.bat deleted file mode 100644 index da61f25080..0000000000 --- a/packaging/windows/krita_installer/res/package/env.bat +++ /dev/null @@ -1,12 +0,0 @@ -:: Set Calligra variables -:: -@echo off -set KDEHOME=%appdata%\krita -set KDESYCOCA=%~dp0sycoca -set XDG_DATA_DIRS=%~dp0share -set KDEDIRS=%~dp0 -set KDEDIR=%~dp0 -set PATH=%~dp0bin;%~dp0lib;%~dp0lib\krita;%PATH% -set QT_PLUGIN_PATH=%~dp0lib -:: Launch application and all additional parameters -%* diff --git a/packaging/windows/krita_installer/res/wix_snippets/BundleVC2015x64.wxs b/packaging/windows/krita_installer/res/wix_snippets/BundleVC2015x64.wxs deleted file mode 100644 index c453944538..0000000000 --- a/packaging/windows/krita_installer/res/wix_snippets/BundleVC2015x64.wxs +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/packaging/windows/krita_installer/res/wix_snippets/FindVC2015ia64.wxi b/packaging/windows/krita_installer/res/wix_snippets/FindVC2015ia64.wxi deleted file mode 100644 index 0a8623490c..0000000000 --- a/packaging/windows/krita_installer/res/wix_snippets/FindVC2015ia64.wxi +++ /dev/null @@ -1,24 +0,0 @@ - - - - 4 - - - - Installed OR (HASVCPP2015) - diff --git a/packaging/windows/krita_installer/res/wix_snippets/FindVC2015x64.wxi b/packaging/windows/krita_installer/res/wix_snippets/FindVC2015x64.wxi deleted file mode 100644 index d7eaf5c0c1..0000000000 --- a/packaging/windows/krita_installer/res/wix_snippets/FindVC2015x64.wxi +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - Installed OR (HASVCPP2010) - diff --git a/packaging/windows/krita_installer/res/wix_snippets/FindVC2015x86.wxi b/packaging/windows/krita_installer/res/wix_snippets/FindVC2015x86.wxi deleted file mode 100644 index 1a62970615..0000000000 --- a/packaging/windows/krita_installer/res/wix_snippets/FindVC2015x86.wxi +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - Installed OR (HASVCPP2015) - diff --git a/packaging/windows/krita_installer/res/wix_snippets/MergeModules_x64.wxi b/packaging/windows/krita_installer/res/wix_snippets/MergeModules_x64.wxi deleted file mode 100644 index ee6e9c9998..0000000000 --- a/packaging/windows/krita_installer/res/wix_snippets/MergeModules_x64.wxi +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - diff --git a/packaging/windows/krita_installer/res/wix_snippets/MergeModules_x86.wxi b/packaging/windows/krita_installer/res/wix_snippets/MergeModules_x86.wxi deleted file mode 100644 index a88871e87d..0000000000 --- a/packaging/windows/krita_installer/res/wix_snippets/MergeModules_x86.wxi +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - diff --git a/packaging/windows/krita_installer/res/wix_snippets/VC2015DLLs.wxi b/packaging/windows/krita_installer/res/wix_snippets/VC2015DLLs.wxi deleted file mode 100644 index a9b67d8ecf..0000000000 --- a/packaging/windows/krita_installer/res/wix_snippets/VC2015DLLs.wxi +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/packaging/windows/krita_installer/run_heat.bat b/packaging/windows/krita_installer/run_heat.bat deleted file mode 100644 index bfae72308b..0000000000 --- a/packaging/windows/krita_installer/run_heat.bat +++ /dev/null @@ -1,13 +0,0 @@ -:: Copyright (c) 2011-2012 KO GmbH. All rights reserved. -:: Copyright (c) 2011-2012 Stuart Dickson -:: -:: The use and distribution terms for this software are covered by the -:: Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) -:: which can be found in the file CPL.TXT at the root of this distribution. -:: By using this software in any fashion, you are agreeing to be bound by -:: the terms of this license. -:: -:: You must not remove this notice, or any other, from this software. -:: ------------------------------------------------------------------------ -"m:\package\wix36\heat.exe" dir "%C2WINSTALL_INPUT%" -cg CalligraBaseFiles -gg -scom -sreg -sfrag -srd -dr CALLIGRADIR -var env.C2WINSTALL_INPUT -out "%~dp0HeatFragment.wxs" -copy HeatFragment.wxs PreviousHeatFragment.xml /Y diff --git a/packaging/windows/makepkg.bat b/packaging/windows/makepkg.bat deleted file mode 100644 index 9da38fc3f0..0000000000 --- a/packaging/windows/makepkg.bat +++ /dev/null @@ -1,77 +0,0 @@ -:: 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. -:: -:: TODO: Ask if the user want to make an archive and with which tool -:: TODO: Maybe ask for a custom install folder name? - -@echo off - -if not exist i\ ( -echo Cannot find the install folder! -pause -exit /B -) - -set /P pkg_root=Insert krita package name: - -if [%pkg_root%] == [] ( -echo You entered an empty name! -pause -exit /B -) - -:: Initial folder setup -mkdir %pkg_root% -mkdir %pkg_root%\bin -mkdir %pkg_root%\lib -mkdir %pkg_root%\share - -:: Bin folder -copy i\bin\krita.exe %pkg_root%\bin -copy i\bin\*.dll %pkg_root%\bin -copy i\lib\*.dll %pkg_root%\bin -xcopy /S /Y /I i\lib\plugins\imageformats %pkg_root%\bin\imageformats -xcopy /S /Y /I i\lib\plugins\kf5 %pkg_root%\bin\kf5 -xcopy /S /Y /I i\plugins\platforms\qwindows.dll %pkg_root%\bin\platforms\ -xcopy /Y i\plugins\iconengines\*.dll %pkg_root%\bin\iconengines\ - -:: Translations -mkdir %pkg_root%\bin\translations -copy i\translations\qt_ca.qm %pkg_root%\bin\translations\qt_ca.qm -copy i\translations\qt_cs.qm %pkg_root%\bin\translations\qt_cs.qm -copy i\translations\qt_de.qm %pkg_root%\bin\translations\qt_de.qm -copy i\translations\qt_en.qm %pkg_root%\bin\translations\qt_en.qm -copy i\translations\qt_fi.qm %pkg_root%\bin\translations\qt_fi.qm -copy i\translations\qt_he.qm %pkg_root%\bin\translations\qt_hu.qm -copy i\translations\qt_it.qm %pkg_root%\bin\translations\qt_it.qm -copy i\translations\qt_ja.qm %pkg_root%\bin\translations\qt_ja.qm -copy i\translations\qt_ko.qm %pkg_root%\bin\translations\qt_ko.qm -copy i\translations\qt_lv.qm %pkg_root%\bin\translations\qt_lv.qm -copy i\translations\qt_ru.qm %pkg_root%\bin\translations\qt_ru.qm -copy i\translations\qt_sk.qm %pkg_root%\bin\translations\qt_sk.qm -copy i\translations\qt_uk.qm %pkg_root%\bin\translations\qt_uk.qm -copy i\translations\qt_fr.qm %pkg_root%\bin\translations\qt_fr.qm - -:: Lib -xcopy /Y i\lib\kritaplugins\*.dll %pkg_root%\lib\kritaplugins\ - -:: Share -xcopy /Y /S /I i\share\appdata %pkg_root%\share\appdata -xcopy /Y /S /I i\share\applications %pkg_root%\share\applications -xcopy /Y /S /I i\share\color %pkg_root%\share\color -xcopy /Y /S /I i\share\color-schemes %pkg_root%\share\color-schemes -xcopy /Y /S /I i\share\doc %pkg_root%\share\doc -xcopy /Y /S /I i\share\icons %pkg_root%\share\icons -xcopy /Y /S /I i\share\kf5 %pkg_root%\share\kf5 -xcopy /Y /S /I i\share\krita %pkg_root%\share\krita -xcopy /Y /S /I i\share\kritaplugins %pkg_root%\share\kritaplugins -xcopy /Y /S /I i\share\kservices5 %pkg_root%\share\kservices5 -xcopy /Y /S /I i\share\locale %pkg_root%\share\locale -xcopy /Y /S /I i\share\man %pkg_root%\share\man -xcopy /Y /S /I i\share\mime %pkg_root%\share\mime -xcopy /Y /S /I i\share\ocio %pkg_root%\share\ocio - -::Link -mklink %pkg_root%\krita.exe bin\krita.exe diff --git a/packaging/windows/package-complete.cmd b/packaging/windows/package-complete.cmd index 44af24301e..8a2a9798d6 100644 --- a/packaging/windows/package-complete.cmd +++ b/packaging/windows/package-complete.cmd @@ -1,772 +1,777 @@ @echo off :: This batch script is meant to prepare a Krita package folder to be zipped or :: to be a base for the installer. :: -------- setlocal enabledelayedexpansion goto begin :: Subroutines :find_on_path out_variable file_name set %1=%~f$PATH:2 goto :EOF :get_dir_path out_variable file_path set %1=%~dp2 goto :EOF :get_full_path out_variable file_path setlocal set FULL_PATH=%~f2 if not exist "%FULL_PATH%" ( set FULL_PATH= ) else ( if exist "%FULL_PATH%\" ( set FULL_PATH= ) ) endlocal & set "%1=%FULL_PATH%" goto :EOF :get_full_path_dir out_variable file_path setlocal set FULL_PATH=%~dp2 if not exist "%FULL_PATH%" ( set FULL_PATH= ) endlocal & set "%1=%FULL_PATH%" goto :EOF :prompt_for_string out_variable prompt set /p %1=%~2^> goto :EOF :prompt_for_positive_integer out_variable prompt setlocal call :prompt_for_string USER_INPUT "%~2" if "%USER_INPUT%" == "" set USER_INPUT=0 set /a RESULT=%USER_INPUT% if not %RESULT% GTR 0 ( set RESULT= ) endlocal & set "%1=%RESULT%" goto :EOF :prompt_for_file out_variable prompt setlocal :prompt_for_file__retry call :prompt_for_string USER_INPUT "%~2" if "%USER_INPUT%" == "" ( endlocal set %1= goto :EOF ) call :get_full_path RESULT "%USER_INPUT%" if "%RESULT%" == "" ( echo Input does not point to valid file! set USER_INPUT= goto prompt_for_file__retry ) endlocal & set "%1=%RESULT%" goto :EOF :prompt_for_dir out_variable prompt setlocal :prompt_for_dir__retry call :prompt_for_string USER_INPUT "%~2" if "%USER_INPUT%" == "" ( endlocal set %1= goto :EOF ) call :get_full_path_dir RESULT "%USER_INPUT%\" if "%RESULT%" == "" ( echo Input does not point to valid dir! set USER_INPUT= goto prompt_for_dir__retry ) endlocal & set "%1=%RESULT%" goto :EOF :usage echo Usage: echo %~n0 [--no-interactive --package-name ^] [ OPTIONS ... ] echo. echo Basic options: echo --no-interactive Run without interactive prompts echo When not specified, the script will prompt echo for some of the parameters. echo --package-name ^ Specify the package name echo. echo Path options: echo --src-dir ^ Specify Krita source dir echo If unspecified, this will be determined from echo the script location echo --deps-install-dir ^ Specify deps install dir echo --krita-install-dir ^ Specify Krita install dir echo. echo Special options: echo --pre-zip-hook ^ Specify a script to be called before echo packaging the zip archive, can be used to echo sign the binaries echo. goto :EOF :usage_and_exit call :usage exit /b :usage_and_fail call :usage exit /b 100 :: ---------------------------- :begin echo Krita Windows packaging script echo. :: command-line args parsing set ARG_NO_INTERACTIVE= set ARG_PACKAGE_NAME= set ARG_SRC_DIR= set ARG_DEPS_INSTALL_DIR= set ARG_KRITA_INSTALL_DIR= set ARG_PRE_ZIP_HOOK= :args_parsing_loop set CURRENT_MATCHED= if not "%1" == "" ( if "%1" == "--no-interactive" ( set ARG_NO_INTERACTIVE=1 set CURRENT_MATCHED=1 ) if "%1" == "--package-name" ( if not "%ARG_PACKAGE_NAME%" == "" ( echo ERROR: Arg --package-name specified more than once 1>&2 echo. goto usage_and_fail ) if "%~2" == "" ( echo ERROR: Arg --package-name is empty 1>&2 echo. goto usage_and_fail ) set "ARG_PACKAGE_NAME=%~2" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--src-dir" ( if not "%ARG_SRC_DIR%" == "" ( echo ERROR: Arg --src-dir specified more than once 1>&2 echo. goto usage_and_fail ) if not exist "%~f2\" ( echo ERROR: Arg --src-dir does not point to a directory 1>&2 echo. goto usage_and_fail ) call :get_dir_path ARG_SRC_DIR "%~f2\" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--deps-install-dir" ( if not "%ARG_DEPS_INSTALL_DIR%" == "" ( echo ERROR: Arg --deps-install-dir specified more than once 1>&2 echo. goto usage_and_fail ) if "%~f2" == "" ( echo ERROR: Arg --deps-install-dir does not point to a valid path 1>&2 echo. goto usage_and_fail ) call :get_dir_path ARG_DEPS_INSTALL_DIR "%~f2\" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--krita-install-dir" ( if not "%ARG_KRITA_INSTALL_DIR%" == "" ( echo ERROR: Arg --krita-install-dir specified more than once 1>&2 echo. goto usage_and_fail ) if "%~f2" == "" ( echo ERROR: Arg --krita-install-dir does not point to a valid path 1>&2 echo. goto usage_and_fail ) call :get_dir_path ARG_KRITA_INSTALL_DIR "%~f2\" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--pre-zip-hook" ( if not "%ARG_PRE_ZIP_HOOK%" == "" ( echo ERROR: Arg --pre-zip-hook specified more than once 1>&2 echo. goto usage_and_fail ) if "%~f2" == "" ( echo ERROR: Arg --pre-zip-hook does not point to a valid path 1>&2 echo. goto usage_and_fail ) call :get_full_path ARG_PRE_ZIP_HOOK "%~f2" if "!ARG_PRE_ZIP_HOOK!" == "" ( echo ERROR: Arg --pre-zip-hook does not point to a valid file 1>&2 echo. goto usage_and_fail ) shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--help" ( goto usage_and_exit ) if not "!CURRENT_MATCHED!" == "1" ( echo ERROR: Unknown option %1 1>&2 echo. goto usage_and_fail ) shift /1 goto args_parsing_loop ) if "%ARG_NO_INTERACTIVE%" == "1" ( if "%ARG_PACKAGE_NAME%" == "" ( echo ERROR: Required arg --package-name not specified! 1>&2 echo. goto usage_and_fail ) ) if "%ARG_NO_INTERACTIVE%" == "1" ( echo Non-interactive mode ) else ( echo Interactive mode :: Trick to pause on exit call :real_begin pause exit /b !ERRORLEVEL! ) :real_begin echo. if "%ARG_PACKAGE_NAME%" == "" ( call :prompt_for_string ARG_PACKAGE_NAME "Provide package name" if "ARG_PACKAGE_NAME" == "" ( echo ERROR: Package name not set! 1>&2 exit /b 102 ) ) :: Check environment config if "%SEVENZIP_EXE%" == "" ( call :find_on_path SEVENZIP_EXE 7z.exe ) if "%SEVENZIP_EXE%" == "" ( call :find_on_path SEVENZIP_EXE 7za.exe ) if "!SEVENZIP_EXE!" == "" ( set "SEVENZIP_EXE=%ProgramFiles%\7-Zip\7z.exe" if not exist "!SEVENZIP_EXE!" ( set "SEVENZIP_EXE=%ProgramFiles(x86)%\7-Zip\7z.exe" ) if not exist "!SEVENZIP_EXE!" ( echo 7-Zip not found! 1>&2 exit /b 102 ) ) echo 7-Zip: %SEVENZIP_EXE% if "%MINGW_BIN_DIR%" == "" ( call :find_on_path MINGW_BIN_DIR_MAKE_EXE mingw32-make.exe if "!MINGW_BIN_DIR_MAKE_EXE!" == "" ( if not "%ARG_NO_INTERACTIVE%" == "1" ( call :prompt_for_file MINGW_BIN_DIR_MAKE_EXE "Provide path to mingw32-make.exe of mingw-w64" ) if "!MINGW_BIN_DIR_MAKE_EXE!" == "" ( echo ERROR: mingw-w64 not found! 1>&2 exit /b 102 ) call :get_dir_path MINGW_BIN_DIR "!MINGW_BIN_DIR_MAKE_EXE!" ) else ( call :get_dir_path MINGW_BIN_DIR "!MINGW_BIN_DIR_MAKE_EXE!" echo Found mingw on PATH: !MINGW_BIN_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this correct? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_file MINGW_BIN_DIR_MAKE_EXE "Provide path to mingw32-make.exe of mingw-w64" if "!MINGW_BIN_DIR_MAKE_EXE!" == "" ( echo ERROR: mingw-w64 not found! 1>&2 exit /b 102 ) call :get_dir_path MINGW_BIN_DIR "!MINGW_BIN_DIR_MAKE_EXE!" ) ) ) ) echo mingw-w64: %MINGW_BIN_DIR% :: Windows SDK is needed for windeployqt to get d3dcompiler_xx.dll if "%WindowsSdkDir%" == "" if not "%ProgramFiles(x86)%" == "" set "WindowsSdkDir=%ProgramFiles(x86)%\Windows Kits\10" if "%WindowsSdkDir%" == "" set "WindowsSdkDir=%ProgramFiles(x86)%\Windows Kits\10" if exist "%WindowsSdkDir%\" ( pushd "%WindowsSdkDir%" if exist "bin\x64\fxc.exe" ( set HAVE_FXC_EXE=1 ) else ( for /f "delims=" %%a in ('dir /a:d /b "bin\10.*"') do ( if exist "bin\%%a\x64\fxc.exe" ( set HAVE_FXC_EXE=1 ) ) ) popd ) if not "%HAVE_FXC_EXE%" == "1" ( set WindowsSdkDir= echo Windows SDK 10 with fxc.exe not found echo If Qt was built with ANGLE ^(dynamic OpenGL^) support, the package might not work properly on some systems! ) else echo Windows SDK 10 with fxc.exe found on %WindowsSdkDir% if not "%ARG_SRC_DIR%" == "" ( set "KRITA_SRC_DIR=%ARG_SRC_DIR%" ) if "%KRITA_SRC_DIR%" == "" ( :: Check whether this looks like to be in the source tree set "_temp=%~dp0" if "!_temp:~-19!" == "\packaging\windows\" ( if exist "!_temp:~0,-19!\CMakeLists.txt" ( if exist "!_temp:~0,-19!\3rdparty\CMakeLists.txt" ( set "KRITA_SRC_DIR=!_temp:~0,-19!\" echo Script is running inside Krita src dir ) ) ) ) if "%KRITA_SRC_DIR%" == "" ( if not "%ARG_NO_INTERACTIVE%" == "1" ( call :prompt_for_dir KRITA_SRC_DIR "Provide path of Krita src dir" ) if "!KRITA_SRC_DIR!" == "" ( echo ERROR: Krita src dir not found! 1>&2 exit /b 102 ) ) echo Krita src: %KRITA_SRC_DIR% if not "%ARG_DEPS_INSTALL_DIR%" == "" ( set "DEPS_INSTALL_DIR=%ARG_DEPS_INSTALL_DIR%" ) if "%DEPS_INSTALL_DIR%" == "" ( set DEPS_INSTALL_DIR=%CD%\i_deps\ echo Using default deps install dir: !DEPS_INSTALL_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_dir DEPS_INSTALL_DIR "Provide path of deps install dir" ) ) if "!DEPS_INSTALL_DIR!" == "" ( echo ERROR: Deps install dir not set! 1>&2 exit /b 102 ) ) echo Deps install dir: %DEPS_INSTALL_DIR% if not "%ARG_KRITA_INSTALL_DIR%" == "" ( set "KRITA_INSTALL_DIR=%ARG_KRITA_INSTALL_DIR%" ) if "%KRITA_INSTALL_DIR%" == "" ( set KRITA_INSTALL_DIR=%CD%\i\ echo Using default Krita install dir: !KRITA_INSTALL_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_dir KRITA_INSTALL_DIR "Provide path of Krita install dir" ) ) if "!KRITA_INSTALL_DIR!" == "" ( echo ERROR: Krita install dir not set! 1>&2 exit /b 102 ) ) echo Krita install dir: %KRITA_INSTALL_DIR% :: Simple checking if not exist "%DEPS_INSTALL_DIR%\" ( echo ERROR: Cannot find the deps install folder! 1>&2 exit /B 1 ) if not exist "%KRITA_INSTALL_DIR%\" ( echo ERROR: Cannot find the krita install folder! 1>&2 exit /B 1 ) if not "%DEPS_INSTALL_DIR: =%" == "%DEPS_INSTALL_DIR%" ( echo ERROR: Deps install path contains space, which will not work properly! 1>&2 exit /B 1 ) if not "%KRITA_INSTALL_DIR: =%" == "%KRITA_INSTALL_DIR%" ( echo ERROR: Krita install path contains space, which will not work properly! 1>&2 exit /B 1 ) set pkg_name=%ARG_PACKAGE_NAME% 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. exit /B 1 ) if exist %pkg_root%.zip ( echo ERROR: Packaging zip already exists! Please remove or rename it first. exit /B 1 ) if exist %pkg_root%-dbg.zip ( echo ERROR: Packaging debug zip already exists! Please remove or rename it first. exit /B 1 ) echo. if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is the above ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( exit /b 1 ) echo. ) :: Initialize clean PATH set PATH=%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\ set PATH=%MINGW_BIN_DIR%;%DEPS_INSTALL_DIR%\bin;%PATH% echo. echo Trying to guess GCC version... g++ --version > NUL if errorlevel 1 ( echo ERROR: g++ is not working. 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. exit /B 1 ) for /f "delims=, tokens=1" %%a in ('objdump -f %KRITA_INSTALL_DIR%\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. exit /B 1 ) echo. echo Testing for strip... strip --version > NUL if errorlevel 1 ( echo ERROR: strip is not working. 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_BIN_DIR%\lib%%L.dll" %pkg_root%\bin echo. echo Copying files... :: krita.exe copy %KRITA_INSTALL_DIR%\bin\krita.exe %pkg_root%\bin :: kritarunner.exe copy %KRITA_INSTALL_DIR%\bin\kritarunner.exe %pkg_root%\bin :: DLLs from bin/ echo INFO: Copying all DLLs except Qt5* from bin/ setlocal enableextensions enabledelayedexpansion for /f "delims=" %%F in ('dir /b "%KRITA_INSTALL_DIR%\bin\*.dll"') do ( set file=%%F set file=!file:~0,3! if not x!file! == xQt5 copy %KRITA_INSTALL_DIR%\bin\%%F %pkg_root%\bin ) for /f "delims=" %%F in ('dir /b "%DEPS_INSTALL_DIR%\bin\*.dll"') do ( set file=%%F set file=!file:~0,3! if not x!file! == xQt5 copy %DEPS_INSTALL_DIR%\bin\%%F %pkg_root%\bin ) endlocal :: symsrv.yes for Dr. Mingw copy %DEPS_INSTALL_DIR%\bin\symsrv.yes %pkg_root%\bin :: DLLs from lib/ echo INFO: Copying all DLLs from lib/ (deps) copy %DEPS_INSTALL_DIR%\lib\*.dll %pkg_root%\bin :: Boost, there might be more than one leftover but we can't really do much copy %DEPS_INSTALL_DIR%\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 %DEPS_INSTALL_DIR%\lib\plugins\imageformats %pkg_root%\bin\imageformats xcopy /S /Y /I %DEPS_INSTALL_DIR%\plugins\imageformats %pkg_root%\bin\imageformats xcopy /S /Y /I %DEPS_INSTALL_DIR%\lib\plugins\kf5 %pkg_root%\bin\kf5 xcopy /S /Y /I %DEPS_INSTALL_DIR%\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 "%DEPS_INSTALL_DIR%\translations\qt_*.qm"') do ( :: Exclude qt_help_*.qm set temp=%%F set temp2=!temp:_help=! if x!temp2! == x!temp! copy %DEPS_INSTALL_DIR%\translations\!temp! %pkg_root%\bin\translations\!temp! ) endlocal :: Krita plugins xcopy /Y %KRITA_INSTALL_DIR%\lib\kritaplugins\*.dll %pkg_root%\lib\kritaplugins\ xcopy /Y /S /I %DEPS_INSTALL_DIR%\lib\krita-python-libs %pkg_root%\lib\krita-python-libs xcopy /Y /S /I %KRITA_INSTALL_DIR%\lib\krita-python-libs %pkg_root%\lib\krita-python-libs :: Share xcopy /Y /S /I %KRITA_INSTALL_DIR%\share\color %pkg_root%\share\color xcopy /Y /S /I %KRITA_INSTALL_DIR%\share\color-schemes %pkg_root%\share\color-schemes xcopy /Y /S /I %KRITA_INSTALL_DIR%\share\icons %pkg_root%\share\icons xcopy /Y /S /I %KRITA_INSTALL_DIR%\share\krita %pkg_root%\share\krita xcopy /Y /S /I %KRITA_INSTALL_DIR%\share\kritaplugins %pkg_root%\share\kritaplugins xcopy /Y /S /I %DEPS_INSTALL_DIR%\share\kf5 %pkg_root%\share\kf5 xcopy /Y /S /I %DEPS_INSTALL_DIR%\share\mime %pkg_root%\share\mime :: Python libs xcopy /Y /S /I %DEPS_INSTALL_DIR%\share\krita\pykrita %pkg_root%\share\krita\pykrita :: Not useful on Windows it seems rem xcopy /Y /S /I %KRITA_INSTALL_DIR%\share\appdata %pkg_root%\share\appdata rem xcopy /Y /S /I %KRITA_INSTALL_DIR%\share\applications %pkg_root%\share\applications rem xcopy /Y /S /I %DEPS_INSTALL_DIR%\share\doc %pkg_root%\share\doc rem xcopy /Y /S /I %DEPS_INSTALL_DIR%\share\kservices5 %pkg_root%\share\kservices5 rem xcopy /Y /S /I %DEPS_INSTALL_DIR%\share\man %pkg_root%\share\man rem xcopy /Y /S /I %DEPS_INSTALL_DIR%\share\ocio %pkg_root%\share\ocio :: Copy locale to bin xcopy /Y /S /I %KRITA_INSTALL_DIR%\share\locale %pkg_root%\bin\locale xcopy /Y /S /I %DEPS_INSTALL_DIR%\share\locale %pkg_root%\bin\locale :: Copy shortcut link from source (can't create it dynamically) copy %KRITA_SRC_DIR%\packaging\windows\krita.lnk %pkg_root% set "QMLDIR_ARGS=--qmldir %DEPS_INSTALL_DIR%\qml" if exist "%KRITA_INSTALL_DIR%\lib\qml" ( xcopy /Y /S /I %KRITA_INSTALL_DIR%\lib\qml %pkg_root%\bin :: This doesn't really seem to do anything set "QMLDIR_ARGS=%QMLDIR_ARGS% --qmldir %KRITA_INSTALL_DIR%\lib\qml" ) if EXIST "%DEPS_INSTALL_DIR%\bin\gmic_krita_qt.exe" ( copy %DEPS_INSTALL_DIR%\bin\gmic_krita_qt.exe %pkg_root%\bin set "WINDEPLOYQT_GMIC_ARGS=%pkg_root%\bin\gmic_krita_qt.exe" ) :: windeployqt windeployqt.exe %QMLDIR_ARGS% --release -gui -core -concurrent -network -printsupport -svg -xml -multimedia -qml -quick -quickwidgets %pkg_root%\bin\krita.exe %WINDEPLOYQT_GMIC_ARGS% if errorlevel 1 ( echo ERROR: WinDeployQt failed! 1>&2 exit /B 1 ) +:: ffmpeg +copy %DEPS_INSTALL_DIR%\bin\ffmpeg.exe %pkg_root%\bin +copy %DEPS_INSTALL_DIR%\bin\ffmpeg_LICENSE.txt %pkg_root%\bin +copy %DEPS_INSTALL_DIR%\bin\ffmpeg_README.txt %pkg_root%\bin + :: Copy embedded Python xcopy /Y /S /I %DEPS_INSTALL_DIR%\python %pkg_root%\python del /q %pkg_root%\python\python.exe %pkg_root%\python\pythonw.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. setlocal enableextensions enabledelayedexpansion :: Remove Python cache files for /d /r "%pkg_root%" %%F in (__pycache__\) do ( if EXIST "%%F" ( set relpath=%%F set relpath=!relpath:~%pkg_root_len_plus_one%! echo Deleting Python cache !relpath! rmdir /S /Q "%%F" ) ) endlocal 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 setlocal enableextensions enabledelayedexpansion :: Find all Python native modules for /r "%pkg_root%\share\krita\pykrita\" %%F in (*.pyd) do ( set relpath=%%F set relpath=!relpath:~%pkg_root_len_plus_one%! call :split-debug "%%F" !relpath! ) for /r "%pkg_root%\lib\krita-python-libs\" %%F in (*.pyd) do ( set relpath=%%F set relpath=!relpath:~%pkg_root_len_plus_one%! call :split-debug "%%F" !relpath! ) endlocal if not "%ARG_PRE_ZIP_HOOK%" == "" ( echo Running pre-zip-hook... setlocal cmd /c ""%ARG_PRE_ZIP_HOOK%" "%pkg_root%\"" if errorlevel 1 ( echo ERROR: Got exit code !errorlevel! from pre-zip-hook! 1>&2 exit /b 1 ) endlocal ) echo. echo Packaging stripped binaries... "%SEVENZIP_EXE%" a -tzip %pkg_name%.zip %pkg_root%\ "-xr^!.debug" echo -------- 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. 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. Extract from echo %pkg_name%.zip instead, echo and do _not_ run krita inside the extracted directory because it will echo create extra unnecessary files. 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 --strip-debug %~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/color/lcms2engine/LcmsEnginePlugin.cpp b/plugins/color/lcms2engine/LcmsEnginePlugin.cpp index 716463fcec..0d572b5a56 100644 --- a/plugins/color/lcms2engine/LcmsEnginePlugin.cpp +++ b/plugins/color/lcms2engine/LcmsEnginePlugin.cpp @@ -1,308 +1,321 @@ /* * Copyright (c) 2003 Patrick Julien * Copyright (c) 2004,2010 Cyrille Berger * Copyright (c) 2011 Srikanth Tiyyagura * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; 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 "LcmsEnginePlugin.h" #include #include #include #include #include #include #include #include #include "kis_assert.h" #include #include #include #include #include "IccColorSpaceEngine.h" #include "colorprofiles/LcmsColorProfileContainer.h" #include "colorspaces/cmyk_u8/CmykU8ColorSpace.h" #include "colorspaces/cmyk_u16/CmykU16ColorSpace.h" #include "colorspaces/cmyk_f32/CmykF32ColorSpace.h" #include "colorspaces/gray_u8/GrayU8ColorSpace.h" #include "colorspaces/gray_u16/GrayU16ColorSpace.h" #include "colorspaces/gray_f32/GrayF32ColorSpace.h" #include "colorspaces/lab_u8/LabU8ColorSpace.h" #include "colorspaces/lab_u16/LabColorSpace.h" #include "colorspaces/lab_f32/LabF32ColorSpace.h" #include "colorspaces/xyz_u8/XyzU8ColorSpace.h" #include "colorspaces/xyz_u16/XyzU16ColorSpace.h" #include "colorspaces/xyz_f32/XyzF32ColorSpace.h" #include "colorspaces/rgb_u8/RgbU8ColorSpace.h" #include "colorspaces/rgb_u16/RgbU16ColorSpace.h" #include "colorspaces/rgb_f32/RgbF32ColorSpace.h" #include "colorspaces/ycbcr_u8/YCbCrU8ColorSpace.h" #include "colorspaces/ycbcr_u16/YCbCrU16ColorSpace.h" #include "colorspaces/ycbcr_f32/YCbCrF32ColorSpace.h" #include -#ifdef HAVE_OPENEXR -#include -#ifdef HAVE_LCMS24 -#include "colorspaces/gray_f16/GrayF16ColorSpace.h" -#include "colorspaces/xyz_f16/XyzF16ColorSpace.h" -#include "colorspaces/rgb_f16/RgbF16ColorSpace.h" +#ifdef HAVE_OPENEXR +# include +# ifdef HAVE_LCMS24 +# include "colorspaces/gray_f16/GrayF16ColorSpace.h" +# include "colorspaces/xyz_f16/XyzF16ColorSpace.h" +# include "colorspaces/rgb_f16/RgbF16ColorSpace.h" +# endif #endif -#endif void lcms2LogErrorHandlerFunction(cmsContext /*ContextID*/, cmsUInt32Number ErrorCode, const char *Text) { qCritical() << "Lcms2 error: " << ErrorCode << Text; } K_PLUGIN_FACTORY_WITH_JSON(PluginFactory, "kolcmsengine.json", registerPlugin();) LcmsEnginePlugin::LcmsEnginePlugin(QObject *parent, const QVariantList &) : QObject(parent) { // We need all resource paths to be properly initialized via KisApplication, otherwise we will // initialize this instance with lacking color profiles which will cause lookup errors later on. KIS_ASSERT_X(KoResourcePaths::isReady() || (QApplication::instance()->applicationName() != "krita" && QApplication::instance()->applicationName() != "krita.exe"), "LcmsEnginePlugin::LcmsEnginePlugin", "Resource paths are not ready yet."); // Set the lmcs error reporting function cmsSetLogErrorHandler(&lcms2LogErrorHandlerFunction); KoColorSpaceRegistry *registry = KoColorSpaceRegistry::instance(); // Initialise color engine KoColorSpaceEngineRegistry::instance()->add(new IccColorSpaceEngine); QStringList profileFilenames; profileFilenames += KoResourcePaths::findAllResources("icc_profiles", "*.icm", KoResourcePaths::Recursive); profileFilenames += KoResourcePaths::findAllResources("icc_profiles", "*.ICM", KoResourcePaths::Recursive); profileFilenames += KoResourcePaths::findAllResources("icc_profiles", "*.ICC", KoResourcePaths::Recursive); profileFilenames += KoResourcePaths::findAllResources("icc_profiles", "*.icc", KoResourcePaths::Recursive); + + QStringList iccProfileDirs; + +#ifdef Q_OS_MAC + iccProfileDirs.append(QDir::homePath() + "/Library/ColorSync/Profiles/"); + iccProfileDirs.append("/System/Library/ColorSync/Profiles/"); + iccProfileDirs.append("/Library/ColorSync/Profiles/"); +#endif #ifdef Q_OS_WIN - const QString windowsProfilePath("C:/Windows/System32/spool/drivers/color"); - QDir windowsProfileDir(windowsProfilePath); - Q_FOREACH(const QString &entry, windowsProfileDir.entryList(QStringList() << "*.icm" << "*.icc", QDir::NoDotAndDotDot | QDir::Files | QDir::Readable)) { - profileFilenames << windowsProfilePath + "/" + entry; - } + QString winPath = QString::fromUtf8(qgetenv("windir")); + winPath.replace('\\','/'); + iccProfileDirs.append(winPath + "/System32/Spool/Drivers/Color/"); + #endif + Q_FOREACH(const QString &iccProfiledir, iccProfileDirs) { + QDir profileDir(iccProfiledir); + Q_FOREACH(const QString &entry, profileDir.entryList(QStringList() << "*.icm" << "*.icc", QDir::NoDotAndDotDot | QDir::Files | QDir::Readable)) { + profileFilenames << iccProfiledir + "/" + entry; + } + } // Load the profiles if (!profileFilenames.empty()) { for (QStringList::Iterator it = profileFilenames.begin(); it != profileFilenames.end(); ++it) { KoColorProfile *profile = new IccColorProfile(*it); Q_CHECK_PTR(profile); profile->load(); if (profile->valid()) { //qDebug() << "Valid profile : " << profile->fileName() << profile->name(); registry->addProfileToMap(profile); } else { qDebug() << "Invalid profile : " << profile->fileName() << profile->name(); delete profile; } } } // ------------------- LAB --------------------------------- KoColorProfile *labProfile = LcmsColorProfileContainer::createFromLcmsProfile(cmsCreateLab2Profile(0)); registry->addProfile(labProfile); registry->add(new LabU8ColorSpaceFactory()); registry->add(new LabU16ColorSpaceFactory()); registry->add(new LabF32ColorSpaceFactory()); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory - (KoID("LABAU8HISTO", i18n("L*a*b* Histogram")), LABAColorModelID.id(), Integer8BitsColorDepthID.id())); + (KoID("LABAU8HISTO", i18n("L*a*b*/8 Histogram")), LABAColorModelID.id(), Integer8BitsColorDepthID.id())); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory - (KoID("LABAU16HISTO", i18n("L*a*b* Histogram")), LABAColorModelID.id(), Integer16BitsColorDepthID.id())); + (KoID("LABAU16HISTO", i18n("L*a*b*/16 Histogram")), LABAColorModelID.id(), Integer16BitsColorDepthID.id())); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory - (KoID("LABAF32HISTO", i18n("L*a*b* Histogram")), LABAColorModelID.id(), Float32BitsColorDepthID.id())); + (KoID("LABAF32HISTO", i18n("L*a*b*/32 Histogram")), LABAColorModelID.id(), Float32BitsColorDepthID.id())); // ------------------- RGB --------------------------------- KoColorProfile *rgbProfile = LcmsColorProfileContainer::createFromLcmsProfile(cmsCreate_sRGBProfile()); registry->addProfile(rgbProfile); registry->add(new RgbU8ColorSpaceFactory()); registry->add(new RgbU16ColorSpaceFactory()); #ifdef HAVE_LCMS24 #ifdef HAVE_OPENEXR registry->add(new RgbF16ColorSpaceFactory()); #endif #endif registry->add(new RgbF32ColorSpaceFactory()); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory - (KoID("RGBU8HISTO", i18n("RGB8 Histogram")), RGBAColorModelID.id(), Integer8BitsColorDepthID.id())); + (KoID("RGBU8HISTO", i18n("RGBA/8 Histogram")), RGBAColorModelID.id(), Integer8BitsColorDepthID.id())); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory - (KoID("RGBU16HISTO", i18n("RGB16 Histogram")), RGBAColorModelID.id(), Integer16BitsColorDepthID.id())); + (KoID("RGBU16HISTO", i18n("RGBA/16 Histogram")), RGBAColorModelID.id(), Integer16BitsColorDepthID.id())); #ifdef HAVE_LCMS24 #ifdef HAVE_OPENEXR KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory - (KoID("RGBF16HISTO", i18n("RGBF16 Histogram")), RGBAColorModelID.id(), Float16BitsColorDepthID.id())); + (KoID("RGBF16HISTO", i18n("RGBA/F16 Histogram")), RGBAColorModelID.id(), Float16BitsColorDepthID.id())); #endif #endif KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory - (KoID("RGF328HISTO", i18n("RGBF32 Histogram")), RGBAColorModelID.id(), Float32BitsColorDepthID.id())); + (KoID("RGF328HISTO", i18n("RGBA/F32 Histogram")), RGBAColorModelID.id(), Float32BitsColorDepthID.id())); // ------------------- GRAY --------------------------------- cmsToneCurve *Gamma = cmsBuildGamma(0, 2.2); cmsHPROFILE hProfile = cmsCreateGrayProfile(cmsD50_xyY(), Gamma); cmsFreeToneCurve(Gamma); KoColorProfile *defProfile = LcmsColorProfileContainer::createFromLcmsProfile(hProfile); registry->addProfile(defProfile); registry->add(new GrayAU8ColorSpaceFactory()); registry->add(new GrayAU16ColorSpaceFactory()); #ifdef HAVE_LCMS24 #ifdef HAVE_OPENEXR registry->add(new GrayF16ColorSpaceFactory()); #endif #endif registry->add(new GrayF32ColorSpaceFactory()); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory - (KoID("GRAYA8HISTO", i18n("GRAY/Alpha8 Histogram")), GrayAColorModelID.id(), Integer8BitsColorDepthID.id())); + (KoID("GRAYA8HISTO", i18n("GRAY/8 Histogram")), GrayAColorModelID.id(), Integer8BitsColorDepthID.id())); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory - (KoID("GRAYA16HISTO", i18n("GRAY/Alpha16 Histogram")), GrayAColorModelID.id(), Integer16BitsColorDepthID.id())); + (KoID("GRAYA16HISTO", i18n("GRAY/16 Histogram")), GrayAColorModelID.id(), Integer16BitsColorDepthID.id())); #ifdef HAVE_LCMS24 #ifdef HAVE_OPENEXR KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory - (KoID("GRAYF16HISTO", i18n("GRAYF16 Histogram")), GrayAColorModelID.id(), Float16BitsColorDepthID.id())); + (KoID("GRAYF16HISTO", i18n("GRAYF/F16 Histogram")), GrayAColorModelID.id(), Float16BitsColorDepthID.id())); #endif #endif KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory - (KoID("GRAYAF32HISTO", i18n("GRAY/Alpha 32 float Histogram")), GrayAColorModelID.id(), Float32BitsColorDepthID.id())); + (KoID("GRAYAF32HISTO", i18n("GRAY/F32 float Histogram")), GrayAColorModelID.id(), Float32BitsColorDepthID.id())); // ------------------- CMYK --------------------------------- registry->add(new CmykU8ColorSpaceFactory()); registry->add(new CmykU16ColorSpaceFactory()); registry->add(new CmykF32ColorSpaceFactory()); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory - (KoID("CMYK8HISTO", i18n("CMYK8 Histogram")), CMYKAColorModelID.id(), Integer8BitsColorDepthID.id())); + (KoID("CMYK8HISTO", i18n("CMYK/8 Histogram")), CMYKAColorModelID.id(), Integer8BitsColorDepthID.id())); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory - (KoID("CMYK16HISTO", i18n("CMYK16 Histogram")), CMYKAColorModelID.id(), Integer16BitsColorDepthID.id())); + (KoID("CMYK16HISTO", i18n("CMYK/16 Histogram")), CMYKAColorModelID.id(), Integer16BitsColorDepthID.id())); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory - (KoID("CMYKF32HISTO", i18n("CMYK F32 Histogram")), CMYKAColorModelID.id(), Float32BitsColorDepthID.id())); + (KoID("CMYKF32HISTO", i18n("CMYK/F32 Histogram")), CMYKAColorModelID.id(), Float32BitsColorDepthID.id())); // ------------------- XYZ --------------------------------- KoColorProfile *xyzProfile = LcmsColorProfileContainer::createFromLcmsProfile(cmsCreateXYZProfile()); registry->addProfile(xyzProfile); registry->add(new XyzU8ColorSpaceFactory()); registry->add(new XyzU16ColorSpaceFactory()); #ifdef HAVE_LCMS24 #ifdef HAVE_OPENEXR registry->add(new XyzF16ColorSpaceFactory()); #endif #endif registry->add(new XyzF32ColorSpaceFactory()); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory - (KoID("XYZ8HISTO", i18n("XYZ8 Histogram")), XYZAColorModelID.id(), Integer8BitsColorDepthID.id())); + (KoID("XYZ8HISTO", i18n("XYZ/8 Histogram")), XYZAColorModelID.id(), Integer8BitsColorDepthID.id())); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory - (KoID("XYZ16HISTO", i18n("XYZ16 Histogram")), XYZAColorModelID.id(), Integer16BitsColorDepthID.id())); + (KoID("XYZ16HISTO", i18n("XYZ/16 Histogram")), XYZAColorModelID.id(), Integer16BitsColorDepthID.id())); #ifdef HAVE_LCMS24 #ifdef HAVE_OPENEXR KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory - (KoID("XYZF16HISTO", i18n("XYZF16 Histogram")), XYZAColorModelID.id(), Float16BitsColorDepthID.id())); + (KoID("XYZF16HISTO", i18n("XYZ/F16 Histogram")), XYZAColorModelID.id(), Float16BitsColorDepthID.id())); #endif #endif KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("XYZF32HISTO", i18n("XYZF32 Histogram")), XYZAColorModelID.id(), Float32BitsColorDepthID.id())); // ------------------- YCBCR --------------------------------- // KoColorProfile *yCbCrProfile = LcmsColorProfileContainer::createFromLcmsProfile(cmsCreateYCBCRProfile()); // registry->addProfile(yCbCrProfile); registry->add(new YCbCrU8ColorSpaceFactory()); registry->add(new YCbCrU16ColorSpaceFactory()); registry->add(new YCbCrF32ColorSpaceFactory()); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory - (KoID("YCBCR8HISTO", i18n("YCBCR8 Histogram")), YCbCrAColorModelID.id(), Integer8BitsColorDepthID.id())); + (KoID("YCBCR8HISTO", i18n("YCbCr/8 Histogram")), YCbCrAColorModelID.id(), Integer8BitsColorDepthID.id())); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory - (KoID("YCBCR16HISTO", i18n("YCBCR16 Histogram")), YCbCrAColorModelID.id(), Integer16BitsColorDepthID.id())); + (KoID("YCBCR16HISTO", i18n("YCbCr/16 Histogram")), YCbCrAColorModelID.id(), Integer16BitsColorDepthID.id())); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory - (KoID("YCBCRF32HISTO", i18n("YCBCRF32 Histogram")), YCbCrAColorModelID.id(), Float32BitsColorDepthID.id())); + (KoID("YCBCRF32HISTO", i18n("YCbCr/F32 Histogram")), YCbCrAColorModelID.id(), Float32BitsColorDepthID.id())); // Add profile alias for default profile from lcms1 registry->addProfileAlias("sRGB built-in - (lcms internal)", "sRGB built-in"); registry->addProfileAlias("gray built-in - (lcms internal)", "gray built-in"); registry->addProfileAlias("Lab identity built-in - (lcms internal)", "Lab identity built-in"); registry->addProfileAlias("XYZ built-in - (lcms internal)", "XYZ identity built-in"); } #include diff --git a/plugins/color/lcms2engine/compositeops/RgbCompositeOpValue.h b/plugins/color/lcms2engine/compositeops/RgbCompositeOpValue.h index bb05fa011d..fc1426ad9d 100644 --- a/plugins/color/lcms2engine/compositeops/RgbCompositeOpValue.h +++ b/plugins/color/lcms2engine/compositeops/RgbCompositeOpValue.h @@ -1,138 +1,138 @@ /* * Copyright (c) 2006 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY const; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program const; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef RGBCOMPOSITEOPVALUE_H #define RGBCOMPOSITEOPVALUE_H #include "KoColorConversions.h" #include #define SCALE_TO_FLOAT( v ) KoColorSpaceMaths< channels_type, float>::scaleToA( v ) #define SCALE_FROM_FLOAT( v ) KoColorSpaceMaths< float, channels_type>::scaleToA( v ) template class RgbCompositeOpValue: public KoCompositeOp { typedef typename _CSTraits::channels_type channels_type; typedef typename KoColorSpaceMathsTraits::compositetype compositetype; public: RgbCompositeOpValue(KoColorSpace *cs) - : KoCompositeOp(cs, COMPOSITE_VALUE, i18n("Value"), "") + : KoCompositeOp(cs, COMPOSITE_VALUE, i18nc("HSV Value","Value"), "") { } using KoCompositeOp::composite; void composite(quint8 *dstRowStart, qint32 dstRowStride, const quint8 *srcRowStart, qint32 srcRowStride, const quint8 *maskRowStart, qint32 maskRowStride, qint32 rows, qint32 numColumns, quint8 opacity, const QBitArray &channelFlags) const override { while (rows > 0) { const quint8 *mask = maskRowStart; const channels_type *src = reinterpret_cast(srcRowStart); channels_type *dst = reinterpret_cast(dstRowStart); for (int i = numColumns; i > 0; --i) { channels_type srcAlpha = src[_CSTraits::alpha_pos]; channels_type dstAlpha = dst[_CSTraits::alpha_pos]; srcAlpha = qMin(srcAlpha, dstAlpha); // apply the alphamask if (mask != 0) { if (*mask != OPACITY_OPAQUE_U8) { channels_type tmpOpacity = KoColorSpaceMaths::scaleToA(*mask); srcAlpha = KoColorSpaceMaths::multiply(srcAlpha, tmpOpacity); } mask++; } if (srcAlpha != NATIVE_OPACITY_TRANSPARENT) { if (opacity != OPACITY_OPAQUE_U8) { channels_type tmpOpacity = KoColorSpaceMaths::scaleToA(opacity); srcAlpha = KoColorSpaceMaths::multiply(src[_CSTraits::alpha_pos], tmpOpacity); } channels_type srcBlend; if (dstAlpha == NATIVE_OPACITY_OPAQUE) { srcBlend = srcAlpha; } else { channels_type newAlpha = dstAlpha + KoColorSpaceMaths::multiply(NATIVE_OPACITY_OPAQUE - dstAlpha, srcAlpha); dst[_CSTraits::alpha_pos] = newAlpha; if (newAlpha != 0) { srcBlend = KoColorSpaceMaths::divide(srcAlpha, newAlpha); } else { srcBlend = srcAlpha; } } float dstRed = SCALE_TO_FLOAT(dst[_CSTraits::red_pos]); float dstGreen = SCALE_TO_FLOAT(dst[_CSTraits::green_pos]); float dstBlue = SCALE_TO_FLOAT(dst[_CSTraits::blue_pos]); float srcHue; float srcSaturation; float srcValue; float dstHue; float dstSaturation; float dstValue; float srcRed = SCALE_TO_FLOAT(src[_CSTraits::red_pos]); float srcGreen = SCALE_TO_FLOAT(src[_CSTraits::green_pos]); float srcBlue = SCALE_TO_FLOAT(src[_CSTraits::blue_pos]); RGBToHSV(srcRed, srcGreen, srcBlue, &srcHue, &srcSaturation, &srcValue); RGBToHSV(dstRed, dstGreen, dstBlue, &dstHue, &dstSaturation, &dstValue); HSVToRGB(dstHue, dstSaturation, srcValue, &srcRed, &srcGreen, &srcBlue); if (channelFlags.isEmpty() || channelFlags.testBit(_CSTraits::red_pos)) { dst[_CSTraits::red_pos] = KoColorSpaceMaths::blend(SCALE_FROM_FLOAT(srcRed), SCALE_FROM_FLOAT(dstRed), srcBlend); } if (channelFlags.isEmpty() || channelFlags.testBit(_CSTraits::green_pos)) { dst[_CSTraits::green_pos] = KoColorSpaceMaths::blend(SCALE_FROM_FLOAT(srcGreen), SCALE_FROM_FLOAT(dstGreen), srcBlend); } if (channelFlags.isEmpty() || channelFlags.testBit(_CSTraits::blue_pos)) { dst[_CSTraits::blue_pos] = KoColorSpaceMaths::blend(SCALE_FROM_FLOAT(srcBlue), SCALE_FROM_FLOAT(dstBlue), srcBlend); } } src += _CSTraits::channels_nb; dst += _CSTraits::channels_nb; } rows--; srcRowStart += srcRowStride; dstRowStart += dstRowStride; if (maskRowStart) { maskRowStart += maskRowStride; } } } }; #endif diff --git a/plugins/dockers/CMakeLists.txt b/plugins/dockers/CMakeLists.txt index d9cd652821..6dd261fcca 100644 --- a/plugins/dockers/CMakeLists.txt +++ b/plugins/dockers/CMakeLists.txt @@ -1,32 +1,32 @@ add_subdirectory(defaultdockers) add_subdirectory(smallcolorselector) add_subdirectory(specificcolorselector) add_subdirectory(digitalmixer) add_subdirectory(advancedcolorselector) add_subdirectory(presetdocker) add_subdirectory(historydocker) add_subdirectory(channeldocker) add_subdirectory(artisticcolorselector) add_subdirectory(tasksetdocker) add_subdirectory(compositiondocker) add_subdirectory(patterndocker) add_subdirectory(griddocker) add_subdirectory(arrangedocker) if(HAVE_OCIO) add_subdirectory(lut) endif() add_subdirectory(overview) add_subdirectory(palettedocker) add_subdirectory(colorslider) add_subdirectory(animation) add_subdirectory(presethistory) add_subdirectory(svgcollectiondocker) add_subdirectory(histogram) -if(HAVE_QT_QUICK) +if(NOT APPLE AND HAVE_QT_QUICK) add_subdirectory(touchdocker) option(ENABLE_CPU_THROTTLE "Build the CPU Throttle Docker" OFF) if (ENABLE_CPU_THROTTLE) add_subdirectory(throttle) endif() endif() diff --git a/plugins/dockers/animation/CMakeLists.txt b/plugins/dockers/animation/CMakeLists.txt index dac45e43ba..a0b4b4beb4 100644 --- a/plugins/dockers/animation/CMakeLists.txt +++ b/plugins/dockers/animation/CMakeLists.txt @@ -1,55 +1,56 @@ if (NOT WIN32 AND NOT APPLE) add_subdirectory(tests) endif() set(KRITA_ANIMATIONDOCKER_SOURCES animation_dockers.cpp animation_docker.cpp timeline_docker.cpp onion_skins_docker.cpp timeline_layers_header.cpp timeline_ruler_header.cpp kis_time_based_item_model.cpp timeline_frames_model.cpp timeline_frames_view.cpp timeline_frames_item_delegate.cpp timeline_frames_index_converter.cpp timeline_node_list_keeper.cpp timeline_color_scheme.cpp + timeline_insert_keyframe_dialog.cpp kis_draggable_tool_button.cpp kis_zoom_button.cpp kis_animation_utils.cpp kis_custom_modifiers_catcher.cpp kis_equalizer_column.cpp kis_equalizer_slider.cpp kis_equalizer_button.cpp kis_equalizer_widget.cpp kis_animation_curve_docker.cpp kis_animation_curves_model.cpp kis_animation_curves_view.cpp kis_animation_curves_value_ruler.cpp kis_animation_curves_keyframe_delegate.cpp kis_animation_curve_channel_list_model.cpp kis_animation_curve_channel_list_delegate.cpp ) ki18n_wrap_ui(KRITA_ANIMATIONDOCKER_SOURCES wdg_animation.ui onion_skins_docker.ui wdg_animation_curves.ui ) add_library(kritaanimationdocker MODULE ${KRITA_ANIMATIONDOCKER_SOURCES}) generate_export_header(kritaanimationdocker BASE_NAME kritaanimationdocker EXPORT_MACRO_NAME KRITAANIMATIONDOCKER_EXPORT) target_link_libraries(kritaanimationdocker kritaui kritawidgets) install(TARGETS kritaanimationdocker DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) diff --git a/plugins/dockers/animation/kis_time_based_item_model.cpp b/plugins/dockers/animation/kis_time_based_item_model.cpp index 46aad05124..994a064e64 100644 --- a/plugins/dockers/animation/kis_time_based_item_model.cpp +++ b/plugins/dockers/animation/kis_time_based_item_model.cpp @@ -1,511 +1,511 @@ /* * 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" #include "kis_processing_applicator.h" #include "KisImageBarrierLockerWithFeedback.h" #include "commands_new/kis_switch_current_time_command.h" #include "kis_command_utils.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 { auto imageSP = image.toStrongRef(); if (!imageSP) return 0; KisImageAnimationInterface *i = imageSP->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.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) { beginResetModel(); endResetModel(); } } 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; { KisImageBarrierLockerWithFeedback locker(m_d->image); Q_FOREACH (const QModelIndex &index, indexes) { int time = index.column(); Q_FOREACH(KisKeyframeChannel *channel, channelsAt(index)) { if (channel->keyframeAt(time)) { frameItems << KisAnimationUtils::FrameItem(channel->node(), channel->id(), index.column()); } } } } if (frameItems.isEmpty()) return false; KisAnimationUtils::removeKeyframes(m_d->image, frameItems); return true; } KUndo2Command* KisTimeBasedItemModel::createOffsetFramesCommand(QModelIndexList srcIndexes, const QPoint &offset, bool copyFrames, KUndo2Command *parentCommand, bool moveEmptyFrames) { if (srcIndexes.isEmpty()) return 0; if (offset.isNull()) return 0; KisAnimationUtils::sortPointsForSafeMove(&srcIndexes, offset); KisAnimationUtils::FrameItemList srcFrameItems; KisAnimationUtils::FrameItemList dstFrameItems; 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 0; } Q_FOREACH(KisKeyframeChannel *channel, channelsAt(srcIndex)) { - if (moveEmptyFrames || channel->keyframeAt(srcIndex.column())) { + if (moveEmptyFrames || channel->keyframeAt(srcIndex.column())) { srcFrameItems << KisAnimationUtils::FrameItem(srcNode, channel->id(), srcIndex.column()); dstFrameItems << KisAnimationUtils::FrameItem(dstNode, channel->id(), dstIndex.column()); } } } KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(srcFrameItems.size() == dstFrameItems.size(), 0); if (srcFrameItems.isEmpty()) return 0; return KisAnimationUtils::createMoveKeyframesCommand(srcFrameItems, dstFrameItems, copyFrames, parentCommand); } bool KisTimeBasedItemModel::removeFramesAndOffset(QModelIndexList indexes) { if (indexes.isEmpty()) return true; std::sort(indexes.begin(), indexes.end(), [] (const QModelIndex &lhs, const QModelIndex &rhs) { return lhs.column() > rhs.column(); }); const int minColumn = indexes.last().column(); KUndo2Command *parentCommand = new KUndo2Command(kundo2_i18np("Remove frame and shift", "Remove %1 frames and shift", indexes.size())); { KisImageBarrierLockerWithFeedback locker(m_d->image); Q_FOREACH (const QModelIndex &index, indexes) { QModelIndexList movedIndexes; for (int column = index.column() + 1; column < columnCount(); column++) { movedIndexes << this->index(index.row(), column); } createOffsetFramesCommand(movedIndexes, QPoint(-1, 0), false, parentCommand, true); } const int oldTime = m_d->image->animationInterface()->currentUITime(); const int newTime = minColumn; new KisSwitchCurrentTimeCommand(m_d->image->animationInterface(), oldTime, - newTime, parentCommand); - + newTime, + parentCommand); } KisProcessingApplicator::runSingleCommandStroke(m_d->image, parentCommand, KisStrokeJobData::BARRIER); return true; } bool KisTimeBasedItemModel::mirrorFrames(QModelIndexList indexes) { QScopedPointer parentCommand(new KUndo2Command(kundo2_i18n("Mirror Frames"))); { KisImageBarrierLockerWithFeedback locker(m_d->image); QMap rowsList; Q_FOREACH (const QModelIndex &index, indexes) { rowsList[index.row()].append(index); } Q_FOREACH (int row, rowsList.keys()) { QModelIndexList &list = rowsList[row]; KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!list.isEmpty(), false); std::sort(list.begin(), list.end(), [] (const QModelIndex &lhs, const QModelIndex &rhs) { return lhs.column() < rhs.column(); }); auto srcIt = list.begin(); auto dstIt = list.end(); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(srcIt != dstIt, false); --dstIt; QList channels = channelsAt(*srcIt).values(); while (srcIt < dstIt) { Q_FOREACH (KisKeyframeChannel *channel, channels) { channel->swapFrames(srcIt->column(), dstIt->column(), parentCommand.data()); } srcIt++; dstIt--; } } } KisProcessingApplicator::runSingleCommandStroke(m_d->image, new KisCommandUtils::SkipFirstRedoWrapper(parentCommand.take()), KisStrokeJobData::BARRIER); return true; } 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/kis_time_based_item_model.h b/plugins/dockers/animation/kis_time_based_item_model.h index 7634fcefc4..9631956028 100644 --- a/plugins/dockers/animation/kis_time_based_item_model.h +++ b/plugins/dockers/animation/kis_time_based_item_model.h @@ -1,101 +1,101 @@ /* * 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. */ #ifndef _KIS_TIME_BASED_ITEM_MODEL_H #define _KIS_TIME_BASED_ITEM_MODEL_H #include #include #include "kritaanimationdocker_export.h" #include "kis_types.h" class KisTimeRange; class KisAnimationPlayer; class KisKeyframeChannel; class KRITAANIMATIONDOCKER_EXPORT KisTimeBasedItemModel : public QAbstractTableModel { Q_OBJECT public: KisTimeBasedItemModel(QObject *parent); ~KisTimeBasedItemModel() override; void setImage(KisImageWSP image); void setFrameCache(KisAnimationFrameCacheSP cache); void setAnimationPlayer(KisAnimationPlayer *player); void setLastVisibleFrame(int time); int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role) override; bool removeFrames(const QModelIndexList &indexes); bool removeFramesAndOffset(QModelIndexList indexes); bool mirrorFrames(QModelIndexList indexes); void setScrubState(bool active); void scrubTo(int time, bool preview); void setPlaybackRange(const KisTimeRange &range); bool isPlaybackActive() const; int currentTime() const; enum ItemDataRole { ActiveFrameRole = Qt::UserRole + 101, FrameExistsRole, SpecialKeyframeExists, FrameCachedRole, FrameEditableRole, FramesPerSecondRole, - UserRole + UserRole, + FrameHasContent // is it an empty frame with nothing in it? }; protected: virtual KisNodeSP nodeAt(QModelIndex index) const = 0; virtual QMap channelsAt(QModelIndex index) const = 0; KisImageWSP image() const; KUndo2Command* createOffsetFramesCommand(QModelIndexList srcIndexes, const QPoint &offset, bool copyFrames, KUndo2Command *parentCommand = 0, bool moveEmptyFrames = true); + private Q_SLOTS: void slotFramerateChanged(); void slotCurrentTimeChanged(int time); - void slotCacheChanged(); - void slotInternalScrubPreviewRequested(int time); void slotPlaybackFrameChanged(); void slotPlaybackStopped(); private: struct Private; const QScopedPointer m_d; }; #endif diff --git a/plugins/dockers/animation/timeline_color_scheme.cpp b/plugins/dockers/animation/timeline_color_scheme.cpp index 24efe39474..404cdd6e0b 100644 --- a/plugins/dockers/animation/timeline_color_scheme.cpp +++ b/plugins/dockers/animation/timeline_color_scheme.cpp @@ -1,144 +1,126 @@ /* * 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_color_scheme.h" #include #include #include #include #include #include #include "kis_debug.h" #include "krita_utils.h" #include Q_GLOBAL_STATIC(TimelineColorScheme, s_instance) struct TimelineColorScheme::Private { QColor baseColor; }; TimelineColorScheme::TimelineColorScheme() : m_d(new Private) { m_d->baseColor = QColor(137, 192, 221); } TimelineColorScheme::~TimelineColorScheme() { } TimelineColorScheme* TimelineColorScheme::instance() { return s_instance; } QColor TimelineColorScheme::selectorColor() const { return QColor(223, 148, 51); } QColor TimelineColorScheme::selectionColor() const { //return qApp->palette().color(QPalette::Highlight); return selectorColor(); } QColor TimelineColorScheme::activeLayerBackground() const { QColor color = qApp->palette().color(QPalette::Highlight); return color; } QBrush TimelineColorScheme::headerEmpty() const { return qApp->palette().brush(QPalette::Button); } QBrush TimelineColorScheme::headerCachedFrame() const { QColor bgColor = qApp->palette().color(QPalette::Base); int darkenCoeff = bgColor.value() > 128 ? 150 : 50; return headerEmpty().color().darker(darkenCoeff); } QBrush TimelineColorScheme::headerActive() const { return selectorColor(); } -QColor TimelineColorScheme::frameColor(bool present, bool active) const -{ - QColor color = Qt::transparent; - - if (present && !active) { - color = m_d->baseColor; - } else if (present && active) { - QColor bgColor = qApp->palette().color(QPalette::Base); - int darkenCoeff = bgColor.value() > 128 ? 130 : 80; - color = m_d->baseColor.darker(darkenCoeff); - } else if (!present && active) { - QColor bgColor = qApp->palette().color(QPalette::Base); - return KritaUtils::blendColors(m_d->baseColor, bgColor, 0.2); - } - - return color; -} - QColor TimelineColorScheme::onionSkinsSliderEnabledColor() const { return m_d->baseColor; } QColor TimelineColorScheme::onionSkinsSliderDisabledColor() const { return qApp->palette().color(QPalette::Disabled, QPalette::HighlightedText); } QColor TimelineColorScheme::onionSkinsButtonColor() const { QColor bgColor = qApp->palette().color(QPalette::Base); const int lighterCoeff = bgColor.value() > 128 ? 120 : 80; return m_d->baseColor.lighter(lighterCoeff); } QFont TimelineColorScheme::getOnionSkinsFont(const QString &maxString, const QSize &availableSize) const { QFont font = qApp->font(); while(font.pointSize() > 8) { QFontMetrics fm(font); QRect rc = fm.boundingRect(maxString); if (rc.width() > availableSize.width() || rc.height() > availableSize.height()) { font.setPointSize(font.pointSize() - 1); } else { break; } } return font; } diff --git a/plugins/dockers/animation/timeline_color_scheme.h b/plugins/dockers/animation/timeline_color_scheme.h index d0ee71c1f1..be96729a46 100644 --- a/plugins/dockers/animation/timeline_color_scheme.h +++ b/plugins/dockers/animation/timeline_color_scheme.h @@ -1,59 +1,57 @@ /* * 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_COLOR_SCHEME_H #define __TIMELINE_COLOR_SCHEME_H #include class QColor; class QBrush; class QFont; class QSize; class TimelineColorScheme { public: TimelineColorScheme(); ~TimelineColorScheme(); static TimelineColorScheme* instance(); QColor selectorColor() const; QColor selectionColor() const; QColor activeLayerBackground() const; QBrush headerEmpty() const; QBrush headerCachedFrame() const; QBrush headerActive() const; - QColor frameColor(bool present, bool active)const ; - QColor onionSkinsSliderEnabledColor() const; QColor onionSkinsSliderDisabledColor() const; QColor onionSkinsButtonColor() const; QFont getOnionSkinsFont(const QString &maxString, const QSize &availableSize) const; private: struct Private; const QScopedPointer m_d; }; #endif /* __TIMELINE_COLOR_SCHEME_H */ diff --git a/plugins/dockers/animation/timeline_docker.cpp b/plugins/dockers/animation/timeline_docker.cpp index b166ff7db6..ca90098274 100644 --- a/plugins/dockers/animation/timeline_docker.cpp +++ b/plugins/dockers/animation/timeline_docker.cpp @@ -1,149 +1,150 @@ /* * 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 "timeline_docker.h" #include "kis_canvas2.h" #include "kis_image.h" #include #include "KisViewManager.h" #include "kis_paint_layer.h" #include "KisDocument.h" #include "kis_dummies_facade.h" #include "kis_shape_controller.h" #include "kis_action.h" #include "kis_action_manager.h" #include "timeline_frames_model.h" #include "timeline_frames_view.h" #include "kis_animation_frame_cache.h" +#include "kis_image_animation_interface.h" #include "kis_signal_auto_connection.h" #include "kis_node_manager.h" #include struct TimelineDocker::Private { Private(QWidget *parent) : model(new TimelineFramesModel(parent)), view(new TimelineFramesView(parent)) { view->setModel(model); } TimelineFramesModel *model; TimelineFramesView *view; QPointer canvas; KisSignalAutoConnectionsStore canvasConnections; }; TimelineDocker::TimelineDocker() : QDockWidget(i18n("Timeline")) , m_d(new Private(this)) { setWidget(m_d->view); } TimelineDocker::~TimelineDocker() { } struct NodeManagerInterface : TimelineFramesModel::NodeManipulationInterface { NodeManagerInterface(KisNodeManager *manager) : m_manager(manager) {} KisLayerSP addPaintLayer() const override { return m_manager->createPaintLayer(); } void removeNode(KisNodeSP node) const override { m_manager->removeSingleNode(node); } private: KisNodeManager *m_manager; }; void TimelineDocker::setCanvas(KoCanvasBase * canvas) { if (canvas && m_d->canvas == canvas) return; if (m_d->model->hasConnectionToCanvas()) { m_d->canvasConnections.clear(); m_d->model->setDummiesFacade(0, 0); m_d->model->setFrameCache(0); m_d->model->setAnimationPlayer(0); m_d->model->setNodeManipulationInterface(0); if (m_d->canvas) { m_d->canvas->disconnectCanvasObserver(this); } } m_d->canvas = dynamic_cast(canvas); setEnabled(m_d->canvas != 0); if(m_d->canvas) { KisDocument *doc = static_cast(m_d->canvas->imageView()->document()); KisShapeController *kritaShapeController = dynamic_cast(doc->shapeController()); m_d->model->setDummiesFacade(kritaShapeController, m_d->canvas->image()); m_d->model->setFrameCache(m_d->canvas->frameCache()); m_d->model->setAnimationPlayer(m_d->canvas->animationPlayer()); m_d->model->setNodeManipulationInterface( new NodeManagerInterface(m_d->canvas->viewManager()->nodeManager())); m_d->canvasConnections.addConnection( m_d->canvas->viewManager()->nodeManager(), SIGNAL(sigNodeActivated(KisNodeSP)), m_d->model, SLOT(slotCurrentNodeChanged(KisNodeSP))); m_d->canvasConnections.addConnection( m_d->model, SIGNAL(requestCurrentNodeChanged(KisNodeSP)), m_d->canvas->viewManager()->nodeManager(), SLOT(slotNonUiActivatedNode(KisNodeSP))); m_d->model->slotCurrentNodeChanged(m_d->canvas->viewManager()->activeNode()); m_d->canvasConnections.addConnection( m_d->canvas->viewManager()->mainWindow(), SIGNAL(themeChanged()), this, SLOT(slotUpdateIcons()) ); } } void TimelineDocker::slotUpdateIcons() { if (m_d->view) { m_d->view->slotUpdateIcons(); } } void TimelineDocker::unsetCanvas() { setCanvas(0); } void TimelineDocker::setMainWindow(KisViewManager *view) { KisActionManager *actionManager = view->actionManager(); m_d->view->setShowInTimeline(actionManager->actionByName("show_in_timeline")); m_d->view->setActionManager(actionManager); } diff --git a/plugins/dockers/animation/timeline_frames_item_delegate.cpp b/plugins/dockers/animation/timeline_frames_item_delegate.cpp index 0ee1384ed4..13a9a0ffd8 100644 --- a/plugins/dockers/animation/timeline_frames_item_delegate.cpp +++ b/plugins/dockers/animation/timeline_frames_item_delegate.cpp @@ -1,180 +1,265 @@ /* * 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_item_delegate.h" #include #include #include - +#include "krita_utils.h" #include "timeline_frames_model.h" #include "timeline_color_scheme.h" #include "kis_node_view_color_scheme.h" TimelineFramesItemDelegate::TimelineFramesItemDelegate(QObject *parent) : QItemDelegate(parent) { KisNodeViewColorScheme scm; labelColors = scm.allColorLabels(); } TimelineFramesItemDelegate::~TimelineFramesItemDelegate() { } void TimelineFramesItemDelegate::paintActiveFrameSelector(QPainter *painter, const QRect &rc, bool isCurrentFrame) { QColor lineColor = TimelineColorScheme::instance()->selectorColor(); const int lineWidth = rc.width() > 20 ? 4 : 2; const int x0 = rc.x(); const int y0 = rc.y(); const int x1 = rc.right(); const int y1 = rc.bottom(); QVector linesDark; linesDark << QLine(x0 + lineWidth / 2, y0, x0 + lineWidth / 2, y1); linesDark << QLine(x1 - lineWidth / 2 + 1, y0, x1 - lineWidth / 2 + 1, y1); QPen oldPen = painter->pen(); painter->setPen(QPen(lineColor, lineWidth)); painter->drawLines(linesDark); painter->setPen(oldPen); if (isCurrentFrame) { QPen oldPen = painter->pen(); QBrush oldBrush(painter->brush()); painter->setPen(QPen(lineColor, 0)); painter->setBrush(lineColor); painter->drawEllipse(rc.center(), 2,2); painter->setBrush(oldBrush); painter->setPen(oldPen); } } -void TimelineFramesItemDelegate::paintSpecialKeyframeIndicator(QPainter *painter, const QModelIndex &index, const QRect &rc) +void TimelineFramesItemDelegate::paintSpecialKeyframeIndicator(QPainter *painter, const QModelIndex &index, const QRect &rc) const { - bool active = index.data(TimelineFramesModel::ActiveLayerRole).toBool(); - bool framePresent = index.data(TimelineFramesModel::FrameExistsRole).toBool(); - bool editable = index.data(TimelineFramesModel::FrameEditableRole).toBool(); + bool doesFrameExist = index.data(TimelineFramesModel::FrameExistsRole).toBool(); /// does normal keyframe exist + bool isEditable = index.data(TimelineFramesModel::FrameEditableRole).toBool(); + bool hasContent = index.data(TimelineFramesModel::FrameHasContent).toBool(); /// find out if frame is empty - QColor color = TimelineColorScheme::instance()->frameColor(!framePresent, active); + QColor color = qApp->palette().color(QPalette::Highlight); + QColor baseColor = qApp->palette().color(QPalette::Base); + QColor noLabelSetColor = qApp->palette().color(QPalette::Highlight); // if no color label is used - if (!editable && color.alpha() > 0) { - const int l = color.lightness(); - color = QColor(l, l, l); + // use color label if it exists. coloring follows very similar logic to the drawBackground() function except this is a bit simpler + QVariant colorLabel = index.data(TimelineFramesModel::FrameColorLabelIndexRole); + if (colorLabel.isValid()) { + color = labelColors.at(colorLabel.toInt()); + } else { + color = noLabelSetColor; + } + + if (!isEditable) { + color = KritaUtils::blendColors(baseColor, color, 0.5); + } + + if (doesFrameExist && hasContent) { + color = baseColor; // special keyframe will be over filled in frame, so switch color so it is shown } QPen oldPen = painter->pen(); QBrush oldBrush(painter->brush()); painter->setPen(QPen(color, 0)); painter->setBrush(color); QPointF center = rc.center(); QPointF points[4] = { QPointF(center.x() + 4, center.y() ), QPointF(center.x() , center.y() - 4), QPointF(center.x() - 4, center.y() ), QPointF(center.x() , center.y() + 4) }; painter->drawConvexPolygon(points, 4); painter->setBrush(oldBrush); painter->setPen(oldPen); } void TimelineFramesItemDelegate::drawBackground(QPainter *painter, const QModelIndex &index, const QRect &rc) const { - bool active = index.data(TimelineFramesModel::ActiveLayerRole).toBool(); - bool present = index.data(TimelineFramesModel::FrameExistsRole).toBool(); - bool editable = index.data(TimelineFramesModel::FrameEditableRole).toBool(); + /// is the current layer actively selected (this is not the same as visibility) + bool hasActiveLayerRole = index.data(TimelineFramesModel::ActiveLayerRole).toBool(); + bool doesFrameExist = index.data(TimelineFramesModel::FrameExistsRole).toBool(); /// does keyframe exist + bool isEditable = index.data(TimelineFramesModel::FrameEditableRole).toBool(); /// is layer editable + bool hasContent = index.data(TimelineFramesModel::FrameHasContent).toBool(); /// find out if frame is empty + + QColor color; // will get re-used for determining color + QColor noLabelSetColor = qApp->palette().color(QPalette::Highlight); // if no color label is used + QColor highlightColor = qApp->palette().color(QPalette::Highlight); + QColor baseColor = qApp->palette().color(QPalette::Base); + + + // pass for filling in the active row with slightly color difference + if (hasActiveLayerRole) { + color = KritaUtils::blendColors(baseColor, highlightColor, 0.8); + painter->fillRect(rc, color); + } + + // assign background color for frame depending on if the frame has a color label or not QVariant colorLabel = index.data(TimelineFramesModel::FrameColorLabelIndexRole); + if (colorLabel.isValid()) { + color = labelColors.at(colorLabel.toInt()); + } else { + color = noLabelSetColor; + } + + + // if layer is hidden, make the entire color more subtle. + // this should be in effect for both fill color and empty outline color + if (!isEditable) { + color = KritaUtils::blendColors(baseColor, color, 0.7); + } + + + // how do we fill in a frame that has content + // a keyframe will be totally filled in. A hold frame will have a line running through it + if (hasContent && doesFrameExist) { + painter->fillRect(rc, color); + } + + + + // pass of outline for empty keyframes + if (doesFrameExist && !hasContent) { - QColor color = colorLabel.isValid() ? labelColors.at(colorLabel.toInt()) : - TimelineColorScheme::instance()->frameColor(present, active); + QPen oldPen = painter->pen(); + QBrush oldBrush(painter->brush()); + + painter->setPen(QPen(color, 2)); + painter->setBrush(Qt::NoBrush); + painter->drawRect(rc); + + painter->setBrush(oldBrush); + painter->setPen(oldPen); + } + + + // pass of hold frame line + if (!doesFrameExist && hasContent) { - if (!editable && color.alpha() > 0) { - const int l = color.lightness(); - color = QColor(l, l, l); + // pretty much the same check as "isValid" above, but that isn't working with hold frames + if (colorLabel.toInt() == 0) { + color = noLabelSetColor; + + if (!isEditable) { + color = KritaUtils::blendColors(baseColor, color, 0.7); + } + } + + + QPoint lineStart(rc.x(), (rc.y() + rc.height()/2)); + QPoint lineEnd(rc.x() + rc.width(), (rc.y() + rc.height()/2)); + + QPen holdFramePen(color); + holdFramePen.setWidth(1); + + painter->setPen(holdFramePen); + painter->drawLine(QLine(lineStart, lineEnd)); } - painter->fillRect(rc, color); + + } void TimelineFramesItemDelegate::drawFocus(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect) const { // copied form Qt 4.8! if ((option.state & QStyle::State_HasFocus) == 0 || !rect.isValid()) return; QStyleOptionFocusRect o; o.QStyleOption::operator=(option); o.rect = rect; o.state |= QStyle::State_KeyboardFocusChange; o.state |= QStyle::State_Item; QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; o.backgroundColor = option.palette.color(cg, (option.state & QStyle::State_Selected) ? QPalette::Highlight : QPalette::Window); const QWidget *widget = qobject_cast(parent()); QStyle *style = widget ? widget->style() : QApplication::style(); style->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter, widget); } void TimelineFramesItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ +{ + // draws background as well as fills normal keyframes drawBackground(painter, index, option.rect); + // creates a semi transparent orange rectangle in the frame that is actively selected on the active row if (option.showDecorationSelected && (option.state & QStyle::State_Selected)) { - QPalette::ColorGroup cg = option.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) cg = QPalette::Inactive; QBrush brush = TimelineColorScheme::instance()->selectionColor(); int oldOpacity = painter->opacity(); painter->setOpacity(0.5); painter->fillRect(option.rect, brush); painter->setOpacity(oldOpacity); } + // not sure what this is drawing drawFocus(painter, option, option.rect); + // opacity keyframe, but NOT normal keyframes bool specialKeys = index.data(TimelineFramesModel::SpecialKeyframeExists).toBool(); if (specialKeys) { paintSpecialKeyframeIndicator(painter, index, option.rect); } + // creates a border and dot on the selected frame on the active row bool active = index.data(TimelineFramesModel::ActiveFrameRole).toBool(); bool layerIsCurrent = index.data(TimelineFramesModel::ActiveLayerRole).toBool(); if (active) { paintActiveFrameSelector(painter, option.rect, layerIsCurrent); } } diff --git a/plugins/dockers/animation/timeline_frames_item_delegate.h b/plugins/dockers/animation/timeline_frames_item_delegate.h index 8dcdc6483f..43bb3029d7 100644 --- a/plugins/dockers/animation/timeline_frames_item_delegate.h +++ b/plugins/dockers/animation/timeline_frames_item_delegate.h @@ -1,44 +1,47 @@ /* * 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_ITEM_DELEGATE_H #define __TIMELINE_FRAMES_ITEM_DELEGATE_H #include class TimelineFramesItemDelegate : public QItemDelegate { public: TimelineFramesItemDelegate(QObject *parent); ~TimelineFramesItemDelegate() override; static void paintActiveFrameSelector(QPainter *painter, const QRect &rc, bool isCurrentFrame); - static void paintSpecialKeyframeIndicator(QPainter *painter, const QModelIndex &index, const QRect &rc); + + /// the opacity keyframe + void paintSpecialKeyframeIndicator(QPainter *painter, const QModelIndex &index, const QRect &rc) const; + void drawBackground(QPainter *painter, const QModelIndex &index, const QRect &rc) const; void drawFocus(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect) const override; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; private: QVector labelColors; }; #endif /* __TIMELINE_FRAMES_ITEM_DELEGATE_H */ diff --git a/plugins/dockers/animation/timeline_frames_model.cpp b/plugins/dockers/animation/timeline_frames_model.cpp index b7b906d908..2ce7b096c5 100644 --- a/plugins/dockers/animation/timeline_frames_model.cpp +++ b/plugins/dockers/animation/timeline_frames_model.cpp @@ -1,951 +1,980 @@ /* * 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 #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 #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" #include "kis_node_view_color_scheme.h" #include "krita_utils.h" #include #include "kis_processing_applicator.h" #include #include "kis_node_uuid_info.h" struct TimelineFramesModel::Private { Private() : activeLayerIndex(0), dummiesFacade(0), needFinishInsertRows(false), needFinishRemoveRows(false), updateTimer(200, KisSignalCompressor::FIRST_INACTIVE), parentOfRemovedNode(0) {} int activeLayerIndex; QPointer 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 frameHasContent(int row, int column) { + + KisNodeDummy *dummy = converter->dummyFromRow(row); + + KisKeyframeChannel *primaryChannel = dummy->node()->getKeyframeChannel(KisKeyframeChannel::Content.id()); + if (!primaryChannel) return false; + + // first check if we are a key frame + KisKeyframeSP frame = primaryChannel->activeKeyframeAt(column); + if (!frame) return false; + + return frame->hasContent(); + } + bool specialKeyframeExists(int row, int column) { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return false; Q_FOREACH(KisKeyframeChannel *channel, dummy->node()->keyframeChannels()) { 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); + KisKeyframeSP frame = primaryChannel->activeKeyframeAt(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); } int layerColorLabel(int row) const { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return -1; return dummy->node()->colorLabelIndex(); } 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; KisAnimationUtils::createKeyframeLazy(image, node, KisKeyframeChannel::Content.id(), column, copy); return true; } 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 { /** * The dummy might not exist because the user could (quickly) change * active layer and the list of the nodes in m_d->converter will change. */ KisNodeDummy *dummy = m_d->converter->dummyFromRow(index.row()); return dummy ? dummy->node() : 0; } QMap 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) { 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())); + connect(m_d->image, SIGNAL(sigImageModified()), SLOT(slotImageContentChanged())); } if (m_d->dummiesFacade != oldDummiesFacade) { beginResetModel(); endResetModel(); } 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::slotImageContentChanged() +{ + if (m_d->activeLayerIndex < 0) return; + + KisNodeDummy *dummy = m_d->converter->dummyFromRow(m_d->activeLayerIndex); + if (!dummy) return; + + slotDummyChanged(dummy); +} + 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); if (!dummy) { // It's perfectly normal that dummyForNode returns 0; that happens // when views get activated while Krita is closing down. return; } 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 FrameHasContent: { + return m_d->frameHasContent(index.row(), index.column()); + } case FrameExistsRole: { return m_d->frameExists(index.row(), index.column()); } case SpecialKeyframeExists: { return m_d->specialKeyframeExists(index.row(), index.column()); } case FrameColorLabelIndexRole: { int label = m_d->frameColorLabel(index.row(), index.column()); return label > 0 ? label : QVariant(); } case Qt::DisplayRole: { return m_d->layerName(index.row()); } case Qt::TextAlignmentRole: { return QVariant(Qt::AlignHCenter | Qt::AlignVCenter); } case KoResourceModel::LargeThumbnailRole: { KisNodeDummy *dummy = m_d->converter->dummyFromRow(index.row()); if (!dummy) { return QVariant(); } const int maxSize = 200; QSize size = dummy->node()->extent().size(); size.scale(maxSize, maxSize, Qt::KeepAspectRatio); if (size.width() == 0 || size.height() == 0) { // No thumbnail can be shown if there isn't width or height... return QVariant(); } QImage image(dummy->node()->createThumbnailForFrame(size.width(), size.height(), index.column())); return image; } } 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 FrameColorLabelIndexRole: { 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(); } case Qt::BackgroundRole: { int label = m_d->layerColorLabel(section); if (label > 0) { KisNodeViewColorScheme scm; QColor color = scm.colorLabel(label); QPalette pal = qApp->palette(); color = KritaUtils::blendColors(color, pal.color(QPalette::Button), 0.3); return QBrush(color); } else { return QVariant(); } } } } 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 { return mimeDataExtended(indexes, m_d->lastClickedIndex, UndefinedPolicy); } QMimeData *TimelineFramesModel::mimeDataExtended(const QModelIndexList &indexes, const QModelIndex &baseIndex, TimelineFramesModel::MimeCopyPolicy copyPolicy) const { QMimeData *data = new QMimeData(); QByteArray encoded; QDataStream stream(&encoded, QIODevice::WriteOnly); const int baseRow = baseIndex.row(); const int baseColumn = baseIndex.column(); stream << indexes.size(); stream << baseRow << baseColumn; Q_FOREACH (const QModelIndex &index, indexes) { KisNodeSP node = nodeAt(index); KIS_SAFE_ASSERT_RECOVER(node) { continue; } stream << index.row() - baseRow << index.column() - baseColumn; const QByteArray uuidData = node->uuid().toRfc4122(); stream << int(uuidData.size()); stream.writeRawData(uuidData.data(), uuidData.size()); } stream << int(copyPolicy); 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); return dropMimeDataExtended(data, action, parent); } bool TimelineFramesModel::dropMimeDataExtended(const QMimeData *data, Qt::DropAction action, const QModelIndex &parent, bool *dataMoved) { bool result = false; if ((action != Qt::MoveAction && action != Qt::CopyAction) || !parent.isValid()) return result; QByteArray encoded = data->data("application/x-krita-frame"); QDataStream stream(&encoded, QIODevice::ReadOnly); int size, baseRow, baseColumn; stream >> size >> baseRow >> baseColumn; const QPoint offset(parent.column() - baseColumn, parent.row() - baseRow); KisAnimationUtils::FrameMovePairList frameMoves; for (int i = 0; i < size; i++) { int relRow, relColumn; stream >> relRow >> relColumn; const int srcRow = baseRow + relRow; const int srcColumn = baseColumn + relColumn; int uuidLen = 0; stream >> uuidLen; QByteArray uuidData(uuidLen, '\0'); stream.readRawData(uuidData.data(), uuidLen); QUuid nodeUuid = QUuid::fromRfc4122(uuidData); KisNodeSP srcNode; if (!nodeUuid.isNull()) { KisNodeUuidInfo nodeInfo(nodeUuid); srcNode = nodeInfo.findNode(m_d->image->root()); } else { QModelIndex index = this->index(srcRow, srcColumn); srcNode = nodeAt(index); } KIS_SAFE_ASSERT_RECOVER(srcNode) { continue; } const QModelIndex dstIndex = this->index(srcRow + offset.y(), srcColumn + offset.x()); if (!dstIndex.isValid()) continue; KisNodeSP dstNode = nodeAt(dstIndex); KIS_SAFE_ASSERT_RECOVER(dstNode) { continue; } Q_FOREACH (KisKeyframeChannel *channel, srcNode->keyframeChannels().values()) { KisAnimationUtils::FrameItem srcItem(srcNode, channel->id(), srcColumn); KisAnimationUtils::FrameItem dstItem(dstNode, channel->id(), dstIndex.column()); frameMoves << std::make_pair(srcItem, dstItem); } } MimeCopyPolicy copyPolicy = UndefinedPolicy; if (!stream.atEnd()) { int value = 0; stream >> value; copyPolicy = MimeCopyPolicy(value); } const bool copyFrames = copyPolicy == UndefinedPolicy ? action == Qt::CopyAction : copyPolicy == CopyFramesPolicy; if (dataMoved) { *dataMoved = !copyFrames; } KUndo2Command *cmd = 0; if (!frameMoves.isEmpty()) { KisImageBarrierLockerWithFeedback locker(m_d->image); cmd = KisAnimationUtils::createMoveKeyframesCommand(frameMoves, copyFrames, 0); } if (cmd) { KisProcessingApplicator::runSingleCommandStroke(m_d->image, cmd, KisStrokeJobData::BARRIER); } return cmd; } 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; return m_d->addKeyframe(dstIndex.row(), dstIndex.column(), false); } bool TimelineFramesModel::copyFrame(const QModelIndex &dstIndex) { if (!dstIndex.isValid()) return false; return m_d->addKeyframe(dstIndex.row(), dstIndex.column(), true); } -bool TimelineFramesModel::insertFrames(int dstColumn, const QList &dstRows, int count) +bool TimelineFramesModel::insertFrames(int dstColumn, const QList &dstRows, int count, int timing) { if (dstRows.isEmpty() || count <= 0) return true; + timing = qMax(timing, 1); KUndo2Command *parentCommand = new KUndo2Command(kundo2_i18np("Insert frame", "Insert %1 frames", count)); { KisImageBarrierLockerWithFeedback locker(m_d->image); QModelIndexList indexes; Q_FOREACH (int row, dstRows) { for (int column = dstColumn; column < columnCount(); column++) { indexes << index(row, column); } } - setLastVisibleFrame(columnCount() + count - 1); - + setLastVisibleFrame(columnCount() + (count * timing) - 1); - createOffsetFramesCommand(indexes, QPoint(count, 0), false, parentCommand); + createOffsetFramesCommand(indexes, QPoint((count * timing), 0), false, parentCommand); Q_FOREACH (int row, dstRows) { KisNodeDummy *dummy = m_d->converter->dummyFromRow(row); if (!dummy) continue; KisNodeSP node = dummy->node(); if (!KisAnimationUtils::supportsContentFrames(node)) continue; - for (int column = dstColumn; column < dstColumn + count; column++) { + for (int column = dstColumn; column < dstColumn + (count * timing); column += timing) { KisAnimationUtils::createKeyframeCommand(m_d->image, node, KisKeyframeChannel::Content.id(), column, false, parentCommand); } } const int oldTime = m_d->image->animationInterface()->currentUITime(); - const int newTime = dstColumn > oldTime ? dstColumn : dstColumn + count - 1; + const int newTime = dstColumn > oldTime ? dstColumn : dstColumn + (count * timing) - 1; new KisSwitchCurrentTimeCommand(m_d->image->animationInterface(), oldTime, newTime, parentCommand); } KisProcessingApplicator::runSingleCommandStroke(m_d->image, parentCommand, KisStrokeJobData::BARRIER); return true; } bool TimelineFramesModel::insertHoldFrames(QModelIndexList selectedIndexes, int count) { if (selectedIndexes.isEmpty() || count == 0) return true; QScopedPointer parentCommand(new KUndo2Command(kundo2_i18np("Insert frame", "Insert %1 frames", count))); { KisImageBarrierLockerWithFeedback locker(m_d->image); QSet uniqueKeyframesInSelection; + int minSelectedTime = std::numeric_limits::max(); + Q_FOREACH (const QModelIndex &index, selectedIndexes) { KisNodeSP node = nodeAt(index); KIS_SAFE_ASSERT_RECOVER(node) { continue; } KisKeyframeChannel *channel = node->getKeyframeChannel(KisKeyframeChannel::Content.id()); if (!channel) continue; + minSelectedTime = qMin(minSelectedTime, index.column()); KisKeyframeSP keyFrame = channel->activeKeyframeAt(index.column()); if (keyFrame) { uniqueKeyframesInSelection.insert(keyFrame); } } - int minSelectedTime = std::numeric_limits::max(); QList keyframesToMove; for (auto it = uniqueKeyframesInSelection.begin(); it != uniqueKeyframesInSelection.end(); ++it) { KisKeyframeSP keyframe = *it; - minSelectedTime = qMin(minSelectedTime, keyframe->time()); - KisKeyframeChannel *channel = keyframe->channel(); KisKeyframeSP nextKeyframe = channel->nextKeyframe(keyframe); if (nextKeyframe) { keyframesToMove << nextKeyframe; } } std::sort(keyframesToMove.begin(), keyframesToMove.end(), [] (KisKeyframeSP lhs, KisKeyframeSP rhs) { return lhs->time() > rhs->time(); }); if (keyframesToMove.isEmpty()) return true; const int maxColumn = columnCount(); if (count > 0) { setLastVisibleFrame(columnCount() + count); } Q_FOREACH (KisKeyframeSP keyframe, keyframesToMove) { int plannedFrameMove = count; if (count < 0) { KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(keyframe->time() > 0, false); KisKeyframeSP prevFrame = keyframe->channel()->previousKeyframe(keyframe); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(prevFrame, false); plannedFrameMove = qMax(count, prevFrame->time() - keyframe->time() + 1); + + minSelectedTime = qMin(minSelectedTime, prevFrame->time()); } KisNodeDummy *dummy = m_d->dummiesFacade->dummyForNode(keyframe->channel()->node()); KIS_SAFE_ASSERT_RECOVER(dummy) { continue; } const int row = m_d->converter->rowForDummy(dummy); KIS_SAFE_ASSERT_RECOVER(row >= 0) { continue; } QModelIndexList indexes; for (int column = keyframe->time(); column < maxColumn; column++) { indexes << index(row, column); } createOffsetFramesCommand(indexes, QPoint(plannedFrameMove, 0), false, parentCommand.data()); } const int oldTime = m_d->image->animationInterface()->currentUITime(); const int newTime = minSelectedTime; new KisSwitchCurrentTimeCommand(m_d->image->animationInterface(), oldTime, newTime, parentCommand.data()); } KisProcessingApplicator::runSingleCommandStroke(m_d->image, parentCommand.take(), KisStrokeJobData::BARRIER); return true; } - 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); } void TimelineFramesModel::setFullClipRangeStart(int column) { m_d->image->animationInterface()->setFullClipRangeStartTime(column); } void TimelineFramesModel::setFullClipRangeEnd(int column) { m_d->image->animationInterface()->setFullClipRangeEndTime(column); } diff --git a/plugins/dockers/animation/timeline_frames_model.h b/plugins/dockers/animation/timeline_frames_model.h index fd96f8edad..020135fd22 100644 --- a/plugins/dockers/animation/timeline_frames_model.h +++ b/plugins/dockers/animation/timeline_frames_model.h @@ -1,151 +1,153 @@ /* * 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: enum MimeCopyPolicy { UndefinedPolicy = 0, MoveFramesPolicy, CopyFramesPolicy }; public: TimelineFramesModel(QObject *parent); ~TimelineFramesModel() override; 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); - bool insertFrames(int dstColumn, const QList &dstRows, int count); + bool insertFrames(int dstColumn, const QList &dstRows, int count, int timing = 1); bool insertHoldFrames(QModelIndexList selectedIndexes, int count); QString audioChannelFileName() const; void setAudioChannelFileName(const QString &fileName); bool isAudioMuted() const; void setAudioMuted(bool value); qreal audioVolume() const; void setAudioVolume(qreal value); void setFullClipRangeStart(int column); void setFullClipRangeEnd(int column); void setLastClickedIndex(const QModelIndex &index); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role) override; Qt::DropActions supportedDragActions() const override; Qt::DropActions supportedDropActions() const override; QStringList mimeTypes() const override; - QMimeData * mimeData(const QModelIndexList &indexes) const override; - QMimeData * mimeDataExtended(const QModelIndexList &indexes, const QModelIndex &baseIndex, MimeCopyPolicy copyPolicy) const; - bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) override; + QMimeData *mimeData(const QModelIndexList &indexes) const override; + QMimeData *mimeDataExtended(const QModelIndexList &indexes, const QModelIndex &baseIndex, MimeCopyPolicy copyPolicy) const; + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; bool dropMimeDataExtended(const QMimeData *data, Qt::DropAction action, const QModelIndex &parent, bool *dataMoved = 0); Qt::ItemFlags flags(const QModelIndex &index) const override; bool insertRows(int row, int count, const QModelIndex &parent) override; bool removeRows(int row, int count, const QModelIndex &parent) override; enum ItemDataRole { ActiveLayerRole = KisTimeBasedItemModel::UserRole, TimelinePropertiesRole, OtherLayersRole, LayerUsedInTimelineRole, FrameColorLabelIndexRole }; // 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 override; QMap channelsAt(QModelIndex index) const override; private Q_SLOTS: void slotDummyChanged(KisNodeDummy *dummy); + void slotImageContentChanged(); 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 48f663c24b..0d128a1540 100644 --- a/plugins/dockers/animation/timeline_frames_view.cpp +++ b/plugins/dockers/animation/timeline_frames_view.cpp @@ -1,1752 +1,1550 @@ /* * 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 "timeline_insert_keyframe_dialog.h" +#include "timeline_frames_item_delegate.h" #include - -#include #include -#include #include -#include #include #include -#include #include -#include -#include -#include +#include +#include +#include #include "KSharedConfig" -#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 -#include -#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; + TimelineInsertKeyframeDialog *insertKeyframeDialog; + KisZoomButton *zoomDragButton; bool dragInProgress; bool dragWasSuccessful; KisCustomModifiersCatcher *modifiersCatcher; QPoint lastPressedPosition; Qt::KeyboardModifiers lastPressedModifier; KisSignalCompressor selectionChangedCompressor; QStyleOptionViewItem viewOptionsV4() const; QItemViewPaintPairs draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const; QPixmap renderToPixmap(const QModelIndexList &indexes, QRect *r) const; KoIconToolTip tip; - KisActionManager * actionMan = 0; + KisActionManager *actionMan = 0; }; 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); - connect(m_d->horizontalRuler, SIGNAL(sigInsertColumnsLeft()), SLOT(slotInsertColumnsLeft())); - connect(m_d->horizontalRuler, SIGNAL(sigInsertColumnsRight()), SLOT(slotInsertColumnsRight())); + connect(m_d->horizontalRuler, SIGNAL(sigInsertColumnLeft()), SLOT(slotInsertKeyframeColumnLeft())); + connect(m_d->horizontalRuler, SIGNAL(sigInsertColumnRight()), SLOT(slotInsertKeyframeColumnRight())); - connect(m_d->horizontalRuler, SIGNAL(sigInsertColumnsLeftCustom()), SLOT(slotInsertColumnsLeftCustom())); - connect(m_d->horizontalRuler, SIGNAL(sigInsertColumnsRightCustom()), SLOT(slotInsertColumnsRightCustom())); + connect(m_d->horizontalRuler, SIGNAL(sigInsertMultipleColumns()), SLOT(slotInsertMultipleKeyframeColumns())); - connect(m_d->horizontalRuler, SIGNAL(sigRemoveColumns()), SLOT(slotRemoveColumns())); - connect(m_d->horizontalRuler, SIGNAL(sigRemoveColumnsAndShift()), SLOT(slotRemoveColumnsAndShift())); + connect(m_d->horizontalRuler, SIGNAL(sigRemoveColumns()), SLOT(slotRemoveSelectedColumns())); + connect(m_d->horizontalRuler, SIGNAL(sigRemoveColumnsAndShift()), SLOT(slotRemoveSelectedColumnsAndShift())); - connect(m_d->horizontalRuler, SIGNAL(sigInsertHoldColumns()), SLOT(slotInsertHoldColumns())); - connect(m_d->horizontalRuler, SIGNAL(sigRemoveHoldColumns()), SLOT(slotRemoveHoldColumns())); + connect(m_d->horizontalRuler, SIGNAL(sigInsertHoldColumns()), SLOT(slotInsertHoldFrameColumn())); + connect(m_d->horizontalRuler, SIGNAL(sigRemoveHoldColumns()), SLOT(slotRemoveHoldFrameColumn())); - connect(m_d->horizontalRuler, SIGNAL(sigInsertHoldColumnsCustom()), SLOT(slotInsertHoldColumnsCustom())); - connect(m_d->horizontalRuler, SIGNAL(sigRemoveHoldColumnsCustom()), SLOT(slotRemoveHoldColumnsCustom())); + connect(m_d->horizontalRuler, SIGNAL(sigInsertHoldColumnsCustom()), SLOT(slotInsertMultipleHoldFrameColumns())); + connect(m_d->horizontalRuler, SIGNAL(sigRemoveHoldColumnsCustom()), SLOT(slotRemoveMultipleHoldFrameColumns())); connect(m_d->horizontalRuler, SIGNAL(sigMirrorColumns()), SLOT(slotMirrorColumns())); connect(m_d->horizontalRuler, SIGNAL(sigCopyColumns()), SLOT(slotCopyColumns())); connect(m_d->horizontalRuler, SIGNAL(sigCutColumns()), SLOT(slotCutColumns())); connect(m_d->horizontalRuler, SIGNAL(sigPasteColumns()), SLOT(slotPasteColumns())); 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->setIconSize(QSize(20, 20)); 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->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->setIconSize(QSize(20, 20)); // very small on windows if not explicity set 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->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->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); + /********** Insert Keyframes Dialog **************************************************/ + + m_d->insertKeyframeDialog = new TimelineInsertKeyframeDialog(this); + /********** Zoom Button **************************************************************/ m_d->zoomDragButton = new KisZoomButton(this); m_d->zoomDragButton->setAutoRaise(true); m_d->zoomDragButton->setIcon(KisIconUtils::loadIcon("zoom-horizontal")); m_d->zoomDragButton->setIconSize(QSize(20, 20)); // this icon is very small on windows if no explicity set 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())); connect(&m_d->selectionChangedCompressor, SIGNAL(timeout()), SLOT(slotUpdateFrameActions())); { QClipboard *cb = QApplication::clipboard(); connect(cb, SIGNAL(dataChanged()), SLOT(slotUpdateFrameActions())); } } TimelineFramesView::~TimelineFramesView() { } -void TimelineFramesView::setShowInTimeline(KisAction* action) +void TimelineFramesView::setShowInTimeline(KisAction *action) { m_d->showHideLayerAction = action; m_d->layerEditingMenu->addAction(m_d->showHideLayerAction); } -void TimelineFramesView::setActionManager( KisActionManager * actionManager) +void TimelineFramesView::setActionManager(KisActionManager *actionManager) { m_d->actionMan = actionManager; m_d->horizontalRuler->setActionManager(actionManager); if (actionManager) { KisAction *action = 0; action = m_d->actionMan->createAction("add_blank_frame"); - connect(action, SIGNAL(triggered()), SLOT(slotNewFrame())); + connect(action, SIGNAL(triggered()), SLOT(slotAddBlankFrame())); action = m_d->actionMan->createAction("add_duplicate_frame"); - connect(action, SIGNAL(triggered()), SLOT(slotCopyFrame())); - - action = m_d->actionMan->createAction("insert_keyframes_right"); - connect(action, SIGNAL(triggered()), SLOT(slotInsertKeyframesRight())); + connect(action, SIGNAL(triggered()), SLOT(slotAddDuplicateFrame())); - action = m_d->actionMan->createAction("insert_n_keyframes_right"); - connect(action, SIGNAL(triggered()), SLOT(slotInsertKeyframesRightCustom())); + action = m_d->actionMan->createAction("insert_keyframe_left"); + connect(action, SIGNAL(triggered()), SLOT(slotInsertKeyframeLeft())); - action = m_d->actionMan->createAction("insert_keyframes_left"); - connect(action, SIGNAL(triggered()), SLOT(slotInsertKeyframesLeft())); + action = m_d->actionMan->createAction("insert_keyframe_right"); + connect(action, SIGNAL(triggered()), SLOT(slotInsertKeyframeRight())); - action = m_d->actionMan->createAction("insert_n_keyframes_left"); - connect(action, SIGNAL(triggered()), SLOT(slotInsertKeyframesLeftCustom())); + action = m_d->actionMan->createAction("insert_multiple_keyframes"); + connect(action, SIGNAL(triggered()), SLOT(slotInsertMultipleKeyframes())); action = m_d->actionMan->createAction("remove_frames_and_pull"); - connect(action, SIGNAL(triggered()), SLOT(slotRemoveFramesAndShift())); + connect(action, SIGNAL(triggered()), SLOT(slotRemoveSelectedFramesAndShift())); action = m_d->actionMan->createAction("remove_frames"); - connect(action, SIGNAL(triggered()), SLOT(slotRemoveFrame())); + connect(action, SIGNAL(triggered()), SLOT(slotRemoveSelectedFrames())); action = m_d->actionMan->createAction("insert_hold_frame"); - connect(action, SIGNAL(triggered()), SLOT(slotInsertHoldFrames())); + connect(action, SIGNAL(triggered()), SLOT(slotInsertHoldFrame())); - action = m_d->actionMan->createAction("insert_n_hold_frames"); - connect(action, SIGNAL(triggered()), SLOT(slotInsertHoldFramesCustom())); + action = m_d->actionMan->createAction("insert_multiple_hold_frames"); + connect(action, SIGNAL(triggered()), SLOT(slotInsertMultipleHoldFrames())); action = m_d->actionMan->createAction("remove_hold_frame"); - connect(action, SIGNAL(triggered()), SLOT(slotRemoveHoldFrames())); + connect(action, SIGNAL(triggered()), SLOT(slotRemoveHoldFrame())); - action = m_d->actionMan->createAction("remove_n_hold_frames"); - connect(action, SIGNAL(triggered()), SLOT(slotRemoveHoldFramesCustom())); + action = m_d->actionMan->createAction("remove_multiple_hold_frames"); + connect(action, SIGNAL(triggered()), SLOT(slotRemoveMultipleHoldFrames())); action = m_d->actionMan->createAction("mirror_frames"); connect(action, SIGNAL(triggered()), SLOT(slotMirrorFrames())); action = m_d->actionMan->createAction("copy_frames_to_clipboard"); connect(action, SIGNAL(triggered()), SLOT(slotCopyFrames())); action = m_d->actionMan->createAction("cut_frames_to_clipboard"); connect(action, SIGNAL(triggered()), SLOT(slotCutFrames())); action = m_d->actionMan->createAction("paste_frames_from_clipboard"); connect(action, SIGNAL(triggered()), SLOT(slotPasteFrames())); action = m_d->actionMan->createAction("set_start_time"); connect(action, SIGNAL(triggered()), SLOT(slotSetStartTimeToCurrentPosition())); action = m_d->actionMan->createAction("set_end_time"); connect(action, SIGNAL(triggered()), SLOT(slotSetEndTimeToCurrentPosition())); action = m_d->actionMan->createAction("update_playback_range"); connect(action, SIGNAL(triggered()), SLOT(slotUpdatePlackbackRange())); - - - } } 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::FrameColorLabelIndexRole); } KisImageConfig config; config.setDefaultFrameColorLabel(label); } void TimelineFramesView::slotSelectAudioChannelFile() { if (!m_d->model) return; QString defaultDir = QStandardPaths::writableLocation(QStandardPaths::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::slotUpdateIcons() { m_d->addLayersButton->setIcon(KisIconUtils::loadIcon("addlayer")); m_d->audioOptionsButton->setIcon(KisIconUtils::loadIcon("audio-none")); m_d->zoomDragButton->setIcon(KisIconUtils::loadIcon("zoom-horizontal")); } 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; selectionModel()->setCurrentIndex(m_d->model->index(row, selectedColumn), QItemSelectionModel::ClearAndSelect); } } 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); } } } void TimelineFramesView::rowsInserted(const QModelIndex& parent, int start, int end) { QTableView::rowsInserted(parent, start, end); } - 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 bug: if we start a dragging action right during * Shift-selection, Qt will get crazy. We cannot workaround it easily, * because we would need to fork mouseMoveEvent() for that (where the * decision about drag state is done). So we just abort dragging in that * case. * * BUG:373067 */ if (m_d->lastPressedModifier & Qt::ShiftModifier) { return; } /** * 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::createFrameEditingMenuActions(QMenu *menu, bool addFrameCreationActions) { slotUpdateFrameActions(); // calculate if selection range is set. This will determine if the update playback range is available QSet rows; int minColumn = 0; int maxColumn = 0; calculateSelectionMetrics(minColumn, maxColumn, rows); bool selectionExists = minColumn != maxColumn; if (selectionExists) { - KisActionManager::safePopulateMenu(menu, "update_playback_range", m_d->actionMan); + KisActionManager::safePopulateMenu(menu, "update_playback_range", m_d->actionMan); } else { KisActionManager::safePopulateMenu(menu, "set_start_time", m_d->actionMan); KisActionManager::safePopulateMenu(menu, "set_end_time", m_d->actionMan); } - menu->addSeparator(); + menu->addSeparator(); KisActionManager::safePopulateMenu(menu, "cut_frames_to_clipboard", m_d->actionMan); KisActionManager::safePopulateMenu(menu, "copy_frames_to_clipboard", m_d->actionMan); KisActionManager::safePopulateMenu(menu, "paste_frames_from_clipboard", m_d->actionMan); menu->addSeparator(); - QMenu *frames = menu->addMenu(i18nc("@item:inmenu", "Keyframes")); - KisActionManager::safePopulateMenu(frames, "insert_keyframes_right", m_d->actionMan); - KisActionManager::safePopulateMenu(frames, "insert_keyframes_left", m_d->actionMan); - - frames->addSeparator(); - KisActionManager::safePopulateMenu(frames, "insert_n_keyframes_right", m_d->actionMan); - KisActionManager::safePopulateMenu(frames, "insert_n_keyframes_left", m_d->actionMan); - QMenu *hold = menu->addMenu(i18nc("@item:inmenu", "Hold Frames")); - KisActionManager::safePopulateMenu(hold, "insert_hold_frame", m_d->actionMan); - KisActionManager::safePopulateMenu(hold, "remove_hold_frame", m_d->actionMan); + { //Frames submenu. + QMenu *frames = menu->addMenu(i18nc("@item:inmenu", "Keyframes")); + KisActionManager::safePopulateMenu(frames, "insert_keyframe_left", m_d->actionMan); + KisActionManager::safePopulateMenu(frames, "insert_keyframe_right", m_d->actionMan); + frames->addSeparator(); + KisActionManager::safePopulateMenu(frames, "insert_multiple_keyframes", m_d->actionMan); + } - hold->addSeparator(); - KisActionManager::safePopulateMenu(hold, "insert_n_hold_frames", m_d->actionMan); - KisActionManager::safePopulateMenu(hold, "remove_n_hold_frames", m_d->actionMan); + { //Holds submenu. + QMenu *hold = menu->addMenu(i18nc("@item:inmenu", "Hold Frames")); + KisActionManager::safePopulateMenu(hold, "insert_hold_frame", m_d->actionMan); + KisActionManager::safePopulateMenu(hold, "remove_hold_frame", m_d->actionMan); + hold->addSeparator(); + KisActionManager::safePopulateMenu(hold, "insert_multiple_hold_frames", m_d->actionMan); + KisActionManager::safePopulateMenu(hold, "remove_multiple_hold_frames", m_d->actionMan); + } menu->addSeparator(); + KisActionManager::safePopulateMenu(menu, "remove_frames", m_d->actionMan); KisActionManager::safePopulateMenu(menu, "remove_frames_and_pull", m_d->actionMan); menu->addSeparator(); + if (addFrameCreationActions) { KisActionManager::safePopulateMenu(menu, "add_blank_frame", m_d->actionMan); KisActionManager::safePopulateMenu(menu, "add_duplicate_frame", m_d->actionMan); menu->addSeparator(); } - } 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::FrameColorLabelIndexRole); int labelIndex = colorLabel.isValid() ? colorLabel.toInt() : 0; m_d->colorSelector->setCurrentIndex(labelIndex); } QMenu menu; createFrameEditingMenuActions(&menu, false); menu.addSeparator(); menu.addAction(m_d->colorSelectorAction); menu.exec(event->globalPos()); } else { { KisSignalsBlocker b(m_d->colorSelector); KisImageConfig cfg; const int labelIndex = cfg.defaultFrameColorLabel(); m_d->colorSelector->setCurrentIndex(labelIndex); } QMenu menu; createFrameEditingMenuActions(&menu, true); menu.addSeparator(); menu.addAction(m_d->colorSelectorAction); menu.exec(event->globalPos()); } } else if (numSelectedItems > 1) { int labelIndex = -1; bool haveFrames = false; Q_FOREACH(QModelIndex index, selectedIndexes()) { haveFrames |= index.data(TimelineFramesModel::FrameExistsRole).toBool(); QVariant colorLabel = index.data(TimelineFramesModel::FrameColorLabelIndexRole); if (colorLabel.isValid()) { if (labelIndex == -1) { // First label labelIndex = colorLabel.toInt(); } else if (labelIndex != colorLabel.toInt()) { // Mixed colors in selection labelIndex = -1; break; } } } if (haveFrames) { KisSignalsBlocker b(m_d->multiframeColorSelector); m_d->multiframeColorSelector->setCurrentIndex(labelIndex); } QMenu menu; createFrameEditingMenuActions(&menu, false); menu.addSeparator(); KisActionManager::safePopulateMenu(&menu, "mirror_frames", m_d->actionMan); menu.addSeparator(); menu.addAction(m_d->multiframeColorSelectorAction); menu.exec(event->globalPos()); } } else if (event->button() == Qt::MidButton) { QModelIndex index = model()->buddy(indexAt(event->pos())); if (index.isValid()) { QStyleOptionViewItem option = viewOptions(); option.rect = visualRect(index); // The offset of the headers is needed to get the correct position inside the view. m_d->tip.showTip(this, event->pos() + QPoint(verticalHeader()->width(), horizontalHeader()->height()), option, index); } event->accept(); } else { if (index.isValid()) { m_d->model->setLastClickedIndex(index); } m_d->lastPressedPosition = QPoint(horizontalOffset(), verticalOffset()) + event->pos(); m_d->lastPressedModifier = event->modifiers(); 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 if (e->buttons() == Qt::MidButton) { QModelIndex index = model()->buddy(indexAt(e->pos())); if (index.isValid()) { QStyleOptionViewItem option = viewOptions(); option.rect = visualRect(index); // The offset of the headers is needed to get the correct position inside the view. m_d->tip.showTip(this, e->pos() + QPoint(verticalHeader()->width(), horizontalHeader()->height()), option, index); } 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::slotUpdateFrameActions() { if (!m_d->actionMan) return; const QModelIndexList editableIndexes = calculateSelectionSpan(false, true); const bool hasEditableFrames = !editableIndexes.isEmpty(); bool hasExistingFrames = false; Q_FOREACH (const QModelIndex &index, editableIndexes) { if (model()->data(index, TimelineFramesModel::FrameExistsRole).toBool()) { hasExistingFrames = true; break; } } auto enableAction = [this] (const QString &id, bool value) { KisAction *action = m_d->actionMan->actionByName(id); KIS_SAFE_ASSERT_RECOVER_RETURN(action); action->setEnabled(value); }; enableAction("add_blank_frame", hasEditableFrames); enableAction("add_duplicate_frame", hasEditableFrames); - enableAction("insert_keyframes_right", hasEditableFrames); - enableAction("insert_n_keyframes_right", hasEditableFrames); - - enableAction("insert_keyframes_left", hasEditableFrames); - enableAction("insert_n_keyframes_left", hasEditableFrames); + enableAction("insert_keyframe_left", hasEditableFrames); + enableAction("insert_keyframe_right", hasEditableFrames); + enableAction("insert_multiple_keyframes", hasEditableFrames); enableAction("remove_frames", hasEditableFrames && hasExistingFrames); enableAction("remove_frames_and_pull", hasEditableFrames); enableAction("insert_hold_frame", hasEditableFrames); - enableAction("insert_n_hold_frames", hasEditableFrames); + enableAction("insert_multiple_hold_frames", hasEditableFrames); enableAction("remove_hold_frame", hasEditableFrames); - enableAction("remove_n_hold_frames", hasEditableFrames); + enableAction("remove_multiple_hold_frames", hasEditableFrames); enableAction("mirror_frames", hasEditableFrames && editableIndexes.size() > 1); enableAction("copy_frames_to_clipboard", true); enableAction("cut_frames_to_clipboard", hasEditableFrames); QClipboard *cp = QApplication::clipboard(); const QMimeData *data = cp->mimeData(); enableAction("paste_frames_from_clipboard", data && data->hasFormat("application/x-krita-frame")); //TODO: update column actions! } void TimelineFramesView::slotSetStartTimeToCurrentPosition() { m_d->model->setFullClipRangeStart(this->currentIndex().column()); } void TimelineFramesView::slotSetEndTimeToCurrentPosition() { m_d->model->setFullClipRangeEnd(this->currentIndex().column()); } void TimelineFramesView::slotUpdatePlackbackRange() { QSet rows; int minColumn = 0; int maxColumn = 0; calculateSelectionMetrics(minColumn, maxColumn, rows); m_d->model->setFullClipRangeStart(minColumn); m_d->model->setFullClipRangeEnd(maxColumn); } void TimelineFramesView::slotLayerContextMenuRequested(const QPoint &globalPos) { m_d->layerEditingMenu->exec(globalPos); } 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::slotNewFrame() +void TimelineFramesView::slotAddBlankFrame() { QModelIndex index = currentIndex(); if (!index.isValid() || !m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) { return; } m_d->model->createFrame(index); } -void TimelineFramesView::slotCopyFrame() +void TimelineFramesView::slotAddDuplicateFrame() { QModelIndex index = currentIndex(); if (!index.isValid() || !m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) { return; } m_d->model->copyFrame(index); } void TimelineFramesView::calculateSelectionMetrics(int &minColumn, int &maxColumn, QSet &rows) const { minColumn = std::numeric_limits::max(); maxColumn = std::numeric_limits::min(); Q_FOREACH (const QModelIndex &index, selectionModel()->selectedIndexes()) { if (!m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) continue; rows.insert(index.row()); minColumn = qMin(minColumn, index.column()); maxColumn = qMax(maxColumn, index.column()); } } -void TimelineFramesView::insertFramesImpl(int insertAtColumn, int count, QSet rows, bool forceEntireColumn) -{ - if (forceEntireColumn) { - rows.clear(); - for (int i = 0; i < m_d->model->rowCount(); i++) { - if (!m_d->model->data(m_d->model->index(i, insertAtColumn), TimelineFramesModel::FrameEditableRole).toBool()) continue; - rows.insert(i); - } - } - - if (!rows.isEmpty()) { - m_d->model->insertFrames(insertAtColumn, rows.toList(), count); - } -} - -void TimelineFramesView::slotInsertKeyframesLeft(int count, bool forceEntireColumn) -{ - QSet rows; - int minColumn = 0; - int maxColumn = 0; - - calculateSelectionMetrics(minColumn, maxColumn, rows); - - if (count <= 0) { - count = qMax(1, maxColumn - minColumn + 1); - } - - insertFramesImpl(minColumn, count, rows, forceEntireColumn); -} - -void TimelineFramesView::slotInsertKeyframesRight(int count, bool forceEntireColumn) +void TimelineFramesView::insertKeyframes(int count, int timing, TimelineDirection direction, bool entireColumn) { QSet rows; - int minColumn = 0; - int maxColumn = 0; + int minColumn = 0, maxColumn = 0; calculateSelectionMetrics(minColumn, maxColumn, rows); - if (count <= 0) { + if (count <= 0) { //Negative count? Use number of selected frames. count = qMax(1, maxColumn - minColumn + 1); } - insertFramesImpl(maxColumn + 1, count, rows, forceEntireColumn); -} - -void TimelineFramesView::slotInsertColumnsLeft(int count) -{ - slotInsertKeyframesLeft(count, true); -} - -void TimelineFramesView::slotInsertColumnsRight(int count) -{ - slotInsertKeyframesRight(count, true); -} - -void TimelineFramesView::slotInsertKeyframesLeftCustom() -{ - bool ok = false; - const int count = QInputDialog::getInt(this, - i18nc("@title:window", "Insert left"), - i18nc("@label:spinbox", "Enter number of frames"), - defaultNumberOfFramesToAdd(), - 1, 10000, 1, &ok); - - if (ok) { - setDefaultNumberOfFramesToAdd(count); - slotInsertKeyframesLeft(count); - } -} - - -void TimelineFramesView::slotInsertKeyframesRightCustom() -{ - bool ok = false; - const int count = QInputDialog::getInt(this, - i18nc("@title:window", "Insert right"), - i18nc("@label:spinbox", "Enter number of frames"), - defaultNumberOfFramesToAdd(), - 1, 10000, 1, &ok); + const int insertionColumn = + direction == TimelineDirection::RIGHT ? + maxColumn + 1 : minColumn; - if (ok) { - setDefaultNumberOfFramesToAdd(count); - slotInsertKeyframesRight(count); + if (entireColumn) { + rows.clear(); + for (int i = 0; i < m_d->model->rowCount(); i++) { + if (!m_d->model->data(m_d->model->index(i, insertionColumn), TimelineFramesModel::FrameEditableRole).toBool()) continue; + rows.insert(i); + } } -} -void TimelineFramesView::slotInsertColumnsLeftCustom() -{ - bool ok = false; - const int count = QInputDialog::getInt(this, - i18nc("@title:window", "Insert left"), - i18nc("@label:spinbox", "Enter number of columns"), - defaultNumberOfColumnsToAdd(), - 1, 10000, 1, &ok); - - if (ok) { - setDefaultNumberOfColumnsToAdd(count); - slotInsertColumnsLeft(count); + if (!rows.isEmpty()) { + m_d->model->insertFrames(insertionColumn, rows.toList(), count, timing); } } - -void TimelineFramesView::slotInsertColumnsRightCustom() +void TimelineFramesView::insertMultipleKeyframes(bool entireColumn) { - bool ok = false; - const int count = QInputDialog::getInt(this, - i18nc("@title:window", "Insert right"), - i18nc("@label:spinbox", "Enter number of columns"), - defaultNumberOfColumnsToAdd(), - 1, 10000, 1, &ok); + int count, timing; + TimelineDirection direction; - if (ok) { - setDefaultNumberOfColumnsToAdd(count); - slotInsertColumnsRight(count); + if (m_d->insertKeyframeDialog->promptUserSettings(count, timing, direction)) { + insertKeyframes(count, timing, direction, entireColumn); } } -QModelIndexList TimelineFramesView::calculateSelectionSpan(bool forceEntireColumn, bool editableOnly) const +QModelIndexList TimelineFramesView::calculateSelectionSpan(bool entireColumn, bool editableOnly) const { QModelIndexList indexes; - if (forceEntireColumn) { + if (entireColumn) { QSet rows; int minColumn = 0; int maxColumn = 0; calculateSelectionMetrics(minColumn, maxColumn, rows); rows.clear(); for (int i = 0; i < m_d->model->rowCount(); i++) { if (editableOnly && !m_d->model->data(m_d->model->index(i, minColumn), TimelineFramesModel::FrameEditableRole).toBool()) continue; for (int column = minColumn; column <= maxColumn; column++) { indexes << m_d->model->index(i, column); } } } else { Q_FOREACH (const QModelIndex &index, selectionModel()->selectedIndexes()) { if (!editableOnly || m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) { indexes << index; } } } return indexes; } -void TimelineFramesView::slotRemoveFrame(bool forceEntireColumn, bool needsOffset) +void TimelineFramesView::slotRemoveSelectedFrames(bool entireColumn, bool needsOffset) { - const QModelIndexList indexes = calculateSelectionSpan(forceEntireColumn); + const QModelIndexList indexes = calculateSelectionSpan(entireColumn); if (!indexes.isEmpty()) { if (needsOffset) { m_d->model->removeFramesAndOffset(indexes); } else { m_d->model->removeFrames(indexes); } } } -void TimelineFramesView::slotRemoveColumns() -{ - slotRemoveFrame(true); -} - -void TimelineFramesView::slotRemoveFramesAndShift(bool forceEntireColumn) -{ - slotRemoveFrame(forceEntireColumn, true); -} - -void TimelineFramesView::slotRemoveColumnsAndShift() -{ - slotRemoveFramesAndShift(true); -} - -void TimelineFramesView::slotInsertHoldFrames(int count, bool forceEntireColumn) +void TimelineFramesView::insertOrRemoveHoldFrames(int count, bool entireColumn) { QModelIndexList indexes; - if (!forceEntireColumn) { + if (!entireColumn) { Q_FOREACH (const QModelIndex &index, selectionModel()->selectedIndexes()) { if (m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) { indexes << index; } } } else { const int column = selectionModel()->currentIndex().column(); for (int i = 0; i < m_d->model->rowCount(); i++) { const QModelIndex index = m_d->model->index(i, column); if (m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) { indexes << index; } } } if (!indexes.isEmpty()) { m_d->model->insertHoldFrames(indexes, count); } } -void TimelineFramesView::slotRemoveHoldFrames(int count, bool forceEntireColumn) -{ - slotInsertHoldFrames(-count, forceEntireColumn); -} - -void TimelineFramesView::slotInsertHoldFramesCustom() +void TimelineFramesView::insertOrRemoveMultipleHoldFrames(bool insertion, bool entireColumn) { bool ok = false; const int count = QInputDialog::getInt(this, - i18nc("@title:window", "Insert hold frames"), + i18nc("@title:window", "Insert or Remove Hold Frames"), i18nc("@label:spinbox", "Enter number of frames"), defaultNumberOfFramesToAdd(), 1, 10000, 1, &ok); if (ok) { - setDefaultNumberOfFramesToAdd(count); - slotInsertHoldFrames(count); - } -} - -void TimelineFramesView::slotRemoveHoldFramesCustom() -{ - bool ok = false; - const int count = QInputDialog::getInt(this, - i18nc("@title:window", "Remove hold frames"), - i18nc("@label:spinbox", "Enter number of frames"), - defaultNumberOfFramesToRemove(), - 1, 10000, 1, &ok); - - if (ok) { - setDefaultNumberOfFramesToRemove(count); - slotRemoveHoldFrames(count); - } -} - -void TimelineFramesView::slotInsertHoldColumns(int count) -{ - slotInsertHoldFrames(count, true); -} - -void TimelineFramesView::slotRemoveHoldColumns(int count) -{ - slotRemoveHoldFrames(count, true); -} - -void TimelineFramesView::slotInsertHoldColumnsCustom() -{ - bool ok = false; - const int count = QInputDialog::getInt(this, - i18nc("@title:window", "Insert hold columns"), - i18nc("@label:spinbox", "Enter number of columns"), - defaultNumberOfColumnsToAdd(), - 1, 10000, 1, &ok); - - if (ok) { - setDefaultNumberOfColumnsToAdd(count); - slotInsertHoldColumns(count); - } -} - -void TimelineFramesView::slotRemoveHoldColumnsCustom() -{ - bool ok = false; - const int count = QInputDialog::getInt(this, - i18nc("@title:window", "Remove hold columns"), - i18nc("@label:spinbox", "Enter number of columns"), - defaultNumberOfColumnsToRemove(), - 1, 10000, 1, &ok); - - if (ok) { - setDefaultNumberOfColumnsToRemove(count); - slotRemoveHoldColumns(count); + if (insertion) { + setDefaultNumberOfFramesToAdd(count); + insertOrRemoveHoldFrames(count, entireColumn); + } else { + setDefaultNumberOfFramesToRemove(count); + insertOrRemoveHoldFrames(-count, entireColumn); + } } } -void TimelineFramesView::slotMirrorFrames(bool forceEntireColumn) +void TimelineFramesView::slotMirrorFrames(bool entireColumn) { - const QModelIndexList indexes = calculateSelectionSpan(forceEntireColumn); + const QModelIndexList indexes = calculateSelectionSpan(entireColumn); if (!indexes.isEmpty()) { m_d->model->mirrorFrames(indexes); } } -void TimelineFramesView::slotMirrorColumns() +void TimelineFramesView::cutCopyImpl(bool entireColumn, bool copy) { - slotMirrorFrames(true); -} - -void TimelineFramesView::cutCopyImpl(bool forceEntireColumn, bool copy) -{ - const QModelIndexList indexes = calculateSelectionSpan(forceEntireColumn, !copy); + const QModelIndexList indexes = calculateSelectionSpan(entireColumn, !copy); if (indexes.isEmpty()) return; int minColumn = std::numeric_limits::max(); int minRow = std::numeric_limits::max(); Q_FOREACH (const QModelIndex &index, indexes) { minRow = qMin(minRow, index.row()); minColumn = qMin(minColumn, index.column()); } const QModelIndex baseIndex = m_d->model->index(minRow, minColumn); QMimeData *data = m_d->model->mimeDataExtended(indexes, baseIndex, copy ? TimelineFramesModel::CopyFramesPolicy : TimelineFramesModel::MoveFramesPolicy); if (data) { QClipboard *cb = QApplication::clipboard(); cb->setMimeData(data); } } -void TimelineFramesView::slotCopyFrames(bool forceEntireColumn) -{ - cutCopyImpl(forceEntireColumn, true); -} - -void TimelineFramesView::slotCutFrames(bool forceEntireColumn) -{ - cutCopyImpl(forceEntireColumn, false); -} - -void TimelineFramesView::slotPasteFrames(bool forceEntireColumn) +void TimelineFramesView::slotPasteFrames(bool entireColumn) { const QModelIndex currentIndex = - !forceEntireColumn ? this->currentIndex() : m_d->model->index(0, this->currentIndex().column()); + !entireColumn ? this->currentIndex() : m_d->model->index(0, this->currentIndex().column()); if (!currentIndex.isValid()) return; QClipboard *cb = QApplication::clipboard(); const QMimeData *data = cb->mimeData(); if (data && data->hasFormat("application/x-krita-frame")) { bool dataMoved = false; bool result = m_d->model->dropMimeDataExtended(data, Qt::MoveAction, currentIndex, &dataMoved); if (result && dataMoved) { cb->clear(); } } } -void TimelineFramesView::slotCutColumns() -{ - slotCutFrames(true); -} - -void TimelineFramesView::slotPasteColumns() -{ - slotPasteFrames(true); -} - -void TimelineFramesView::slotCopyColumns() -{ - slotCopyFrames(true); -} - int TimelineFramesView::defaultNumberOfFramesToAdd() const { KConfigGroup cfg = KSharedConfig::openConfig()->group("FrameActionsDefaultValues"); return cfg.readEntry("defaultNumberOfFramesToAdd", 1); } void TimelineFramesView::setDefaultNumberOfFramesToAdd(int value) const { KConfigGroup cfg = KSharedConfig::openConfig()->group("FrameActionsDefaultValues"); cfg.writeEntry("defaultNumberOfFramesToAdd", value); } int TimelineFramesView::defaultNumberOfColumnsToAdd() const { KConfigGroup cfg = KSharedConfig::openConfig()->group("FrameActionsDefaultValues"); return cfg.readEntry("defaultNumberOfColumnsToAdd", 1); } void TimelineFramesView::setDefaultNumberOfColumnsToAdd(int value) const { KConfigGroup cfg = KSharedConfig::openConfig()->group("FrameActionsDefaultValues"); cfg.writeEntry("defaultNumberOfColumnsToAdd", value); } int TimelineFramesView::defaultNumberOfFramesToRemove() const { KConfigGroup cfg = KSharedConfig::openConfig()->group("FrameActionsDefaultValues"); return cfg.readEntry("defaultNumberOfFramesToRemove", 1); } void TimelineFramesView::setDefaultNumberOfFramesToRemove(int value) const { KConfigGroup cfg = KSharedConfig::openConfig()->group("FrameActionsDefaultValues"); cfg.writeEntry("defaultNumberOfFramesToRemove", value); } int TimelineFramesView::defaultNumberOfColumnsToRemove() const { KConfigGroup cfg = KSharedConfig::openConfig()->group("FrameActionsDefaultValues"); return cfg.readEntry("defaultNumberOfColumnsToRemove", 1); } void TimelineFramesView::setDefaultNumberOfColumnsToRemove(int value) const { KConfigGroup cfg = KSharedConfig::openConfig()->group("FrameActionsDefaultValues"); cfg.writeEntry("defaultNumberOfColumnsToRemove", value); } bool TimelineFramesView::viewportEvent(QEvent *event) { if (event->type() == QEvent::ToolTip && model()) { QHelpEvent *he = static_cast(event); QModelIndex index = model()->buddy(indexAt(he->pos())); if (index.isValid()) { QStyleOptionViewItem option = viewOptions(); option.rect = visualRect(index); // The offset of the headers is needed to get the correct position inside the view. m_d->tip.showTip(this, he->pos() + QPoint(verticalHeader()->width(), horizontalHeader()->height()), option, index); return true; } } return QTableView::viewportEvent(event); } diff --git a/plugins/dockers/animation/timeline_frames_view.h b/plugins/dockers/animation/timeline_frames_view.h index d099160cf5..9acfe3be03 100644 --- a/plugins/dockers/animation/timeline_frames_view.h +++ b/plugins/dockers/animation/timeline_frames_view.h @@ -1,176 +1,198 @@ /* * 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 "kis_action_manager.h" #include "kritaanimationdocker_export.h" class KisAction; class TimelineWidget; +enum TimelineDirection : short +{ + LEFT = -1, + BEFORE = -1, + + RIGHT = 1, + AFTER = 1 +}; + class KRITAANIMATIONDOCKER_EXPORT TimelineFramesView : public QTableView { Q_OBJECT + public: TimelineFramesView(QWidget *parent); ~TimelineFramesView() override; void setModel(QAbstractItemModel *model) override; void updateGeometries() override; void setShowInTimeline(KisAction *action); - void setActionManager( KisActionManager * actionManager); + void setActionManager(KisActionManager *actionManager); public Q_SLOTS: void slotSelectionChanged(); void slotUpdateIcons(); private Q_SLOTS: void slotUpdateLayersMenu(); void slotUpdateFrameActions(); void slotSetStartTimeToCurrentPosition(); void slotSetEndTimeToCurrentPosition(); void slotUpdatePlackbackRange(); + // Layer void slotAddNewLayer(); void slotAddExistingLayer(QAction *action); void slotDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); - void slotRemoveLayer(); - void slotLayerContextMenuRequested(const QPoint &globalPos); - void slotNewFrame(); - void slotCopyFrame(); - + // New, Insert and Remove Frames + void slotAddBlankFrame(); + void slotAddDuplicateFrame(); - void slotInsertKeyframesLeft(int count = -1, bool forceEntireColumn = false); - void slotInsertKeyframesRight(int count = -1, bool forceEntireColumn = false); + void slotInsertKeyframeLeft() {insertKeyframes(-1, 1, TimelineDirection::LEFT, false);} + void slotInsertKeyframeRight() {insertKeyframes(-1, 1, TimelineDirection::RIGHT, false);} - void slotInsertKeyframesLeftCustom(); - void slotInsertKeyframesRightCustom(); + void slotInsertKeyframeColumnLeft() {insertKeyframes(-1, 1, TimelineDirection::LEFT, true);} + void slotInsertKeyframeColumnRight() {insertKeyframes(-1, 1, TimelineDirection::RIGHT, true);} - void slotRemoveFrame(bool forceEntireColumn = false, bool needsOffset = false); - void slotRemoveFramesAndShift(bool forceEntireColumn = false); + void slotInsertMultipleKeyframes() {insertMultipleKeyframes(false);} + void slotInsertMultipleKeyframeColumns() {insertMultipleKeyframes(true);} - void slotInsertColumnsLeft(int count = -1); - void slotInsertColumnsLeftCustom(); + void slotRemoveSelectedFrames(bool entireColumn = false, bool needsOffset = false); + void slotRemoveSelectedFramesAndShift() {slotRemoveSelectedFrames(false, true);} - void slotInsertColumnsRight(int count = -1); - void slotInsertColumnsRightCustom(); + void slotRemoveSelectedColumns() {slotRemoveSelectedFrames(true);} + void slotRemoveSelectedColumnsAndShift() {slotRemoveSelectedFrames(true, true);} - void slotRemoveColumns(); - void slotRemoveColumnsAndShift(); + void slotInsertHoldFrame() {insertOrRemoveHoldFrames(1);} + void slotRemoveHoldFrame() {insertOrRemoveHoldFrames(-1);} - void slotInsertHoldFrames(int count = 1, bool forceEntireColumn = false); - void slotRemoveHoldFrames(int count = 1, bool forceEntireColumn = false); + void slotInsertHoldFrameColumn() {insertOrRemoveHoldFrames(1,true);} + void slotRemoveHoldFrameColumn() {insertOrRemoveHoldFrames(-1,true);} - void slotInsertHoldFramesCustom(); - void slotRemoveHoldFramesCustom(); + void slotInsertMultipleHoldFrames() {insertOrRemoveMultipleHoldFrames(true);} + void slotRemoveMultipleHoldFrames() {insertOrRemoveMultipleHoldFrames(false);} - void slotInsertHoldColumns(int count = 1); - void slotRemoveHoldColumns(int count = 1); + void slotInsertMultipleHoldFrameColumns() {insertOrRemoveMultipleHoldFrames(true, true);} + void slotRemoveMultipleHoldFrameColumns() {insertOrRemoveMultipleHoldFrames(false, true);} - void slotInsertHoldColumnsCustom(); - void slotRemoveHoldColumnsCustom(); + void slotMirrorFrames(bool entireColumn = false); + void slotMirrorColumns() {slotMirrorFrames(true);} - void slotMirrorFrames(bool forceEntireColumn = false); - void slotMirrorColumns(); + // Copy-paste + void slotCopyFrames() {cutCopyImpl(false, true);} + void slotCutFrames() {cutCopyImpl(false, false);} + void slotCopyColumns() {cutCopyImpl(true, true);} + void slotCutColumns() {cutCopyImpl(true, false);} - void slotCopyFrames(bool forceEntireColumn = false); - void slotCutFrames(bool forceEntireColumn = false); - void slotPasteFrames(bool forceEntireColumn = false); - - void slotCopyColumns(); - void slotCutColumns(); - void slotPasteColumns(); + void slotPasteFrames(bool entireColumn = false); + void slotPasteColumns() {slotPasteFrames(true);} 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); - + // Audio void slotSelectAudioChannelFile(); void slotAudioChannelMute(bool value); void slotAudioChannelRemove(); void slotUpdateAudioActions(); void slotAudioVolumeChanged(int value); private: void setFramesPerSecond(int fps); + void calculateSelectionMetrics(int &minColumn, int &maxColumn, QSet &rows) const; - void insertFramesImpl(int insertAtColumn, int count, QSet rows, bool forceEntireColumn); + /* Insert new keyframes/columns. + * + * count - Number of frames to add. If <0, use number of currently SELECTED frames. + * timing - Animation timing of frames to be added (on 1s, 2s, 3s, etc.) + * direction - Insert frames before (left) or after (right) selection scrubber. + * entireColumn - Create frames on all layers (rows) instead of just the active layer? + */ + void insertKeyframes(int count = 1, int timing = 1, + TimelineDirection direction = TimelineDirection::LEFT, bool entireColumn = false); + void insertMultipleKeyframes(bool entireColumn = false); + + void insertOrRemoveHoldFrames(int count, bool entireColumn = false); + void insertOrRemoveMultipleHoldFrames(bool insertion, bool entireColumn = false); + + void cutCopyImpl(bool entireColumn, bool copy); void createFrameEditingMenuActions(QMenu *menu, bool addFrameCreationActions); - QModelIndexList calculateSelectionSpan(bool forceEntireColumn, bool editableOnly = true) const; - void cutCopyImpl(bool forceEntireColumn, bool copy); + QModelIndexList calculateSelectionSpan(bool entireColumn, bool editableOnly = true) const; int defaultNumberOfFramesToAdd() const; void setDefaultNumberOfFramesToAdd(int value) const; int defaultNumberOfColumnsToAdd() const; void setDefaultNumberOfColumnsToAdd(int value) const; int defaultNumberOfFramesToRemove() const; void setDefaultNumberOfFramesToRemove(int value) const; int defaultNumberOfColumnsToRemove() const; void setDefaultNumberOfColumnsToRemove(int value) const; protected: QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex &index, const QEvent *event) const override; void currentChanged(const QModelIndex ¤t, const QModelIndex &previous) override; void startDrag(Qt::DropActions supportedActions) override; void dragEnterEvent(QDragEnterEvent *event) override; void dragMoveEvent(QDragMoveEvent *event) override; void dropEvent(QDropEvent *event) override; void dragLeaveEvent(QDragLeaveEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void wheelEvent(QWheelEvent *e) override; - void rowsInserted(const QModelIndex& parent, int start, int end) override; + void rowsInserted(const QModelIndex &parent, int start, int end) override; bool viewportEvent(QEvent *event) override; private: struct Private; const QScopedPointer m_d; }; #endif /* __TIMELINE_FRAMES_VIEW_H */ diff --git a/plugins/dockers/animation/timeline_insert_keyframe_dialog.cpp b/plugins/dockers/animation/timeline_insert_keyframe_dialog.cpp new file mode 100644 index 0000000000..cb66d5b68b --- /dev/null +++ b/plugins/dockers/animation/timeline_insert_keyframe_dialog.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018 Emmet O'Neill + * Copyright (c) 2018 Eoin O'Neill + * + * 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_insert_keyframe_dialog.h" +#include "timeline_frames_view.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +TimelineInsertKeyframeDialog::TimelineInsertKeyframeDialog(QWidget *parent) : + QDialog(parent) +{ + setWindowTitle(i18nc("@title:window","Insert Keyframes")); + setModal(true); + setLayout(new QVBoxLayout(this)); + + { // Count and Spacing Forms. + QWidget *forms = new QWidget(this); + layout()->addWidget(forms); + + frameCountSpinbox.setMinimum(1); + frameCountSpinbox.setValue(1); + + frameTimingSpinbox.setMinimum(1); + frameTimingSpinbox.setValue(1); + + QFormLayout *LO = new QFormLayout(this); + forms->setLayout(LO); + + LO->addRow(QString(i18nc("@label:spinbox", "Number of frames:")), &frameCountSpinbox); + LO->addRow(QString(i18nc("@label:spinbox", "Frame timing:")), &frameTimingSpinbox); + } + + { // Side Buttons. + QGroupBox *sideRadioButtons = new QGroupBox(i18nc("@label:group","Side:"), this); + layout()->addWidget(sideRadioButtons); + + leftBefore = new QRadioButton(i18nc("@label:radio", "Left / Before"), sideRadioButtons); + rightAfter = new QRadioButton(i18nc("@label:radio", "Right / After"), sideRadioButtons); + leftBefore->setChecked(true); + + QVBoxLayout *LO = new QVBoxLayout(this); + sideRadioButtons->setLayout(LO); + + LO->addWidget(leftBefore); + LO->addWidget(rightAfter); + } + + { // Cancel / OK Buttons. + QDialogButtonBox *buttonbox = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); + layout()->addWidget(buttonbox); + + connect(buttonbox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonbox, SIGNAL(rejected()), this, SLOT(reject())); + } +} + +bool TimelineInsertKeyframeDialog::promptUserSettings(int &out_count, int &out_timing, TimelineDirection &out_direction) +{ + if (exec() == QDialog::Accepted) { + out_count = frameCountSpinbox.value(); + out_timing = frameTimingSpinbox.value(); + + out_direction = TimelineDirection::LEFT; // Default + if (rightAfter && rightAfter->isChecked()) { + out_direction = TimelineDirection::RIGHT; + } + + return true; + } + return false; +} diff --git a/libs/ui/dialogs/kis_about_application.h b/plugins/dockers/animation/timeline_insert_keyframe_dialog.h similarity index 51% copy from libs/ui/dialogs/kis_about_application.h copy to plugins/dockers/animation/timeline_insert_keyframe_dialog.h index 23a94d2dcb..b6392ce54f 100644 --- a/libs/ui/dialogs/kis_about_application.h +++ b/plugins/dockers/animation/timeline_insert_keyframe_dialog.h @@ -1,35 +1,45 @@ /* - * Copyright (c) 2014 Boudewijn Rempt + * Copyright (c) 2018 Emmet O'Neill + * Copyright (c) 2018 Eoin O'Neill * * 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_ABOUT_APPLICATION_H -#define KIS_ABOUT_APPLICATION_H +#ifndef __TIMELINE_INSERT_KEYFRAME_DIALOG_H +#define __TIMELINE_INSERT_KEYFRAME_DIALOG_H + +#include "kritaanimationdocker_export.h" #include +#include +#include + +enum TimelineDirection : short; -class KisAboutApplication : public QDialog -{ +class KRITAANIMATIONDOCKER_EXPORT TimelineInsertKeyframeDialog : QDialog { Q_OBJECT -public: - explicit KisAboutApplication(QWidget *parent = 0); +private: + QSpinBox frameCountSpinbox; + QSpinBox frameTimingSpinbox; -Q_SIGNALS: + QRadioButton *leftBefore; + QRadioButton *rightAfter; -public Q_SLOTS: +public: + TimelineInsertKeyframeDialog(QWidget *parent = 0); + bool promptUserSettings(int &count, int &timing, TimelineDirection &out_direction); }; -#endif // KIS_ABOUT_APPLICATION_H +#endif // __TIMELINE_INSERT_KEYFRAME_DIALOG_H diff --git a/plugins/dockers/animation/timeline_ruler_header.cpp b/plugins/dockers/animation/timeline_ruler_header.cpp index a7978bf835..0203debbcd 100644 --- a/plugins/dockers/animation/timeline_ruler_header.cpp +++ b/plugins/dockers/animation/timeline_ruler_header.cpp @@ -1,534 +1,531 @@ /* * 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_ruler_header.h" #include #include #include #include #include #include #include "kis_time_based_item_model.h" #include "timeline_color_scheme.h" #include "kis_action.h" #include "kis_debug.h" struct TimelineRulerHeader::Private { Private() : fps(12), lastPressSectionIndex(-1) {} int fps; KisTimeBasedItemModel *model; int lastPressSectionIndex; int calcSpanWidth(const int sectionWidth); QModelIndexList prepareFramesSlab(int startCol, int endCol); KisActionManager* actionMan = 0; }; TimelineRulerHeader::TimelineRulerHeader(QWidget *parent) : QHeaderView(Qt::Horizontal, parent), m_d(new Private) { setSectionResizeMode(QHeaderView::Fixed); setDefaultSectionSize(18); - - - - } TimelineRulerHeader::~TimelineRulerHeader() { } -void TimelineRulerHeader::setActionManager( KisActionManager * actionManager) +void TimelineRulerHeader::setActionManager(KisActionManager *actionManager) { m_d->actionMan = actionManager; if (actionManager) { KisAction *action; - action = actionManager->createAction("insert_columns_right"); - connect(action, SIGNAL(triggered()), SIGNAL(sigInsertColumnsRight())); - - action = actionManager->createAction("insert_n_columns_right"); - connect(action, SIGNAL(triggered()), SIGNAL(sigInsertColumnsRightCustom())); + action = actionManager->createAction("insert_column_left"); + connect(action, SIGNAL(triggered()), SIGNAL(sigInsertColumnLeft())); - action = actionManager->createAction("insert_columns_left"); - connect(action, SIGNAL(triggered()), SIGNAL(sigInsertColumnsLeft())); + action = actionManager->createAction("insert_column_right"); + connect(action, SIGNAL(triggered()), SIGNAL(sigInsertColumnRight())); - action = actionManager->createAction("insert_n_columns_left"); - connect(action, SIGNAL(triggered()), SIGNAL(sigInsertColumnsLeftCustom())); + action = actionManager->createAction("insert_multiple_columns"); + connect(action, SIGNAL(triggered()), SIGNAL(sigInsertMultipleColumns())); action = actionManager->createAction("remove_columns_and_pull"); connect(action, SIGNAL(triggered()), SIGNAL(sigRemoveColumnsAndShift())); action = actionManager->createAction("remove_columns"); connect(action, SIGNAL(triggered()), SIGNAL(sigRemoveColumns())); action = actionManager->createAction("insert_hold_column"); connect(action, SIGNAL(triggered()), SIGNAL(sigInsertHoldColumns())); - action = actionManager->createAction("insert_n_hold_columns"); + action = actionManager->createAction("insert_multiple_hold_columns"); connect(action, SIGNAL(triggered()), SIGNAL(sigInsertHoldColumnsCustom())); action = actionManager->createAction("remove_hold_column"); connect(action, SIGNAL(triggered()), SIGNAL(sigRemoveHoldColumns())); - action = actionManager->createAction("remove_n_hold_columns"); + action = actionManager->createAction("remove_multiple_hold_columns"); connect(action, SIGNAL(triggered()), SIGNAL(sigRemoveHoldColumnsCustom())); action = actionManager->createAction("mirror_columns"); connect(action, SIGNAL(triggered()), SIGNAL(sigMirrorColumns())); action = actionManager->createAction("copy_columns_to_clipboard"); connect(action, SIGNAL(triggered()), SIGNAL(sigCopyColumns())); action = actionManager->createAction("cut_columns_to_clipboard"); connect(action, SIGNAL(triggered()), SIGNAL(sigCutColumns())); action = actionManager->createAction("paste_columns_from_clipboard"); connect(action, SIGNAL(triggered()), SIGNAL(sigPasteColumns())); } } void TimelineRulerHeader::paintEvent(QPaintEvent *e) { QHeaderView::paintEvent(e); // Copied from Qt 4.8... if (count() == 0) return; QPainter painter(viewport()); const QPoint offset = dirtyRegionOffset(); QRect translatedEventRect = e->rect(); translatedEventRect.translate(offset); int start = -1; int end = -1; if (orientation() == Qt::Horizontal) { start = visualIndexAt(translatedEventRect.left()); end = visualIndexAt(translatedEventRect.right()); } else { start = visualIndexAt(translatedEventRect.top()); end = visualIndexAt(translatedEventRect.bottom()); } const bool reverseImpl = orientation() == Qt::Horizontal && isRightToLeft(); if (reverseImpl) { start = (start == -1 ? count() - 1 : start); end = (end == -1 ? 0 : end); } else { start = (start == -1 ? 0 : start); end = (end == -1 ? count() - 1 : end); } int tmp = start; start = qMin(start, end); end = qMax(tmp, end); /////////////////////////////////////////////////// /// Krita specific code. We should update in spans! const int spanStart = start - start % m_d->fps; const int spanEnd = end - end % m_d->fps + m_d->fps - 1; start = spanStart; end = qMin(count() - 1, spanEnd); /// End of Krita specific code /////////////////////////////////////////////////// QRect currentSectionRect; int logical; const int width = viewport()->width(); const int height = viewport()->height(); for (int i = start; i <= end; ++i) { // DK: cannot copy-paste easily... // if (d->isVisualIndexHidden(i)) // continue; painter.save(); logical = logicalIndex(i); if (orientation() == Qt::Horizontal) { currentSectionRect.setRect(sectionViewportPosition(logical), 0, sectionSize(logical), height); } else { currentSectionRect.setRect(0, sectionViewportPosition(logical), width, sectionSize(logical)); } currentSectionRect.translate(offset); QVariant variant = model()->headerData(logical, orientation(), Qt::FontRole); if (variant.isValid() && variant.canConvert()) { QFont sectionFont = qvariant_cast(variant); painter.setFont(sectionFont); } paintSection1(&painter, currentSectionRect, logical); painter.restore(); } } void TimelineRulerHeader::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const { // Base paint event should paint nothing in the sections area Q_UNUSED(painter); Q_UNUSED(rect); Q_UNUSED(logicalIndex); } void TimelineRulerHeader::paintSpan(QPainter *painter, int userFrameId, const QRect &spanRect, bool isIntegralLine, bool isPrevIntegralLine, QStyle *style, const QPalette &palette, const QPen &gridPen) const { painter->fillRect(spanRect, palette.brush(QPalette::Button)); int safeRight = spanRect.right(); QPen oldPen = painter->pen(); painter->setPen(gridPen); int adjustedTop = spanRect.top() + (!isIntegralLine ? spanRect.height() / 2 : 0); painter->drawLine(safeRight, adjustedTop, safeRight, spanRect.bottom()); if (isPrevIntegralLine) { painter->drawLine(spanRect.left() + 1, spanRect.top(), spanRect.left() + 1, spanRect.bottom()); } painter->setPen(oldPen); QString frameIdText = QString::number(userFrameId); QRect textRect(spanRect.topLeft() + QPoint(2, 0), QSize(spanRect.width() - 2, spanRect.height())); QStyleOptionHeader opt; initStyleOption(&opt); QStyle::State state = QStyle::State_None; if (isEnabled()) state |= QStyle::State_Enabled; if (window()->isActiveWindow()) state |= QStyle::State_Active; opt.state |= state; opt.selectedPosition = QStyleOptionHeader::NotAdjacent; opt.textAlignment = Qt::AlignLeft | Qt::AlignTop; opt.rect = textRect; opt.text = frameIdText; style->drawControl(QStyle::CE_HeaderLabel, &opt, painter, this); } int TimelineRulerHeader::Private::calcSpanWidth(const int sectionWidth) { const int minWidth = 36; int spanWidth = this->fps; while (spanWidth * sectionWidth < minWidth) { spanWidth *= 2; } bool splitHappened = false; do { splitHappened = false; if (!(spanWidth & 0x1) && spanWidth * sectionWidth / 2 > minWidth) { spanWidth /= 2; splitHappened = true; } else if (!(spanWidth % 3) && spanWidth * sectionWidth / 3 > minWidth) { spanWidth /= 3; splitHappened = true; } else if (!(spanWidth % 5) && spanWidth * sectionWidth / 5 > minWidth) { spanWidth /= 5; splitHappened = true; } } while (splitHappened); if (sectionWidth > minWidth) { spanWidth = 1; } return spanWidth; } void TimelineRulerHeader::paintSection1(QPainter *painter, const QRect &rect, int logicalIndex) const { if (!rect.isValid()) return; QFontMetrics metrics(this->font()); const int textHeight = metrics.height(); QPoint p1 = rect.topLeft() + QPoint(0, textHeight); QPoint p2 = rect.topRight() + QPoint(0, textHeight); QRect frameRect = QRect(p1, QSize(rect.width(), rect.height() - textHeight)); const int width = rect.width(); int spanWidth = m_d->calcSpanWidth(width); const int internalIndex = logicalIndex % spanWidth; const int userFrameId = logicalIndex; const int spanEnd = qMin(count(), logicalIndex + spanWidth); QRect spanRect(rect.topLeft(), QSize(width * (spanEnd - logicalIndex), textHeight)); QStyleOptionViewItem option = viewOptions(); const int gridHint = style()->styleHint(QStyle::SH_Table_GridLineColor, &option, this); const QColor gridColor = static_cast(gridHint); const QPen gridPen = QPen(gridColor); if (!internalIndex) { bool isIntegralLine = (logicalIndex + spanWidth) % m_d->fps == 0; bool isPrevIntegralLine = logicalIndex % m_d->fps == 0; paintSpan(painter, userFrameId, spanRect, isIntegralLine, isPrevIntegralLine, style(), palette(), gridPen); } { QBrush fillColor = TimelineColorScheme::instance()->headerEmpty(); QVariant activeValue = model()->headerData(logicalIndex, orientation(), KisTimeBasedItemModel::ActiveFrameRole); QVariant cachedValue = model()->headerData(logicalIndex, orientation(), KisTimeBasedItemModel::FrameCachedRole); if (activeValue.isValid() && activeValue.toBool()) { fillColor = TimelineColorScheme::instance()->headerActive(); } else if (cachedValue.isValid() && cachedValue.toBool()) { fillColor = TimelineColorScheme::instance()->headerCachedFrame(); } painter->fillRect(frameRect, fillColor); QVector lines; lines << QLine(p1, p2); lines << QLine(frameRect.topRight(), frameRect.bottomRight()); lines << QLine(frameRect.bottomLeft(), frameRect.bottomRight()); QPen oldPen = painter->pen(); painter->setPen(gridPen); painter->drawLines(lines); painter->setPen(oldPen); } } void TimelineRulerHeader::changeEvent(QEvent *event) { Q_UNUSED(event); updateMinimumSize(); } void TimelineRulerHeader::setFramePerSecond(int fps) { m_d->fps = fps; update(); } bool TimelineRulerHeader::setZoom(qreal zoom) { const int minSectionSize = 4; const int unitSectionSize = 18; int newSectionSize = zoom * unitSectionSize; if (newSectionSize < minSectionSize) { newSectionSize = minSectionSize; zoom = qreal(newSectionSize) / unitSectionSize; } if (newSectionSize != defaultSectionSize()) { setDefaultSectionSize(newSectionSize); return true; } return false; } void TimelineRulerHeader::updateMinimumSize() { QFontMetrics metrics(this->font()); const int textHeight = metrics.height(); setMinimumSize(0, 1.5 * textHeight); } void TimelineRulerHeader::setModel(QAbstractItemModel *model) { KisTimeBasedItemModel *framesModel = qobject_cast(model); m_d->model = framesModel; QHeaderView::setModel(model); } int getColumnCount(const QModelIndexList &indexes, int *leftmostCol, int *rightmostCol) { QVector columns; int leftmost = std::numeric_limits::max(); int rightmost = std::numeric_limits::min(); Q_FOREACH (const QModelIndex &index, indexes) { leftmost = qMin(leftmost, index.column()); rightmost = qMax(rightmost, index.column()); if (!columns.contains(index.column())) { columns.append(index.column()); } } if (leftmostCol) *leftmostCol = leftmost; if (rightmostCol) *rightmostCol = rightmost; return columns.size(); } void TimelineRulerHeader::mousePressEvent(QMouseEvent *e) { int logical = logicalIndexAt(e->pos()); if (logical != -1) { QModelIndexList selectedIndexes = selectionModel()->selectedIndexes(); int numSelectedColumns = getColumnCount(selectedIndexes, 0, 0); if (e->button() == Qt::RightButton) { if (numSelectedColumns <= 1) { model()->setHeaderData(logical, orientation(), true, KisTimeBasedItemModel::ActiveFrameRole); } QMenu menu; KisActionManager::safePopulateMenu(&menu, "cut_columns_to_clipboard", m_d->actionMan); KisActionManager::safePopulateMenu(&menu, "copy_columns_to_clipboard", m_d->actionMan); KisActionManager::safePopulateMenu(&menu, "paste_columns_from_clipboard", m_d->actionMan); - menu.addSeparator(); - QMenu *frames = menu.addMenu(i18nc("@item:inmenu", "Keyframe Columns")); - KisActionManager::safePopulateMenu(frames, "insert_columns_right", m_d->actionMan); - KisActionManager::safePopulateMenu(frames, "insert_columns_left", m_d->actionMan); menu.addSeparator(); - KisActionManager::safePopulateMenu(frames, "insert_n_columns_right", m_d->actionMan); - KisActionManager::safePopulateMenu(frames, "insert_n_columns_left", m_d->actionMan); - QMenu *hold = menu.addMenu(i18nc("@item:inmenu", "Hold Frame Columns")); - KisActionManager::safePopulateMenu(hold, "insert_hold_column", m_d->actionMan); - KisActionManager::safePopulateMenu(hold, "remove_hold_column", m_d->actionMan); - menu.addSeparator(); - KisActionManager::safePopulateMenu(hold, "insert_n_hold_columns", m_d->actionMan); - KisActionManager::safePopulateMenu(hold, "remove_n_hold_columns", m_d->actionMan); + { //Frame Columns Submenu + QMenu *frames = menu.addMenu(i18nc("@item:inmenu", "Keyframe Columns")); + KisActionManager::safePopulateMenu(frames, "insert_column_left", m_d->actionMan); + KisActionManager::safePopulateMenu(frames, "insert_column_right", m_d->actionMan); + frames->addSeparator(); + KisActionManager::safePopulateMenu(frames, "insert_multiple_columns", m_d->actionMan); + } + + { //Hold Columns Submenu + QMenu *hold = menu.addMenu(i18nc("@item:inmenu", "Hold Frame Columns")); + KisActionManager::safePopulateMenu(hold, "insert_hold_column", m_d->actionMan); + KisActionManager::safePopulateMenu(hold, "remove_hold_column", m_d->actionMan); + hold->addSeparator(); + KisActionManager::safePopulateMenu(hold, "insert_multiple_hold_columns", m_d->actionMan); + KisActionManager::safePopulateMenu(hold, "remove_multiple_hold_columns", m_d->actionMan); + } menu.addSeparator(); + KisActionManager::safePopulateMenu(&menu, "remove_columns", m_d->actionMan); KisActionManager::safePopulateMenu(&menu, "remove_columns_and_pull", m_d->actionMan); - if (numSelectedColumns > 1) { menu.addSeparator(); KisActionManager::safePopulateMenu(&menu, "mirror_columns", m_d->actionMan); } menu.exec(e->globalPos()); return; } else if (e->button() == Qt::LeftButton) { m_d->lastPressSectionIndex = logical; model()->setHeaderData(logical, orientation(), true, KisTimeBasedItemModel::ActiveFrameRole); } } QHeaderView::mousePressEvent(e); } void TimelineRulerHeader::mouseMoveEvent(QMouseEvent *e) { int logical = logicalIndexAt(e->pos()); if (logical != -1) { if (e->buttons() & Qt::LeftButton) { m_d->model->setScrubState(true); model()->setHeaderData(logical, orientation(), true, KisTimeBasedItemModel::ActiveFrameRole); if (m_d->lastPressSectionIndex >= 0 && logical != m_d->lastPressSectionIndex && e->modifiers() & Qt::ShiftModifier) { const int minCol = qMin(m_d->lastPressSectionIndex, logical); const int maxCol = qMax(m_d->lastPressSectionIndex, logical); QItemSelection sel(m_d->model->index(0, minCol), m_d->model->index(0, maxCol)); selectionModel()->select(sel, QItemSelectionModel::Columns | QItemSelectionModel::SelectCurrent); } } } QHeaderView::mouseMoveEvent(e); } void TimelineRulerHeader::mouseReleaseEvent(QMouseEvent *e) { if (e->button() == Qt::LeftButton) { m_d->model->setScrubState(false); } QHeaderView::mouseReleaseEvent(e); } QModelIndexList TimelineRulerHeader::Private::prepareFramesSlab(int startCol, int endCol) { QModelIndexList frames; const int numRows = model->rowCount(); for (int i = 0; i < numRows; i++) { for (int j = startCol; j <= endCol; j++) { QModelIndex index = model->index(i, j); const bool exists = model->data(index, KisTimeBasedItemModel::FrameExistsRole).toBool(); if (exists) { frames << index; } } } return frames; } diff --git a/plugins/dockers/animation/timeline_ruler_header.h b/plugins/dockers/animation/timeline_ruler_header.h index 035e28ddfd..a99baf516b 100644 --- a/plugins/dockers/animation/timeline_ruler_header.h +++ b/plugins/dockers/animation/timeline_ruler_header.h @@ -1,87 +1,87 @@ /* * 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_RULER_HEADER_H #define TIMELINE_RULER_HEADER_H #include #include #include "kis_action_manager.h" class QPaintEvent; class TimelineRulerHeader : public QHeaderView { Q_OBJECT public: TimelineRulerHeader(QWidget *parent = 0); ~TimelineRulerHeader() override; void setFramePerSecond(int fps); bool setZoom(qreal zoomLevel); void setModel(QAbstractItemModel *model) override; - void setActionManager( KisActionManager * actionManager); + void setActionManager(KisActionManager *actionManager); protected: void mousePressEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void paintEvent(QPaintEvent *e) override; void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const override; void paintSection1(QPainter *painter, const QRect &rect, int logicalIndex) const; void changeEvent(QEvent *event) override; private: void updateMinimumSize(); void paintSpan(QPainter *painter, int userFrameId, const QRect &spanRect, bool isIntegralLine, bool isPrevIntegralLine, QStyle *style, const QPalette &palette, const QPen &gridPen) const; Q_SIGNALS: - void sigInsertColumnsLeft(); - void sigInsertColumnsRight(); - void sigInsertColumnsLeftCustom(); - void sigInsertColumnsRightCustom(); + void sigInsertColumnLeft(); + void sigInsertColumnRight(); + void sigInsertMultipleColumns(); + void sigRemoveColumns(); void sigRemoveColumnsAndShift(); void sigInsertHoldColumns(); void sigRemoveHoldColumns(); void sigInsertHoldColumnsCustom(); void sigRemoveHoldColumnsCustom(); void sigMirrorColumns(); void sigCutColumns(); void sigCopyColumns(); void sigPasteColumns(); private: struct Private; const QScopedPointer m_d; }; #endif // TIMELINE_RULER_HEADER_H diff --git a/plugins/dockers/colorslider/kis_color_slider_input.cpp b/plugins/dockers/colorslider/kis_color_slider_input.cpp index 5401270737..1054cc0beb 100644 --- a/plugins/dockers/colorslider/kis_color_slider_input.cpp +++ b/plugins/dockers/colorslider/kis_color_slider_input.cpp @@ -1,709 +1,709 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2014 Wolthera van Hövell * Copyright (c) 2015 Moritz Molch * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_color_slider_input.h" #include #ifdef HAVE_OPENEXR #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "kis_hsv_slider.h" #include "kis_display_color_converter.h" #include "kis_double_parse_spin_box.h" KisColorSliderInput::KisColorSliderInput(QWidget* parent, KoColor* color, const int type, KoColorDisplayRendererInterface *displayRenderer, KisCanvas2* canvas) : QWidget(parent), m_type(type), m_color(color), m_displayRenderer(displayRenderer), m_canvas(canvas) { //init } void KisColorSliderInput::init() { QHBoxLayout* m_layout = new QHBoxLayout(this); m_layout->setContentsMargins(0, 0, 0, 0); m_layout->setSpacing(1); QString m_name; switch (m_type){ case 0: m_name=i18n("Hue"); break; case 1: m_name=i18n("Saturation"); break; - case 2: m_name=i18n("Value"); break; + case 2: m_name=i18nc("HSV Value","Value"); break; case 3: m_name=i18n("Hue"); break; case 4: m_name=i18n("Saturation"); break; case 5: m_name=i18n("Lightness"); break; case 6: m_name=i18n("Hue"); break; case 7: m_name=i18n("Saturation"); break; case 8: m_name=i18n("Intensity"); break; case 9: m_name=i18n("Hue"); break; case 10: m_name=i18n("Saturation"); break; case 11: m_name=i18n("Luma"); break; } QLabel* m_label = new QLabel(i18n("%1:", m_name), this); m_layout->addWidget(m_label); m_hsvSlider = new KisHSVSlider(Qt::Horizontal, this, m_displayRenderer, m_canvas); m_hsvSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); m_layout->addWidget(m_hsvSlider); connect (m_hsvSlider, SIGNAL(sliderPressed()), SLOT(sliderIn())); connect (m_hsvSlider, SIGNAL(sliderReleased()), SLOT(sliderOut())); QWidget* m_input = createInput(); m_hsvSlider->setFixedHeight(m_input->sizeHint().height()); m_layout->addWidget(m_input); } KisHSXColorSliderInput::KisHSXColorSliderInput(QWidget* parent, const int type, KoColor* color, KoColorDisplayRendererInterface *displayRenderer, KisCanvas2* canvas) : KisColorSliderInput(parent, color, type, displayRenderer, canvas), m_canvas(canvas), m_hue(0), m_sat(0), m_val(0), R(0), G(0), B(0), Gamma(0) { m_hueupdating = false; m_satupdating = false; m_toneupdating = false; m_sliderisupdating = false; init(); } void KisHSXColorSliderInput::setValue(double v) { //This function returns the colour based on the type of the slider as well as the value// qreal h=0.0; qreal s=0.0; qreal l=0.0; KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); R = cfg.readEntry("lumaR", 0.2126); G = cfg.readEntry("lumaG", 0.7152); B = cfg.readEntry("lumaB", 0.0722); Gamma = cfg.readEntry("gamma", 2.2); switch (m_type) { case 0: m_hue = v; h=m_hue/360.0; s=m_sat/100.0; l=m_val/100.0; *m_color = this->converter()->fromHsvF(h, s, l); if (m_hueupdating==false) { emit(hueUpdated(static_cast(m_hue))); } else { m_hueupdating=false; } break; case 3: m_hue = v; h=m_hue/360.0; s=m_sat/100.0; l=m_val/100.0; *m_color = this->converter()->fromHslF(h, s, l); if (m_hueupdating==false) { emit(hueUpdated(static_cast(m_hue))); } else { m_hueupdating=false; } break; case 6: m_hue = v; h=m_hue/360.0; s=m_sat/100.0; l=m_val/100.0; *m_color = this->converter()->fromHsiF(h, s, l); if (m_hueupdating==false) { emit(hueUpdated(static_cast(m_hue))); } else { m_hueupdating=false; } break; case 9: m_hue = v; h=m_hue/360.0f; s=m_sat/100.0f; l=m_val/100.0f; *m_color = this->converter()->fromHsyF(h, s, l, R, G, B, Gamma); if (m_hueupdating==false) { emit(hueUpdated(static_cast(m_hue))); } else { m_hueupdating=false; } break; case 1: m_sat = v; h=m_hue/360.0f; s=m_sat/100.0f; l=m_val/100.0f; *m_color = this->converter()->fromHsvF(h, s, l); if (m_satupdating==false) { emit(satUpdated(static_cast(m_sat), m_type)); } else { m_satupdating=false; } break; case 2: m_val = v; h=m_hue/360.0f; s=m_sat/100.0f; l=m_val/100.0f; *m_color = this->converter()->fromHsvF(h, s, l); if (m_toneupdating==false) { emit(toneUpdated(static_cast(m_val), m_type)); } else { m_toneupdating=false; } break; case 4: m_sat = v; h=m_hue/360.0f; s=m_sat/100.0f; l=m_val/100.0f; *m_color = this->converter()->fromHslF(h, s, l); if (m_satupdating==false) { emit(satUpdated(static_cast(m_sat), m_type)); } else { m_satupdating=false; } break; case 5: m_val = v; h=m_hue/360.0f; s=m_sat/100.0f; l=m_val/100.0f; *m_color = this->converter()->fromHslF(h, s, l); if (m_toneupdating==false) { emit(toneUpdated(static_cast(m_val), m_type)); } else { m_toneupdating=false; } break; case 7: m_sat = v; h=m_hue/360.0f; s=m_sat/100.0f; l=m_val/100.0f; *m_color = this->converter()->fromHsiF(h, s, l); if (m_satupdating==false) { emit(satUpdated(static_cast(m_sat), m_type)); } else { m_satupdating=false; } break; case 8: m_val = v; h=m_hue/360.0f; s=m_sat/100.0f; l=m_val/100.0f; *m_color = this->converter()->fromHsiF(h, s, l); if (m_toneupdating==false) { emit(toneUpdated(static_cast(m_val), m_type)); } else { m_toneupdating=false; } break; case 10: m_sat = v; h=m_hue/360.0f; s=m_sat/100.0f; l=m_val/100.0f; *m_color = this->converter()->fromHsyF(h, s, l, R, G, B, Gamma); if (m_satupdating==false) { emit(satUpdated(static_cast(m_sat), m_type)); } else { m_satupdating=false; } break; case 11: m_val = v; h=m_hue/360.0f; s=m_sat/100.0f; l=m_val/100.0f; *m_color = this->converter()->fromHsyF(h, s, l, R, G, B, Gamma); if (m_toneupdating==false) { emit(toneUpdated(static_cast(m_val), m_type)); } else { m_toneupdating=false; } break; default: Q_ASSERT(false); } emit(updated()); } //update void KisHSXColorSliderInput::update() { // KoColor min = *m_color; // KoColor max = *m_color; qreal hue = 0.0; qreal sat = 0.0; qreal val = 0.0; qreal hue_backup, sat_backup, val_backup; //gets the hsv for the appropriate type// hue_backup = m_hue; sat_backup = m_sat; val_backup = m_val; switch (m_type) { case 0: this->converter()->getHsvF(*m_color, &hue, &sat, &val); if (m_sliderisupdating) { if ((sat * 100.0) < m_sat + 2 && (sat * 100.0) > m_sat - 2) { sat = (sat_backup * 0.01); } if ((val * 100.0) < m_val + 2 && (val * 100.0) > m_val - 2) { val = (val_backup*0.01); } } else { if((hue * 360.0) < m_hue + 2 && (hue * 360.0) > m_hue - 2) { hue = (hue_backup/360.0); } } break; case 1: this->converter()->getHsvF(*m_color, &hue, &sat, &val); if (m_sliderisupdating) { if ((hue * 360.0) < m_hue + 2 && (hue * 360.0) > m_hue - 2 ) { hue = (hue_backup/360.0); } if ((val * 100.0) < m_val + 2 && (val * 100.0) > m_val - 2) { val = (val_backup*0.01); } } else { if ((sat*100.0)m_sat-2) { sat = (sat_backup*0.01); } } break; case 2: this->converter()->getHsvF(*m_color, &hue, &sat, &val); if (m_sliderisupdating==true) { if((sat*100.0)m_sat-2) { sat = (sat_backup*0.01); } if((hue*360.0)m_hue-2) { hue = (hue_backup/360.0); } } else{ if((val*100.0)m_val-2) { val = (val_backup*0.01); } } break; case 3: this->converter()->getHslF(*m_color, &hue, &sat, &val); if (m_sliderisupdating==true) { if((sat*100.0)m_sat-2) { sat = (sat_backup*0.01); } if((val*100.0)m_val-2) { val = (val_backup*0.01); } } else{ if((hue*360.0)m_hue-2) { hue = (hue_backup/360.0); } } break; case 4: this->converter()->getHslF(*m_color, &hue, &sat, &val); if (m_sliderisupdating==true) { if((hue*360.0)m_hue-2) { hue = (hue_backup/360.0); } if((val*100.0)m_val-2) { val = (val_backup*0.01); } } else{ if((sat*100.0)m_sat-2) { sat = (sat_backup*0.01); } } break; case 5: this->converter()->getHslF(*m_color, &hue, &sat, &val); if (m_sliderisupdating==true) { if((sat*100.0)m_sat-2) { sat = (sat_backup*0.01); } if((hue*360.0)m_hue-2) { hue = (hue_backup/360.0); } } else{ if((val*100.0)m_val-2) { val = (val_backup*0.01); } } break; case 6: this->converter()->getHsiF(*m_color, &hue, &sat, &val); if (m_sliderisupdating==true) { if((sat*100.0)m_sat-2) { sat = (sat_backup*0.01); } if((val*100.0)m_val-2) { val = (val_backup*0.01); } } else{ if((hue*360.0)m_hue-2) { hue = (hue_backup/360.0); } } break; case 7: this->converter()->getHsiF(*m_color, &hue, &sat, &val); if (m_sliderisupdating==true) { if((hue*360.0)m_hue-2) { hue = (hue_backup/360.0); } if((val*100.0)m_val-2) { val = (val_backup*0.01); } } else{ if((sat*100.0)m_sat-2) { sat = (sat_backup*0.01); } } break; case 8: this->converter()->getHsiF(*m_color, &hue, &sat, &val); if (m_sliderisupdating==true) { if((sat*100.0)m_sat-2) { sat = (sat_backup*0.01); } if((hue*360.0)m_hue-2) { hue = (hue_backup/360.0); } } else{ if((val*100.0)m_val-2) { val = (val_backup*0.01); } } break; case 9: this->converter()->getHsyF(*m_color, &hue, &sat, &val, R, G, B, Gamma); if (m_sliderisupdating==true) { if((sat*100.0)m_sat-2) { sat = (sat_backup*0.01); } if((val*100.0)m_val-2) { val = (val_backup*0.01); } } else{ if((hue*360.0)m_hue-2) { hue = (hue_backup/360.0); } } break; case 10: this->converter()->getHsyF(*m_color, &hue, &sat, &val, R, G, B, Gamma); if (m_sliderisupdating==true) { if((hue*360.0)m_hue-2) { hue = (hue_backup/360.0); } if((val*100.0)m_val-2) { val = (val_backup*0.01); } } else{ if((sat*100.0)m_sat-2) { sat = (sat_backup*0.01); } } break; case 11: this->converter()->getHsyF(*m_color, &hue, &sat, &val, R, G, B, Gamma); if (m_sliderisupdating == true) { if((sat*100.0)m_sat-2) { sat = (sat_backup*0.01); } if((hue*360.0)m_hue-2) { hue = (hue_backup/360.0); } } else{ if((val*100.0)m_val-2) { val = (val_backup*0.01); } } break; } //this prevents the hue going to 0 when used with grey// if (sat<=0.0) { m_hue = hue_backup; } else{ m_hue=(hue*360.0); } if (val == 0) { m_sat = sat_backup; } else{ m_sat=(sat*100.0); } m_val=(val*100.0); if (m_hueupdating==true){m_val=val_backup; m_sat = sat_backup; m_hueupdating=false;} else if (m_satupdating==true){m_val=val_backup; m_hue = hue_backup; m_satupdating=false;} else if (m_toneupdating==true){m_sat=sat_backup; m_hue = hue_backup;m_toneupdating=false;} //sets slider and num-input according to type// switch (m_type) { case 0: case 3: case 6: case 9: m_NumInput->setValue(m_hue); m_hsvSlider->setValue(static_cast(m_hue)); break; case 1: m_NumInput->setValue(m_sat); m_hsvSlider->setValue(static_cast(m_sat)); break; case 2: m_NumInput->setValue(m_val); m_hsvSlider->setValue(static_cast(m_val)); break; case 4: m_NumInput->setValue(m_sat); m_hsvSlider->setValue(static_cast(m_sat)); break; case 5: m_NumInput->setValue(m_val); m_hsvSlider->setValue(static_cast(m_val)); break; case 7: m_NumInput->setValue(m_sat); m_hsvSlider->setValue(static_cast(m_sat)); break; case 8: m_NumInput->setValue(m_val); m_hsvSlider->setValue(static_cast(m_val)); break; case 10: m_NumInput->setValue(m_sat); m_hsvSlider->setValue(static_cast(m_sat)); break; case 11: m_NumInput->setValue(m_val); m_hsvSlider->setValue(static_cast(m_val)); break; default: Q_ASSERT(false); } m_hsvSlider->setColors(*m_color,m_type, m_hue, R, G, B, Gamma); } QWidget* KisHSXColorSliderInput::createInput() { m_NumInput = new KisDoubleParseSpinBox(this); m_NumInput->setMinimum(0); m_NumInput->setMaximum(100.0); m_NumInput->setKeyboardTracking(false);//this makes sure that only full values are sent after loss of focus. Much more user friendly// m_hsvSlider->setMaximum(100); switch (m_type) { case 0: case 3: case 6: case 9: m_NumInput->setMaximum(360.0); m_NumInput->setWrapping(true); m_hsvSlider->setMaximum(360); m_NumInput->setSingleStep (5.0); break; case 1: case 2: case 4: case 5: case 7: case 8: case 10: case 11: m_NumInput->setMaximum(100.0); m_hsvSlider->setMaximum(100); m_NumInput->setSingleStep (10.0); break; default: Q_ASSERT(false); } connect(m_hsvSlider, SIGNAL(valueChanged(int)), this, SLOT(sliderChanged(int))); connect(m_NumInput, SIGNAL(valueChanged(double)), this, SLOT(numInputChanged(double))); return m_NumInput; } void KisHSXColorSliderInput::sliderChanged(int i) { m_NumInput->setValue(i*1.0); setValue(i*1.0); } void KisHSXColorSliderInput::sliderIn(){ m_sliderisupdating=true; } void KisHSXColorSliderInput::sliderOut(){ m_sliderisupdating=false; } //attempt at getting rid of dancing sliders... #2859 //The nminput should not be changing the sliders if the sliders are the one changing the input. //As numinpit rounds off at 2 decimals(and there's no point at letting it continue the signal circle). void KisHSXColorSliderInput::numInputChanged(double v) { if (m_sliderisupdating==true){ return; } else { setValue(v); } } //this connects to the display converter. Important for OCIO, breaks on missing of m_canvas somehow. KisDisplayColorConverter* KisHSXColorSliderInput::converter() const { return m_canvas ? m_canvas->displayColorConverter() : KisDisplayColorConverter::dumbConverterInstance(); } void KisHSXColorSliderInput::hueUpdate(int h) { if (h<=m_hue-2 || h>=m_hue+2) { m_hue=h; m_hueupdating=true; update(); } } void KisHSXColorSliderInput::satUpdate(int s, int type) { if (m_type==type+1 || m_type==type-1) { if (s<=m_sat-3 || s>=m_sat+3) { m_sat=s; m_satupdating=true; update(); } } } void KisHSXColorSliderInput::toneUpdate(int l, int type) { if (m_type==type-1 || m_type==type-2) { if (l<25 || l>75){ if (l<=m_val-10 || l>=m_val+10) { m_val=l; m_toneupdating=true; update(); } } else { if (l<=m_val-3 || l>=m_val+3) { m_val=l; m_toneupdating=true; update(); } } } } #include "moc_kis_color_slider_input.cpp" diff --git a/plugins/dockers/throttle/Throttle.cpp b/plugins/dockers/throttle/Throttle.cpp index 6ddf806a09..256d8c9948 100644 --- a/plugins/dockers/throttle/Throttle.cpp +++ b/plugins/dockers/throttle/Throttle.cpp @@ -1,95 +1,113 @@ /* * Copyright (c) 2017 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "Throttle.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_signal_compressor.h" #include "KisUpdateSchedulerConfigNotifier.h" +#include + +namespace +{ + +bool shouldSetAcceptTouchEvents() +{ + // See https://bugreports.qt.io/browse/QTBUG-66718 + static QVersionNumber qtVersion = QVersionNumber::fromString(qVersion()); + static bool retval = qtVersion > QVersionNumber(5, 9, 3) && qtVersion.normalized() != QVersionNumber(5, 10); + return retval; +} + +} // namespace + ThreadManager::ThreadManager(QObject *parent) : QObject(parent), m_configUpdateCompressor(new KisSignalCompressor(500, KisSignalCompressor::POSTPONE, this)) { connect(m_configUpdateCompressor, SIGNAL(timeout()), SLOT(slotDoUpdateConfig())); } ThreadManager::~ThreadManager() { } void ThreadManager::setThreadCount(int threadCount) { threadCount = 1 + qreal(threadCount) * (maxThreadCount() - 1) / 100.0; if (m_threadCount != threadCount) { m_threadCount = threadCount; m_configUpdateCompressor->start(); emit threadCountChanged(); } } int ThreadManager::threadCount() const { return m_threadCount; } int ThreadManager::maxThreadCount() const { return QThread::idealThreadCount(); } void ThreadManager::slotDoUpdateConfig() { KisImageConfig cfg; cfg.setMaxNumberOfThreads(m_threadCount); cfg.setFrameRenderingClones(qCeil(m_threadCount * 0.5)); KisUpdateSchedulerConfigNotifier::instance()->notifyConfigChanged(); } Throttle::Throttle(QWidget *parent) : QQuickWidget(parent) { + if (shouldSetAcceptTouchEvents()) { + setAttribute(Qt::WA_AcceptTouchEvents); + } m_threadManager = new ThreadManager(); // In % of available cores... engine()->rootContext()->setContextProperty("ThreadManager", m_threadManager); m_threadManager->setThreadCount(100); setSource(QUrl("qrc:/slider.qml")); setResizeMode(SizeRootObjectToView); } Throttle::~Throttle() { setSource(QUrl()); } diff --git a/plugins/dockers/touchdocker/TouchDockerDock.cpp b/plugins/dockers/touchdocker/TouchDockerDock.cpp index a8fe23633a..0d5a6add8c 100644 --- a/plugins/dockers/touchdocker/TouchDockerDock.cpp +++ b/plugins/dockers/touchdocker/TouchDockerDock.cpp @@ -1,382 +1,403 @@ /* * Copyright (c) 2017 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "TouchDockerDock.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 #include #include +#include + +namespace +{ + +bool shouldSetAcceptTouchEvents() +{ + // See https://bugreports.qt.io/browse/QTBUG-66718 + static QVersionNumber qtVersion = QVersionNumber::fromString(qVersion()); + static bool retval = qtVersion > QVersionNumber(5, 9, 3) && qtVersion.normalized() != QVersionNumber(5, 10); + return retval; +} + +} // namespace + class TouchDockerDock::Private { public: Private() { } TouchDockerDock *q; bool allowClose {true}; KisSketchView *sketchView {0}; QString currentSketchPage; KoDialog *openDialog {0}; KoDialog *saveAsDialog {0}; QMap buttonMapping; bool shiftOn {false}; bool ctrlOn {false}; bool altOn {false}; }; TouchDockerDock::TouchDockerDock() : QDockWidget(i18n("Touch Docker")) , d(new Private()) { QStringList defaultMapping = QStringList() << "decrease_opacity" << "increase_opacity" << "make_brush_color_lighter" << "make_brush_color_darker" << "decrease_brush_size" << "increase_brush_size" << "previous_preset" << "clear"; QStringList mapping = KisConfig().readEntry("touchdockermapping", defaultMapping.join(',')).split(','); for (int i = 0; i < 8; ++i) { if (i < mapping.size()) { d->buttonMapping[QString("button%1").arg(i + 1)] = mapping[i]; } else if (i < defaultMapping.size()) { d->buttonMapping[QString("button%1").arg(i + 1)] = defaultMapping[i]; } } m_quickWidget = new QQuickWidget(this); + if (shouldSetAcceptTouchEvents()) { + m_quickWidget->setAttribute(Qt::WA_AcceptTouchEvents); + } setWidget(m_quickWidget); setEnabled(true); m_quickWidget->engine()->rootContext()->setContextProperty("mainWindow", this); m_quickWidget->engine()->addImportPath(KoResourcePaths::getApplicationRoot() + "/lib/qml/"); m_quickWidget->engine()->addImportPath(KoResourcePaths::getApplicationRoot() + "/lib64/qml/"); m_quickWidget->engine()->addPluginPath(KoResourcePaths::getApplicationRoot() + "/lib/qml/"); m_quickWidget->engine()->addPluginPath(KoResourcePaths::getApplicationRoot() + "/lib64/qml/"); Settings *settings = new Settings(this); DocumentManager::instance()->setSettingsManager(settings); m_quickWidget->engine()->rootContext()->setContextProperty("Settings", settings); Theme *theme = Theme::load(KSharedConfig::openConfig()->group("General").readEntry("theme", "default"), m_quickWidget->engine()); if (theme) { settings->setTheme(theme); } m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); m_quickWidget->setSource(QUrl("qrc:/touchstrip.qml")); } TouchDockerDock::~TouchDockerDock() { } bool TouchDockerDock::allowClose() const { return d->allowClose; } void TouchDockerDock::setAllowClose(bool allow) { d->allowClose = allow; } QString TouchDockerDock::currentSketchPage() const { return d->currentSketchPage; } void TouchDockerDock::setCurrentSketchPage(QString newPage) { d->currentSketchPage = newPage; emit currentSketchPageChanged(); } void TouchDockerDock::closeEvent(QCloseEvent* event) { if (!d->allowClose) { event->ignore(); emit closeRequested(); } else { event->accept(); } } void TouchDockerDock::slotButtonPressed(const QString &id) { if (id == "fileOpenButton") { showFileOpenDialog(); } else if (id == "fileSaveButton" && m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->document()) { bool batchMode = m_canvas->viewManager()->document()->fileBatchMode(); m_canvas->viewManager()->document()->setFileBatchMode(true); m_canvas->viewManager()->document()->save(true, 0); m_canvas->viewManager()->document()->setFileBatchMode(batchMode); } else if (id == "fileSaveAsButton" && m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->document()) { showFileSaveAsDialog(); } else { QAction *a = action(id); if (a) { if (a->isCheckable()) { a->toggle(); } else { a->trigger(); } } else if (id == "shift") { // set shift state for the next pointer event, somehow QKeyEvent event(d->shiftOn ? QEvent::KeyRelease : QEvent::KeyPress, 0, Qt::ShiftModifier); QApplication::sendEvent(KisPart::instance()->currentMainwindow(), &event); d->shiftOn = !d->shiftOn; } else if (id == "ctrl") { // set ctrl state for the next pointer event, somehow QKeyEvent event(d->ctrlOn ? QEvent::KeyRelease : QEvent::KeyPress, 0, Qt::ControlModifier); QApplication::sendEvent(KisPart::instance()->currentMainwindow(), &event); d->ctrlOn = !d->ctrlOn; } else if (id == "alt") { // set alt state for the next pointer event, somehow QKeyEvent event(d->altOn ? QEvent::KeyRelease : QEvent::KeyPress, 0, Qt::AltModifier); QApplication::sendEvent(KisPart::instance()->currentMainwindow(), &event); d->altOn = !d->altOn; } } } void TouchDockerDock::slotOpenImage(QString path) { if (d->openDialog) { d->openDialog->accept(); } KisPart::instance()->currentMainwindow()->openDocument(QUrl::fromLocalFile(path), KisMainWindow::None); } void TouchDockerDock::slotSaveAs(QString path, QString mime) { if (d->saveAsDialog) { d->saveAsDialog->accept(); } m_canvas->viewManager()->document()->saveAs(QUrl::fromLocalFile(path), mime.toLatin1(), true); m_canvas->viewManager()->document()->waitForSavingToComplete(); } void TouchDockerDock::hideFileOpenDialog() { if (d->openDialog) { d->openDialog->accept(); } } void TouchDockerDock::hideFileSaveAsDialog() { if (d->saveAsDialog) { d->saveAsDialog->accept(); } } QString TouchDockerDock::imageForButton(QString id) { if (d->buttonMapping.contains(id)) { id = d->buttonMapping[id]; } if (KisActionRegistry::instance()->hasAction(id)) { QString a = KisActionRegistry::instance()->getActionProperty(id, "icon"); if (!a.isEmpty()) { return "image://icon/" + a; } } return QString(); } QString TouchDockerDock::textForButton(QString id) { if (d->buttonMapping.contains(id)) { id = d->buttonMapping[id]; } if (KisActionRegistry::instance()->hasAction(id)) { QString a = KisActionRegistry::instance()->getActionProperty(id, "iconText"); if (a.isEmpty()) { a = KisActionRegistry::instance()->getActionProperty(id, "text"); } return a; } return id; } QAction *TouchDockerDock::action(QString id) const { if (m_canvas && m_canvas->viewManager()) { if (d->buttonMapping.contains(id)) { id = d->buttonMapping[id]; } return m_canvas->viewManager()->actionManager()->actionByName(id); } return 0; } void TouchDockerDock::showFileOpenDialog() { if (!d->openDialog) { d->openDialog = createDialog("qrc:/opendialog.qml"); } d->openDialog->exec(); } void TouchDockerDock::showFileSaveAsDialog() { if (!d->openDialog) { d->openDialog = createDialog("qrc:/saveasdialog.qml"); } d->openDialog->exec(); } KoDialog *TouchDockerDock::createDialog(const QString qml) { KoDialog *dlg = new KoDialog(this); dlg->setButtons(KoDialog::None); QQuickWidget *quickWidget = new QQuickWidget(this); + if (shouldSetAcceptTouchEvents()) { + quickWidget->setAttribute(Qt::WA_AcceptTouchEvents); + } dlg->setMainWidget(quickWidget); setEnabled(true); quickWidget->engine()->rootContext()->setContextProperty("mainWindow", this); quickWidget->engine()->addImportPath(KoResourcePaths::getApplicationRoot() + "/lib/qml/"); quickWidget->engine()->addImportPath(KoResourcePaths::getApplicationRoot() + "/lib64/qml/"); quickWidget->engine()->addPluginPath(KoResourcePaths::getApplicationRoot() + "/lib/qml/"); quickWidget->engine()->addPluginPath(KoResourcePaths::getApplicationRoot() + "/lib64/qml/"); Settings *settings = new Settings(this); DocumentManager::instance()->setSettingsManager(settings); quickWidget->engine()->rootContext()->setContextProperty("Settings", settings); Theme *theme = Theme::load(KSharedConfig::openConfig()->group("General").readEntry("theme", "default"), quickWidget->engine()); settings->setTheme(theme); quickWidget->setSource(QUrl(qml)); quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); dlg->setMinimumSize(1280, 768); return dlg; } QObject *TouchDockerDock::sketchKisView() const { return d->sketchView; } void TouchDockerDock::setSketchKisView(QObject* newView) { if (d->sketchView) { d->sketchView->disconnect(this); } if (d->sketchView != newView) { d->sketchView = qobject_cast(newView); emit sketchKisViewChanged(); } } void TouchDockerDock::setCanvas(KoCanvasBase *canvas) { setEnabled(true); if (m_canvas == canvas) { return; } if (m_canvas) { m_canvas->disconnectCanvasObserver(this); } if (!canvas) { m_canvas = 0; return; } m_canvas = dynamic_cast(canvas); } void TouchDockerDock::unsetCanvas() { setEnabled(true); m_canvas = 0; } diff --git a/plugins/extensions/animationrenderer/DlgAnimationRenderer.cpp b/plugins/extensions/animationrenderer/DlgAnimationRenderer.cpp index 00d070b967..70dfd6bf80 100644 --- a/plugins/extensions/animationrenderer/DlgAnimationRenderer.cpp +++ b/plugins/extensions/animationrenderer/DlgAnimationRenderer.cpp @@ -1,609 +1,627 @@ /* * 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 #include "kis_slider_spin_box.h" #include "kis_acyclic_signal_connector.h" 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 = new WdgAnimationRenderer(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()); // animators sometimes want to export after end frame m_page->intEnd->setValue(doc->image()->animationInterface()->playbackRange().end()); m_page->intHeight->setMinimum(1); m_page->intHeight->setMaximum(10000); m_page->intHeight->setValue(doc->image()->height()); m_page->intWidth->setMinimum(1); m_page->intWidth->setMaximum(10000); m_page->intWidth->setValue(doc->image()->width()); // try to lock the width and height being updated KisAcyclicSignalConnector *constrainsConnector = new KisAcyclicSignalConnector(this); constrainsConnector->createCoordinatedConnector()->connectBackwardInt(m_page->intWidth, SIGNAL(valueChanged(int)), this, SLOT(slotLockAspectRatioDimensionsWidth(int))); constrainsConnector->createCoordinatedConnector()->connectForwardInt(m_page->intHeight, SIGNAL(valueChanged(int)), this, SLOT(slotLockAspectRatioDimensionsHeight(int))); m_page->intFramesPerSecond->setValue(doc->image()->animationInterface()->framerate()); 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); QListlist = KoJsonTrader::instance()->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->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->cmbRenderType->setCurrentIndex(cfg.readEntry("AnimationRenderer/render_type", 0)); connect(m_page->shouldExportOnlyImageSequence, SIGNAL(toggled(bool)), this, SLOT(slotExportTypeChanged())); connect(m_page->shouldExportOnlyVideo, SIGNAL(toggled(bool)), this, SLOT(slotExportTypeChanged())); connect(m_page->shouldExportAll, SIGNAL(toggled(bool)), this, SLOT(slotExportTypeChanged())); updateExportUIOptions(); // connect and cold init connect(m_page->cmbRenderType, SIGNAL(currentIndexChanged(int)), this, SLOT(selectRenderType(int))); selectRenderType(m_page->cmbRenderType->currentIndex()); resize(m_page->sizeHint()); } DlgAnimationRenderer::~DlgAnimationRenderer() { KisConfig cfg; cfg.writeEntry("AnimationRenderer/last_sequence_export_location", m_page->dirRequester->fileName()); cfg.writeEntry("AnimationRenderer/render_type", m_page->cmbRenderType->currentIndex()); 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; } QString DlgAnimationRenderer::fetchRenderingDirectory() const { QString result = m_page->dirRequester->fileName(); if (m_page->shouldExportOnlyVideo->isChecked()) { const QFileInfo info(fetchRenderingFileName()); if (info.isAbsolute()) { result = info.absolutePath(); } } return result; } QString DlgAnimationRenderer::fetchRenderingFileName() const { 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(); } if (QFileInfo(filename).isRelative()) { QDir baseDir(m_page->dirRequester->fileName()); if (m_page->shouldExportOnlyVideo->isChecked()) { QString documentDir = QFileInfo(m_doc->url().toLocalFile()).absolutePath(); if (!documentDir.isEmpty()) { baseDir = documentDir; } } filename = baseDir.absoluteFilePath(filename); } return filename; } 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", fetchRenderingDirectory()); 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->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", fetchRenderingDirectory()); 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; } KisPropertiesConfigurationSP DlgAnimationRenderer::getVideoConfiguration() const { // don't continue if we are only exporting image sequence if (m_page->shouldExportOnlyImageSequence->isChecked()) { return 0; } KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration(); cfg->setProperty("filename", fetchRenderingFileName()); cfg->setProperty("first_frame", m_page->intStart->value()); cfg->setProperty("last_frame", m_page->intEnd->value()); cfg->setProperty("sequence_start", m_page->sequenceStart->value()); // delete image sequence if we are only exporting out video cfg->setProperty("delete_sequence", m_page->shouldExportOnlyVideo->isChecked()); return cfg; } void DlgAnimationRenderer::setVideoConfiguration(KisPropertiesConfigurationSP /*cfg*/) { } KisPropertiesConfigurationSP DlgAnimationRenderer::getEncoderConfiguration() const { // don't continue if we are only exporting image sequence if (m_page->shouldExportOnlyImageSequence->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", fetchRenderingDirectory()); cfg->setProperty("first_frame", m_page->intStart->value()); cfg->setProperty("last_frame", m_page->intEnd->value()); cfg->setProperty("framerate", m_page->intFramesPerSecond->value()); cfg->setProperty("height", m_page->intHeight->value()); cfg->setProperty("width", m_page->intWidth->value()); cfg->setProperty("sequence_start", m_page->sequenceStart->value()); cfg->setProperty("include_audio", m_page->chkIncludeAudio->isChecked()); return cfg; } void DlgAnimationRenderer::setEncoderConfiguration(KisPropertiesConfigurationSP cfg) { m_page->intHeight->setValue(cfg->getInt("height", int(m_image->height()))); m_page->intWidth->setValue(cfg->getInt("width", int(m_image->width()))); m_page->intFramesPerSecond->setValue(cfg->getInt("framerate", int(m_image->animationInterface()->framerate()))); if (m_encoderConfigWidget) { m_encoderConfigWidget->setConfiguration(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()); } else { KisConfig().setExportConfiguration(mimetype.toLatin1(), m_encoderConfigWidget->configuration()); } dlg.setMainWidget(0); m_encoderConfigWidget->hide(); m_encoderConfigWidget->setParent(0); } } else { m_encoderConfigWidget = 0; } } 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::updateExportUIOptions() { KisConfig cfg; // read in what type to export to. Defaults to image sequence only QString exportType = cfg.readEntry("AnimationRenderer/export_type", "ImageSequence"); if (exportType == "ImageSequence") { m_page->shouldExportOnlyImageSequence->setChecked(true); } else if (exportType == "Video") { m_page->shouldExportOnlyVideo->setChecked(true); } else { m_page->shouldExportAll->setChecked(true); // export to both } } void DlgAnimationRenderer::slotButtonClicked(int button) { if (button == KoDialog::Ok && !m_page->shouldExportOnlyImageSequence->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"; + if (!customPath.isEmpty()) { + proposedPaths << customPath; + proposedPaths << customPath + QDir::separator() + "ffmpeg"; + } +#ifndef Q_OS_WIN proposedPaths << QDir::homePath() + "/bin/ffmpeg"; proposedPaths << "/usr/bin/ffmpeg"; proposedPaths << "/usr/local/bin/ffmpeg"; +#endif proposedPaths << KoResourcePaths::getApplicationRoot() + QDir::separator() + "bin" + QDir::separator() + "ffmpeg"; - Q_FOREACH (const QString &path, proposedPaths) { + Q_FOREACH (QString path, proposedPaths) { if (path.isEmpty()) continue; +#ifdef Q_OS_WIN + path = QDir::toNativeSeparators(QDir::cleanPath(path)); + if (path.endsWith(QDir::separator())) { + continue; + } + if (!path.endsWith(".exe")) { + if (!QFile::exists(path)) { + path += ".exe"; + if (!QFile::exists(path)) { + continue; + } + } + } +#endif QProcess testProcess; testProcess.start(path, QStringList() << "-version"); if (testProcess.waitForStarted(1000)) { testProcess.waitForFinished(1000); } const bool successfulStart = testProcess.state() == QProcess::NotRunning && testProcess.error() == QProcess::UnknownError; if (successfulStart) { result = path; break; } } return result; } void DlgAnimationRenderer::slotExportTypeChanged() { KisConfig cfg; bool willEncodeVideo = m_page->shouldExportAll->isChecked() || m_page->shouldExportOnlyVideo->isChecked(); // if a video format needs to be outputted if (willEncodeVideo) { // videos always uses PNG for creating video, so disable the ability to change the format m_page->cmbMimetype->setEnabled(false); 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; } } } m_page->intWidth->setVisible(willEncodeVideo); m_page->intHeight->setVisible(willEncodeVideo); m_page->intFramesPerSecond->setVisible(willEncodeVideo); m_page->fpsLabel->setVisible(willEncodeVideo); m_page->lblWidth->setVisible(willEncodeVideo); m_page->lblHeight->setVisible(willEncodeVideo); // if only exporting video if (m_page->shouldExportOnlyVideo->isChecked()) { m_page->cmbMimetype->setEnabled(false); // allow to change image format m_page->imageSequenceOptionsGroup->setVisible(false); m_page->videoOptionsGroup->setVisible(false); //shrinks the horizontal space temporarily to help resize() work m_page->videoOptionsGroup->setVisible(true); cfg.writeEntry("AnimationRenderer/export_type", "Video"); } // if only an image sequence needs to be output if (m_page->shouldExportOnlyImageSequence->isChecked()) { m_page->cmbMimetype->setEnabled(true); // allow to change image format m_page->videoOptionsGroup->setVisible(false); m_page->imageSequenceOptionsGroup->setVisible(false); m_page->imageSequenceOptionsGroup->setVisible(true); cfg.writeEntry("AnimationRenderer/export_type", "ImageSequence"); } // show all options if (m_page->shouldExportAll->isChecked() ) { m_page->imageSequenceOptionsGroup->setVisible(true); m_page->videoOptionsGroup->setVisible(true); cfg.writeEntry("AnimationRenderer/export_type", "VideoAndImageSequence"); } // for the resize to work as expected, try to hide elements first before displaying other ones. // if the widget gets bigger at any point, the resize will use that, even if elements are hidden later to make it smaller resize(m_page->sizeHint()); } void DlgAnimationRenderer::slotLockAspectRatioDimensionsWidth(int width) { Q_UNUSED(width); float aspectRatio = (float)m_image->width() / (float)m_image->height(); // update height here float newHeight = m_page->intWidth->value() / aspectRatio ; m_page->intHeight->setValue(newHeight); } void DlgAnimationRenderer::slotLockAspectRatioDimensionsHeight(int height) { Q_UNUSED(height); float aspectRatio = (float)m_image->width() / (float)m_image->height(); // update width here float newWidth = aspectRatio * m_page->intHeight->value(); m_page->intWidth->setValue(newWidth); } diff --git a/plugins/extensions/animationrenderer/DlgAnimationRenderer.h b/plugins/extensions/animationrenderer/DlgAnimationRenderer.h index 8f48f1ae70..8336b94523 100644 --- a/plugins/extensions/animationrenderer/DlgAnimationRenderer.h +++ b/plugins/extensions/animationrenderer/DlgAnimationRenderer.h @@ -1,106 +1,106 @@ /* * 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 +class WdgAnimationRenderer : public QWidget, public Ui::WdgAnimaterionRenderer { Q_OBJECT public: - WdgAnimaterionRenderer(QWidget *parent) + WdgAnimationRenderer(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class DlgAnimationRenderer: public KoDialog { Q_OBJECT public: DlgAnimationRenderer(KisDocument *doc, QWidget *parent = 0); ~DlgAnimationRenderer() override; KisPropertiesConfigurationSP getSequenceConfiguration() const; void setSequenceConfiguration(KisPropertiesConfigurationSP cfg); KisPropertiesConfigurationSP getFrameExportConfiguration() const; KisPropertiesConfigurationSP getVideoConfiguration() const; void setVideoConfiguration(KisPropertiesConfigurationSP cfg); KisPropertiesConfigurationSP getEncoderConfiguration() const; void setEncoderConfiguration(KisPropertiesConfigurationSP cfg); QSharedPointer encoderFilter() const; // fires when the render animation action is called. makes sure the correct export type is selected for the UI void updateExportUIOptions(); private Q_SLOTS: void selectRenderType(int i); void selectRenderOptions(); void sequenceMimeTypeSelected(); void ffmpegLocationChanged(const QString&); void slotLockAspectRatioDimensionsWidth(int width); void slotLockAspectRatioDimensionsHeight(int height); void slotExportTypeChanged(); protected Q_SLOTS: void slotButtonClicked(int button) override; private: QString fetchRenderingDirectory() const; QString fetchRenderingFileName() const; private: static QString findFFMpeg(); KisImageSP m_image; KisDocument *m_doc; - WdgAnimaterionRenderer *m_page {0}; + WdgAnimationRenderer *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/metadataeditor/editors/exif.ui b/plugins/extensions/metadataeditor/editors/exif.ui index 2f12c4579e..d0d3a518b5 100644 --- a/plugins/extensions/metadataeditor/editors/exif.ui +++ b/plugins/extensions/metadataeditor/editors/exif.ui @@ -1,1109 +1,1109 @@ Exif 0 0 559 483 0 Exposure - &Brightness value: + Brightness &value: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editBrightnessValue &ISO: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editISO 3600 100 100 Exposure &time: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editExposureTime E&xposure mode: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editExposureMode Auto Manual Auto bracket Exposure pro&gram: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editExposureProgram Not defined Manual Normal program Aperture priority Shutter priority Creative program Action program Portrait mode Landscape mode Exposure index: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editExposureIndex Exposure bias: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editExposureBiasValue Ape&rture: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editApertureValue Shutter speed: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editShutterSpeedValue &F Number: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editFNumber Qt::Vertical 517 51 Lens &Focal length: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editFocalLength 1000 1 35 Focal length (&35mm equivalent): Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editFocalLengthIn35mmFilm 1000 1 35 Max aperture: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editMaxApertureValue Qt::Vertical 517 101 Autofocus - Su&bject distance: + Sub&ject distance: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editSubjectDistance Meterin&g mode: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editMeteringMode Unknown Average Center weighted average Spot Multi spot Pattern Partial Other D&istance range: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editSubjectDistanceRange Unknown Macro Close view Distant view Qt::Vertical 517 151 Flash Fired Stro&be return: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editStrobeReturn No strobe return detection Undefined No strobe return light detected Strobe return light detected Mode: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editFlashMode Unknown Compulsory flash fired Compulsory flash suppression Auto mode Function Red-eye removal Flash ener&gy: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editFlashEnergy Qt::Vertical 517 91 Postprocessing &Gain control: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editGainControl 1 0 None Low gain up High gain up Low gain down High gain down L&ight source: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editLightSource Unknown Daylight Fluorescent Tungsten Flash Undefined Undefined Undefined Undefined Fine weather Cloudy weather Shade Daylight fluorescent (D5700 - 7100K) Day white fluorescent (N4600 - 5400K) Cool white fluorescent (W3900 - 4500K) White fluorescent (WW 3200 - 3700K) Undefined Standard light A Standard light B Standard light C D55 D65 D75 D50 ISO studio tungsten other Sharpness: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editSharpness Normal Soft Hard Contrast: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editContrast Normal Soft Hard White &balance: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editWhiteBalance Auto Custom Qt::Vertical 517 91 Misc Scene capture t&ype: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editSceneCaptureType Standard Landscape Portrait Night scene Ma&ker: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editMake Model: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editModel Sens&ing method type: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter editSensingMethod Not Defined Not Defined One-chip color area sensor Two-chip color area sensor Three-chip color area sensor Color sequential area sensor Trilinear sensor Color sequential linear sensor Qt::Vertical 517 71 KisIntParseSpinBox QSpinBox
    kis_int_parse_spin_box.h
    diff --git a/plugins/extensions/metadataeditor/kis_meta_data_model.cpp b/plugins/extensions/metadataeditor/kis_meta_data_model.cpp index 6f75f048f1..4f91736f8b 100644 --- a/plugins/extensions/metadataeditor/kis_meta_data_model.cpp +++ b/plugins/extensions/metadataeditor/kis_meta_data_model.cpp @@ -1,113 +1,113 @@ /* * Copyright (c) 2010 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_meta_data_model.h" #include #include #include #include KisMetaDataModel::KisMetaDataModel(KisMetaData::Store* store) : m_store(store) { } int KisMetaDataModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_store->keys().count(); } int KisMetaDataModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 3; } QVariant KisMetaDataModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } Q_ASSERT(index.row() < m_store->keys().count()); switch (role) { case Qt::DisplayRole: { switch (index.column()) { case 0: return m_store->keys()[index.row()]; case 1: { KisMetaData::Value::ValueType vt = m_store->entries()[index.row()].value().type(); switch (vt) { case KisMetaData::Value::Invalid: return i18n("Invalid"); case KisMetaData::Value::Variant: { int vt = m_store->entries()[index.row()].value().asVariant().type(); switch (vt) { case QVariant::Date: case QVariant::DateTime: return i18n("Date"); case QVariant::Double: case QVariant::Int: return i18n("Number"); case QVariant::String: return i18n("String"); default: return i18n("Variant (%1)", vt); } } case KisMetaData::Value::OrderedArray: return i18n("Ordered array"); case KisMetaData::Value::UnorderedArray: return i18n("Unordered array"); case KisMetaData::Value::AlternativeArray: return i18n("Alternative array"); case KisMetaData::Value::LangArray: return i18n("Language array"); case KisMetaData::Value::Structure: return i18n("Structure"); case KisMetaData::Value::Rational: return i18n("Rational"); } break; } case 2: return m_store->entries()[index.row()].value().toString(); } } default: return QVariant(); } } QVariant KisMetaDataModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { Q_ASSERT(section < 3); switch (section) { case 0: return i18n("Key"); case 1: return i18n("Type"); case 2: - return i18n("Value"); + return i18nc("Metadata item value", "Value"); } } return QVariant(); } diff --git a/plugins/extensions/pykrita/plugin/CMakeLists.txt b/plugins/extensions/pykrita/plugin/CMakeLists.txt index fea46c4b05..f3d98e3762 100644 --- a/plugins/extensions/pykrita/plugin/CMakeLists.txt +++ b/plugins/extensions/pykrita/plugin/CMakeLists.txt @@ -1,41 +1,41 @@ # NOTE Disable trivial Qt keywords due conflicts w/ some Python.h header # (at least version 3.3 of it has a member PyType_Spec::slots) add_definitions(-DQT_NO_KEYWORDS) configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) -set(SOURCES - plugin.cpp +set(SOURCES + plugin.cpp pyqtpluginsettings.cpp utilities.cpp PykritaModule.cpp PythonPluginManager.cpp PythonPluginsModel.cpp ) -ki18n_wrap_ui(SOURCES - info.ui +ki18n_wrap_ui(SOURCES + info.ui manager.ui ) add_library(kritapykrita MODULE ${SOURCES}) target_link_libraries( kritapykrita ${PYTHON_LIBRARY} kritaui kritalibkis ) if (MINGW) target_compile_definitions(kritapykrita PRIVATE _hypot=hypot) endif (MINGW) install(TARGETS kritapykrita DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) # Install "built-in" api install( DIRECTORY krita DESTINATION ${LIB_INSTALL_DIR}/krita-python-libs FILES_MATCHING PATTERN "*.py" ) diff --git a/plugins/extensions/pykrita/plugin/PykritaModule.cpp b/plugins/extensions/pykrita/plugin/PykritaModule.cpp index 5238f272c2..517468a805 100644 --- a/plugins/extensions/pykrita/plugin/PykritaModule.cpp +++ b/plugins/extensions/pykrita/plugin/PykritaModule.cpp @@ -1,79 +1,115 @@ // This file is part of PyKrita, Krita' Python scripting plugin. // // Copyright (C) 2006 Paul Giannaros // Copyright (C) 2012, 2013 Shaheed Haque // Copyright (C) 2013 Alex Turbov // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) version 3, or any // later version accepted by the membership of KDE e.V. (or its // successor approved by the membership of KDE e.V.), which shall // act as a proxy defined in Section 6 of version 3 of the license. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library. If not, see . // #include "PykritaModule.h" #include "kis_debug.h" #define PYKRITA_INIT PyInit_pykrita +struct module_state { + PyObject *error; +}; + +#if defined(IS_PY3K) +#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) +#else +#define GETSTATE(m) (&_state) +static struct module_state _state; +#endif + /// \note Namespace name written in uppercase intentionally! /// It will appear in debug output from Python plugins... namespace PYKRITA { PyObject* debug(PyObject* /*self*/, PyObject* args) { const char* text; if (PyArg_ParseTuple(args, "s", &text)) dbgScript << text; Py_INCREF(Py_None); return Py_None; } } // namespace PYKRITA namespace { PyMethodDef pykritaMethods[] = { { "qDebug" , &PYKRITA::debug , METH_VARARGS , "True KDE way to show debug info" } , { 0, 0, 0, 0 } }; } // anonymous namespace //BEGIN Python module registration +#if defined(IS_PY3K) +// Python 3 initializes modules differently from Python 2 +// +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT + , "pykrita" + , "The pykrita module" + , -1 + , pykritaMethods + , 0 + , 0 + , 0 + , 0 +}; + +#define INITERROR return NULL + PyMODINIT_FUNC PyInit_pykrita() + +#else +#define INITERROR return + +void +initpykrita(void) +#endif { - static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT - , "pykrita" - , "The pykrita module" - , -1 - , pykritaMethods - , 0 - , 0 - , 0 - , 0 - }; +#if defined(IS_PY3K) PyObject *pykritaModule = PyModule_Create(&moduledef); +#else + PyObject *pykritaModule = Py_InitModule("pykrita", pykritaMethods); +#endif + + if (pykritaModule == NULL) + INITERROR; + PyModule_AddStringConstant(pykritaModule, "__file__", __FILE__); + +#if defined(IS_PY3K) return pykritaModule; +#endif } + //END Python module registration // krita: space-indent on; indent-width 4; #undef PYKRITA_INIT diff --git a/plugins/extensions/pykrita/plugin/PykritaModule.h b/plugins/extensions/pykrita/plugin/PykritaModule.h index 4bf9953475..28a674eed1 100644 --- a/plugins/extensions/pykrita/plugin/PykritaModule.h +++ b/plugins/extensions/pykrita/plugin/PykritaModule.h @@ -1,33 +1,43 @@ /* * This file is part of PyKrita, Krita' Python scripting plugin. * * Copyright (C) 2013 Alex Turbov * Copyright (C) 2014-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) version 3. * * 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 __PYKRITA_MODULE_H__ #define __PYKRITA_MODULE_H__ #include +#if PY_MAJOR_VERSION >= 3 +#ifndef IS_PY3K +#define IS_PY3K +#endif +#endif + /** * Initializer for the built-in Python module. */ +#if defined(IS_PY3K) PyMODINIT_FUNC PyInit_pykrita(); +#else +void initpykrita(); +#endif #endif diff --git a/plugins/extensions/pykrita/plugin/PythonPluginManager.cpp b/plugins/extensions/pykrita/plugin/PythonPluginManager.cpp index 2a6b1c5e36..49f3c6bd5a 100644 --- a/plugins/extensions/pykrita/plugin/PythonPluginManager.cpp +++ b/plugins/extensions/pykrita/plugin/PythonPluginManager.cpp @@ -1,411 +1,418 @@ /* * This file is part of PyKrita, Krita' Python scripting plugin. * * Copyright (C) 2013 Alex Turbov * Copyright (C) 2014-2016 Boudewijn Rempt * Copyright (C) 2017 Jouni Pentikäinen (joupent@gmail.com) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; 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 "PythonPluginManager.h" #include #include #include #include #include #include #include #include #include "config.h" #include "version_checker.h" PythonPluginManager* instance = 0; // PythonPlugin implementation QString PythonPlugin::moduleFilePathPart() const { QString filePath = m_moduleName; return filePath.replace(".", "/"); } bool PythonPlugin::isValid() const { dbgScript << "Got Krita/PythonPlugin: " << name() << ", module-path=" << moduleName() ; // Make sure mandatory properties are here if (m_name.isEmpty()) { dbgScript << "Ignore desktop file w/o a name"; return false; } if (m_moduleName.isEmpty()) { dbgScript << "Ignore desktop file w/o a module to import"; return false; } +#if PY_MAJOR_VERSION == 2 + // Check if the plug-in is compatible with Python 2 or not. + if (m_properties["X-Python-2-Compatible"].toBool() != true) { + dbgScript << "Ignoring plug-in. It is marked incompatible with Python 2."; + return false; + } +#endif return true; } // PythonPluginManager implementation PythonPluginManager::PythonPluginManager() : QObject(0) , m_model(0, this) {} const QList& PythonPluginManager::plugins() const { return m_plugins; } PythonPlugin * PythonPluginManager::plugin(int index) { if (index >= 0 && index < m_plugins.count()) { return &m_plugins[index]; } return nullptr; } PythonPluginsModel * PythonPluginManager::model() { return &m_model; } void PythonPluginManager::unloadAllModules() { Q_FOREACH(PythonPlugin plugin, m_plugins) { if (plugin.m_loaded) { unloadModule(plugin); } } } bool PythonPluginManager::verifyModuleExists(PythonPlugin &plugin) { // Find the module: // 0) try to locate directory based plugin first QString rel_path = plugin.moduleFilePathPart(); rel_path = rel_path + "/" + "__init__.py"; dbgScript << "Finding Python module with rel_path:" << rel_path; QString module_path = KoResourcePaths::findResource("pythonscripts", rel_path); dbgScript << "module_path:" << module_path; if (module_path.isEmpty()) { // 1) Nothing found, then try file based plugin rel_path = plugin.moduleFilePathPart() + ".py"; dbgScript << "Finding Python module with rel_path:" << rel_path; module_path = KoResourcePaths::findResource("pythonscripts", rel_path); dbgScript << "module_path:" << module_path; } // Is anything found at all? if (module_path.isEmpty()) { plugin.m_broken = true; plugin.m_errorReason = i18nc( "@info:tooltip" , "Unable to find the module specified %1" , plugin.moduleName() ); dbgScript << "Cannot load module:" << plugin.m_errorReason; return false; } dbgScript << "Found module path:" << module_path; return true; } QPair PythonPluginManager::parseDependency(const QString& d) { // Check if dependency has package info attached const int pnfo = d.indexOf('('); if (pnfo != -1) { QString dependency = d.mid(0, pnfo); QString version_str = d.mid(pnfo + 1, d.size() - pnfo - 2).trimmed(); dbgScript << "Desired version spec [" << dependency << "]:" << version_str; PyKrita::version_checker checker = PyKrita::version_checker::fromString(version_str); if (!(checker.isValid() && d.endsWith(')'))) { dbgScript << "Invalid version spec " << d; QString reason = i18nc( "@info:tooltip" , "

    Specified version has invalid format for dependency %1: " "%2. Skipped

    " , dependency , version_str ); return qMakePair(reason, PyKrita::version_checker()); } return qMakePair(dependency, checker); } return qMakePair(d, PyKrita::version_checker(PyKrita::version_checker::undefined)); } /** * Collect dependencies and check them. To do it * just try to import a module... when unload it ;) * * \c X-Python-Dependencies property of \c .desktop file has the following format: * python-module(version-info), where python-module * a python module name to be imported, version-spec * is a version triplet delimited by dots, possible w/ leading compare * operator: \c =, \c <, \c >, \c <=, \c >= */ void PythonPluginManager::verifyDependenciesSetStatus(PythonPlugin& plugin) { QStringList dependencies = plugin.property("X-Python-Dependencies").toStringList(); PyKrita::Python py = PyKrita::Python(); QString reason = i18nc("@info:tooltip", "Dependency check"); Q_FOREACH(const QString & d, dependencies) { QPair info_pair = parseDependency(d); PyKrita::version_checker& checker = info_pair.second; if (!checker.isValid()) { plugin.m_broken = true; reason += info_pair.first; continue; } dbgScript << "Try to import dependency module/package:" << d; // Try to import a module const QString& dependency = info_pair.first; PyObject* module = py.moduleImport(PQ(dependency)); if (module) { if (checker.isEmpty()) { // Need to check smth? dbgScript << "No version to check, just make sure it's loaded:" << dependency; Py_DECREF(module); continue; } // Try to get __version__ from module // See PEP396: http://www.python.org/dev/peps/pep-0396/ PyObject* version_obj = py.itemString("__version__", PQ(dependency)); if (!version_obj) { dbgScript << "No __version__ for " << dependency << "[" << plugin.name() << "]:\n" << py.lastTraceback() ; plugin.m_unstable = true; reason += i18nc( "@info:tooltip" , "

    Failed to check version of dependency %1: " "Module do not have PEP396 __version__ attribute. " "It is not disabled, but behaviour is unpredictable...

    " , dependency ); } PyKrita::version dep_version = PyKrita::version::fromPythonObject(version_obj); if (!dep_version.isValid()) { // Dunno what is this... Giving up! dbgScript << "***: Can't parse module version for" << dependency; plugin.m_unstable = true; reason += i18nc( "@info:tooltip" , "

    %1: Unexpected module's version format" , dependency ); } else if (!checker(dep_version)) { dbgScript << "Version requirement check failed [" << plugin.name() << "] for " << dependency << ": wanted " << checker.operationToString() << QString(checker.required()) << ", but found" << QString(dep_version) ; plugin.m_broken = true; reason += i18nc( "@info:tooltip" , "

    %1: No suitable version found. " "Required version %2 %3, but found %4

    " , dependency , checker.operationToString() , QString(checker.required()) , QString(dep_version) ); } // Do not need this module anymore... Py_DECREF(module); } else { dbgScript << "Load failure [" << plugin.name() << "]:\n" << py.lastTraceback(); plugin.m_broken = true; reason += i18nc( "@info:tooltip" , "

    Failure on module load %1:

    %2
    " , dependency , py.lastTraceback() ); } } if (plugin.isBroken() || plugin.isUnstable()) { plugin.m_errorReason = reason; } } void PythonPluginManager::scanPlugins() { m_plugins.clear(); KConfigGroup pluginSettings(KSharedConfig::openConfig(), "python"); QStringList desktopFiles = KoResourcePaths::findAllResources("data", "pykrita/*desktop"); Q_FOREACH(const QString &desktopFile, desktopFiles) { QSettings s(desktopFile, QSettings::IniFormat); s.beginGroup("Desktop Entry"); if (s.value("ServiceTypes").toString() == "Krita/PythonPlugin") { PythonPlugin plugin; plugin.m_comment = s.value("Comment").toString(); plugin.m_name = s.value("Name").toString(); plugin.m_moduleName = s.value("X-KDE-Library").toString(); plugin.m_properties["X-Python-2-Compatible"] = s.value("X-Python-2-Compatible", false).toBool(); QString manual = s.value("X-Krita-Manual").toString(); if (!manual.isEmpty()) { QFile f(QFileInfo(desktopFile).path() + "/" + plugin.m_moduleName + "/" + manual); if (f.exists()) { f.open(QFile::ReadOnly); QByteArray ba = f.readAll(); f.close(); plugin.m_manual = QString::fromUtf8(ba); } } if (!plugin.isValid()) { dbgScript << plugin.name() << "is not usable"; continue; } if (!verifyModuleExists(plugin)) { dbgScript << "Cannot load" << plugin.name() << ": broken" << plugin.isBroken() << "because:" << plugin.errorReason(); continue; } verifyDependenciesSetStatus(plugin); plugin.m_enabled = pluginSettings.readEntry(QString("enable_") + plugin.moduleName(), false); m_plugins.append(plugin); } } } void PythonPluginManager::tryLoadEnabledPlugins() { for (PythonPlugin &plugin : m_plugins) { dbgScript << "Trying to load plugin" << plugin.moduleName() << ". Enabled:" << plugin.isEnabled() << ". Broken: " << plugin.isBroken(); if (plugin.m_enabled && !plugin.isBroken()) { loadModule(plugin); } } } void PythonPluginManager::loadModule(PythonPlugin &plugin) { KIS_SAFE_ASSERT_RECOVER_RETURN(plugin.isEnabled() && !plugin.isBroken()); QString module_name = plugin.moduleName(); dbgScript << "Loading module: " << module_name; PyKrita::Python py = PyKrita::Python(); // Get 'plugins' key from 'pykrita' module dictionary. // Every entry has a module name as a key and 2 elements tuple as a value PyObject* plugins = py.itemString("plugins"); KIS_SAFE_ASSERT_RECOVER_RETURN(plugins); PyObject* module = py.moduleImport(PQ(module_name)); if (module) { // Move just loaded module to the dict const int ins_result = PyDict_SetItemString(plugins, PQ(module_name), module); KIS_SAFE_ASSERT_RECOVER_NOOP(ins_result == 0); Py_DECREF(module); // Handle failure in release mode. if (ins_result == 0) { // Initialize the module from Python's side PyObject* const args = Py_BuildValue("(s)", PQ(module_name)); PyObject* result = py.functionCall("_pluginLoaded", PyKrita::Python::PYKRITA_ENGINE, args); Py_DECREF(args); if (result) { dbgScript << "\t" << "success!"; plugin.m_loaded = true; return; } } plugin.m_errorReason = i18nc("@info:tooltip", "Internal engine failure"); } else { plugin.m_errorReason = i18nc( "@info:tooltip" , "Module not loaded:
    %1" , py.lastTraceback().replace("\n", "
    ") ); } plugin.m_broken = true; warnScript << "Error loading plugin" << module_name; } void PythonPluginManager::unloadModule(PythonPlugin &plugin) { KIS_SAFE_ASSERT_RECOVER_RETURN(plugin.m_loaded); KIS_SAFE_ASSERT_RECOVER_RETURN(!plugin.isBroken()); dbgScript << "Unloading module: " << plugin.moduleName(); PyKrita::Python py = PyKrita::Python(); // Get 'plugins' key from 'pykrita' module dictionary PyObject* plugins = py.itemString("plugins"); KIS_SAFE_ASSERT_RECOVER_RETURN(plugins); PyObject* const args = Py_BuildValue("(s)", PQ(plugin.moduleName())); py.functionCall("_pluginUnloading", PyKrita::Python::PYKRITA_ENGINE, args); Py_DECREF(args); // This will just decrement a reference count for module instance PyDict_DelItemString(plugins, PQ(plugin.moduleName())); // Remove the module also from 'sys.modules' dict to really unload it, // so if reloaded all @init actions will work again! PyObject* sys_modules = py.itemString("modules", "sys"); KIS_SAFE_ASSERT_RECOVER_RETURN(sys_modules); PyDict_DelItemString(sys_modules, PQ(plugin.moduleName())); plugin.m_loaded = false; } void PythonPluginManager::setPluginEnabled(PythonPlugin &plugin, bool enabled) { bool wasEnabled = plugin.isEnabled(); if (wasEnabled && !enabled) { unloadModule(plugin); } plugin.m_enabled = enabled; KConfigGroup pluginSettings(KSharedConfig::openConfig(), "python"); pluginSettings.writeEntry(QString("enable_") + plugin.moduleName(), enabled); if (!wasEnabled && enabled) { loadModule(plugin); } } diff --git a/plugins/extensions/pykrita/plugin/krita/__init__.py b/plugins/extensions/pykrita/plugin/krita/__init__.py index 3052b96e32..0feb22fbc4 100644 --- a/plugins/extensions/pykrita/plugin/krita/__init__.py +++ b/plugins/extensions/pykrita/plugin/krita/__init__.py @@ -1,72 +1,77 @@ +from __future__ import print_function + import pykrita import os import sys -import signal -signal.signal(signal.SIGINT, signal.SIG_DFL) - from .api import * from .decorators import * from .dockwidgetfactory import * from PyKrita import krita +import signal +signal.signal(signal.SIGINT, signal.SIG_DFL) + krita_path = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, krita_path) print("%s added to PYTHONPATH" % krita_path, file=sys.stderr) # Look for PyQt try: from PyQt5 import QtCore except ImportError: print("Python cannot find the Qt5 bindings.", file=sys.stderr) print("Please make sure, that the needed packages are installed.", file=sys.stderr) raise # Shows nice looking error dialog if an unhandled exception occurs. import excepthook excepthook.install() -import builtins +if sys.version_info[0] > 2: + import builtins +else: + import __builtin__ as builtins builtins.i18n = lambda s: QCoreApplication.translate("PyKrita", s) builtins.Scripter = Krita.instance() builtins.Application = Krita.instance() builtins.Krita = Krita.instance() def qDebug(text): '''Use KDE way to show debug info TODO Add a way to control debug output from partucular plugins (?) ''' plugin = sys._getframe(1).f_globals['__name__'] pykrita.qDebug('{}: {}'.format(plugin, text)) @pykritaEventHandler('_pluginLoaded') def on_load(plugin): if plugin in init.functions: # Call registered init functions for the plugin init.fire(plugin=plugin) del init.functions[plugin] return True @pykritaEventHandler('_pluginUnloading') def on_unload(plugin): if plugin in unload.functions: # Deinitialize plugin unload.fire(plugin=plugin) del unload.functions[plugin] return True @pykritaEventHandler('_pykritaLoaded') def on_pykrita_loaded(): qDebug('PYKRITA LOADED') return True @pykritaEventHandler('_pykritaUnloading') def on_pykrita_unloading(): qDebug('UNLOADING PYKRITA') return True diff --git a/plugins/extensions/pykrita/plugin/krita/dockwidgetfactory.py b/plugins/extensions/pykrita/plugin/krita/dockwidgetfactory.py index 164785e19f..bfbacd9051 100644 --- a/plugins/extensions/pykrita/plugin/krita/dockwidgetfactory.py +++ b/plugins/extensions/pykrita/plugin/krita/dockwidgetfactory.py @@ -1,14 +1,14 @@ from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * from PyKrita.krita import * class DockWidgetFactory(DockWidgetFactoryBase): def __init__(self, _id, _dockPosition, _klass): - super().__init__(_id, _dockPosition) + super(DockWidgetFactory, self).__init__(_id, _dockPosition) self.klass = _klass def createDockWidget(self): return self.klass() diff --git a/plugins/extensions/pykrita/plugin/utilities.cpp b/plugins/extensions/pykrita/plugin/utilities.cpp index e8e63dde5f..b6e6753107 100644 --- a/plugins/extensions/pykrita/plugin/utilities.cpp +++ b/plugins/extensions/pykrita/plugin/utilities.cpp @@ -1,693 +1,679 @@ // This file is part of PyKrita, Krita' Python scripting plugin. // // Copyright (C) 2006 Paul Giannaros // Copyright (C) 2012, 2013 Shaheed Haque // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) version 3, or any // later version accepted by the membership of KDE e.V. (or its // successor approved by the membership of KDE e.V.), which shall // act as a proxy defined in Section 6 of version 3 of the license. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library. If not, see . // // config.h defines PYKRITA_PYTHON_LIBRARY, the path to libpython.so // on the build system #include "config.h" #include "utilities.h" #include "PythonPluginManager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "PykritaModule.h" #define THREADED 1 namespace PyKrita { static InitResult initStatus = INIT_UNINITIALIZED; static QScopedPointer pluginManagerInstance; InitResult initialize() { // Already initialized? if (initStatus == INIT_OK) return INIT_OK; dbgScript << "Initializing Python plugin for Python" << PY_MAJOR_VERSION << "," << PY_MINOR_VERSION; if (!Python::libraryLoad()) { return INIT_CANNOT_LOAD_PYTHON_LIBRARY; } // Update PYTHONPATH // 0) custom plugin directories (prefer local dir over systems') // 1) shipped krita module's dir QStringList pluginDirectories = KoResourcePaths::findDirs("pythonscripts"); dbgScript << "Plugin Directories: " << pluginDirectories; if (!Python::setPath(pluginDirectories)) { initStatus = INIT_CANNOT_SET_PYTHON_PATHS; return initStatus; } +#if defined(IS_PY3K) if (0 != PyImport_AppendInittab(Python::PYKRITA_ENGINE, PyInit_pykrita)) { +#else + if (0 != PyImport_AppendInittab(Python::PYKRITA_ENGINE, initpykrita)) { +#endif initStatus = INIT_CANNOT_LOAD_PYKRITA_MODULE; return initStatus; } Python::ensureInitialized(); Python py = Python(); PyRun_SimpleString( "import sip\n" "sip.setapi('QDate', 2)\n" "sip.setapi('QTime', 2)\n" "sip.setapi('QDateTime', 2)\n" "sip.setapi('QUrl', 2)\n" "sip.setapi('QTextStream', 2)\n" "sip.setapi('QString', 2)\n" "sip.setapi('QVariant', 2)\n" ); // Initialize 'plugins' dict of module 'pykrita' PyObject* plugins = PyDict_New(); py.itemStringSet("plugins", plugins); pluginManagerInstance.reset(new PythonPluginManager()); +#if defined(IS_PY3K) // Initialize our built-in module. auto pykritaModule = PyInit_pykrita(); if (!pykritaModule) { initStatus = INIT_CANNOT_LOAD_PYKRITA_MODULE; return initStatus; //return i18nc("@info:tooltip ", "No pykrita built-in module"); } +#else + initpykrita(); +#endif initStatus = INIT_OK; return initStatus; } PythonPluginManager *pluginManager() { auto pluginManager = pluginManagerInstance.data(); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(pluginManager, nullptr); return pluginManager; } void finalize() { dbgScript << "Going to destroy the Python engine"; if (pluginManagerInstance) { pluginManagerInstance->unloadAllModules(); PyKrita::Python::maybeFinalize(); PyKrita::Python::libraryUnload(); pluginManagerInstance.reset(); initStatus = INIT_UNINITIALIZED; } } namespace { #ifndef Q_OS_WIN QLibrary* s_pythonLibrary = 0; #endif PyThreadState* s_pythonThreadState = 0; bool isPythonPathSet = false; } // anonymous namespace const char* Python::PYKRITA_ENGINE = "pykrita"; Python::Python() { #if THREADED m_state = PyGILState_Ensure(); #endif } Python::~Python() { #if THREADED PyGILState_Release(m_state); #endif } bool Python::prependStringToList(PyObject* const list, const QString& value) { PyObject* const u = unicode(value); bool result = !PyList_Insert(list, 0, u); Py_DECREF(u); if (!result) traceback(QString("Failed to prepend %1").arg(value)); return result; } bool Python::functionCall(const char* const functionName, const char* const moduleName) { PyObject* const result = functionCall(functionName, moduleName, PyTuple_New(0)); if (result) Py_DECREF(result); return bool(result); } PyObject* Python::functionCall( const char* const functionName , const char* const moduleName , PyObject* const arguments ) { if (!arguments) { errScript << "Missing arguments for" << moduleName << functionName; return 0; } PyObject* const func = itemString(functionName, moduleName); if (!func) { errScript << "Failed to resolve" << moduleName << functionName; return 0; } if (!PyCallable_Check(func)) { traceback(QString("Not callable %1.%2").arg(moduleName).arg(functionName)); return 0; } PyObject* const result = PyObject_CallObject(func, arguments); Py_DECREF(arguments); if (!result) traceback(QString("No result from %1.%2").arg(moduleName).arg(functionName)); return result; } bool Python::itemStringDel(const char* const item, const char* const moduleName) { PyObject* const dict = moduleDict(moduleName); const bool result = dict && PyDict_DelItemString(dict, item); if (!result) traceback(QString("Could not delete item string %1.%2").arg(moduleName).arg(item)); return result; } PyObject* Python::itemString(const char* const item, const char* const moduleName) { if (PyObject* const value = itemString(item, moduleDict(moduleName))) return value; errScript << "Could not get item string" << moduleName << item; return 0; } PyObject* Python::itemString(const char* item, PyObject* dict) { if (dict) if (PyObject* const value = PyDict_GetItemString(dict, item)) return value; traceback(QString("Could not get item string %1").arg(item)); return 0; } bool Python::itemStringSet(const char* const item, PyObject* const value, const char* const moduleName) { PyObject* const dict = moduleDict(moduleName); const bool result = dict && !PyDict_SetItemString(dict, item, value); if (!result) traceback(QString("Could not set item string %1.%2").arg(moduleName).arg(item)); return result; } PyObject* Python::kritaHandler(const char* const moduleName, const char* const handler) { if (PyObject* const module = moduleImport(moduleName)) return functionCall(handler, "krita", Py_BuildValue("(O)", module)); return 0; } QString Python::lastTraceback() const { QString result; result.swap(m_traceback); return result; } bool Python::libraryLoad() { // no-op on Windows -#ifdef Q_OS_LINUX +#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) if (!s_pythonLibrary) { QFileInfo fi(PYKRITA_PYTHON_LIBRARY); - dbgScript << fi.canonicalFilePath() << fi.exists(); - if (fi.exists()) { - dbgScript << "Creating s_pythonLibrary" << PYKRITA_PYTHON_LIBRARY; - s_pythonLibrary = new QLibrary(PYKRITA_PYTHON_LIBRARY); - } - else { - QString libraryName = fi.fileName(); - QString applicationRoot = KoResourcePaths::getApplicationRoot(); - QStringList locations; - locations << applicationRoot + "/lib" - << applicationRoot + "/lib64" - << applicationRoot + "/lib/X64_86-linux-gnu"; - Q_FOREACH(const QString &location, locations) { - QDir d(location); - QStringList entries = d.entryList(QStringList() << libraryName + "*"); - dbgScript << entries; - Q_FOREACH(const QString &entry, entries) { - QFileInfo fi2(location + "/" + entry); - if (fi2.exists()) { - s_pythonLibrary = new QLibrary(fi2.canonicalFilePath()); - break; - } - } - } - } - if (!s_pythonLibrary) { - dbgScript << "Could not create" << PYKRITA_PYTHON_LIBRARY; - return false; - } - + // get the filename of the configured Python library, without the .so suffix + const QString libraryName = fi.completeBaseName(); + // 1.0 is the SONAME of the shared Python library + s_pythonLibrary = new QLibrary(libraryName, "1.0"); s_pythonLibrary->setLoadHints(QLibrary::ExportExternalSymbolsHint); if (!s_pythonLibrary->load()) { dbgScript << QString("Could not load %1 -- Reason: %2").arg(s_pythonLibrary->fileName()).arg(s_pythonLibrary->errorString()); + delete s_pythonLibrary; + s_pythonLibrary = 0; return false; } } #endif return true; } namespace { QString findKritaPythonLibsPath(const QString &libdir) { QDir rootDir(KoResourcePaths::getApplicationRoot()); QFileInfoList candidates = rootDir.entryInfoList(QStringList() << "lib*", QDir::Dirs | QDir::NoDotAndDotDot) + rootDir.entryInfoList(QStringList() << "Frameworks", QDir::Dirs | QDir::NoDotAndDotDot); Q_FOREACH (const QFileInfo &entry, candidates) { QDir libDir(entry.absoluteFilePath()); if (libDir.cd(libdir)) { return libDir.absolutePath(); } else { // Handle cases like Linux where libs are placed in a sub-dir // with the ABI name Q_FOREACH (const QFileInfo &subEntry, libDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) { QDir subDir(subEntry.absoluteFilePath()); if (subDir.cd(libdir)) { return subDir.absolutePath(); } } } } return QString(); } } // namespace bool Python::setPath(const QStringList& scriptPaths) { KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!Py_IsInitialized(), false); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!isPythonPathSet, false); bool runningInBundle = (KoResourcePaths::getApplicationRoot().toLower().contains(".mount_krita") || KoResourcePaths::getApplicationRoot().toLower().contains("krita.app")); qDebug() << "Python::setPath. Script paths:" << scriptPaths << runningInBundle; #ifdef Q_OS_WIN constexpr char pathSeparator = ';'; #else constexpr char pathSeparator = ':'; #endif QString originalPath; // Start with the script paths QStringList paths(scriptPaths); // Append the Krita libraries path QString pythonLibsPath = findKritaPythonLibsPath("krita-python-libs"); qDebug() << "pythonLibsPath (krita-python-libs)" << pythonLibsPath; if (pythonLibsPath.isEmpty()) { dbgScript << "Cannot find krita-python-libs"; return false; } dbgScript << "Found krita-python-libs at" << pythonLibsPath; paths.append(pythonLibsPath); #ifndef Q_OS_WIN // Append the sip libraries path pythonLibsPath = findKritaPythonLibsPath("sip"); qDebug() << "pythonLibsPath (sip)" << pythonLibsPath; if (!pythonLibsPath.isEmpty()) { dbgScript << "Found sip at" << pythonLibsPath; paths.append(pythonLibsPath); } #endif #ifdef Q_OS_WIN // Find embeddable Python at /python QDir pythonDir(KoResourcePaths::getApplicationRoot()); if (pythonDir.cd("python")) { dbgScript << "Found bundled Python at" << pythonDir.absolutePath(); // The default paths for Windows embeddable Python is ./python36.zip;./ // HACK: Assuming bundled Python is version 3.6.* // FIXME: Should we read python36._pth for the paths or use Py_GetPath? paths.append(pythonDir.absoluteFilePath("python36.zip")); paths.append(pythonDir.absolutePath()); } else { errScript << "Bundled Python not found, cannot set Python library paths"; return false; } #else // If using a system Python install, respect the current PYTHONPATH if (KoResourcePaths::getApplicationRoot().toLower().contains(".mount_krita")) { // We're running from an appimage, so we need our local python QString p = QFileInfo(PYKRITA_PYTHON_LIBRARY).fileName(); QString p2 = p.remove("lib").remove("m.so"); dbgScript << "\t" << p << p2; originalPath = findKritaPythonLibsPath(p); paths.append(originalPath + "/lib-dynload"); paths.append(originalPath + "/site-packages"); paths.append(originalPath + "/site-packages/PyQt5"); } else { // Use the system path originalPath = QString::fromLocal8Bit(qgetenv("PYTHONPATH")); } #endif QString joinedPaths = paths.join(pathSeparator); if (!originalPath.isEmpty()) { joinedPaths = joinedPaths + pathSeparator + originalPath; } dbgScript << "Setting python paths:" << joinedPaths; #ifdef Q_OS_WIN QVector joinedPathsWChars(joinedPaths.size() + 1, 0); joinedPaths.toWCharArray(joinedPathsWChars.data()); Py_SetPath(joinedPathsWChars.data()); #else if (KoResourcePaths::getApplicationRoot().contains(".mount_Krita")) { QVector joinedPathsWChars(joinedPaths.size() + 1, 0); joinedPaths.toWCharArray(joinedPathsWChars.data()); - Py_SetPath(joinedPathsWChars.data()); + PyRun_SimpleString("import sys; import os"); + QString pathCommand = QString("sys.path += '") + joinedPaths + QString("'.split(os.pathsep)"); + PyRun_SimpleString(pathCommand.toUtf8().constData()); } else { qputenv("PYTHONPATH", joinedPaths.toLocal8Bit()); } #endif isPythonPathSet = true; return true; } void Python::ensureInitialized() { if (Py_IsInitialized()) { warnScript << "Python interpreter is already initialized, not initializing again"; } else { dbgScript << "Initializing Python interpreter"; Py_InitializeEx(0); if (!Py_IsInitialized()) { errScript << "Could not initialize Python interpreter"; } #if THREADED PyEval_InitThreads(); s_pythonThreadState = PyGILState_GetThisThreadState(); PyEval_ReleaseThread(s_pythonThreadState); #endif } } void Python::maybeFinalize() { if (!Py_IsInitialized()) { warnScript << "Python interpreter not initialized, no need to finalize"; } else { #if THREADED PyEval_AcquireThread(s_pythonThreadState); #endif Py_Finalize(); } } void Python::libraryUnload() { // no-op on Windows #ifndef Q_OS_WIN if (s_pythonLibrary) { // Shut the interpreter down if it has been started. if (s_pythonLibrary->isLoaded()) { s_pythonLibrary->unload(); } delete s_pythonLibrary; s_pythonLibrary = 0; } #endif } PyObject* Python::moduleActions(const char* moduleName) { return kritaHandler(moduleName, "moduleGetActions"); } PyObject* Python::moduleConfigPages(const char* const moduleName) { return kritaHandler(moduleName, "moduleGetConfigPages"); } QString Python::moduleHelp(const char* moduleName) { QString r; PyObject* const result = kritaHandler(moduleName, "moduleGetHelp"); if (result) { r = unicode(result); Py_DECREF(result); } return r; } PyObject* Python::moduleDict(const char* const moduleName) { PyObject* const module = moduleImport(moduleName); if (module) if (PyObject* const dictionary = PyModule_GetDict(module)) return dictionary; traceback(QString("Could not get dict %1").arg(moduleName)); return 0; } PyObject* Python::moduleImport(const char* const moduleName) { PyObject* const module = PyImport_ImportModule(moduleName); if (module) return module; traceback(QString("Could not import %1").arg(moduleName)); return 0; } // Inspired by http://www.gossamer-threads.com/lists/python/python/150924. void Python::traceback(const QString& description) { m_traceback.clear(); if (!PyErr_Occurred()) // Return an empty string on no error. // NOTE "Return a string?" really?? return; PyObject* exc_typ; PyObject* exc_val; PyObject* exc_tb; PyErr_Fetch(&exc_typ, &exc_val, &exc_tb); PyErr_NormalizeException(&exc_typ, &exc_val, &exc_tb); // Include the traceback. if (exc_tb) { m_traceback = "Traceback (most recent call last):\n"; PyObject* const arguments = PyTuple_New(1); PyTuple_SetItem(arguments, 0, exc_tb); PyObject* const result = functionCall("format_tb", "traceback", arguments); if (result) { for (int i = 0, j = PyList_Size(result); i < j; i++) { PyObject* const tt = PyList_GetItem(result, i); PyObject* const t = Py_BuildValue("(O)", tt); char* buffer; if (!PyArg_ParseTuple(t, "s", &buffer)) break; m_traceback += buffer; } Py_DECREF(result); } Py_DECREF(exc_tb); } // Include the exception type and value. if (exc_typ) { PyObject* const temp = PyObject_GetAttrString(exc_typ, "__name__"); if (temp) { m_traceback += unicode(temp); m_traceback += ": "; } Py_DECREF(exc_typ); } if (exc_val) { PyObject* const temp = PyObject_Str(exc_val); if (temp) { m_traceback += unicode(temp); m_traceback += "\n"; } Py_DECREF(exc_val); } m_traceback += description; QStringList l = m_traceback.split("\n"); Q_FOREACH(const QString &s, l) { errScript << s; } /// \todo How about to show it somewhere else than "console output"? } PyObject* Python::unicode(const QString& string) { #if PY_MAJOR_VERSION < 3 /* Python 2.x. http://docs.python.org/2/c-api/unicode.html */ PyObject* s = PyString_FromString(PQ(string)); PyObject* u = PyUnicode_FromEncodedObject(s, "utf-8", "strict"); Py_DECREF(s); return u; #elif PY_MINOR_VERSION < 3 /* Python 3.2 or less. http://docs.python.org/3.2/c-api/unicode.html#unicode-objects */ # ifdef Py_UNICODE_WIDE return PyUnicode_DecodeUTF16((const char*)string.constData(), string.length() * 2, 0, 0); # else return PyUnicode_FromUnicode(string.constData(), string.length()); # endif #else /* Python 3.3 or greater. http://docs.python.org/3.3/c-api/unicode.html#unicode-objects */ return PyUnicode_FromKindAndData(PyUnicode_2BYTE_KIND, string.constData(), string.length()); #endif } QString Python::unicode(PyObject* const string) { #if PY_MAJOR_VERSION < 3 /* Python 2.x. http://docs.python.org/2/c-api/unicode.html */ if (PyString_Check(string)) return QString(PyString_AsString(string)); else if (PyUnicode_Check(string)) { const int unichars = PyUnicode_GetSize(string); # ifdef HAVE_USABLE_WCHAR_T return QString::fromWCharArray(PyUnicode_AsUnicode(string), unichars); # else # ifdef Py_UNICODE_WIDE return QString::fromUcs4((const unsigned int*)PyUnicode_AsUnicode(string), unichars); # else return QString::fromUtf16(PyUnicode_AsUnicode(string), unichars); # endif # endif } else return QString(); #elif PY_MINOR_VERSION < 3 /* Python 3.2 or less. http://docs.python.org/3.2/c-api/unicode.html#unicode-objects */ if (!PyUnicode_Check(string)) return QString(); const int unichars = PyUnicode_GetSize(string); # ifdef HAVE_USABLE_WCHAR_T return QString::fromWCharArray(PyUnicode_AsUnicode(string), unichars); # else # ifdef Py_UNICODE_WIDE return QString::fromUcs4(PyUnicode_AsUnicode(string), unichars); # else return QString::fromUtf16(PyUnicode_AsUnicode(string), unichars); # endif # endif #else /* Python 3.3 or greater. http://docs.python.org/3.3/c-api/unicode.html#unicode-objects */ if (!PyUnicode_Check(string)) return QString(); const int unichars = PyUnicode_GetLength(string); if (0 != PyUnicode_READY(string)) return QString(); switch (PyUnicode_KIND(string)) { case PyUnicode_1BYTE_KIND: return QString::fromLatin1((const char*)PyUnicode_1BYTE_DATA(string), unichars); case PyUnicode_2BYTE_KIND: return QString::fromUtf16(PyUnicode_2BYTE_DATA(string), unichars); case PyUnicode_4BYTE_KIND: return QString::fromUcs4(PyUnicode_4BYTE_DATA(string), unichars); default: break; } return QString(); #endif } bool Python::isUnicode(PyObject* const string) { #if PY_MAJOR_VERSION < 3 return PyString_Check(string) || PyUnicode_Check(string); #else return PyUnicode_Check(string); #endif } bool Python::prependPythonPaths(const QString& path) { PyObject* sys_path = itemString("path", "sys"); return bool(sys_path) && prependPythonPaths(path, sys_path); } bool Python::prependPythonPaths(const QStringList& paths) { PyObject* sys_path = itemString("path", "sys"); if (!sys_path) return false; /// \todo Heh, boosts' range adaptors would be good here! QStringList reversed_paths; std::reverse_copy( paths.begin() , paths.end() , std::back_inserter(reversed_paths) ); Q_FOREACH(const QString & path, reversed_paths) if (!prependPythonPaths(path, sys_path)) return false; return true; } bool Python::prependPythonPaths(const QString& path, PyObject* sys_path) { Q_ASSERT("Dir entry expected to be valid" && sys_path); return bool(prependStringToList(sys_path, path)); } } // namespace PyKrita // krita: indent-width 4; diff --git a/plugins/extensions/pykrita/sip/CMakeLists.txt b/plugins/extensions/pykrita/sip/CMakeLists.txt index e9b3d3a2b8..1f47f0af1c 100644 --- a/plugins/extensions/pykrita/sip/CMakeLists.txt +++ b/plugins/extensions/pykrita/sip/CMakeLists.txt @@ -1,27 +1,30 @@ include(SIPMacros) message( ${SIP_VERSION} " - The version of SIP found expressed as a 6 digit hex number suitable for comparison as a string.") message( ${SIP_VERSION_STR} " - The version of SIP found as a human readable string.") message( ${SIP_EXECUTABLE} " - Path and filename of the SIP command line executable.") message( ${SIP_INCLUDE_DIR} " - Directory holding the SIP C++ header file.") message( ${SIP_DEFAULT_SIP_DIR} " - default SIP dir" ) -set(SIP_INCLUDES +set(SIP_INCLUDES ${SIP_DEFAULT_SIP_DIR} ${PYQT5_SIP_DIR} ${PYQT_SIP_DIR_OVERRIDE} ./krita) set(SIP_CONCAT_PARTS 1) set(SIP_TAGS ALL WS_X11 ${PYQT5_VERSION_TAG}) set(SIP_EXTRA_OPTIONS -g -x PyKDE_QVector) set(PYTHON_SITE_PACKAGES_INSTALL_DIR ${LIB_INSTALL_DIR}/krita-python-libs) file(GLOB PYKRITA_KRITA_sip_files ./krita/*.sip) set(SIP_EXTRA_FILES_DEPEND ${PYKRITA_KRITA_sip_files}) add_sip_python_module(PyKrita.krita ./krita/kritamod.sip kritalibkis kritaui kritaimage kritalibbrush) -#install(FILES -# ./__init__.py -# DESTINATION ${PYTHON_SITE_PACKAGES_INSTALL_DIR}) - +if (ENABLE_PYTHON_2) + # Add an init file to turn it into a valid py2 module. + # Otherwise PyKrita cannot be loaded. + install(FILES + ./__init__.py + DESTINATION ${PYTHON_SITE_PACKAGES_INSTALL_DIR}/PyKrita) +endif (ENABLE_PYTHON_2) diff --git a/plugins/extensions/pykrita/sip/__init__.py b/plugins/extensions/pykrita/sip/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/extensions/qmic/PluginSettings.cpp b/plugins/extensions/qmic/PluginSettings.cpp index 4b56b24110..4a5a18bdd2 100644 --- a/plugins/extensions/qmic/PluginSettings.cpp +++ b/plugins/extensions/qmic/PluginSettings.cpp @@ -1,119 +1,119 @@ /* * Copyright (c) 2017 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License. * * 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 Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "PluginSettings.h" #include #include #include #include #include #include #include #include "kis_config.h" PluginSettings::PluginSettings(QWidget *parent) : KisPreferenceSet(parent) { setupUi(this); fileRequester->setFileName(gmicQtPath()); - fileRequester->setConfiguratioName("gmic_qt"); + fileRequester->setConfigurationName("gmic_qt"); fileRequester->setStartDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); } PluginSettings::~PluginSettings() { KisConfig().writeEntry("gmic_qt_plugin_path", fileRequester->fileName()); } QString PluginSettings::id() { return QString("qmicsettings"); } QString PluginSettings::name() { return header(); } QString PluginSettings::header() { return QString(i18n("G'Mic-Qt Integration")); } QIcon PluginSettings::icon() { return koIcon("gmic"); } QString PluginSettings::gmicQtPath() { QString gmicqt = "gmic_krita_qt"; #ifdef Q_OS_WIN gmicqt += ".exe"; #endif QString gmic_qt_path = KisConfig().readEntry("gmic_qt_plugin_path", ""); if (!gmic_qt_path.isEmpty() && QFileInfo(gmic_qt_path).exists()) { return gmic_qt_path; } QFileInfo fi(qApp->applicationDirPath() + "/" + gmicqt); // Check for gmic-qt next to krita if (fi.exists() && fi.isFile()) { // qDebug() << 1 << fi.canonicalFilePath(); return fi.canonicalFilePath(); } // Check whether we've got a gmic subfolder QDir d(qApp->applicationDirPath()); QStringList gmicdirs = d.entryList(QStringList() << "gmic*", QDir::Dirs); qDebug() << gmicdirs; if (gmicdirs.isEmpty()) { // qDebug() << 2; return ""; } fi = QFileInfo(qApp->applicationDirPath() + "/" + gmicdirs.first() + "/" + gmicqt); if (fi.exists() && fi.isFile()) { // qDebug() << "3" << fi.canonicalFilePath(); return fi.canonicalFilePath(); } // qDebug() << 4 << gmicqt; return gmicqt; } void PluginSettings::savePreferences() const { KisConfig().writeEntry("gmic_qt_plugin_path", fileRequester->fileName()); Q_EMIT(settingsChanged()); } void PluginSettings::loadPreferences() { fileRequester->setFileName(gmicQtPath()); } void PluginSettings::loadDefaultPreferences() { fileRequester->setFileName(gmicQtPath()); } diff --git a/plugins/filters/asccdl/kis_asccdl_filter.cpp b/plugins/filters/asccdl/kis_asccdl_filter.cpp index ee4fce5c13..2278ca293d 100644 --- a/plugins/filters/asccdl/kis_asccdl_filter.cpp +++ b/plugins/filters/asccdl/kis_asccdl_filter.cpp @@ -1,129 +1,129 @@ /* * Copyright (c) 2017 Wolthera van Hövell tot Westerflier * * 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_asccdl_filter.h" #include "kis_wdg_asccdl.h" #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(KritaASCCDLFactory, "kritaasccdl.json", registerPlugin();) KritaASCCDL::KritaASCCDL(QObject *parent, const QVariantList &) : QObject(parent) { KisFilterRegistry::instance()->add(KisFilterSP(new KisFilterASCCDL())); } KritaASCCDL::~KritaASCCDL() { } -KisFilterASCCDL::KisFilterASCCDL(): KisColorTransformationFilter(id(), categoryAdjust(), i18n("&Slope, Offset, Power..")) +KisFilterASCCDL::KisFilterASCCDL(): KisColorTransformationFilter(id(), categoryAdjust(), i18n("&Slope, Offset, Power...")) { setSupportsPainting(true); setSupportsAdjustmentLayers(true); setSupportsLevelOfDetail(true); setSupportsThreading(true); setColorSpaceIndependence(FULLY_INDEPENDENT); setShowConfigurationWidget(true); } KoColorTransformation *KisFilterASCCDL::createTransformation(const KoColorSpace *cs, const KisFilterConfigurationSP config) const { KoColor black(Qt::black, cs); return new KisASCCDLTransformation(cs, config->getColor("slope", black), config->getColor("offset", black), config->getColor("power", black)); } KisConfigWidget *KisFilterASCCDL::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev) const { return new KisASCCDLConfigWidget(parent, dev->colorSpace()); } bool KisFilterASCCDL::needsTransparentPixels(const KisFilterConfigurationSP config, const KoColorSpace *cs) const { KoColor black(Qt::black, cs); KoColor offset = config->getColor("offset", black); offset.convertTo(cs); if (cs->difference(black.data(), offset.data())>0) { return true; } return false; } KisFilterConfigurationSP KisFilterASCCDL::factoryConfiguration() const { KisColorTransformationConfigurationSP config = new KisColorTransformationConfiguration(id().id(), 0); QVariant colorVariant("KoColor"); KoColor black; black.fromQColor(QColor(Qt::black)); KoColor white; white.fromQColor(QColor(Qt::white)); colorVariant.setValue(white); config->setProperty( "slope", colorVariant); config->setProperty( "power", colorVariant); colorVariant.setValue(black); config->setProperty("offset", colorVariant); return config; } KisASCCDLTransformation::KisASCCDLTransformation(const KoColorSpace *cs, KoColor slope, KoColor offset, KoColor power) { QVector slopeN(cs->channelCount()); slope.convertTo(cs); slope.colorSpace()->normalisedChannelsValue(slope.data(), slopeN); m_slope = slopeN; offset.convertTo(cs); QVector offsetN(cs->channelCount()); offset.colorSpace()->normalisedChannelsValue(offset.data(), offsetN); m_offset = offsetN; power.convertTo(cs); QVector powerN(cs->channelCount()); power.colorSpace()->normalisedChannelsValue(power.data(), powerN); m_power = powerN; m_cs = cs; } void KisASCCDLTransformation::transform(const quint8 *src, quint8 *dst, qint32 nPixels) const { QVector normalised(m_cs->channelCount()); const int pixelSize = m_cs->pixelSize(); while (nPixels--) { m_cs->normalisedChannelsValue(src, normalised); for (uint c=0; cchannelCount(); c++){ if (m_cs->channels().at(c)->channelType()!=KoChannelInfo::ALPHA) { normalised[c] = qPow( (normalised.at(c)*m_slope.at(c))+m_offset.at(c), m_power.at(c)); } } m_cs->fromNormalisedChannelsValue(dst, normalised); src += pixelSize; dst += pixelSize; } } #include "kis_asccdl_filter.moc" diff --git a/plugins/filters/colorsfilters/wdg_desaturate.ui b/plugins/filters/colorsfilters/wdg_desaturate.ui index 3cfc1cc682..f873b60d85 100644 --- a/plugins/filters/colorsfilters/wdg_desaturate.ui +++ b/plugins/filters/colorsfilters/wdg_desaturate.ui @@ -1,109 +1,109 @@ WdgDesaturate 0 0 241 194 0 0 0 6 0 0 Desaturation method: 4 9 &Lightness true Luminosity (ITU-R BT.&709) Luminosity (ITU-R BT.&601) &Average &Min - &Max + M&ax Qt::Vertical 20 40 diff --git a/plugins/filters/convertheightnormalmap/kis_convert_height_to_normal_map_filter.cpp b/plugins/filters/convertheightnormalmap/kis_convert_height_to_normal_map_filter.cpp index adb3157bf7..8ee397a572 100644 --- a/plugins/filters/convertheightnormalmap/kis_convert_height_to_normal_map_filter.cpp +++ b/plugins/filters/convertheightnormalmap/kis_convert_height_to_normal_map_filter.cpp @@ -1,169 +1,169 @@ /* * Copyright (c) 2017 Wolthera van Hövell tot Westerflier * * 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_convert_height_to_normal_map_filter.h" #include "kis_wdg_convert_height_to_normal_map.h" #include #include #include #include #include "kis_lod_transform.h" #include K_PLUGIN_FACTORY_WITH_JSON(KritaConvertHeightToNormalMapFilterFactory, "kritaconvertheighttonormalmap.json", registerPlugin();) KritaConvertHeightToNormalMapFilter::KritaConvertHeightToNormalMapFilter(QObject *parent, const QVariantList &) : QObject(parent) { KisFilterRegistry::instance()->add(KisFilterSP(new KisConvertHeightToNormalMapFilter())); } KritaConvertHeightToNormalMapFilter::~KritaConvertHeightToNormalMapFilter() { } KisConvertHeightToNormalMapFilter::KisConvertHeightToNormalMapFilter(): KisFilter(id(), categoryEdgeDetection(), i18n("&Height to Normal Map...")) { setSupportsPainting(true); setSupportsAdjustmentLayers(true); setSupportsLevelOfDetail(true); setColorSpaceIndependence(FULLY_INDEPENDENT); setShowConfigurationWidget(true); } void KisConvertHeightToNormalMapFilter::processImpl(KisPaintDeviceSP device, const QRect &rect, const KisFilterConfigurationSP config, KoUpdater *progressUpdater) const { Q_ASSERT(device != 0); KisFilterConfigurationSP configuration = config ? config : new KisFilterConfiguration(id().id(), 1); KisLodTransformScalar t(device); QVariant value; configuration->getProperty("horizRadius", value); float horizontalRadius = t.scale(value.toFloat()); configuration->getProperty("vertRadius", value); float verticalRadius = t.scale(value.toFloat()); QBitArray channelFlags; if (configuration) { channelFlags = configuration->channelFlags(); } if (channelFlags.isEmpty() || !configuration) { channelFlags = device->colorSpace()->channelFlags(); } - KisEdgeDetectionKernel::FilterType type = KisEdgeDetectionKernel::SobolVector; + KisEdgeDetectionKernel::FilterType type = KisEdgeDetectionKernel::SobelVector; if (configuration->getString("type") == "prewitt") { type = KisEdgeDetectionKernel::Prewit; } else if (configuration->getString("type") == "simple") { type = KisEdgeDetectionKernel::Simple; } int channelToConvert = configuration->getInt("channelToConvert", 0); QVector channelOrder(3); QVector channelFlip(3); channelFlip.fill(false); int i = config->getInt("redSwizzle", 0); if (i%2==1 || i==2) { channelFlip[0] = true; } if (i==3) { channelFlip[0] = false; } channelOrder[device->colorSpace()->channels().at(0)->displayPosition()] = qMax(i/2,0); i = config->getInt("greenSwizzle", 2); if (i%2==1 || i==2) { channelFlip[1] = true; } if (i==3) { channelFlip[1] = false; } channelOrder[device->colorSpace()->channels().at(1)->displayPosition()] = qMax(i/2,0); i = config->getInt("blueSwizzle", 4); if (i%2==1 || i==2) { channelFlip[2] = true; } if (i==3) { channelFlip[2] = false; } channelOrder[device->colorSpace()->channels().at(2)->displayPosition()] = qMax(i/2,0); KisEdgeDetectionKernel::convertToNormalMap(device, rect, horizontalRadius, verticalRadius, type, channelToConvert, channelOrder, channelFlip, channelFlags, progressUpdater); } KisFilterConfigurationSP KisConvertHeightToNormalMapFilter::factoryConfiguration() const { KisFilterConfigurationSP config = new KisFilterConfiguration(id().id(), 1); config->setProperty("horizRadius", 1); config->setProperty("vertRadius", 1); config->setProperty("type", "sobol"); config->setProperty("channelToConvert", 0); config->setProperty("lockAspect", true); config->setProperty("redSwizzle", KisWdgConvertHeightToNormalMap::xPlus); config->setProperty("greenSwizzle", KisWdgConvertHeightToNormalMap::yPlus); config->setProperty("blueSwizzle", KisWdgConvertHeightToNormalMap::zPlus); return config; } KisConfigWidget *KisConvertHeightToNormalMapFilter::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev) const { return new KisWdgConvertHeightToNormalMap(parent, dev->colorSpace()); } QRect KisConvertHeightToNormalMapFilter::neededRect(const QRect &rect, const KisFilterConfigurationSP _config, int lod) const { KisLodTransformScalar t(lod); QVariant value; /** * NOTE: integer devision by two is done on purpose, * because the kernel size is always odd */ const int halfWidth = _config->getProperty("horizRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5; const int halfHeight = _config->getProperty("vertRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5; return rect.adjusted(-halfWidth * 2, -halfHeight * 2, halfWidth * 2, halfHeight * 2); } QRect KisConvertHeightToNormalMapFilter::changedRect(const QRect &rect, const KisFilterConfigurationSP _config, int lod) const { KisLodTransformScalar t(lod); QVariant value; const int halfWidth = _config->getProperty("horizRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5; const int halfHeight = _config->getProperty("vertRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5; return rect.adjusted( -halfWidth, -halfHeight, halfWidth, halfHeight); } #include "kis_convert_height_to_normal_map_filter.moc" diff --git a/plugins/filters/convertheightnormalmap/kis_wdg_convert_height_to_normal_map.cpp b/plugins/filters/convertheightnormalmap/kis_wdg_convert_height_to_normal_map.cpp index 4724ee5210..b91901cb73 100644 --- a/plugins/filters/convertheightnormalmap/kis_wdg_convert_height_to_normal_map.cpp +++ b/plugins/filters/convertheightnormalmap/kis_wdg_convert_height_to_normal_map.cpp @@ -1,132 +1,132 @@ /* * Copyright (c) 2017 Wolthera van Hövell tot Westerflier * * 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_wdg_convert_height_to_normal_map.h" #include #include #include #include KisWdgConvertHeightToNormalMap::KisWdgConvertHeightToNormalMap(QWidget *parent, const KoColorSpace *cs) : KisConfigWidget(parent), ui(new Ui_WidgetConvertHeightToNormalMap), m_cs(cs) { ui->setupUi(this); m_types << "prewitt"<< "sobol"<< "simple"; - m_types_translatable << i18n("Prewitt") << i18n("Sobol") << i18n("Simple"); + m_types_translatable << i18n("Prewitt") << i18n("Sobel") << i18n("Simple"); QStringList swizzle; swizzle<< "X+" << "X-" << "Y+" << "Y-" << "Z+" << "Z-"; ui->cmbType->addItems(m_types_translatable); ui->cmbRed->addItems(swizzle); ui->cmbGreen->addItems(swizzle); ui->cmbBlue->addItems(swizzle); for (int c = 0; c < (int)m_cs->channelCount(); c++) { ui->cmbChannel->addItem(m_cs->channels().at(c)->name()); } ui->btnAspect->setKeepAspectRatio(false); ui->sldHorizontalRadius->setRange(1.0, 100.0, 2); ui->sldHorizontalRadius->setPrefix(i18n("Horizontal Radius:")); connect(ui->sldHorizontalRadius, SIGNAL(valueChanged(qreal)), this, SLOT(horizontalRadiusChanged(qreal))); ui->sldVerticalRadius->setRange(1.0, 100.0, 2); ui->sldVerticalRadius->setPrefix(i18n("Vertical Radius:")); connect(ui->sldVerticalRadius, SIGNAL(valueChanged(qreal)), this, SLOT(verticalRadiusChanged(qreal))); connect(ui->sldHorizontalRadius, SIGNAL(valueChanged(qreal)), this, SIGNAL(sigConfigurationItemChanged())); connect(ui->sldVerticalRadius, SIGNAL(valueChanged(qreal)), this, SIGNAL(sigConfigurationItemChanged())); connect(ui->btnAspect, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(aspectLockChanged(bool))); connect(ui->cmbType, SIGNAL(currentIndexChanged(int)), this, SIGNAL(sigConfigurationItemChanged())); connect(ui->cmbChannel, SIGNAL(currentIndexChanged(int)), this, SIGNAL(sigConfigurationItemChanged())); connect(ui->cmbRed, SIGNAL(currentIndexChanged(int)), this, SIGNAL(sigConfigurationItemChanged())); connect(ui->cmbGreen, SIGNAL(currentIndexChanged(int)), this, SIGNAL(sigConfigurationItemChanged())); connect(ui->cmbBlue, SIGNAL(currentIndexChanged(int)), this, SIGNAL(sigConfigurationItemChanged())); } KisWdgConvertHeightToNormalMap::~KisWdgConvertHeightToNormalMap() { delete ui; } KisPropertiesConfigurationSP KisWdgConvertHeightToNormalMap::configuration() const { KisFilterConfigurationSP config = new KisFilterConfiguration("height to normal", 1); config->setProperty("horizRadius", ui->sldHorizontalRadius->value()); config->setProperty("vertRadius", ui->sldVerticalRadius->value()); config->setProperty("type", m_types.at(ui->cmbType->currentIndex())); config->setProperty("lockAspect", ui->btnAspect->keepAspectRatio()); config->setProperty("channelToConvert", ui->cmbChannel->currentIndex()); config->setProperty("redSwizzle", ui->cmbRed->currentIndex()); config->setProperty("greenSwizzle", ui->cmbGreen->currentIndex()); config->setProperty("blueSwizzle", ui->cmbBlue->currentIndex()); return config; } void KisWdgConvertHeightToNormalMap::setConfiguration(const KisPropertiesConfigurationSP config) { ui->sldHorizontalRadius->setValue(config->getFloat("horizRadius", 1.0)); ui->sldVerticalRadius->setValue(config->getFloat("vertRadius", 1.0)); int index = 0; if (m_types.contains(config->getString("type", "prewitt"))){ index = m_types.indexOf(config->getString("type", "sobol")); } ui->cmbType->setCurrentIndex(index); ui->cmbChannel->setCurrentIndex(config->getInt("channelToConvert", 0)); ui->btnAspect->setKeepAspectRatio(config->getBool("lockAspect", false)); ui->cmbRed->setCurrentIndex(config->getInt("redSwizzle", xPlus)); ui->cmbGreen->setCurrentIndex(config->getInt("greenSwizzle", yPlus)); ui->cmbBlue->setCurrentIndex(config->getInt("blueSwizzle", zPlus)); } void KisWdgConvertHeightToNormalMap::horizontalRadiusChanged(qreal r) { ui->sldHorizontalRadius->blockSignals(true); ui->sldHorizontalRadius->setValue(r); ui->sldHorizontalRadius->blockSignals(false); if (ui->btnAspect->keepAspectRatio()) { ui->sldVerticalRadius->blockSignals(true); ui->sldVerticalRadius->setValue(r); ui->sldVerticalRadius->blockSignals(false); } } void KisWdgConvertHeightToNormalMap::verticalRadiusChanged(qreal r) { ui->sldVerticalRadius->blockSignals(true); ui->sldVerticalRadius->setValue(r); ui->sldVerticalRadius->blockSignals(false); if (ui->btnAspect->keepAspectRatio()) { ui->sldHorizontalRadius->blockSignals(true); ui->sldHorizontalRadius->setValue(r); ui->sldHorizontalRadius->blockSignals(false); } } void KisWdgConvertHeightToNormalMap::aspectLockChanged(bool v) { if (v) { ui->sldVerticalRadius->setValue( ui->sldHorizontalRadius->value() ); } } diff --git a/plugins/filters/edgedetection/kis_edge_detection_filter.cpp b/plugins/filters/edgedetection/kis_edge_detection_filter.cpp index 5775d1d166..3bd48dbb19 100644 --- a/plugins/filters/edgedetection/kis_edge_detection_filter.cpp +++ b/plugins/filters/edgedetection/kis_edge_detection_filter.cpp @@ -1,158 +1,158 @@ /* * Copyright (c) 2017 Wolthera van Hövell tot Westerflier * * 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_edge_detection_filter.h" #include "kis_wdg_edge_detection.h" #include #include #include #include #include #include #include #include #include #include "kis_lod_transform.h" #include #include #include K_PLUGIN_FACTORY_WITH_JSON(KritaEdgeDetectionFilterFactory, "kritaedgedetection.json", registerPlugin();) KritaEdgeDetectionFilter::KritaEdgeDetectionFilter(QObject *parent, const QVariantList &) : QObject(parent) { KisFilterRegistry::instance()->add(KisFilterSP(new KisEdgeDetectionFilter())); } KritaEdgeDetectionFilter::~KritaEdgeDetectionFilter() { } KisEdgeDetectionFilter::KisEdgeDetectionFilter(): KisFilter(id(), categoryEdgeDetection(), i18n("&Edge Detection...")) { setSupportsPainting(true); setSupportsAdjustmentLayers(true); setSupportsLevelOfDetail(true); setColorSpaceIndependence(FULLY_INDEPENDENT); setShowConfigurationWidget(true); } void KisEdgeDetectionFilter::processImpl(KisPaintDeviceSP device, const QRect &rect, const KisFilterConfigurationSP config, KoUpdater *progressUpdater) const { Q_ASSERT(device != 0); KisFilterConfigurationSP configuration = config ? config : new KisFilterConfiguration(id().id(), 1); KisLodTransformScalar t(device); QVariant value; configuration->getProperty("horizRadius", value); float horizontalRadius = t.scale(value.toFloat()); configuration->getProperty("vertRadius", value); float verticalRadius = t.scale(value.toFloat()); QBitArray channelFlags; if (configuration) { channelFlags = configuration->channelFlags(); } if (channelFlags.isEmpty() || !configuration) { channelFlags = device->colorSpace()->channelFlags(); } - KisEdgeDetectionKernel::FilterType type = KisEdgeDetectionKernel::SobolVector; + KisEdgeDetectionKernel::FilterType type = KisEdgeDetectionKernel::SobelVector; if (config->getString("type") == "prewitt") { type = KisEdgeDetectionKernel::Prewit; } else if (config->getString("type") == "simple") { type = KisEdgeDetectionKernel::Simple; } KisEdgeDetectionKernel::FilterOutput output = KisEdgeDetectionKernel::pythagorean; if (config->getString("output") == "xGrowth") { output = KisEdgeDetectionKernel::xGrowth; } else if (config->getString("output") == "xFall") { output = KisEdgeDetectionKernel::xFall; } else if (config->getString("output") == "yGrowth") { output = KisEdgeDetectionKernel::yGrowth; } else if (config->getString("output") == "yFall") { output = KisEdgeDetectionKernel::yFall; } else if (config->getString("output") == "radian") { output = KisEdgeDetectionKernel::radian; } KisEdgeDetectionKernel::applyEdgeDetection(device, rect, horizontalRadius, verticalRadius, type, channelFlags, progressUpdater, output, config->getBool("transparency", false)); } KisFilterConfigurationSP KisEdgeDetectionFilter::factoryConfiguration() const { KisFilterConfigurationSP config = new KisFilterConfiguration(id().id(), 1); config->setProperty("horizRadius", 1); config->setProperty("vertRadius", 1); config->setProperty("type", "prewitt"); config->setProperty("output", "pythagorean"); config->setProperty("lockAspect", true); config->setProperty("transparency", false); return config; } KisConfigWidget *KisEdgeDetectionFilter::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev) const { Q_UNUSED(dev); return new KisWdgEdgeDetection(parent); } QRect KisEdgeDetectionFilter::neededRect(const QRect &rect, const KisFilterConfigurationSP _config, int lod) const { KisLodTransformScalar t(lod); QVariant value; /** * NOTE: integer devision by two is done on purpose, * because the kernel size is always odd */ const int halfWidth = _config->getProperty("horizRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5; const int halfHeight = _config->getProperty("vertRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5; return rect.adjusted(-halfWidth * 2, -halfHeight * 2, halfWidth * 2, halfHeight * 2); } QRect KisEdgeDetectionFilter::changedRect(const QRect &rect, const KisFilterConfigurationSP _config, int lod) const { KisLodTransformScalar t(lod); QVariant value; const int halfWidth = _config->getProperty("horizRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5; const int halfHeight = _config->getProperty("vertRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5; return rect.adjusted( -halfWidth, -halfHeight, halfWidth, halfHeight); } #include "kis_edge_detection_filter.moc" diff --git a/plugins/filters/edgedetection/kis_wdg_edge_detection.cpp b/plugins/filters/edgedetection/kis_wdg_edge_detection.cpp index 43d2cfde5a..2828216ebe 100644 --- a/plugins/filters/edgedetection/kis_wdg_edge_detection.cpp +++ b/plugins/filters/edgedetection/kis_wdg_edge_detection.cpp @@ -1,129 +1,129 @@ /* * Copyright (c) 2017 Wolthera van Hövell tot Westerflier * * 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_wdg_edge_detection.h" #include #include #include KisWdgEdgeDetection::KisWdgEdgeDetection(QWidget *parent) : KisConfigWidget(parent), ui(new Ui_WidgetEdgeDetection) { ui->setupUi(this); m_types << "prewitt"<< "sobol"<< "simple"; - m_types_translatable << i18n("Prewitt") << i18n("Sobol") << i18n("Simple"); + m_types_translatable << i18n("Prewitt") << i18n("Sobel") << i18n("Simple"); m_output << "pythagorean" << "xGrowth" << "xFall" << "yGrowth" << "yFall" << "radian"; m_output_translatable << i18n("All sides") << i18n("Top Edge") << i18n("Bottom Edge") << i18n("Right Edge") << i18n("Left Edge") << i18n("Direction in Radians"); ui->cmbType->addItems(m_types_translatable); ui->cmbOutput->addItems(m_output_translatable); ui->btnAspect->setKeepAspectRatio(false); ui->sldHorizontalRadius->setRange(1.0, 100.0, 2); ui->sldHorizontalRadius->setPrefix(i18n("Horizontal Radius:")); connect(ui->sldHorizontalRadius, SIGNAL(valueChanged(qreal)), this, SLOT(horizontalRadiusChanged(qreal))); ui->sldVerticalRadius->setRange(1.0, 100.0, 2); ui->sldVerticalRadius->setPrefix(i18n("Vertical Radius:")); connect(ui->sldVerticalRadius, SIGNAL(valueChanged(qreal)), this, SLOT(verticalRadiusChanged(qreal))); connect(ui->btnAspect, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(aspectLockChanged(bool))); connect(ui->cmbType, SIGNAL(currentIndexChanged(int)), this, SIGNAL(sigConfigurationItemChanged())); connect(ui->cmbOutput, SIGNAL(currentIndexChanged(int)), this, SIGNAL(sigConfigurationItemChanged())); connect(ui->sldHorizontalRadius, SIGNAL(valueChanged(qreal)), this, SIGNAL(sigConfigurationItemChanged())); connect(ui->sldVerticalRadius, SIGNAL(valueChanged(qreal)), this, SIGNAL(sigConfigurationItemChanged())); connect(ui->chkTransparent, SIGNAL(clicked()), this, SIGNAL(sigConfigurationItemChanged())); } KisWdgEdgeDetection::~KisWdgEdgeDetection() { delete ui; } KisPropertiesConfigurationSP KisWdgEdgeDetection::configuration() const { KisFilterConfigurationSP config = new KisFilterConfiguration("edge detection", 1); config->setProperty("horizRadius", ui->sldHorizontalRadius->value()); config->setProperty("vertRadius", ui->sldVerticalRadius->value()); config->setProperty("type", m_types.at(ui->cmbType->currentIndex())); config->setProperty("output", m_output.at(ui->cmbOutput->currentIndex())); config->setProperty("lockAspect", ui->btnAspect->keepAspectRatio()); config->setProperty("transparency", ui->chkTransparent->isChecked()); return config; } void KisWdgEdgeDetection::setConfiguration(const KisPropertiesConfigurationSP config) { ui->sldHorizontalRadius->setValue(config->getFloat("horizRadius", 1.0)); ui->sldVerticalRadius->setValue(config->getFloat("vertRadius", 1.0)); int index = 0; if (m_types.contains(config->getString("type", "prewitt"))){ index = m_types.indexOf(config->getString("type", "prewitt")); } ui->cmbType->setCurrentIndex(index); index = 0; if (m_output.contains(config->getString("output", "pythagorean"))){ index = m_output.indexOf(config->getString("output", "pythagorean")); } ui->cmbOutput->setCurrentIndex(index); ui->chkTransparent->setChecked(config->getBool("transparency", false)); ui->btnAspect->setKeepAspectRatio(config->getBool("lockAspect", false)); } void KisWdgEdgeDetection::horizontalRadiusChanged(qreal r) { ui->sldHorizontalRadius->blockSignals(true); ui->sldHorizontalRadius->setValue(r); ui->sldHorizontalRadius->blockSignals(false); if (ui->btnAspect->keepAspectRatio()) { ui->sldVerticalRadius->blockSignals(true); ui->sldVerticalRadius->setValue(r); ui->sldVerticalRadius->blockSignals(false); } } void KisWdgEdgeDetection::verticalRadiusChanged(qreal r) { ui->sldVerticalRadius->blockSignals(true); ui->sldVerticalRadius->setValue(r); ui->sldVerticalRadius->blockSignals(false); if (ui->btnAspect->keepAspectRatio()) { ui->sldHorizontalRadius->blockSignals(true); ui->sldHorizontalRadius->setValue(r); ui->sldHorizontalRadius->blockSignals(false); } } void KisWdgEdgeDetection::aspectLockChanged(bool v) { if (v) { ui->sldVerticalRadius->setValue( ui->sldHorizontalRadius->value() ); } } diff --git a/plugins/filters/embossfilter/kis_emboss_filter.cpp b/plugins/filters/embossfilter/kis_emboss_filter.cpp index 3d855019f9..d4997110e3 100644 --- a/plugins/filters/embossfilter/kis_emboss_filter.cpp +++ b/plugins/filters/embossfilter/kis_emboss_filter.cpp @@ -1,159 +1,159 @@ /* * This file is part of Krita * * Copyright (c) 2004 Michael Thaler * * ported from digikam, Copyrighted 2004 Gilles Caulier, * Original Emboss algorithm copyrighted 2004 by * Pieter Z. Voloshyn . * * 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_emboss_filter.h" #include #include #include #include #include #include #include #include "KoIntegerMaths.h" #include #include #include #include #include #include #include #include #include #include "widgets/kis_multi_integer_filter_widget.h" #include KisEmbossFilter::KisEmbossFilter() : KisFilter(id(), categoryEmboss(), i18n("&Emboss with Variable Depth...")) { setSupportsPainting(false); setColorSpaceIndependence(TO_RGBA8); setSupportsThreading(false); setSupportsAdjustmentLayers(false); } KisFilterConfigurationSP KisEmbossFilter::factoryConfiguration() const { KisFilterConfigurationSP config = new KisFilterConfiguration(id().id(), 0); config->setProperty("depth", 30); return config; } // This method have been ported from Pieter Z. Voloshyn algorithm code. /* Function to apply the Emboss effect * * data => The image data in RGBA mode. * Width => Width of image. * Height => Height of image. * d => Emboss value * * Theory => This is an amazing effect. And the theory is very simple to * understand. You get the diference between the colors and * increase it. After this, get the gray tone */ void KisEmbossFilter::processImpl(KisPaintDeviceSP device, const QRect& applyRect, const KisFilterConfigurationSP config, KoUpdater* progressUpdater ) const { QPoint srcTopLeft = applyRect.topLeft(); Q_ASSERT(device); //read the filter configuration values from the KisFilterConfiguration object quint32 embossdepth = config ? config->getInt("depth", 30) : 30; //the actual filter function from digikam. It needs a pointer to a quint8 array //with the actual pixel data. float Depth = embossdepth / 10.0; int R = 0, G = 0, B = 0; uchar Gray = 0; int Width = applyRect.width(); int Height = applyRect.height(); KisSequentialIteratorProgress it(device, applyRect, progressUpdater); QColor color1; QColor color2; KisRandomConstAccessorSP acc = device->createRandomAccessorNG(srcTopLeft.x(), srcTopLeft.y()); while (it.nextPixel()) { - + // XXX: COLORSPACE_INDEPENDENCE or at least work IN RGB16A device->colorSpace()->toQColor(it.oldRawData(), &color1); acc->moveTo(srcTopLeft.x() + it.x() + Lim_Max(it.x(), 1, Width), srcTopLeft.y() + it.y() + Lim_Max(it.y(), 1, Height)); device->colorSpace()->toQColor(acc->oldRawData(), &color2); R = abs((int)((color1.red() - color2.red()) * Depth + (quint8_MAX / 2))); G = abs((int)((color1.green() - color2.green()) * Depth + (quint8_MAX / 2))); B = abs((int)((color1.blue() - color2.blue()) * Depth + (quint8_MAX / 2))); Gray = CLAMP((R + G + B) / 3, 0, quint8_MAX); device->colorSpace()->fromQColor(QColor(Gray, Gray, Gray, color1.alpha()), it.rawData()); } } // This method have been ported from Pieter Z. Voloshyn algorithm code. /* This function limits the max and min values * defined by the developer * * Now => Original value * Up => Increments * Max => Maximum value * * Theory => This function is used in some functions to limit the * "for step". E.g. I have a picture with 309 pixels (width), and * my "for step" is 5. All the code goes alright until it reaches the * w = 305, because in the next step we will go to 310, but we want * to analyze all the pixels. So, this function will reduce the * "for step", when necessary, until we reach the last possible value */ int KisEmbossFilter::Lim_Max(int Now, int Up, int Max) const { --Max; while (Now > Max - Up) --Up; return (Up); } KisConfigWidget * KisEmbossFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev) const { Q_UNUSED(dev); vKisIntegerWidgetParam param; - param.push_back(KisIntegerWidgetParam(10, 300, 30, i18n("Depth"), "depth")); + param.push_back(KisIntegerWidgetParam(10, 300, 30, i18nc("Emboss depth", "Depth"), "depth")); KisConfigWidget * w = new KisMultiIntegerFilterWidget(id().id(), parent, id().id(), param); Q_CHECK_PTR(w); return w; } diff --git a/plugins/impex/CMakeLists.txt b/plugins/impex/CMakeLists.txt index 6468ef842d..e7edc2ecfe 100644 --- a/plugins/impex/CMakeLists.txt +++ b/plugins/impex/CMakeLists.txt @@ -1,50 +1,54 @@ project(kritafilters) add_subdirectory(libkra) if(CMAKE_SIZEOF_VOID_P EQUAL 4) add_definitions( -DCPU_32_BITS ) endif() if(JPEG_FOUND AND HAVE_LCMS2) add_subdirectory(jpeg) endif() if(TIFF_FOUND) add_subdirectory(tiff) endif() if(PNG_FOUND) add_subdirectory(png) add_subdirectory(csv) endif() if(OPENEXR_FOUND) add_subdirectory(exr) endif() if(Poppler_Qt5_FOUND) add_subdirectory(pdf) endif() if(LIBRAW_FOUND) add_subdirectory(raw) endif() add_subdirectory(svg) add_subdirectory(qimageio) add_subdirectory(ora) add_subdirectory(ppm) add_subdirectory(xcf) add_subdirectory(psd) add_subdirectory(qml) add_subdirectory(tga) add_subdirectory(heightmap) add_subdirectory(brush) add_subdirectory(spriter) add_subdirectory(video) add_subdirectory(kra) if (GIF_FOUND) add_subdirectory(gif) endif() + +if (HEIF_FOUND) + add_subdirectory(heif) +endif() diff --git a/plugins/impex/csv/csv_loader.cpp b/plugins/impex/csv/csv_loader.cpp index bbf387b6de..c47e7026e3 100644 --- a/plugins/impex/csv/csv_loader.cpp +++ b/plugins/impex/csv/csv_loader.cpp @@ -1,489 +1,489 @@ /* * Copyright (c) 2016 Laszlo Fazekas * * 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 "csv_loader.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "csv_read_line.h" #include "csv_layer_record.h" CSVLoader::CSVLoader(KisDocument *doc, bool batchMode) : m_image(0) , m_doc(doc) , m_batchMode(batchMode) , m_stop(false) { } CSVLoader::~CSVLoader() { } KisImageBuilder_Result CSVLoader::decode(QIODevice *io, const QString &filename) { QString field; int idx; int frame = 0; QString projName; int width = 0; int height = 0; int frameCount = 1; float framerate = 24.0; float pixelRatio = 1.0; int projNameIdx = -1; int widthIdx = -1; int heightIdx = -1; int frameCountIdx = -1; int framerateIdx = -1; int pixelRatioIdx = -1; QVector layers; QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); idx = filename.lastIndexOf(QRegExp("[\\/]")); QString base = (idx == -1) ? QString() : filename.left(idx + 1); //include separator QString path = filename; if (path.right(4).toUpper() == ".CSV") path = path.left(path.size() - 4); //according to the QT docs, the slash is a universal directory separator path.append(".frames/"); KisImageBuilder_Result retval = KisImageBuilder_RESULT_OK; dbgFile << "pos:" << io->pos(); CSVReadLine readLine; QScopedPointer importDoc(KisPart::instance()->createDocument()); - importDoc->setAutoSaveDelay(0); + importDoc->setInfiniteAutoSaveInterval(); importDoc->setFileBatchMode(true); KisView *setView(0); if (!m_batchMode) { // TODO: use other systems of progress reporting (KisViewManager::createUnthreadedUpdater() // //show the statusbar message even if no view // Q_FOREACH (KisView* view, KisPart::instance()->views()) { // if (view && view->document() == m_doc) { // setView = view; // break; // } // } // if (!setView) { // QStatusBar *sb = KisPart::instance()->currentMainwindow()->statusBar(); // if (sb) { // sb->showMessage(i18n("Loading CSV file...")); // } // } else { // emit m_doc->statusBarMessage(i18n("Loading CSV file...")); // } // emit m_doc->sigProgress(0); // connect(m_doc, SIGNAL(sigProgressCanceled()), this, SLOT(cancel())); } int step = 0; do { qApp->processEvents(); if (m_stop) { retval = KisImageBuilder_RESULT_CANCEL; break; } if ((idx = readLine.nextLine(io)) <= 0) { if ((idx < 0) ||(step < 5)) retval = KisImageBuilder_RESULT_FAILURE; break; } field = readLine.nextField(); //first field of the line if (field.isNull()) continue; //empty row switch (step) { case 0 : //skip first row step = 1; break; case 1 : //scene header names step = 2; for (idx = 0; !field.isNull(); idx++) { if (field == "Project Name") { projNameIdx = idx; } else if (field == "Width") { widthIdx = idx; } else if (field == "Height") { heightIdx = idx; } else if (field == "Frame Count") { frameCountIdx = idx; } else if (field == "Frame Rate") { framerateIdx = idx; } else if (field == "Pixel Aspect Ratio") { pixelRatioIdx = idx; } field= readLine.nextField(); } break; case 2 : //scene header values step= 3; for (idx= 0; !field.isNull(); idx++) { if (idx == projNameIdx) { projName = field; } else if (idx == widthIdx) { width = field.toInt(); } else if (idx == heightIdx) { height = field.toInt(); } else if (idx == frameCountIdx) { frameCount = field.toInt(); if (frameCount < 1) frameCount= 1; } else if (idx == framerateIdx) { framerate = field.toFloat(); } else if (idx == pixelRatioIdx) { pixelRatio = field.toFloat(); } field= readLine.nextField(); } if ((width < 1) || (height < 1)) { retval = KisImageBuilder_RESULT_FAILURE; break; } retval = createNewImage(width, height, pixelRatio, projName.isNull() ? filename : projName); break; case 3 : //create level headers if (field[0] != '#') break; for (; !(field = readLine.nextField()).isNull(); ) { CSVLayerRecord* layerRecord = new CSVLayerRecord(); layers.append(layerRecord); } readLine.rewind(); field = readLine.nextField(); step = 4; /* Falls through */ case 4 : //level header if (field == "#Layers") { //layer name for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++) layers.at(idx)->name = field; break; } if (field == "#Density") { //layer opacity for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++) layers.at(idx)->density = field.toFloat(); break; } if (field == "#Blending") { //layer blending mode for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++) layers.at(idx)->blending = field; break; } if (field == "#Visible") { //layer visibility for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++) layers.at(idx)->visible = field.toInt(); break; } if (field == "#Folder") { //CSV 1.1 folder location for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++) layers.at(idx)->path = validPath(field, base); break; } if ((field.size() < 2) || (field[0] != '#') || !field[1].isDigit()) break; step = 5; /* Falls through */ case 5 : //frames if ((field.size() < 2) || (field[0] != '#') || !field[1].isDigit()) break; for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++) { CSVLayerRecord* layer = layers.at(idx); if (layer->last != field) { if (!m_batchMode) { //emit m_doc->sigProgress((frame * layers.size() + idx) * 100 / // (frameCount * layers.size())); } retval = setLayer(layer, importDoc.data(), path); layer->last = field; layer->frame = frame; } } frame++; break; } } while (retval == KisImageBuilder_RESULT_OK); //finish the layers if (retval == KisImageBuilder_RESULT_OK) { if (m_image) { KisImageAnimationInterface *animation = m_image->animationInterface(); if (frame > frameCount) frameCount = frame; animation->setFullClipRange(KisTimeRange::fromTime(0,frameCount - 1)); animation->setFramerate((int)framerate); } for (idx = 0; idx < layers.size(); idx++) { CSVLayerRecord* layer = layers.at(idx); //empty layers without any pictures are dropped if ((layer->frame > 0) || !layer->last.isEmpty()) { retval = setLayer(layer, importDoc.data(), path); if (retval != KisImageBuilder_RESULT_OK) break; } } } if (m_image) { //insert the existing layers by the right order for (idx = layers.size() - 1; idx >= 0; idx--) { CSVLayerRecord* layer = layers.at(idx); if (layer->layer) { m_image->addNode(layer->layer, m_image->root()); } } m_image->unlock(); } qDeleteAll(layers); io->close(); if (!m_batchMode) { // disconnect(m_doc, SIGNAL(sigProgressCanceled()), this, SLOT(cancel())); // emit m_doc->sigProgress(100); if (!setView) { QStatusBar *sb = KisPart::instance()->currentMainwindow()->statusBar(); if (sb) { sb->clearMessage(); } } else { emit m_doc->clearStatusBarMessage(); } } QApplication::restoreOverrideCursor(); return retval; } QString CSVLoader::convertBlending(const QString &blending) { if (blending == "Color") return COMPOSITE_OVER; if (blending == "Behind") return COMPOSITE_BEHIND; if (blending == "Erase") return COMPOSITE_ERASE; // "Shade" if (blending == "Light") return COMPOSITE_LINEAR_LIGHT; if (blending == "Colorize") return COMPOSITE_COLORIZE; if (blending == "Hue") return COMPOSITE_HUE; if (blending == "Add") return COMPOSITE_ADD; if (blending == "Sub") return COMPOSITE_INVERSE_SUBTRACT; if (blending == "Multiply") return COMPOSITE_MULT; if (blending == "Screen") return COMPOSITE_SCREEN; // "Replace" // "Substitute" if (blending == "Difference") return COMPOSITE_DIFF; if (blending == "Divide") return COMPOSITE_DIVIDE; if (blending == "Overlay") return COMPOSITE_OVERLAY; if (blending == "Light2") return COMPOSITE_DODGE; if (blending == "Shade2") return COMPOSITE_BURN; if (blending == "HardLight") return COMPOSITE_HARD_LIGHT; if (blending == "SoftLight") return COMPOSITE_SOFT_LIGHT_PHOTOSHOP; if (blending == "GrainExtract") return COMPOSITE_GRAIN_EXTRACT; if (blending == "GrainMerge") return COMPOSITE_GRAIN_MERGE; if (blending == "Sub2") return COMPOSITE_SUBTRACT; if (blending == "Darken") return COMPOSITE_DARKEN; if (blending == "Lighten") return COMPOSITE_LIGHTEN; if (blending == "Saturation") return COMPOSITE_SATURATION; return COMPOSITE_OVER; } QString CSVLoader::validPath(const QString &path,const QString &base) { //replace Windows directory separators with the universal / QString tryPath= QString(path).replace(QString("\\"), QString("/")); int i = tryPath.lastIndexOf("/"); if (i == (tryPath.size() - 1)) tryPath= tryPath.left(i); //remove the ending separator if exists if (QFileInfo(tryPath).isDir()) return tryPath.append("/"); QString scan(tryPath); i = -1; while ((i= (scan.lastIndexOf("/",i) - 1)) > 0) { //avoid testing if the next level will be the default xxxx.layers folder if ((i >= 6) && (scan.mid(i - 6, 7) == ".layers")) continue; tryPath= QString(base).append(scan.mid(i + 2)); //base already ending with a / if (QFileInfo(tryPath).isDir()) return tryPath.append("/"); } return QString(); //NULL string } KisImageBuilder_Result CSVLoader::setLayer(CSVLayerRecord* layer, KisDocument *importDoc, const QString &path) { bool result = true; if (layer->channel == 0) { //create a new document layer float opacity = layer->density; if (opacity > 1.0) opacity = 1.0; else if (opacity < 0.0) opacity = 0.0; const KoColorSpace* cs = m_image->colorSpace(); const QString layerName = (layer->name).isEmpty() ? m_image->nextLayerName() : layer->name; KisPaintLayer* paintLayer = new KisPaintLayer(m_image, layerName, (quint8)(opacity * OPACITY_OPAQUE_U8), cs); paintLayer->setCompositeOpId(convertBlending(layer->blending)); paintLayer->setVisible(layer->visible); paintLayer->enableAnimation(); layer->layer = paintLayer; layer->channel = qobject_cast (paintLayer->getKeyframeChannel(KisKeyframeChannel::Content.id(), true)); } if (!layer->last.isEmpty()) { //png image QString filename = layer->path.isNull() ? path : layer->path; filename.append(layer->last); result = importDoc->openUrl(QUrl::fromLocalFile(filename), KisDocument::DontAddToRecent); if (result) layer->channel->importFrame(layer->frame, importDoc->image()->projection(), 0); } else { //blank layer->channel->addKeyframe(layer->frame); } return (result) ? KisImageBuilder_RESULT_OK : KisImageBuilder_RESULT_FAILURE; } KisImageBuilder_Result CSVLoader::createNewImage(int width, int height, float ratio, const QString &name) { //the CSV is RGBA 8bits, sRGB if (!m_image) { const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace( RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), 0); if (cs) m_image = new KisImage(m_doc->createUndoStore(), width, height, cs, name); if (!m_image) return KisImageBuilder_RESULT_FAILURE; m_image->setResolution(ratio, 1.0); m_image->lock(); } return KisImageBuilder_RESULT_OK; } KisImageBuilder_Result CSVLoader::buildAnimation(QIODevice *io, const QString &filename) { return decode(io, filename); } KisImageSP CSVLoader::image() { return m_image; } void CSVLoader::cancel() { m_stop = true; } diff --git a/plugins/impex/csv/csv_saver.cpp b/plugins/impex/csv/csv_saver.cpp index 073c5ef121..40925974ab 100644 --- a/plugins/impex/csv/csv_saver.cpp +++ b/plugins/impex/csv/csv_saver.cpp @@ -1,479 +1,479 @@ /* * Copyright (c) 2016 Laszlo Fazekas * * 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 "csv_saver.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 "csv_layer_record.h" CSVSaver::CSVSaver(KisDocument *doc, bool batchMode) : m_image(doc->savingImage()) , m_doc(doc) , m_batchMode(batchMode) , m_stop(false) { } CSVSaver::~CSVSaver() { } KisImageSP CSVSaver::image() { return m_image; } KisImageBuilder_Result CSVSaver::encode(QIODevice *io) { int idx; int start, end; KisNodeSP node; QByteArray ba; KisKeyframeSP keyframe; QVector layers; KisImageAnimationInterface *animation = m_image->animationInterface(); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // XXX: Stream was unused? // //DataStream instead of TextStream for correct line endings // QDataStream stream(&f); //Using the original local path QString path = m_doc->localFilePath(); if (path.right(4).toUpper() == ".CSV") path = path.left(path.size() - 4); else { // something is wrong: the local file name is not .csv! // trying the given (probably temporary) filename as well KIS_SAFE_ASSERT_RECOVER(0 && "Wrong extension of the saved file!") { path = path.left(path.size() - 4); } } path.append(".frames"); //create directory QDir dir(path); if (!dir.exists()) { dir.mkpath("."); } //according to the QT docs, the slash is a universal directory separator path.append("/"); node = m_image->rootLayer()->firstChild(); //TODO: correct handling of the layer tree. //for now, only top level paint layers are saved idx = 0; while (node) { if (node->inherits("KisLayer")) { KisLayer* paintLayer = qobject_cast(node.data()); CSVLayerRecord* layerRecord = new CSVLayerRecord(); layers.prepend(layerRecord); //reverse order! layerRecord->name = paintLayer->name(); layerRecord->name.replace(QRegExp("[\"\\r\\n]"), "_"); if (layerRecord->name.isEmpty()) layerRecord->name= QString("Unnamed-%1").arg(idx); layerRecord->visible = (paintLayer->visible()) ? 1 : 0; layerRecord->density = (float)(paintLayer->opacity()) / OPACITY_OPAQUE_U8; layerRecord->blending = convertToBlending(paintLayer->compositeOpId()); layerRecord->layer = paintLayer; layerRecord->channel = paintLayer->original()->keyframeChannel(); layerRecord->last = ""; layerRecord->frame = 0; idx++; } node = node->nextSibling(); } KisTimeRange range = animation->fullClipRange(); start = (range.isValid()) ? range.start() : 0; if (!range.isInfinite()) { end = range.end(); if (end < start) end = start; } else { //undefined length, searching for the last keyframe end = start; for (idx = 0; idx < layers.size(); idx++) { KisRasterKeyframeChannel *channel = layers.at(idx)->channel; if (channel) { keyframe = channel->lastKeyframe(); if ( (!keyframe.isNull()) && (keyframe->time() > end) ) end = keyframe->time(); } } } //create temporary doc for exporting QScopedPointer exportDoc(KisPart::instance()->createDocument()); createTempImage(exportDoc.data()); KisImageBuilder_Result retval= KisImageBuilder_RESULT_OK; if (!m_batchMode) { // TODO: use other systems of progress reporting (KisViewManager::createUnthreadedUpdater() //emit m_doc->statusBarMessage(i18n("Saving CSV file...")); //emit m_doc->sigProgress(0); //connect(m_doc, SIGNAL(sigProgressCanceled()), this, SLOT(cancel())); } int frame = start; int step = 0; do { qApp->processEvents(); if (m_stop) { retval = KisImageBuilder_RESULT_CANCEL; break; } switch(step) { case 0 : //first row if (io->write("UTF-8, TVPaint, \"CSV 1.0\"\r\n") < 0) { retval = KisImageBuilder_RESULT_FAILURE; } break; case 1 : //scene header names if (io->write("Project Name, Width, Height, Frame Count, Layer Count, Frame Rate, Pixel Aspect Ratio, Field Mode\r\n") < 0) { retval = KisImageBuilder_RESULT_FAILURE; } break; case 2 : //scene header values ba = QString("\"%1\", ").arg(m_image->objectName()).toUtf8(); if (io->write(ba.data()) < 0) { retval = KisImageBuilder_RESULT_FAILURE; break; } ba = QString("%1, %2, ").arg(m_image->width()).arg(m_image->height()).toUtf8(); if (io->write(ba.data()) < 0) { retval = KisImageBuilder_RESULT_FAILURE; break; } ba = QString("%1, %2, ").arg(end - start + 1).arg(layers.size()).toUtf8(); if (io->write(ba.data()) < 0) { retval = KisImageBuilder_RESULT_FAILURE; break; } //the framerate is an integer here ba = QString("%1, ").arg((double)(animation->framerate()),0,'f',6).toUtf8(); if (io->write(ba.data()) < 0) { retval = KisImageBuilder_RESULT_FAILURE; break; } ba = QString("%1, Progressive\r\n").arg((double)(m_image->xRes() / m_image->yRes()),0,'f',6).toUtf8(); if (io->write(ba.data()) < 0) { retval = KisImageBuilder_RESULT_FAILURE; break; } break; case 3 : //layer header values if (io->write("#Layers") < 0) { //Layers retval = KisImageBuilder_RESULT_FAILURE; break; } for (idx = 0; idx < layers.size(); idx++) { ba = QString(", \"%1\"").arg(layers.at(idx)->name).toUtf8(); if (io->write(ba.data()) < 0) break; } break; case 4 : if (io->write("\r\n#Density") < 0) { //Density retval = KisImageBuilder_RESULT_FAILURE; break; } for (idx = 0; idx < layers.size(); idx++) { ba = QString(", %1").arg((double)(layers.at(idx)->density), 0, 'f', 6).toUtf8(); if (io->write(ba.data()) < 0) break; } break; case 5 : if (io->write("\r\n#Blending") < 0) { //Blending retval = KisImageBuilder_RESULT_FAILURE; break; } for (idx = 0; idx < layers.size(); idx++) { ba = QString(", \"%1\"").arg(layers.at(idx)->blending).toUtf8(); if (io->write(ba.data()) < 0) break; } break; case 6 : if (io->write("\r\n#Visible") < 0) { //Visible retval = KisImageBuilder_RESULT_FAILURE; break; } for (idx = 0; idx < layers.size(); idx++) { ba = QString(", %1").arg(layers.at(idx)->visible).toUtf8(); if (io->write(ba.data()) < 0) break; } if (idx < layers.size()) { retval = KisImageBuilder_RESULT_FAILURE; } break; default : //frames if (frame > end) { if (io->write("\r\n") < 0) retval = KisImageBuilder_RESULT_FAILURE; step = 8; break; } ba = QString("\r\n#%1").arg(frame, 5, 10, QChar('0')).toUtf8(); if (io->write(ba.data()) < 0) { retval = KisImageBuilder_RESULT_FAILURE; break; } for (idx = 0; idx < layers.size(); idx++) { CSVLayerRecord *layer = layers.at(idx); KisRasterKeyframeChannel *channel = layer->channel; if (channel) { if (frame == start) { keyframe = channel->activeKeyframeAt(frame); } else { keyframe = channel->keyframeAt(frame); } } else { keyframe.clear(); // without animation } if ( !keyframe.isNull() || (frame == start) ) { if (!m_batchMode) { //emit m_doc->sigProgress(((frame - start) * layers.size() + idx) * 100 / // ((end - start) * layers.size())); } retval = getLayer(layer, exportDoc.data(), keyframe, path, frame, idx); if (retval != KisImageBuilder_RESULT_OK) break; } ba = QString(", \"%1\"").arg(layer->last).toUtf8(); if (io->write(ba.data()) < 0) break; } if (idx < layers.size()) retval = KisImageBuilder_RESULT_FAILURE; frame++; step = 6; //keep step here break; } step++; } while((retval == KisImageBuilder_RESULT_OK) && (step < 8)); qDeleteAll(layers); // io->close(); it seems this is not required anymore if (!m_batchMode) { //disconnect(m_doc, SIGNAL(sigProgressCanceled()), this, SLOT(cancel())); //emit m_doc->sigProgress(100); //emit m_doc->clearStatusBarMessage(); } QApplication::restoreOverrideCursor(); return retval; } QString CSVSaver::convertToBlending(const QString &opid) { if (opid == COMPOSITE_OVER) return "Color"; if (opid == COMPOSITE_BEHIND) return "Behind"; if (opid == COMPOSITE_ERASE) return "Erase"; // "Shade" if (opid == COMPOSITE_LINEAR_LIGHT) return "Light"; if (opid == COMPOSITE_COLORIZE) return "Colorize"; if (opid == COMPOSITE_HUE) return "Hue"; if ((opid == COMPOSITE_ADD) || (opid == COMPOSITE_LINEAR_DODGE)) return "Add"; if (opid == COMPOSITE_INVERSE_SUBTRACT) return "Sub"; if (opid == COMPOSITE_MULT) return "Multiply"; if (opid == COMPOSITE_SCREEN) return "Screen"; // "Replace" // "Substitute" if (opid == COMPOSITE_DIFF) return "Difference"; if (opid == COMPOSITE_DIVIDE) return "Divide"; if (opid == COMPOSITE_OVERLAY) return "Overlay"; if (opid == COMPOSITE_DODGE) return "Light2"; if (opid == COMPOSITE_BURN) return "Shade2"; if (opid == COMPOSITE_HARD_LIGHT) return "HardLight"; if ((opid == COMPOSITE_SOFT_LIGHT_PHOTOSHOP) || (opid == COMPOSITE_SOFT_LIGHT_SVG)) return "SoftLight"; if (opid == COMPOSITE_GRAIN_EXTRACT) return "GrainExtract"; if (opid == COMPOSITE_GRAIN_MERGE) return "GrainMerge"; if (opid == COMPOSITE_SUBTRACT) return "Sub2"; if (opid == COMPOSITE_DARKEN) return "Darken"; if (opid == COMPOSITE_LIGHTEN) return "Lighten"; if (opid == COMPOSITE_SATURATION) return "Saturation"; return "Color"; } KisImageBuilder_Result CSVSaver::getLayer(CSVLayerRecord* layer, KisDocument* exportDoc, KisKeyframeSP keyframe, const QString &path, int frame, int idx) { //render to the temp layer KisImageSP image = exportDoc->savingImage(); if (!image) image= exportDoc->image(); KisPaintDeviceSP device = image->rootLayer()->firstChild()->projection(); if (!keyframe.isNull()) { layer->channel->fetchFrame(keyframe, device); } else { device->makeCloneFrom(layer->layer->projection(),image->bounds()); // without animation } QRect bounds = device->exactBounds(); if (bounds.isEmpty()) { layer->last = ""; //empty frame return KisImageBuilder_RESULT_OK; } layer->last = QString("frame%1-%2.png").arg(idx + 1,5,10,QChar('0')).arg(frame,5,10,QChar('0')); QString filename = path; filename.append(layer->last); //save to PNG KisSequentialConstIterator it(device, image->bounds()); const KoColorSpace* cs = device->colorSpace(); bool isThereAlpha = false; while (it.nextPixel()) { if (cs->opacityU8(it.oldRawData()) != OPACITY_OPAQUE_U8) { isThereAlpha = true; break; } } if (!KisPNGConverter::isColorSpaceSupported(cs)) { device = new KisPaintDevice(*device.data()); KUndo2Command *cmd= device->convertTo(KoColorSpaceRegistry::instance()->rgb8()); delete cmd; } KisPNGOptions options; options.alpha = isThereAlpha; options.interlace = false; options.compression = 8; options.tryToSaveAsIndexed = false; options.transparencyFillColor = QColor(0,0,0); options.saveSRGBProfile = true; //TVPaint can use only sRGB options.forceSRGB = false; KisPNGConverter kpc(exportDoc); KisImageBuilder_Result result = kpc.buildFile(filename, image->bounds(), image->xRes(), image->yRes(), device, image->beginAnnotations(), image->endAnnotations(), options, (KisMetaData::Store* )0 ); return result; } void CSVSaver::createTempImage(KisDocument* exportDoc) { - exportDoc->setAutoSaveDelay(0); + exportDoc->setInfiniteAutoSaveInterval(); exportDoc->setFileBatchMode(true); KisImageSP exportImage = new KisImage(exportDoc->createUndoStore(), m_image->width(), m_image->height(), m_image->colorSpace(), QString()); exportImage->setResolution(m_image->xRes(), m_image->yRes()); exportDoc->setCurrentImage(exportImage); KisPaintLayer* paintLayer = new KisPaintLayer(exportImage, "paint device", OPACITY_OPAQUE_U8); exportImage->addNode(paintLayer, exportImage->rootLayer(), KisLayerSP(0)); } KisImageBuilder_Result CSVSaver::buildAnimation(QIODevice *io) { if (!m_image) { return KisImageBuilder_RESULT_EMPTY; } return encode(io); } void CSVSaver::cancel() { m_stop = true; } diff --git a/plugins/impex/exr/exr_converter.cc b/plugins/impex/exr/exr_converter.cc index fdd85b303d..a0f8099624 100644 --- a/plugins/impex/exr/exr_converter.cc +++ b/plugins/impex/exr/exr_converter.cc @@ -1,1344 +1,1354 @@ /* * Copyright (c) 2005 Adrian Page * Copyright (c) 2010 Cyrille Berger * * 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 "exr_converter.h" #include #include #include #include #include #include #include "exr_extra_tags.h" #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_iterator_ng.h" #include #include #include #include #include #include #include "kis_kra_savexml_visitor.h" // Do not translate! #define HDR_LAYER "HDR Layer" template struct Rgba { _T_ r; _T_ g; _T_ b; _T_ a; }; struct ExrGroupLayerInfo; struct ExrLayerInfoBase { ExrLayerInfoBase() : colorSpace(0), parent(0) { } const KoColorSpace* colorSpace; QString name; const ExrGroupLayerInfo* parent; }; struct ExrGroupLayerInfo : public ExrLayerInfoBase { ExrGroupLayerInfo() : groupLayer(0) {} KisGroupLayerSP groupLayer; }; enum ImageType { IT_UNKNOWN, IT_FLOAT16, IT_FLOAT32, IT_UNSUPPORTED }; struct ExrPaintLayerInfo : public ExrLayerInfoBase { ExrPaintLayerInfo() : imageType(IT_UNKNOWN) { } ImageType imageType; QMap< QString, QString> channelMap; ///< first is either R, G, B or A second is the EXR channel name struct Remap { Remap(const QString& _original, const QString& _current) : original(_original), current(_current) { } QString original; QString current; }; QList< Remap > remappedChannels; ///< this is used to store in the metadata the mapping between exr channel name, and channels used in Krita void updateImageType(ImageType channelType); }; void ExrPaintLayerInfo::updateImageType(ImageType channelType) { if (imageType == IT_UNKNOWN) { imageType = channelType; } else if (imageType != channelType) { imageType = IT_UNSUPPORTED; } } struct ExrPaintLayerSaveInfo { QString name; ///< name of the layer with a "." at the end (ie "group1.group2.layer1.") KisPaintLayerSP layer; QList channels; Imf::PixelType pixelType; }; struct EXRConverter::Private { Private() : doc(0) - , warnedAboutChangedAlpha(false) + , alphaWasModified(false) , showNotifications(false) {} KisImageSP image; KisDocument *doc; - bool warnedAboutChangedAlpha; + bool alphaWasModified; bool showNotifications; QString errorMessage; template void unmultiplyAlpha(typename WrapperType::pixel_type *pixel); template void decodeData4(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype); template void decodeData1(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype); QDomDocument loadExtraLayersInfo(const Imf::Header &header); bool checkExtraLayersInfoConsistent(const QDomDocument &doc, std::set exrLayerNames); void makeLayerNamesUnique(QList& informationObjects); void recBuildPaintLayerSaveInfo(QList& informationObjects, const QString& name, KisGroupLayerSP parent); void reportLayersNotSaved(const QSet &layersNotSaved); QString fetchExtraLayersInfo(QList& informationObjects); }; EXRConverter::EXRConverter(KisDocument *doc, bool showNotifications) : d(new Private) { d->doc = doc; d->showNotifications = showNotifications; + + // Set thread count for IlmImf library + Imf::setGlobalThreadCount(QThread::idealThreadCount()); + dbgFile << "EXR Threadcount was set to: " << QThread::idealThreadCount(); } EXRConverter::~EXRConverter() { } ImageType imfTypeToKisType(Imf::PixelType type) { switch (type) { case Imf::UINT: case Imf::NUM_PIXELTYPES: return IT_UNSUPPORTED; case Imf::HALF: return IT_FLOAT16; case Imf::FLOAT: return IT_FLOAT32; default: qFatal("Out of bound enum"); return IT_UNKNOWN; } } const KoColorSpace* kisTypeToColorSpace(QString model, ImageType imageType) { switch (imageType) { case IT_FLOAT16: return KoColorSpaceRegistry::instance()->colorSpace(model, Float16BitsColorDepthID.id(), ""); case IT_FLOAT32: return KoColorSpaceRegistry::instance()->colorSpace(model, Float32BitsColorDepthID.id(), ""); case IT_UNKNOWN: case IT_UNSUPPORTED: return 0; default: qFatal("Out of bound enum"); return 0; } } template static inline T alphaEpsilon() { return static_cast(HALF_EPSILON); } template static inline T alphaNoiseThreshold() { return static_cast(0.01); // 1% } template struct RgbPixelWrapper { typedef T channel_type; typedef Rgba pixel_type; RgbPixelWrapper(Rgba &_pixel) : pixel(_pixel) {} inline T alpha() const { return pixel.a; } inline bool checkMultipliedColorsConsistent() const { return !(pixel.a < alphaEpsilon() && (pixel.r > 0.0 || pixel.g > 0.0 || pixel.b > 0.0)); } inline bool checkUnmultipliedColorsConsistent(const Rgba &mult) const { const T alpha = pixel.a; return abs(alpha) >= alphaNoiseThreshold() || (pixel.r * alpha == mult.r && pixel.g * alpha == mult.g && pixel.b * alpha == mult.b); } inline void setUnmultiplied(const Rgba &mult, qreal newAlpha) { pixel.r = mult.r / newAlpha; pixel.g = mult.g / newAlpha; pixel.b = mult.b / newAlpha; pixel.a = newAlpha; } Rgba &pixel; }; template struct GrayPixelWrapper { typedef T channel_type; typedef typename KoGrayTraits::Pixel pixel_type; GrayPixelWrapper(pixel_type &_pixel) : pixel(_pixel) {} inline T alpha() const { return pixel.alpha; } inline bool checkMultipliedColorsConsistent() const { return !(pixel.alpha < alphaEpsilon() && pixel.gray > 0.0); } inline bool checkUnmultipliedColorsConsistent(const pixel_type &mult) const { const T alpha = pixel.alpha; return alpha >= alphaNoiseThreshold() || pixel.gray * alpha == mult.gray; } inline void setUnmultiplied(const pixel_type &mult, qreal newAlpha) { pixel.gray = mult.gray / newAlpha; pixel.alpha = newAlpha; } pixel_type &pixel; }; template void EXRConverter::Private::unmultiplyAlpha(typename WrapperType::pixel_type *pixel) { typedef typename WrapperType::pixel_type pixel_type; typedef typename WrapperType::channel_type channel_type; WrapperType srcPixel(*pixel); if (!srcPixel.checkMultipliedColorsConsistent()) { - bool alphaWasModified = false; channel_type newAlpha = srcPixel.alpha(); pixel_type __dstPixelData; WrapperType dstPixel(__dstPixelData); /** * Division by a tiny alpha may result in an overflow of half * value. That is why we use safe iterational approach. */ while (1) { dstPixel.setUnmultiplied(srcPixel.pixel, newAlpha); if (dstPixel.checkUnmultipliedColorsConsistent(srcPixel.pixel)) { break; } newAlpha += alphaEpsilon(); alphaWasModified = true; } *pixel = dstPixel.pixel; - if (alphaWasModified && - !this->warnedAboutChangedAlpha) { - - QString msg = - i18nc("@info", - "The image contains pixels with zero alpha channel and non-zero " - "color channels. Krita will have to modify those pixels to have " - "at least some alpha. The initial values will not " - "be reverted on saving the image back." - "

    " - "This will hardly make any visual difference just keep it in mind." - "

    " - "Modified alpha will have a range from %1 to %2", - alphaEpsilon(), - alphaNoiseThreshold()); - - if (this->showNotifications) { - QMessageBox::information(0, i18nc("@title:window", "EXR image will be modified"), msg); - } else { - warnKrita << "WARNING:" << msg; - } - - this->warnedAboutChangedAlpha = true; - } } else if (srcPixel.alpha() > 0.0) { srcPixel.setUnmultiplied(srcPixel.pixel, srcPixel.alpha()); } } template void multiplyAlpha(Pixel *pixel) { if (alphaPos >= 0) { T alpha = pixel->data[alphaPos]; if (alpha > 0.0) { for (int i = 0; i < size; ++i) { if (i != alphaPos) { pixel->data[i] *= alpha; } } pixel->data[alphaPos] = alpha; } } } template void EXRConverter::Private::decodeData4(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype) { typedef Rgba<_T_> Rgba; - QVector pixels(width); + QVector pixels(width * height); bool hasAlpha = info.channelMap.contains("A"); - for (int y = 0; y < height; ++y) { - Imf::FrameBuffer frameBuffer; - Rgba* frameBufferData = (pixels.data()) - xstart - (ystart + y) * width; - frameBuffer.insert(info.channelMap["R"].toLatin1().constData(), - Imf::Slice(ptype, (char *) &frameBufferData->r, - sizeof(Rgba) * 1, - sizeof(Rgba) * width)); - frameBuffer.insert(info.channelMap["G"].toLatin1().constData(), - Imf::Slice(ptype, (char *) &frameBufferData->g, + Imf::FrameBuffer frameBuffer; + Rgba* frameBufferData = (pixels.data()) - xstart - ystart * width; + frameBuffer.insert(info.channelMap["R"].toLatin1().constData(), + Imf::Slice(ptype, (char *) &frameBufferData->r, + sizeof(Rgba) * 1, + sizeof(Rgba) * width)); + frameBuffer.insert(info.channelMap["G"].toLatin1().constData(), + Imf::Slice(ptype, (char *) &frameBufferData->g, + sizeof(Rgba) * 1, + sizeof(Rgba) * width)); + frameBuffer.insert(info.channelMap["B"].toLatin1().constData(), + Imf::Slice(ptype, (char *) &frameBufferData->b, + sizeof(Rgba) * 1, + sizeof(Rgba) * width)); + if (hasAlpha) { + frameBuffer.insert(info.channelMap["A"].toLatin1().constData(), + Imf::Slice(ptype, (char *) &frameBufferData->a, sizeof(Rgba) * 1, sizeof(Rgba) * width)); - frameBuffer.insert(info.channelMap["B"].toLatin1().constData(), - Imf::Slice(ptype, (char *) &frameBufferData->b, - sizeof(Rgba) * 1, - sizeof(Rgba) * width)); - if (hasAlpha) { - frameBuffer.insert(info.channelMap["A"].toLatin1().constData(), - Imf::Slice(ptype, (char *) &frameBufferData->a, - sizeof(Rgba) * 1, - sizeof(Rgba) * width)); - } - - file.setFrameBuffer(frameBuffer); - file.readPixels(ystart + y); - Rgba *rgba = pixels.data(); - KisHLineIteratorSP it = layer->paintDevice()->createHLineIteratorNG(0, y, width); - do { + } - if (hasAlpha) { - unmultiplyAlpha >(rgba); - } + file.setFrameBuffer(frameBuffer); + file.readPixels(ystart, height + ystart - 1); + Rgba *rgba = pixels.data(); - typename KoRgbTraits<_T_>::Pixel* dst = reinterpret_cast::Pixel*>(it->rawData()); + QRect paintRegion(xstart, ystart, width, height); + KisSequentialIterator it(layer->paintDevice(), paintRegion); + while (it.nextPixel()) { + if (hasAlpha) { + unmultiplyAlpha >(rgba); + } - dst->red = rgba->r; - dst->green = rgba->g; - dst->blue = rgba->b; - if (hasAlpha) { - dst->alpha = rgba->a; - } else { - dst->alpha = 1.0; - } + typename KoRgbTraits<_T_>::Pixel* dst = reinterpret_cast::Pixel*>(it.rawData()); + dst->red = rgba->r; + dst->green = rgba->g; + dst->blue = rgba->b; + if (hasAlpha) { + dst->alpha = rgba->a; + } else { + dst->alpha = 1.0; + } - ++rgba; - } while (it->nextPixel()); + ++rgba; } - } template void EXRConverter::Private::decodeData1(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype) { typedef typename GrayPixelWrapper<_T_>::channel_type channel_type; typedef typename GrayPixelWrapper<_T_>::pixel_type pixel_type; KIS_ASSERT_RECOVER_RETURN( layer->paintDevice()->colorSpace()->colorModelId() == GrayAColorModelID); - QVector pixels(width); + QVector pixels(width * height); Q_ASSERT(info.channelMap.contains("G")); dbgFile << "G -> " << info.channelMap["G"]; bool hasAlpha = info.channelMap.contains("A"); dbgFile << "Has Alpha:" << hasAlpha; - for (int y = 0; y < height; ++y) { - Imf::FrameBuffer frameBuffer; - pixel_type* frameBufferData = (pixels.data()) - xstart - (ystart + y) * width; - frameBuffer.insert(info.channelMap["G"].toLatin1().constData(), - Imf::Slice(ptype, (char *) &frameBufferData->gray, + Imf::FrameBuffer frameBuffer; + pixel_type* frameBufferData = (pixels.data()) - xstart - ystart * width; + frameBuffer.insert(info.channelMap["G"].toLatin1().constData(), + Imf::Slice(ptype, (char *) &frameBufferData->gray, + sizeof(pixel_type) * 1, + sizeof(pixel_type) * width)); + + if (hasAlpha) { + frameBuffer.insert(info.channelMap["A"].toLatin1().constData(), + Imf::Slice(ptype, (char *) &frameBufferData->alpha, sizeof(pixel_type) * 1, sizeof(pixel_type) * width)); + } - if (hasAlpha) { - frameBuffer.insert(info.channelMap["A"].toLatin1().constData(), - Imf::Slice(ptype, (char *) &frameBufferData->alpha, - sizeof(pixel_type) * 1, - sizeof(pixel_type) * width)); - } - - file.setFrameBuffer(frameBuffer); - file.readPixels(ystart + y); + file.setFrameBuffer(frameBuffer); + file.readPixels(ystart, height + ystart - 1); - pixel_type *srcPtr = pixels.data(); - KisHLineIteratorSP it = layer->paintDevice()->createHLineIteratorNG(0, y, width); - do { + pixel_type *srcPtr = pixels.data(); - if (hasAlpha) { - unmultiplyAlpha >(srcPtr); - } + QRect paintRegion(xstart, ystart, width, height); + KisSequentialIterator it(layer->paintDevice(), paintRegion); + do { - pixel_type* dstPtr = reinterpret_cast(it->rawData()); + if (hasAlpha) { + unmultiplyAlpha >(srcPtr); + } - dstPtr->gray = srcPtr->gray; - dstPtr->alpha = hasAlpha ? srcPtr->alpha : channel_type(1.0); + pixel_type* dstPtr = reinterpret_cast(it.rawData()); - ++srcPtr; - } while (it->nextPixel()); - } + dstPtr->gray = srcPtr->gray; + dstPtr->alpha = hasAlpha ? srcPtr->alpha : channel_type(1.0); + ++srcPtr; + } while (it.nextPixel()); } bool recCheckGroup(const ExrGroupLayerInfo& group, QStringList list, int idx1, int idx2) { if (idx1 > idx2) return true; if (group.name == list[idx2]) { return recCheckGroup(*group.parent, list, idx1, idx2 - 1); } return false; } ExrGroupLayerInfo* searchGroup(QList* groups, QStringList list, int idx1, int idx2) { if (idx1 > idx2) { return 0; } // Look for the group for (int i = 0; i < groups->size(); ++i) { if (recCheckGroup(groups->at(i), list, idx1, idx2)) { return &(*groups)[i]; } } // Create the group ExrGroupLayerInfo info; info.name = list.at(idx2); info.parent = searchGroup(groups, list, idx1, idx2 - 1); groups->append(info); return &groups->last(); } QDomDocument EXRConverter::Private::loadExtraLayersInfo(const Imf::Header &header) { const Imf::StringAttribute *layersInfoAttribute = header.findTypedAttribute(EXR_KRITA_LAYERS); if (!layersInfoAttribute) return QDomDocument(); QString layersInfoString = QString::fromUtf8(layersInfoAttribute->value().c_str()); QDomDocument doc; doc.setContent(layersInfoString); return doc; } bool EXRConverter::Private::checkExtraLayersInfoConsistent(const QDomDocument &doc, std::set exrLayerNames) { std::set extraInfoLayers; QDomElement root = doc.documentElement(); KIS_ASSERT_RECOVER(!root.isNull() && root.hasChildNodes()) { return false; }; QDomElement el = root.firstChildElement(); while(!el.isNull()) { KIS_ASSERT_RECOVER(el.hasAttribute(EXR_NAME)) { return false; }; QString layerName = el.attribute(EXR_NAME).toUtf8(); if (layerName != QString(HDR_LAYER)) { extraInfoLayers.insert(el.attribute(EXR_NAME).toUtf8().constData()); } el = el.nextSiblingElement(); } bool result = (extraInfoLayers == exrLayerNames); if (!result) { dbgKrita << "WARINING: Krita EXR extra layers info is inconsistent!"; dbgKrita << ppVar(extraInfoLayers.size()) << ppVar(exrLayerNames.size()); std::set::const_iterator it1 = extraInfoLayers.begin(); std::set::const_iterator it2 = exrLayerNames.begin(); std::set::const_iterator end1 = extraInfoLayers.end(); for (; it1 != end1; ++it1, ++it2) { dbgKrita << it1->c_str() << it2->c_str(); } } return result; } KisImageBuilder_Result EXRConverter::decode(const QString &filename) { Imf::InputFile file(QFile::encodeName(filename)); Imath::Box2i dw = file.header().dataWindow(); + Imath::Box2i displayWindow = file.header().displayWindow(); + int width = dw.max.x - dw.min.x + 1; int height = dw.max.y - dw.min.y + 1; int dx = dw.min.x; int dy = dw.min.y; // Display the attributes of a file for (Imf::Header::ConstIterator it = file.header().begin(); it != file.header().end(); ++it) { dbgFile << "Attribute: " << it.name() << " type: " << it.attribute().typeName(); } // fetch Krita's extra layer info, which might have been stored previously QDomDocument extraLayersInfo = d->loadExtraLayersInfo(file.header()); // Construct the list of LayerInfo QList informationObjects; QList groups; ImageType imageType = IT_UNKNOWN; const Imf::ChannelList &channels = file.header().channels(); std::set layerNames; channels.layers(layerNames); if (!extraLayersInfo.isNull() && !d->checkExtraLayersInfoConsistent(extraLayersInfo, layerNames)) { // it is inconsistent anyway extraLayersInfo = QDomDocument(); } // Check if there are A, R, G, B channels dbgFile << "Checking for ARGB channels, they can occur in single-layer _or_ multi-layer images:"; ExrPaintLayerInfo info; bool topLevelRGBFound = false; info.name = HDR_LAYER; QStringList topLevelChannelNames = QStringList() << "A" << "R" << "G" << "B" << ".A" << ".R" << ".G" << ".B" << "A." << "R." << "G." << "B." << "A." << "R." << "G." << "B." << ".alpha" << ".red" << ".green" << ".blue"; for (Imf::ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { const Imf::Channel &channel = i.channel(); dbgFile << "Channel name = " << i.name() << " type = " << channel.type; QString qname = i.name(); if (topLevelChannelNames.contains(qname)) { topLevelRGBFound = true; dbgFile << "Found top-level channel" << qname; info.channelMap[qname] = qname; info.updateImageType(imfTypeToKisType(channel.type)); } // Channel names that don't contain a "." or that contain a // "." only at the beginning or at the end are not considered // to be part of any layer. else if (!qname.contains('.') || !qname.mid(1).contains('.') || !qname.left(qname.size() - 1).contains('.')) { warnFile << "Found a top-level channel that is not part of the rendered image" << qname << ". Krita will not load this channel."; } } if (topLevelRGBFound) { dbgFile << "Toplevel layer" << info.name << ":Image type:" << imageType << "Layer type" << info.imageType; informationObjects.push_back(info); imageType = info.imageType; } dbgFile << "Extra layers:" << layerNames.size(); for (std::set::const_iterator i = layerNames.begin();i != layerNames.end(); ++i) { info = ExrPaintLayerInfo(); dbgFile << "layer name = " << i->c_str(); info.name = i->c_str(); Imf::ChannelList::ConstIterator layerBegin, layerEnd; channels.channelsInLayer(*i, layerBegin, layerEnd); for (Imf::ChannelList::ConstIterator j = layerBegin; j != layerEnd; ++j) { const Imf::Channel &channel = j.channel(); info.updateImageType(imfTypeToKisType(channel.type)); QString qname = j.name(); QStringList list = qname.split('.'); QString layersuffix = list.last(); dbgFile << "\tchannel " << j.name() << "suffix" << layersuffix << " type = " << channel.type; // Nuke writes the channels for sublayers as .red instead of .R, so convert those. // See https://bugs.kde.org/show_bug.cgi?id=393771 if (topLevelChannelNames.contains("." + layersuffix)) { layersuffix = layersuffix.at(0).toUpper(); } dbgFile << "\t\tsuffix" << layersuffix; if (list.size() > 1) { info.name = list[list.size()-2]; info.parent = searchGroup(&groups, list, 0, list.size() - 3); } info.channelMap[layersuffix] = qname; } if (info.imageType != IT_UNKNOWN && info.imageType != IT_UNSUPPORTED) { informationObjects.push_back(info); if (imageType < info.imageType) { imageType = info.imageType; } } } dbgFile << "File has" << informationObjects.size() << "layer(s)"; // Set the colorspaces for (int i = 0; i < informationObjects.size(); ++i) { ExrPaintLayerInfo& info = informationObjects[i]; QString modelId; if (info.channelMap.size() == 1) { modelId = GrayAColorModelID.id(); QString key = info.channelMap.begin().key(); if (key != "G") { info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(key, "G")); QString channel = info.channelMap.begin().value(); info.channelMap.clear(); info.channelMap["G"] = channel; } } else if (info.channelMap.size() == 2) { modelId = GrayAColorModelID.id(); QMap::const_iterator it = info.channelMap.constBegin(); QMap::const_iterator end = info.channelMap.constEnd(); QString failingChannelKey; for (; it != end; ++it) { if (it.key() != "G" && it.key() != "A") { failingChannelKey = it.key(); break; } } info.remappedChannels.push_back( ExrPaintLayerInfo::Remap(failingChannelKey, "G")); QString failingChannelValue = info.channelMap[failingChannelKey]; info.channelMap.remove(failingChannelKey); info.channelMap["G"] = failingChannelValue; } else if (info.channelMap.size() == 3 || info.channelMap.size() == 4) { if (info.channelMap.contains("R") && info.channelMap.contains("G") && info.channelMap.contains("B")) { modelId = RGBAColorModelID.id(); } else if (info.channelMap.contains("X") && info.channelMap.contains("Y") && info.channelMap.contains("Z")) { modelId = XYZAColorModelID.id(); QMap newChannelMap; if (info.channelMap.contains("W")) { newChannelMap["A"] = info.channelMap["W"]; info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("W", "A")); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("X", "X")); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("Y", "Y")); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("Z", "Z")); } else if (info.channelMap.contains("A")) { newChannelMap["A"] = info.channelMap["A"]; } // The decode function expect R, G, B in the channel map newChannelMap["B"] = info.channelMap["X"]; newChannelMap["G"] = info.channelMap["Y"]; newChannelMap["R"] = info.channelMap["Z"]; info.channelMap = newChannelMap; } else { modelId = RGBAColorModelID.id(); QMap newChannelMap; QMap::iterator it = info.channelMap.begin(); newChannelMap["R"] = it.value(); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "R")); ++it; newChannelMap["G"] = it.value(); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "G")); ++it; newChannelMap["B"] = it.value(); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "B")); if (info.channelMap.size() == 4) { ++it; newChannelMap["A"] = it.value(); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "A")); } info.channelMap = newChannelMap; } } else { dbgFile << info.name << "has" << info.channelMap.size() << "channels, and we don't know what to do."; } if (!modelId.isEmpty()) { info.colorSpace = kisTypeToColorSpace(modelId, info.imageType); } } // Get colorspace dbgFile << "Image type = " << imageType; const KoColorSpace* colorSpace = kisTypeToColorSpace(RGBAColorModelID.id(), imageType); if (!colorSpace) return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; dbgFile << "Colorspace: " << colorSpace->name(); // Set the colorspace on all groups for (int i = 0; i < groups.size(); ++i) { ExrGroupLayerInfo& info = groups[i]; info.colorSpace = colorSpace; } // Create the image - d->image = new KisImage(d->doc->createUndoStore(), width, height, colorSpace, ""); + // Make sure the created image is the same size as the displayWindow since + // the dataWindow can be cropped in some cases. + int displayWidth = displayWindow.max.x - displayWindow.min.x + 1; + int displayHeight = displayWindow.max.y - displayWindow.min.y + 1; + d->image = new KisImage(d->doc->createUndoStore(), displayWidth, displayHeight, colorSpace, ""); if (!d->image) { return KisImageBuilder_RESULT_FAILURE; } /** * EXR semi-transparent images are expected to be rendered on * black to ensure correctness of the light model */ d->image->setDefaultProjectionColor(KoColor(Qt::black, colorSpace)); // Create group layers for (int i = 0; i < groups.size(); ++i) { ExrGroupLayerInfo& info = groups[i]; Q_ASSERT(info.parent == 0 || info.parent->groupLayer); KisGroupLayerSP groupLayerParent = (info.parent) ? info.parent->groupLayer : d->image->rootLayer(); info.groupLayer = new KisGroupLayer(d->image, info.name, OPACITY_OPAQUE_U8); d->image->addNode(info.groupLayer, groupLayerParent); } // Load the layers for (int i = informationObjects.size() - 1; i >= 0; --i) { ExrPaintLayerInfo& info = informationObjects[i]; if (info.colorSpace) { dbgFile << "Decoding " << info.name << " with " << info.channelMap.size() << " channels, and color space " << info.colorSpace->id(); KisPaintLayerSP layer = new KisPaintLayer(d->image, info.name, OPACITY_OPAQUE_U8, info.colorSpace); layer->setCompositeOpId(COMPOSITE_OVER); if (!layer) { return KisImageBuilder_RESULT_FAILURE; } switch (info.channelMap.size()) { case 1: case 2: // Decode the data switch (info.imageType) { case IT_FLOAT16: d->decodeData1(file, info, layer, width, dx, dy, height, Imf::HALF); break; case IT_FLOAT32: d->decodeData1(file, info, layer, width, dx, dy, height, Imf::FLOAT); break; case IT_UNKNOWN: case IT_UNSUPPORTED: qFatal("Impossible error"); } break; case 3: case 4: // Decode the data switch (info.imageType) { case IT_FLOAT16: d->decodeData4(file, info, layer, width, dx, dy, height, Imf::HALF); break; case IT_FLOAT32: d->decodeData4(file, info, layer, width, dx, dy, height, Imf::FLOAT); break; case IT_UNKNOWN: case IT_UNSUPPORTED: qFatal("Impossible error"); } break; default: qFatal("Invalid number of channels: %i", info.channelMap.size()); } // Check if should set the channels if (!info.remappedChannels.isEmpty()) { QList values; Q_FOREACH (const ExrPaintLayerInfo::Remap& remap, info.remappedChannels) { QMap map; map["original"] = KisMetaData::Value(remap.original); map["current"] = KisMetaData::Value(remap.current); values.append(map); } layer->metaData()->addEntry(KisMetaData::Entry(KisMetaData::SchemaRegistry::instance()->create("http://krita.org/exrchannels/1.0/" , "exrchannels"), "channelsmap", values)); } // Add the layer KisGroupLayerSP groupLayerParent = (info.parent) ? info.parent->groupLayer : d->image->rootLayer(); d->image->addNode(layer, groupLayerParent); } else { dbgFile << "No decoding " << info.name << " with " << info.channelMap.size() << " channels, and lack of a color space"; } } + // Set projectionColor to opaque + d->image->setDefaultProjectionColor(KoColor(Qt::transparent, colorSpace)); + + // After reading the image, notify the user about changed alpha. + if (d->alphaWasModified) { + QString msg = + i18nc("@info", + "The image contains pixels with zero alpha channel and non-zero " + "color channels. Krita has modified those pixels to have " + "at least some alpha. The initial values will not " + "be reverted on saving the image back." + "

    " + "This will hardly make any visual difference just keep it in mind."); + if (d->showNotifications) { + QMessageBox::information(0, i18nc("@title:window", "EXR image has been modified"), msg); + } else { + warnKrita << "WARNING:" << msg; + } + } if (!extraLayersInfo.isNull()) { KisExrLayersSorter sorter(extraLayersInfo, d->image); } return KisImageBuilder_RESULT_OK; } KisImageBuilder_Result EXRConverter::buildImage(const QString &filename) { return decode(filename); } KisImageSP EXRConverter::image() { return d->image; } QString EXRConverter::errorMessage() const { return d->errorMessage; } template struct ExrPixel_ { _T_ data[size]; }; class Encoder { public: virtual ~Encoder() {} virtual void prepareFrameBuffer(Imf::FrameBuffer*, int line) = 0; virtual void encodeData(int line) = 0; }; template class EncoderImpl : public Encoder { public: EncoderImpl(Imf::OutputFile* _file, const ExrPaintLayerSaveInfo* _info, int width) : file(_file), info(_info), pixels(width), m_width(width) {} ~EncoderImpl() override {} void prepareFrameBuffer(Imf::FrameBuffer*, int line) override; void encodeData(int line) override; private: typedef ExrPixel_<_T_, size> ExrPixel; Imf::OutputFile* file; const ExrPaintLayerSaveInfo* info; QVector pixels; int m_width; }; template void EncoderImpl<_T_, size, alphaPos>::prepareFrameBuffer(Imf::FrameBuffer* frameBuffer, int line) { int xstart = 0; int ystart = 0; ExrPixel* frameBufferData = (pixels.data()) - xstart - (ystart + line) * m_width; for (int k = 0; k < size; ++k) { frameBuffer->insert(info->channels[k].toUtf8(), Imf::Slice(info->pixelType, (char *) &frameBufferData->data[k], sizeof(ExrPixel) * 1, sizeof(ExrPixel) * m_width)); } } template void EncoderImpl<_T_, size, alphaPos>::encodeData(int line) { ExrPixel *rgba = pixels.data(); KisHLineIteratorSP it = info->layer->paintDevice()->createHLineIteratorNG(0, line, m_width); do { const _T_* dst = reinterpret_cast < const _T_* >(it->oldRawData()); for (int i = 0; i < size; ++i) { rgba->data[i] = dst[i]; } if (alphaPos != -1) { multiplyAlpha<_T_, ExrPixel, size, alphaPos>(rgba); } ++rgba; } while (it->nextPixel()); } Encoder* encoder(Imf::OutputFile& file, const ExrPaintLayerSaveInfo& info, int width) { dbgFile << "Create encoder for" << info.layer->name() << info.channels << info.layer->colorSpace()->channelCount(); switch (info.layer->colorSpace()->channelCount()) { case 1: { if (info.layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) { Q_ASSERT(info.pixelType == Imf::HALF); return new EncoderImpl < half, 1, -1 > (&file, &info, width); } else if (info.layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) { Q_ASSERT(info.pixelType == Imf::FLOAT); return new EncoderImpl < float, 1, -1 > (&file, &info, width); } break; } case 2: { if (info.layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) { Q_ASSERT(info.pixelType == Imf::HALF); return new EncoderImpl(&file, &info, width); } else if (info.layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) { Q_ASSERT(info.pixelType == Imf::FLOAT); return new EncoderImpl(&file, &info, width); } break; } case 4: { if (info.layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) { Q_ASSERT(info.pixelType == Imf::HALF); return new EncoderImpl(&file, &info, width); } else if (info.layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) { Q_ASSERT(info.pixelType == Imf::FLOAT); return new EncoderImpl(&file, &info, width); } break; } default: qFatal("Impossible error"); } return 0; } void encodeData(Imf::OutputFile& file, const QList& informationObjects, int width, int height) { QList encoders; Q_FOREACH (const ExrPaintLayerSaveInfo& info, informationObjects) { encoders.push_back(encoder(file, info, width)); } for (int y = 0; y < height; ++y) { Imf::FrameBuffer frameBuffer; Q_FOREACH (Encoder* encoder, encoders) { encoder->prepareFrameBuffer(&frameBuffer, y); } file.setFrameBuffer(frameBuffer); Q_FOREACH (Encoder* encoder, encoders) { encoder->encodeData(y); } file.writePixels(1); } qDeleteAll(encoders); } KisImageBuilder_Result EXRConverter::buildFile(const QString &filename, KisPaintLayerSP layer) { if (!layer) return KisImageBuilder_RESULT_INVALID_ARG; KisImageSP image = layer->image(); if (!image) return KisImageBuilder_RESULT_EMPTY; // Make the header qint32 height = image->height(); qint32 width = image->width(); Imf::Header header(width, height); Imf::PixelType pixelType = Imf::NUM_PIXELTYPES; if (layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) { pixelType = Imf::HALF; } else if(layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) { pixelType = Imf::FLOAT; } else { const KoColorSpace *cs = 0; if (layer->colorSpace()->colorModelId() == GrayAColorModelID) { cs = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Float16BitsColorDepthID.id()); } else { cs = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float16BitsColorDepthID.id()); } image->convertImageColorSpace(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); pixelType = Imf::HALF; } header.channels().insert("R", Imf::Channel(pixelType)); header.channels().insert("G", Imf::Channel(pixelType)); header.channels().insert("B", Imf::Channel(pixelType)); header.channels().insert("A", Imf::Channel(pixelType)); ExrPaintLayerSaveInfo info; info.layer = layer; info.channels.push_back("R"); info.channels.push_back("G"); info.channels.push_back("B"); info.channels.push_back("A"); info.pixelType = pixelType; // Open file for writing Imf::OutputFile file(QFile::encodeName(filename), header); QList informationObjects; informationObjects.push_back(info); encodeData(file, informationObjects, width, height); return KisImageBuilder_RESULT_OK; } QString remap(const QMap& current2original, const QString& current) { if (current2original.contains(current)) { return current2original[current]; } return current; } void EXRConverter::Private::makeLayerNamesUnique(QList& informationObjects) { typedef QMultiMap::iterator> NamesMap; NamesMap namesMap; { QList::iterator it = informationObjects.begin(); QList::iterator end = informationObjects.end(); for (; it != end; ++it) { namesMap.insert(it->name, it); } } Q_FOREACH (const QString &key, namesMap.keys()) { if (namesMap.count(key) > 1) { KIS_ASSERT_RECOVER(key.endsWith(".")) { continue; } QString strippedName = key.left(key.size() - 1); // trim the ending dot int nameCounter = 0; NamesMap::iterator it = namesMap.find(key); NamesMap::iterator end = namesMap.end(); for (; it != end; ++it) { QString newName = QString("%1_%2.") .arg(strippedName) .arg(nameCounter++); it.value()->name = newName; QList::iterator channelsIt = it.value()->channels.begin(); QList::iterator channelsEnd = it.value()->channels.end(); for (; channelsIt != channelsEnd; ++channelsIt) { channelsIt->replace(key, newName); } } } } } void EXRConverter::Private::recBuildPaintLayerSaveInfo(QList& informationObjects, const QString& name, KisGroupLayerSP parent) { QSet layersNotSaved; for (uint i = 0; i < parent->childCount(); ++i) { KisNodeSP node = parent->at(i); if (KisPaintLayerSP paintLayer = dynamic_cast(node.data())) { QMap current2original; if (paintLayer->metaData()->containsEntry(KisMetaData::SchemaRegistry::instance()->create("http://krita.org/exrchannels/1.0/" , "exrchannels"), "channelsmap")) { const KisMetaData::Entry& entry = paintLayer->metaData()->getEntry(KisMetaData::SchemaRegistry::instance()->create("http://krita.org/exrchannels/1.0/" , "exrchannels"), "channelsmap"); QList< KisMetaData::Value> values = entry.value().asArray(); Q_FOREACH (const KisMetaData::Value& value, values) { QMap map = value.asStructure(); if (map.contains("original") && map.contains("current")) { current2original[map["current"].toString()] = map["original"].toString(); } } } ExrPaintLayerSaveInfo info; info.name = name + paintLayer->name() + '.'; info.layer = paintLayer; if (info.name == QString(HDR_LAYER) + ".") { info.channels.push_back("R"); info.channels.push_back("G"); info.channels.push_back("B"); info.channels.push_back("A"); } else { if (paintLayer->colorSpace()->colorModelId() == RGBAColorModelID) { info.channels.push_back(info.name + remap(current2original, "R")); info.channels.push_back(info.name + remap(current2original, "G")); info.channels.push_back(info.name + remap(current2original, "B")); info.channels.push_back(info.name + remap(current2original, "A")); } else if (paintLayer->colorSpace()->colorModelId() == GrayAColorModelID) { info.channels.push_back(info.name + remap(current2original, "G")); info.channels.push_back(info.name + remap(current2original, "A")); } else if (paintLayer->colorSpace()->colorModelId() == GrayColorModelID) { info.channels.push_back(info.name + remap(current2original, "G")); } else if (paintLayer->colorSpace()->colorModelId() == XYZAColorModelID) { info.channels.push_back(info.name + remap(current2original, "X")); info.channels.push_back(info.name + remap(current2original, "Y")); info.channels.push_back(info.name + remap(current2original, "Z")); info.channels.push_back(info.name + remap(current2original, "A")); } } if (paintLayer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) { info.pixelType = Imf::HALF; } else if (paintLayer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) { info.pixelType = Imf::FLOAT; } else { info.pixelType = Imf::NUM_PIXELTYPES; } if (info.pixelType < Imf::NUM_PIXELTYPES) { dbgFile << "Going to save layer" << info.name; informationObjects.push_back(info); } else { warnFile << "Will not save layer" << info.name; layersNotSaved << node; } } else if (KisGroupLayerSP groupLayer = dynamic_cast(node.data())) { recBuildPaintLayerSaveInfo(informationObjects, name + groupLayer->name() + '.', groupLayer); } else { /** * The EXR can store paint and group layers only. The rest will * go to /dev/null :( */ layersNotSaved.insert(node); } } if (!layersNotSaved.isEmpty()) { reportLayersNotSaved(layersNotSaved); } } void EXRConverter::Private::reportLayersNotSaved(const QSet &layersNotSaved) { QString layersList; QTextStream textStream(&layersList); textStream.setCodec("UTF-8"); Q_FOREACH (KisNodeSP node, layersNotSaved) { textStream << "
  • " << i18nc("@item:unsupported-node-message", "%1 (type: \"%2\")", node->name(), node->metaObject()->className()) << "
  • "; } QString msg = i18nc("@info", "

    The following layers have a type that is not supported by EXR format:

    " "
      %1

    " "

    these layers have not been saved to the final EXR file

    ", layersList); errorMessage = msg; } QString EXRConverter::Private::fetchExtraLayersInfo(QList& informationObjects) { KIS_ASSERT_RECOVER_NOOP(!informationObjects.isEmpty()); if (informationObjects.size() == 1 && informationObjects[0].name == QString(HDR_LAYER) + ".") { return QString(); } QDomDocument doc("krita-extra-layers-info"); doc.appendChild(doc.createElement("root")); QDomElement rootElement = doc.documentElement(); for (int i = 0; i < informationObjects.size(); i++) { ExrPaintLayerSaveInfo &info = informationObjects[i]; quint32 unused; KisSaveXmlVisitor visitor(doc, rootElement, unused, QString(), false); QDomElement el = visitor.savePaintLayerAttributes(info.layer.data(), doc); // cut the ending '.' QString strippedName = info.name.left(info.name.size() - 1); el.setAttribute(EXR_NAME, strippedName); rootElement.appendChild(el); } return doc.toString(); } -KisImageBuilder_Result EXRConverter::buildFile(const QString &filename, KisGroupLayerSP layer) +KisImageBuilder_Result EXRConverter::buildFile(const QString &filename, KisGroupLayerSP layer, bool flatten) { if (!layer) return KisImageBuilder_RESULT_INVALID_ARG; KisImageSP image = layer->image(); if (!image) return KisImageBuilder_RESULT_EMPTY; qint32 height = image->height(); qint32 width = image->width(); Imf::Header header(width, height); if (image->colorSpace()->colorDepthId() != Float16BitsColorDepthID && image->colorSpace()->colorDepthId() != Float32BitsColorDepthID) { const KoColorSpace *cs = 0; if (layer->colorSpace()->colorModelId() == GrayAColorModelID) { cs = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Float16BitsColorDepthID.id()); } else { cs = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float16BitsColorDepthID.id()); } image->convertImageColorSpace(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); } - QList informationObjects; - d->recBuildPaintLayerSaveInfo(informationObjects, "", layer); - - if(informationObjects.isEmpty()) { - return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; + if (flatten) { + image->waitForDone(); // This is to make sure we have a full image to project. + KisPaintDeviceSP pd = new KisPaintDevice(*image->projection()); + KisPaintLayerSP l = new KisPaintLayer(image, "projection", OPACITY_OPAQUE_U8, pd); + return buildFile(filename, l); } + else { - d->makeLayerNamesUnique(informationObjects); + QList informationObjects; + d->recBuildPaintLayerSaveInfo(informationObjects, "", layer); - QByteArray extraLayersInfo = d->fetchExtraLayersInfo(informationObjects).toUtf8(); - if (!extraLayersInfo.isNull()) { - header.insert(EXR_KRITA_LAYERS, Imf::StringAttribute(extraLayersInfo.constData())); - } - dbgFile << informationObjects.size() << " layers to save"; + if(informationObjects.isEmpty()) { + return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; + } - Q_FOREACH (const ExrPaintLayerSaveInfo& info, informationObjects) { - if (info.pixelType < Imf::NUM_PIXELTYPES) { - Q_FOREACH (const QString& channel, info.channels) { - dbgFile << channel << " " << info.pixelType; - header.channels().insert(channel.toUtf8().data(), Imf::Channel(info.pixelType)); + d->makeLayerNamesUnique(informationObjects); + + QByteArray extraLayersInfo = d->fetchExtraLayersInfo(informationObjects).toUtf8(); + if (!extraLayersInfo.isNull()) { + header.insert(EXR_KRITA_LAYERS, Imf::StringAttribute(extraLayersInfo.constData())); + } + dbgFile << informationObjects.size() << " layers to save"; + + Q_FOREACH (const ExrPaintLayerSaveInfo& info, informationObjects) { + if (info.pixelType < Imf::NUM_PIXELTYPES) { + Q_FOREACH (const QString& channel, info.channels) { + dbgFile << channel << " " << info.pixelType; + header.channels().insert(channel.toUtf8().data(), Imf::Channel(info.pixelType)); + } } } - } - // Open file for writing - Imf::OutputFile file(QFile::encodeName(filename), header); + // Open file for writing + Imf::OutputFile file(QFile::encodeName(filename), header); - encodeData(file, informationObjects, width, height); - return KisImageBuilder_RESULT_OK; + encodeData(file, informationObjects, width, height); + return KisImageBuilder_RESULT_OK; + } } void EXRConverter::cancel() { warnKrita << "WARNING: Cancelling of an EXR loading is not supported!"; } diff --git a/plugins/impex/exr/exr_converter.h b/plugins/impex/exr/exr_converter.h index d1d264f1dc..6db6795339 100644 --- a/plugins/impex/exr/exr_converter.h +++ b/plugins/impex/exr/exr_converter.h @@ -1,56 +1,56 @@ /* * Copyright (c) 2010 Cyrille Berger * * 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 _EXR_CONVERTER_H_ #define _EXR_CONVERTER_H_ #include #include #include "kis_types.h" #include class KisDocument; class EXRConverter : public QObject { Q_OBJECT public: EXRConverter(KisDocument *doc, bool showNotifications); ~EXRConverter() override; public: KisImageBuilder_Result buildImage(const QString &filename); KisImageBuilder_Result buildFile(const QString &filename, KisPaintLayerSP layer); - KisImageBuilder_Result buildFile(const QString &filename, KisGroupLayerSP layer); + KisImageBuilder_Result buildFile(const QString &filename, KisGroupLayerSP layer, bool flatten=false); /** * Retrieve the constructed image */ KisImageSP image(); QString errorMessage() const; private: KisImageBuilder_Result decode(const QString &filename); public Q_SLOTS: virtual void cancel(); private: struct Private; const QScopedPointer d; }; #endif diff --git a/plugins/impex/exr/exr_export.cc b/plugins/impex/exr/exr_export.cc index 46f16ecc0a..062b24b644 100644 --- a/plugins/impex/exr/exr_export.cc +++ b/plugins/impex/exr/exr_export.cc @@ -1,158 +1,155 @@ /* * Copyright (c) 2010 Cyrille Berger * * 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 "exr_export.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "exr_converter.h" class KisExternalLayer; K_PLUGIN_FACTORY_WITH_JSON(ExportFactory, "krita_exr_export.json", registerPlugin();) EXRExport::EXRExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent) { } EXRExport::~EXRExport() { } KisPropertiesConfigurationSP EXRExport::defaultConfiguration(const QByteArray &/*from*/, const QByteArray &/*to*/) const { KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration(); cfg->setProperty("flatten", false); return cfg; } KisConfigWidget *EXRExport::createConfigurationWidget(QWidget *parent, const QByteArray &/*from*/, const QByteArray &/*to*/) const { return new KisWdgOptionsExr(parent); } KisImportExportFilter::ConversionStatus EXRExport::convert(KisDocument *document, QIODevice */*io*/, KisPropertiesConfigurationSP configuration) { KisImageSP image = document->savingImage(); EXRConverter exrConverter(document, !batchMode()); KisImageBuilder_Result res; if (configuration->getBool("flatten")) { - KisPaintDeviceSP pd = new KisPaintDevice(*image->projection()); - KisPaintLayerSP l = new KisPaintLayer(image, "projection", OPACITY_OPAQUE_U8, pd); - - res = exrConverter.buildFile(filename(), l); + res = exrConverter.buildFile(filename(), image->rootLayer(), true); } else { res = exrConverter.buildFile(filename(), image->rootLayer()); } dbgFile << " Result =" << res; switch (res) { case KisImageBuilder_RESULT_INVALID_ARG: document->setErrorMessage(i18n("This layer cannot be saved to EXR.")); return KisImportExportFilter::WrongFormat; case KisImageBuilder_RESULT_EMPTY: document->setErrorMessage(i18n("The layer does not have an image associated with it.")); return KisImportExportFilter::WrongFormat; case KisImageBuilder_RESULT_NO_URI: document->setErrorMessage(i18n("The filename is empty.")); return KisImportExportFilter::CreationError; case KisImageBuilder_RESULT_NOT_LOCAL: document->setErrorMessage(i18n("EXR images cannot be saved remotely.")); return KisImportExportFilter::InternalError; case KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE: document->setErrorMessage(i18n("Colorspace not supported: EXR images must be 16 or 32 bits floating point RGB.")); return KisImportExportFilter::WrongFormat; case KisImageBuilder_RESULT_OK: if (!exrConverter.errorMessage().isNull()) { document->setErrorMessage(exrConverter.errorMessage()); } return KisImportExportFilter::OK; default: break; } document->setErrorMessage(i18n("Internal Error")); return KisImportExportFilter::InternalError; } void EXRExport::initializeCapabilities() { addCapability(KisExportCheckRegistry::instance()->get("NodeTypeCheck/KisGroupLayer")->create(KisExportCheckBase::SUPPORTED)); addCapability(KisExportCheckRegistry::instance()->get("MultiLayerCheck")->create(KisExportCheckBase::SUPPORTED)); addCapability(KisExportCheckRegistry::instance()->get("sRGBProfileCheck")->create(KisExportCheckBase::SUPPORTED)); QList > supportedColorModels; supportedColorModels << QPair() << QPair(RGBAColorModelID, Float16BitsColorDepthID) << QPair(RGBAColorModelID, Float32BitsColorDepthID) << QPair(GrayAColorModelID, Float16BitsColorDepthID) << QPair(GrayAColorModelID, Float32BitsColorDepthID) << QPair(GrayColorModelID, Float16BitsColorDepthID) << QPair(GrayColorModelID, Float32BitsColorDepthID) << QPair(XYZAColorModelID, Float16BitsColorDepthID) << QPair(XYZAColorModelID, Float32BitsColorDepthID); addSupportedColorModels(supportedColorModels, "EXR"); } void KisWdgOptionsExr::setConfiguration(const KisPropertiesConfigurationSP cfg) { chkFlatten->setChecked(cfg->getBool("flatten", false)); } KisPropertiesConfigurationSP KisWdgOptionsExr::configuration() const { KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration(); cfg->setProperty("flatten", chkFlatten->isChecked()); return cfg; } #include diff --git a/plugins/impex/heif/CMakeLists.txt b/plugins/impex/heif/CMakeLists.txt new file mode 100644 index 0000000000..725e7afed3 --- /dev/null +++ b/plugins/impex/heif/CMakeLists.txt @@ -0,0 +1,31 @@ +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${HEIF_CFLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${HEIF_CFLAGS}") +add_definitions(${HEIF_DEFINITIONS}) + +set(kritaheifimport_SOURCES + HeifImport.cpp + HeifError.cpp +) + +add_library(kritaheifimport MODULE ${kritaheifimport_SOURCES}) + +target_link_libraries(kritaheifimport kritaui kritalibkra + ${HEIF_LDFLAGS} ${HEIF_LIBRARIES} +) + +install(TARGETS kritaheifimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) + +set(kritaheifexport_SOURCES + HeifExport.cpp + HeifError.cpp +) + +ki18n_wrap_ui(kritaheifexport_SOURCES WdgHeifExport.ui ) + +add_library(kritaheifexport MODULE ${kritaheifexport_SOURCES}) + +target_link_libraries(kritaheifexport kritaui kritalibkra kritaimpex ${HEIF_LDFLAGS} ${HEIF_LIBRARIES} ) + +install(TARGETS kritaheifexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) + +install( PROGRAMS krita_heif.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) diff --git a/plugins/impex/heif/HeifError.cpp b/plugins/impex/heif/HeifError.cpp new file mode 100644 index 0000000000..d6de7109ad --- /dev/null +++ b/plugins/impex/heif/HeifError.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018 Dirk Farin + * + * 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 "HeifError.h" + + +KisImportExportFilter::ConversionStatus setHeifError(KisDocument* document, + heif::Error error) +{ + switch (error.get_code()) { + case heif_error_Ok: + return KisImportExportFilter::OK; + + case heif_error_Input_does_not_exist: + // this should never happen because we do not read from file names + document->setErrorMessage(i18n("Internal error.")); + return KisImportExportFilter::InternalError; + + case heif_error_Invalid_input: + case heif_error_Decoder_plugin_error: + document->setErrorMessage(i18n("The HEIF file is corrupted.")); + return KisImportExportFilter::ParsingError; + + case heif_error_Unsupported_filetype: + case heif_error_Unsupported_feature: + document->setErrorMessage(i18n("Krita does support this type of HEIF file.")); + return KisImportExportFilter::NotImplemented; + + case heif_error_Usage_error: + case heif_error_Encoder_plugin_error: + // this should never happen if we use libheif in the correct way + document->setErrorMessage(i18n("Internal libheif API error.")); + return KisImportExportFilter::InternalError; + + case heif_error_Memory_allocation_error: + document->setErrorMessage(i18n("Could not allocate memory.")); + return KisImportExportFilter::StorageCreationError; + + case heif_error_Encoding_error: + document->setErrorMessage(i18n("Could not encode or write image.")); + return KisImportExportFilter::CreationError; + + default: + // we only get here when we forgot to handle an error ID + document->setErrorMessage(i18n("Unknown error.")); + return KisImportExportFilter::InternalError; + } +} diff --git a/libs/ui/dialogs/kis_about_application.h b/plugins/impex/heif/HeifError.h similarity index 61% copy from libs/ui/dialogs/kis_about_application.h copy to plugins/impex/heif/HeifError.h index 23a94d2dcb..3fd38b018a 100644 --- a/libs/ui/dialogs/kis_about_application.h +++ b/plugins/impex/heif/HeifError.h @@ -1,35 +1,31 @@ /* - * Copyright (c) 2014 Boudewijn Rempt + * Copyright (c) 2018 Dirk Farin * * 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. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ -#ifndef KIS_ABOUT_APPLICATION_H -#define KIS_ABOUT_APPLICATION_H -#include +#ifndef HEIF_ERROR_H_ +#define HEIF_ERROR_H_ -class KisAboutApplication : public QDialog -{ - Q_OBJECT -public: - explicit KisAboutApplication(QWidget *parent = 0); +#include -Q_SIGNALS: +#include "libheif/heif_cxx.h" -public Q_SLOTS: -}; +KisImportExportFilter::ConversionStatus setHeifError(KisDocument* document, + heif::Error error); -#endif // KIS_ABOUT_APPLICATION_H +#endif diff --git a/plugins/impex/heif/HeifExport.cpp b/plugins/impex/heif/HeifExport.cpp new file mode 100644 index 0000000000..2faf8660ab --- /dev/null +++ b/plugins/impex/heif/HeifExport.cpp @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2018 Dirk Farin + * + * 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 "HeifExport.h" +#include "HeifError.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_iterator_ng.h" + +#include "libheif/heif_cxx.h" + + +class KisExternalLayer; + +K_PLUGIN_FACTORY_WITH_JSON(ExportFactory, "krita_heif_export.json", registerPlugin();) + +HeifExport::HeifExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent) +{ +} + +HeifExport::~HeifExport() +{ +} + +KisPropertiesConfigurationSP HeifExport::defaultConfiguration(const QByteArray &/*from*/, const QByteArray &/*to*/) const +{ + KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration(); + cfg->setProperty("quality", 50); + cfg->setProperty("lossless", true); + return cfg; +} + +KisConfigWidget *HeifExport::createConfigurationWidget(QWidget *parent, const QByteArray &/*from*/, const QByteArray &/*to*/) const +{ + return new KisWdgOptionsHeif(parent); +} + + + +class Writer_QIODevice : public heif::Context::Writer +{ +public: + Writer_QIODevice(QIODevice* io) + : m_io(io) + { + } + + heif_error write(const void* data, size_t size) override { + qint64 n = m_io->write((const char*)data,size); + if (n != (qint64)size) { + QString error = m_io->errorString(); + + heif_error err = { + heif_error_Encoding_error, + heif_suberror_Cannot_write_output_data, + "Could not write output data" }; + + return err; + } + + struct heif_error heif_error_ok = { heif_error_Ok, heif_suberror_Unspecified, "Success" }; + return heif_error_ok; + } + +private: + QIODevice* m_io; +}; + + +KisImportExportFilter::ConversionStatus HeifExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration) +{ + KisImageSP image = document->savingImage(); + const KoColorSpace *cs = image->colorSpace(); + + // Convert to 8 bits rgba on saving + if (cs->colorModelId() != RGBAColorModelID || cs->colorDepthId() != Integer8BitsColorDepthID) { + cs = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Integer8BitsColorDepthID.id()); + image->convertImageColorSpace(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); + } + + int quality = configuration->getInt("quality", 50); + bool lossless = configuration->getBool("lossless", false); + + bool has_alpha = configuration->getBool(KisImportExportFilter::ImageContainsTransparencyTag, false); + + KisExifInfoVisitor exivInfoVisitor; + exivInfoVisitor.visit(image->rootLayer().data()); + + QScopedPointer metaDataStore; + if (exivInfoVisitor.metaDataCount() == 1) { + metaDataStore.reset(new KisMetaData::Store(*exivInfoVisitor.exifInfo())); + } + else { + metaDataStore.reset(new KisMetaData::Store()); + } + + if (!metaDataStore->empty()) { + { + KisMetaData::IOBackend* exifIO = KisMetaData::IOBackendRegistry::instance()->value("exif"); + QBuffer buffer; + exifIO->saveTo(metaDataStore.data(), &buffer, KisMetaData::IOBackend::NoHeader); // Or JpegHeader? Or something else? + QByteArray data = buffer.data(); + // Write the data to the file + } + { + KisMetaData::IOBackend* xmpIO = KisMetaData::IOBackendRegistry::instance()->value("xmp"); + QBuffer buffer; + xmpIO->saveTo(metaDataStore.data(), &buffer, KisMetaData::IOBackend::NoHeader); // Or JpegHeader? Or something else? + QByteArray data = buffer.data(); + // Write the data to the file + } + } + + // If we want to add information from the document to the metadata, + // we should do that here. + + try { + // --- use standard HEVC encoder + + heif::Encoder encoder(heif_compression_HEVC); + + encoder.set_lossy_quality(quality); + encoder.set_lossless(lossless); + + + // --- convert KisImage to HEIF image --- + int width = image->width(); + int height = image->height(); + + heif::Context ctx; + + heif::Image img; + img.create(width,height, heif_colorspace_RGB, heif_chroma_444); + img.add_plane(heif_channel_R, width,height, 8); + img.add_plane(heif_channel_G, width,height, 8); + img.add_plane(heif_channel_B, width,height, 8); + + uint8_t* ptrR {0}; + uint8_t* ptrG {0}; + uint8_t* ptrB {0}; + uint8_t* ptrA {0}; + int strideR,strideG,strideB,strideA; + + ptrR = img.get_plane(heif_channel_R, &strideR); + ptrG = img.get_plane(heif_channel_G, &strideG); + ptrB = img.get_plane(heif_channel_B, &strideB); + + if (has_alpha) { + img.add_plane(heif_channel_Alpha, width,height, 8); + ptrA = img.get_plane(heif_channel_Alpha, &strideA); + } + + KisPaintDeviceSP pd = image->projection(); + + for (int y=0; ycreateHLineIteratorNG(0, y, width); + + for (int x=0; x::red(it->rawData()); + ptrG[y*strideG+x] = KoBgrTraits::green(it->rawData()); + ptrB[y*strideB+x] = KoBgrTraits::blue(it->rawData()); + + if (has_alpha) { + ptrA[y*strideA+x] = cs->opacityU8(it->rawData()); + } + + it->nextPixel(); + } + } + + // --- encode and write image + + ctx.encode_image(img, encoder); + + Writer_QIODevice writer(io); + + ctx.write(writer); + } + catch (heif::Error err) { + return setHeifError(document, err); + } + + return KisImportExportFilter::OK; +} + +void HeifExport::initializeCapabilities() +{ + // This checks before saving for what the file format supports: anything that is supported needs to be mentioned here + + QList > supportedColorModels; + supportedColorModels << QPair() + << QPair(RGBAColorModelID, Integer8BitsColorDepthID) + /*<< QPair(GrayAColorModelID, Integer8BitsColorDepthID) + << QPair(RGBAColorModelID, Integer16BitsColorDepthID) + << QPair(GrayAColorModelID, Integer16BitsColorDepthID)*/ + ; + addSupportedColorModels(supportedColorModels, "HEIF"); +} + + +void KisWdgOptionsHeif::setConfiguration(const KisPropertiesConfigurationSP cfg) +{ + chkLossless->setChecked(cfg->getBool("lossless", true)); + sliderQuality->setValue(cfg->getInt("quality", 50)); +} + +KisPropertiesConfigurationSP KisWdgOptionsHeif::configuration() const +{ + KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration(); + cfg->setProperty("lossless", chkLossless->isChecked()); + cfg->setProperty("quality", sliderQuality->value()); + return cfg; +} + +void KisWdgOptionsHeif::toggleQualitySlider(bool toggle) +{ + // Disable the quality slider if lossless is true + sliderQuality->setEnabled(!toggle); +} + +#include diff --git a/plugins/impex/heif/HeifExport.h b/plugins/impex/heif/HeifExport.h new file mode 100644 index 0000000000..3553936dae --- /dev/null +++ b/plugins/impex/heif/HeifExport.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018 Dirk Farin + * + * 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 HEIF_EXPORT_H_ +#define HEIF_EXPORT_H_ + +#include + +#include +#include +#include "ui_WdgHeifExport.h" + +class KisWdgOptionsHeif : public KisConfigWidget, public Ui::WdgHeifExport +{ + Q_OBJECT + +public: + KisWdgOptionsHeif(QWidget *parent) + : KisConfigWidget(parent) + { + setupUi(this); + connect(chkLossless, SIGNAL(toggled(bool)), SLOT(toggleQualitySlider(bool))); + } + + void setConfiguration(const KisPropertiesConfigurationSP cfg) override; + KisPropertiesConfigurationSP configuration() const override; + +private Q_SLOTS: + + void toggleQualitySlider(bool toggle); + +}; + + +class HeifExport : public KisImportExportFilter +{ + Q_OBJECT +public: + HeifExport(QObject *parent, const QVariantList &); + ~HeifExport() override; + + // This should return true if the library can work with a QIODevice, and doesn't want to open the file by itself + bool supportsIO() const override { return true; } + + KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0) override; + KisPropertiesConfigurationSP defaultConfiguration(const QByteArray& from = "", const QByteArray& to = "") const override; + KisConfigWidget *createConfigurationWidget(QWidget *parent, const QByteArray& from = "", const QByteArray& to = "") const override; + void initializeCapabilities() override; + +}; + +#endif diff --git a/plugins/impex/heif/HeifImport.cpp b/plugins/impex/heif/HeifImport.cpp new file mode 100644 index 0000000000..9dc91dee98 --- /dev/null +++ b/plugins/impex/heif/HeifImport.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2018 Dirk Farin + * + * 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 "HeifImport.h" +#include "HeifError.h" + +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "kis_iterator_ng.h" + + +#include "libheif/heif_cxx.h" + + +K_PLUGIN_FACTORY_WITH_JSON(ImportFactory, "krita_heif_import.json", registerPlugin();) + +HeifImport::HeifImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent) +{ +} + +HeifImport::~HeifImport() +{ +} + + +KisImportExportFilter::ConversionStatus HeifImport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/) +{ + // Load the file into memory and decode from there + // TODO: There will be a loader-API in libheif that we should use to connect to QIODevice + + qint64 fileLength = io->bytesAvailable(); + char* mem = new char[fileLength]; + io->read(mem, fileLength); + + try { + heif::Context ctx; + ctx.read_from_memory(mem, fileLength); + + + // decode primary image + + heif::ImageHandle handle = ctx.get_primary_image_handle(); + heif::Image heifimage = handle.decode_image(heif_colorspace_RGB, heif_chroma_444); + + int width =handle.get_width(); + int height=handle.get_height(); + bool hasAlpha = handle.has_alpha_channel(); + + + // convert HEIF image to Krita KisDocument + + int strideR, strideG, strideB, strideA; + const uint8_t* imgR = heifimage.get_plane(heif_channel_R, &strideR); + const uint8_t* imgG = heifimage.get_plane(heif_channel_G, &strideG); + const uint8_t* imgB = heifimage.get_plane(heif_channel_B, &strideB); + const uint8_t* imgA = heifimage.get_plane(heif_channel_Alpha, &strideA); + + const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8(); + KisImageSP image = new KisImage(document->createUndoStore(), width, height, colorSpace, + "HEIF image"); + + KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), 255); + + for (int y=0;ypaintDevice()->createHLineIteratorNG(0, y, width); + + for (int x=0;x::setRed(it->rawData(), imgR[y*strideR+x]); + KoBgrTraits::setGreen(it->rawData(), imgG[y*strideG+x]); + KoBgrTraits::setBlue(it->rawData(), imgB[y*strideB+x]); + + if (hasAlpha) { + colorSpace->setOpacity(it->rawData(), quint8(imgA[y*strideA+x]), 1); + } + else { + colorSpace->setOpacity(it->rawData(), OPACITY_OPAQUE_U8, 1); + } + + it->nextPixel(); + } + } + + image->addNode(layer.data(), image->rootLayer().data()); + + + + // --- Iterate through all metadata blocks and extract Exif and XMP metadata --- + + std::vector metadata_IDs = handle.get_list_of_metadata_block_IDs(); + + for (heif_item_id id : metadata_IDs) { + + if (handle.get_metadata_type(id) == "Exif") { + // Read exif information + + std::vector exif_data = handle.get_metadata(id); + + if (exif_data.size()>4) { + uint32_t skip = ((exif_data[0]<<24) | (exif_data[1]<<16) | (exif_data[2]<<8) | exif_data[3]) + 4; + + if (exif_data.size()>skip) { + KisMetaData::IOBackend* exifIO = KisMetaData::IOBackendRegistry::instance()->value("exif"); + + // Copy the exif data into the byte array + QByteArray ba; + ba.append((char*)(exif_data.data()+skip), exif_data.size()-skip); + QBuffer buf(&ba); + exifIO->loadFrom(layer->metaData(), &buf); + } + } + } + + if (handle.get_metadata_type(id) == "mime" && + handle.get_metadata_content_type(id) == "application/rdf+xml") { + // Read XMP information + + std::vector xmp_data = handle.get_metadata(id); + + KisMetaData::IOBackend* xmpIO = KisMetaData::IOBackendRegistry::instance()->value("xmp"); + + // Copy the xmp data into the byte array + QByteArray ba; + ba.append((char*)(xmp_data.data()), xmp_data.size()); + + QBuffer buf(&ba); + xmpIO->loadFrom(layer->metaData(), &buf); + } + } + + delete[] mem; + + + document->setCurrentImage(image); + return KisImportExportFilter::OK; + } + catch (heif::Error err) { + delete[] mem; + + return setHeifError(document, err); + } +} + +#include diff --git a/libs/ui/dialogs/kis_about_application.h b/plugins/impex/heif/HeifImport.h similarity index 54% copy from libs/ui/dialogs/kis_about_application.h copy to plugins/impex/heif/HeifImport.h index 23a94d2dcb..6f162eeba6 100644 --- a/libs/ui/dialogs/kis_about_application.h +++ b/plugins/impex/heif/HeifImport.h @@ -1,35 +1,37 @@ /* - * Copyright (c) 2014 Boudewijn Rempt + * Copyright (c) 2018 Dirk Farin * * 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. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ -#ifndef KIS_ABOUT_APPLICATION_H -#define KIS_ABOUT_APPLICATION_H -#include +#ifndef HEIF_IMPORT_H_ +#define HEIF_IMPORT_H_ -class KisAboutApplication : public QDialog +#include + +#include + +class HeifImport : public KisImportExportFilter { Q_OBJECT public: - explicit KisAboutApplication(QWidget *parent = 0); - -Q_SIGNALS: - -public Q_SLOTS: - + HeifImport(QObject *parent, const QVariantList &); + ~HeifImport() override; + bool supportsIO() const override { return true; } + KisImportExportFilter::ConversionStatus convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0) override; }; -#endif // KIS_ABOUT_APPLICATION_H +#endif diff --git a/plugins/impex/heif/WdgHeifExport.ui b/plugins/impex/heif/WdgHeifExport.ui new file mode 100644 index 0000000000..4540c0f643 --- /dev/null +++ b/plugins/impex/heif/WdgHeifExport.ui @@ -0,0 +1,73 @@ + + + WdgHeifExport + + + + 0 + 0 + 400 + 243 + + + + + + + + 0 + 0 + + + + This option will merge all layers. It is advisable to check this option, otherwise other applications might not be able to read your file correctly. + + + &Lossless + + + false + + + + + + + &Quality: + + + sliderQuality + + + + + + + 100 + + + 100 + + + Qt::Horizontal + + + + + + + Qt::Vertical + + + + 20 + 200 + + + + + + + + + diff --git a/plugins/impex/heif/krita_heif.desktop b/plugins/impex/heif/krita_heif.desktop new file mode 100644 index 0000000000..e56711a12f --- /dev/null +++ b/plugins/impex/heif/krita_heif.desktop @@ -0,0 +1,124 @@ +[Desktop Entry] +Categories=Qt;KDE;Office;Graphics; +Exec=krita %u +GenericName=Application for Drawing and Handling of Images +GenericName[ar]=تطبيق لرسم الصّور والتّعامل معها +GenericName[bg]=Приложение за рисуване и обработка на изображения +GenericName[bs]=Aplikacija za crtanje i upravljanje slikom +GenericName[ca]=Aplicació per a dibuix i modificació d'imatges +GenericName[ca@valencia]=Aplicació per a dibuix i modificació d'imatges +GenericName[da]=Tegne- og billedbehandlingsprogram +GenericName[de]=Programm zum Zeichnen und Bearbeiten von Bildern +GenericName[el]=Εφαρμογή για επεξεργασία και χειρισμό εικόνων +GenericName[en_GB]=Application for Drawing and Handling of Images +GenericName[eo]=Aplikaĵo por Desegnado kaj Mastrumado de Bildoj +GenericName[es]=Aplicación para dibujo y manipulación de imágenes +GenericName[et]=Joonistamise ja pilditöötluse rakendus +GenericName[eu]=Irudiak marrazteko eta manipulatzeko aplikazioa +GenericName[fa]=کاربرد برای ترسیم و به کار بردن تصاویر +GenericName[fi]=Ohjelma kuvien piirtämiseen ja käsittelyyn +GenericName[fr]=Application pour dessiner et manipuler des images +GenericName[fy]=Aplikaasje om ôfbyldings mei te tekenjen en te bewurkjen +GenericName[ga]=Feidhmchlár le haghaidh Líníochta agus Láimhseála Íomhánna +GenericName[gl]=Aplicativo de debuxo e edición de imaxes +GenericName[he]=יישום לצביעה וניהול תמונות +GenericName[hi]=छवियों को ड्रा करने तथा उन्हें प्रबन्धित करने का अनुप्रयोग +GenericName[hne]=फोटू मन ल ड्रा करे अउ ओ मन ल प्रबन्धित करे के अनुपरयोग +GenericName[hu]=Rajzoló és képkezelő +GenericName[is]=Teikni og myndvinnsluforrit +GenericName[it]=Applicazione di disegno e gestione di immagini +GenericName[ja]=描画と画像操作のためのアプリケーション +GenericName[kk]=Кескінді салу және өңдеу бағдарламасы +GenericName[ko]=그림 그리기 및 처리 프로그램 +GenericName[lv]=Programma zīmēšanai un attēlu apstrādei +GenericName[nb]=Program for tegning og bildehåndtering +GenericName[nds]=Programm för't Teken un Bildhanteren +GenericName[ne]=रेखाचित्र बनाउन र छविको ह्यान्डल गर्नका लागि अनुप्रयोग +GenericName[nl]=Toepassing om afbeeldingen te tekenen en te bewerken +GenericName[pl]=Program do rysowania i obróbki obrazów +GenericName[pt]=Aplicação de Desenho e Manipulação de Imagens +GenericName[pt_BR]=Aplicativo de desenho e manipulação de imagens +GenericName[ru]=Приложение для рисования и редактирования изображений +GenericName[sk]=Aplikácia na kresnenie a manilupáciu s obrázkami +GenericName[sl]=Program za risanje in rokovanje s slikami +GenericName[sv]=Program för att rita och hantera bilder +GenericName[ta]=பிம்பங்களை கையாளுதல் மற்றும் வரைதலுக்கான பயன்னாடு +GenericName[tr]=Çizim ve Resim İşleme Uygulaması +GenericName[uk]=Програма для малювання і обробки зображень +GenericName[uz]=Rasm chizish dasturi +GenericName[uz@cyrillic]=Расм чизиш дастури +GenericName[wa]=Programe po dessiner et apougnî des imådjes +GenericName[x-test]=xxApplication for Drawing and Handling of Imagesxx +GenericName[zh_CN]=绘制和处理图像的应用程序 +GenericName[zh_TW]=繪圖與影像處理的應用程式 +Icon=calligrakrita +MimeType=image/heic; +Name=Krita +Name[af]=Krita +Name[ar]=كريتا +Name[bg]=Krita +Name[br]=Krita +Name[bs]=Krita +Name[ca]=Krita +Name[ca@valencia]=Krita +Name[cs]=Krita +Name[cy]=Krita +Name[da]=Krita +Name[de]=Krita +Name[el]=Krita +Name[en_GB]=Krita +Name[eo]=Krita +Name[es]=Krita +Name[et]=Krita +Name[eu]=Krita +Name[fi]=Krita +Name[fr]=Krita +Name[fy]=Krita +Name[ga]=Krita +Name[gl]=Krita +Name[he]=Krita +Name[hi]=केरिता +Name[hne]=केरिता +Name[hr]=Krita +Name[hu]=Krita +Name[ia]=Krita +Name[is]=Krita +Name[it]=Krita +Name[ja]=Krita +Name[kk]=Krita +Name[ko]=Krita +Name[lt]=Krita +Name[lv]=Krita +Name[mr]=क्रिटा +Name[ms]=Krita +Name[nb]=Krita +Name[nds]=Krita +Name[ne]=क्रिता +Name[nl]=Krita +Name[pl]=Krita +Name[pt]=Krita +Name[pt_BR]=Krita +Name[ro]=Krita +Name[ru]=Krita +Name[se]=Krita +Name[sk]=Krita +Name[sl]=Krita +Name[sv]=Krita +Name[ta]=கிரிட்டா +Name[tg]=Krita +Name[tr]=Krita +Name[ug]=Krita +Name[uk]=Krita +Name[uz]=Krita +Name[uz@cyrillic]=Krita +Name[wa]=Krita +Name[xh]=Krita +Name[x-test]=xxKritaxx +Name[zh_CN]=Krita +Name[zh_TW]=Krita +StartupNotify=true +Terminal=false +Type=Application +X-KDE-SubstituteUID=false +X-KDE-Username= +NoDisplay=true diff --git a/plugins/impex/heif/krita_heif_export.json b/plugins/impex/heif/krita_heif_export.json new file mode 100644 index 0000000000..35ec747a0d --- /dev/null +++ b/plugins/impex/heif/krita_heif_export.json @@ -0,0 +1,12 @@ +{ + "Icon": "", + "Id": "Krita HEIF Export Filter", + "Type": "Service", + "X-KDE-Export": "image/heic", + "X-KDE-Library": "kritaexrexport", + "X-KDE-ServiceTypes": [ + "Krita/FileFilter" + ], + "X-KDE-Weight": "1", + "X-KDE-Extensions" : "heif,heic" +} diff --git a/plugins/impex/heif/krita_heif_import.json b/plugins/impex/heif/krita_heif_import.json new file mode 100644 index 0000000000..71367d5ea4 --- /dev/null +++ b/plugins/impex/heif/krita_heif_import.json @@ -0,0 +1,12 @@ +{ + "Icon": "", + "Id": "Krita HEIF Import Filter", + "Type": "Service", + "X-KDE-Import": "image/heic", + "X-KDE-Library": "kritaheifimport", + "X-KDE-ServiceTypes": [ + "Krita/FileFilter" + ], + "X-KDE-Weight": "1", + "X-KDE-Extensions" : "heif,heic" +} diff --git a/plugins/impex/jpeg/kis_jpeg_converter.cc b/plugins/impex/jpeg/kis_jpeg_converter.cc index 6435bf0d4f..778bf063db 100644 --- a/plugins/impex/jpeg/kis_jpeg_converter.cc +++ b/plugins/impex/jpeg/kis_jpeg_converter.cc @@ -1,732 +1,734 @@ /* * Copyright (c) 2005 Cyrille Berger * * 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_jpeg_converter.h" #include #include #include #ifdef HAVE_LCMS2 # include #else # include #endif extern "C" { #include } #include #include #include #include #include #include #include #include #include #include #include #include #include "KoColorModelStandardIds.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_iterator_ng.h" #include #define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */ #define ICC_OVERHEAD_LEN 14 /* size of non-profile data in APP2 */ #define MAX_BYTES_IN_MARKER 65533 /* maximum data len of a JPEG marker */ #define MAX_DATA_BYTES_IN_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN) const char photoshopMarker[] = "Photoshop 3.0\0"; //const char photoshopBimId_[] = "8BIM"; const uint16_t photoshopIptc = 0x0404; const char xmpMarker[] = "http://ns.adobe.com/xap/1.0/\0"; const QByteArray photoshopIptc_((char*)&photoshopIptc, 2); namespace { void jpegErrorExit ( j_common_ptr cinfo ) { char jpegLastErrorMsg[JMSG_LENGTH_MAX]; /* Create the message */ ( *( cinfo->err->format_message ) ) ( cinfo, jpegLastErrorMsg ); /* Jump to the setjmp point */ throw std::runtime_error( jpegLastErrorMsg ); // or your preffered exception ... } J_COLOR_SPACE getColorTypeforColorSpace(const KoColorSpace * cs) { if (KoID(cs->id()) == KoID("GRAYA") || cs->id() == "GRAYAU16" || cs->id() == "GRAYA16") { return JCS_GRAYSCALE; } if (KoID(cs->id()) == KoID("RGBA") || KoID(cs->id()) == KoID("RGBA16")) { return JCS_RGB; } if (KoID(cs->id()) == KoID("CMYK") || KoID(cs->id()) == KoID("CMYKAU16")) { return JCS_CMYK; } return JCS_UNKNOWN; } QString getColorSpaceModelForColorType(J_COLOR_SPACE color_type) { dbgFile << "color_type =" << color_type; if (color_type == JCS_GRAYSCALE) { return GrayAColorModelID.id(); } else if (color_type == JCS_RGB) { return RGBAColorModelID.id(); } else if (color_type == JCS_CMYK) { return CMYKAColorModelID.id(); } return ""; } } struct KisJPEGConverter::Private { Private(KisDocument *doc, bool batchMode) : doc(doc), stop(false), batchMode(batchMode) {} KisImageSP image; KisDocument *doc; bool stop; bool batchMode; }; KisJPEGConverter::KisJPEGConverter(KisDocument *doc, bool batchMode) : m_d(new Private(doc, batchMode)) { } KisJPEGConverter::~KisJPEGConverter() { } KisImageBuilder_Result KisJPEGConverter::decode(QIODevice *io) { struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; cinfo.err = jpeg_std_error(&jerr); jerr.error_exit = jpegErrorExit; try { jpeg_create_decompress(&cinfo); KisJPEGSource::setSource(&cinfo, io); jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF); /* Save APP0..APP15 markers */ for (int m = 0; m < 16; m++) jpeg_save_markers(&cinfo, JPEG_APP0 + m, 0xFFFF); // setup_read_icc_profile(&cinfo); // read header jpeg_read_header(&cinfo, (boolean)true); // start reading jpeg_start_decompress(&cinfo); // Get the colorspace QString modelId = getColorSpaceModelForColorType(cinfo.out_color_space); if (modelId.isEmpty()) { dbgFile << "unsupported colorspace :" << cinfo.out_color_space; jpeg_destroy_decompress(&cinfo); return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; } uchar* profile_data; uint profile_len; const KoColorProfile* profile = 0; QByteArray profile_rawdata; if (read_icc_profile(&cinfo, &profile_data, &profile_len)) { profile_rawdata.resize(profile_len); memcpy(profile_rawdata.data(), profile_data, profile_len); cmsHPROFILE hProfile = cmsOpenProfileFromMem(profile_data, profile_len); if (hProfile != (cmsHPROFILE) 0) { profile = KoColorSpaceRegistry::instance()->createColorProfile(modelId, Integer8BitsColorDepthID.id(), profile_rawdata); Q_CHECK_PTR(profile); dbgFile <<"profile name:" << profile->name() <<" product information:" << profile->info(); if (!profile->isSuitableForOutput()) { dbgFile << "the profile is not suitable for output and therefore cannot be used in krita, we need to convert the image to a standard profile"; // TODO: in ko2 popup a selection menu to inform the user } } } const QString colorSpaceId = KoColorSpaceRegistry::instance()->colorSpaceId(modelId, Integer8BitsColorDepthID.id()); // Check that the profile is used by the color space if (profile && !KoColorSpaceRegistry::instance()->profileIsCompatible(profile, colorSpaceId)) { warnFile << "The profile " << profile->name() << " is not compatible with the color space model " << modelId; profile = 0; } // Retrieve a pointer to the colorspace const KoColorSpace* cs; if (profile && profile->isSuitableForOutput()) { dbgFile << "image has embedded profile:" << profile -> name() << ""; cs = KoColorSpaceRegistry::instance()->colorSpace(modelId, Integer8BitsColorDepthID.id(), profile); } else cs = KoColorSpaceRegistry::instance()->colorSpace(modelId, Integer8BitsColorDepthID.id(), ""); if (cs == 0) { dbgFile << "unknown colorspace"; jpeg_destroy_decompress(&cinfo); return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; } // TODO fixit // Create the cmsTransform if needed KoColorTransformation* transform = 0; if (profile && !profile->isSuitableForOutput()) { transform = KoColorSpaceRegistry::instance()->colorSpace(modelId, Integer8BitsColorDepthID.id(), profile)->createColorConverter(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); } // Apparently an invalid transform was created from the profile. See bug https://bugs.kde.org/show_bug.cgi?id=255451. // After 2.3: warn the user! if (transform && !transform->isValid()) { delete transform; transform = 0; } // Creating the KisImageSP if (!m_d->image) { m_d->image = new KisImage(m_d->doc->createUndoStore(), cinfo.image_width, cinfo.image_height, cs, "built image"); Q_CHECK_PTR(m_d->image); } // Set resolution double xres = 72, yres = 72; if (cinfo.density_unit == 1) { xres = cinfo.X_density; yres = cinfo.Y_density; } else if (cinfo.density_unit == 2) { xres = cinfo.X_density * 2.54; yres = cinfo.Y_density * 2.54; } if (xres < 72) { xres = 72; } if (yres < 72) { yres = 72; } m_d->image->setResolution(POINT_TO_INCH(xres), POINT_TO_INCH(yres)); // It is the "invert" macro because we convert from pointer-per-inchs to points // Create layer KisPaintLayerSP layer = KisPaintLayerSP(new KisPaintLayer(m_d->image.data(), m_d->image -> nextLayerName(), quint8_MAX)); // Read data JSAMPROW row_pointer = new JSAMPLE[cinfo.image_width*cinfo.num_components]; for (; cinfo.output_scanline < cinfo.image_height;) { KisHLineIteratorSP it = layer->paintDevice()->createHLineIteratorNG(0, cinfo.output_scanline, cinfo.image_width); jpeg_read_scanlines(&cinfo, &row_pointer, 1); quint8 *src = row_pointer; switch (cinfo.out_color_space) { case JCS_GRAYSCALE: do { quint8 *d = it->rawData(); d[0] = *(src++); if (transform) transform->transform(d, d, 1); d[1] = quint8_MAX; } while (it->nextPixel()); break; case JCS_RGB: do { quint8 *d = it->rawData(); d[2] = *(src++); d[1] = *(src++); d[0] = *(src++); if (transform) transform->transform(d, d, 1); d[3] = quint8_MAX; } while (it->nextPixel()); break; case JCS_CMYK: do { quint8 *d = it->rawData(); d[0] = quint8_MAX - *(src++); d[1] = quint8_MAX - *(src++); d[2] = quint8_MAX - *(src++); d[3] = quint8_MAX - *(src++); if (transform) transform->transform(d, d, 1); d[4] = quint8_MAX; } while (it->nextPixel()); break; default: return KisImageBuilder_RESULT_UNSUPPORTED; } } m_d->image->addNode(KisNodeSP(layer.data()), m_d->image->rootLayer().data()); // Read exif information dbgFile << "Looking for exif information"; for (jpeg_saved_marker_ptr marker = cinfo.marker_list; marker != 0; marker = marker->next) { dbgFile << "Marker is" << marker->marker; if (marker->marker != (JOCTET)(JPEG_APP0 + 1) || marker->data_length < 14) { continue; /* Exif data is in an APP1 marker of at least 14 octets */ } if (GETJOCTET(marker->data[0]) != (JOCTET) 0x45 || GETJOCTET(marker->data[1]) != (JOCTET) 0x78 || GETJOCTET(marker->data[2]) != (JOCTET) 0x69 || GETJOCTET(marker->data[3]) != (JOCTET) 0x66 || GETJOCTET(marker->data[4]) != (JOCTET) 0x00 || GETJOCTET(marker->data[5]) != (JOCTET) 0x00) continue; /* no Exif header */ + dbgFile << "Found exif information of length :" << marker->data_length; KisMetaData::IOBackend* exifIO = KisMetaData::IOBackendRegistry::instance()->value("exif"); Q_ASSERT(exifIO); QByteArray byteArray((const char*)marker->data + 6, marker->data_length - 6); QBuffer buf(&byteArray); exifIO->loadFrom(layer->metaData(), &buf); // Interpret orientation tag if (layer->metaData()->containsEntry("http://ns.adobe.com/tiff/1.0/", "Orientation")) { KisMetaData::Entry& entry = layer->metaData()->getEntry("http://ns.adobe.com/tiff/1.0/", "Orientation"); if (entry.value().type() == KisMetaData::Value::Variant) { switch (entry.value().asVariant().toInt()) { case 2: KisTransformWorker::mirrorY(layer->paintDevice()); break; case 3: image()->rotateImage(M_PI); break; case 4: KisTransformWorker::mirrorX(layer->paintDevice()); break; case 5: image()->rotateImage(M_PI / 2); KisTransformWorker::mirrorY(layer->paintDevice()); break; case 6: image()->rotateImage(M_PI / 2); break; case 7: image()->rotateImage(M_PI / 2); KisTransformWorker::mirrorX(layer->paintDevice()); break; case 8: image()->rotateImage(-M_PI / 2 + M_PI*2); break; default: break; } } entry.value().setVariant(1); } break; } dbgFile << "Looking for IPTC information"; for (jpeg_saved_marker_ptr marker = cinfo.marker_list; marker != 0; marker = marker->next) { dbgFile << "Marker is" << marker->marker; if (marker->marker != (JOCTET)(JPEG_APP0 + 13) || marker->data_length < 14) { continue; /* IPTC data is in an APP13 marker of at least 16 octets */ } if (memcmp(marker->data, photoshopMarker, 14) != 0) { for (int i = 0; i < 14; i++) { dbgFile << (int)(*(marker->data + i)) << "" << (int)(photoshopMarker[i]); } dbgFile << "No photoshop marker"; continue; /* No IPTC Header */ } dbgFile << "Found Photoshop information of length :" << marker->data_length; KisMetaData::IOBackend* iptcIO = KisMetaData::IOBackendRegistry::instance()->value("iptc"); Q_ASSERT(iptcIO); const Exiv2::byte *record = 0; uint32_t sizeIptc = 0; uint32_t sizeHdr = 0; // Find actual Iptc data within the APP13 segment if (!Exiv2::Photoshop::locateIptcIrb((Exiv2::byte*)(marker->data + 14), marker->data_length - 14, &record, &sizeHdr, &sizeIptc)) { if (sizeIptc) { // Decode the IPTC data QByteArray byteArray((const char*)(record + sizeHdr), sizeIptc); - iptcIO->loadFrom(layer->metaData(), new QBuffer(&byteArray)); + QBuffer buf(&byteArray); + iptcIO->loadFrom(layer->metaData(), &buf); } else { dbgFile << "IPTC Not found in Photoshop marker"; } } break; } dbgFile << "Looking for XMP information"; for (jpeg_saved_marker_ptr marker = cinfo.marker_list; marker != 0; marker = marker->next) { dbgFile << "Marker is" << marker->marker; if (marker->marker != (JOCTET)(JPEG_APP0 + 1) || marker->data_length < 31) { continue; /* XMP data is in an APP1 marker of at least 31 octets */ } if (memcmp(marker->data, xmpMarker, 29) != 0) { dbgFile << "Not XMP marker"; continue; /* No xmp Header */ } dbgFile << "Found XMP Marker of length " << marker->data_length; QByteArray byteArray((const char*)marker->data + 29, marker->data_length - 29); KisMetaData::IOBackend* xmpIO = KisMetaData::IOBackendRegistry::instance()->value("xmp"); Q_ASSERT(xmpIO); xmpIO->loadFrom(layer->metaData(), new QBuffer(&byteArray)); break; } // Dump loaded metadata layer->metaData()->debugDump(); // Check whether the metadata has resolution info, too... if (cinfo.density_unit == 0 && layer->metaData()->containsEntry("tiff:XResolution") && layer->metaData()->containsEntry("tiff:YResolution")) { double xres = layer->metaData()->getEntry("tiff:XResolution").value().asDouble(); double yres = layer->metaData()->getEntry("tiff:YResolution").value().asDouble(); if (xres != 0 && yres != 0) { m_d->image->setResolution(POINT_TO_INCH(xres), POINT_TO_INCH(yres)); // It is the "invert" macro because we convert from pointer-per-inchs to points } } // Finish decompression jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); delete [] row_pointer; return KisImageBuilder_RESULT_OK; } catch( std::runtime_error &e) { jpeg_destroy_decompress(&cinfo); return KisImageBuilder_RESULT_FAILURE; } } KisImageBuilder_Result KisJPEGConverter::buildImage(QIODevice *io) { return decode(io); } KisImageSP KisJPEGConverter::image() { return m_d->image; } KisImageBuilder_Result KisJPEGConverter::buildFile(QIODevice *io, KisPaintLayerSP layer, KisJPEGOptions options, KisMetaData::Store* metaData) { if (!layer) return KisImageBuilder_RESULT_INVALID_ARG; KisImageSP image = KisImageSP(layer->image()); if (!image) return KisImageBuilder_RESULT_EMPTY; const KoColorSpace * cs = layer->colorSpace(); J_COLOR_SPACE color_type = getColorTypeforColorSpace(cs); if (color_type == JCS_UNKNOWN) { KUndo2Command *tmp = layer->paintDevice()->convertTo(KoColorSpaceRegistry::instance()->rgb8(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); delete tmp; cs = KoColorSpaceRegistry::instance()->rgb8(); color_type = JCS_RGB; } if (options.forceSRGB) { const KoColorSpace* dst = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), layer->colorSpace()->colorDepthId().id(), "sRGB built-in - (lcms internal)"); KUndo2Command *tmp = layer->paintDevice()->convertTo(dst); delete tmp; cs = dst; color_type = JCS_RGB; } uint height = image->height(); uint width = image->width(); // Initialize structure struct jpeg_compress_struct cinfo; // Initialize error output struct jpeg_error_mgr jerr; cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); // Initialize output stream KisJPEGDestination::setDestination(&cinfo, io); cinfo.image_width = width; // image width and height, in pixels cinfo.image_height = height; cinfo.input_components = cs->colorChannelCount(); // number of color channels per pixel */ cinfo.in_color_space = color_type; // colorspace of input image // Set default compression parameters jpeg_set_defaults(&cinfo); // Customize them jpeg_set_quality(&cinfo, options.quality, (boolean)options.baseLineJPEG); if (options.progressive) { jpeg_simple_progression(&cinfo); } // Optimize ? cinfo.optimize_coding = (boolean)options.optimize; // Smoothing cinfo.smoothing_factor = (boolean)options.smooth; // Subsampling switch (options.subsampling) { default: case 0: { cinfo.comp_info[0].h_samp_factor = 2; cinfo.comp_info[0].v_samp_factor = 2; cinfo.comp_info[1].h_samp_factor = 1; cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; cinfo.comp_info[2].v_samp_factor = 1; } break; case 1: { cinfo.comp_info[0].h_samp_factor = 2; cinfo.comp_info[0].v_samp_factor = 1; cinfo.comp_info[1].h_samp_factor = 1; cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; cinfo.comp_info[2].v_samp_factor = 1; } break; case 2: { cinfo.comp_info[0].h_samp_factor = 1; cinfo.comp_info[0].v_samp_factor = 2; cinfo.comp_info[1].h_samp_factor = 1; cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; cinfo.comp_info[2].v_samp_factor = 1; } break; case 3: { cinfo.comp_info[0].h_samp_factor = 1; cinfo.comp_info[0].v_samp_factor = 1; cinfo.comp_info[1].h_samp_factor = 1; cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; cinfo.comp_info[2].v_samp_factor = 1; } break; } // Save resolution cinfo.X_density = INCH_TO_POINT(image->xRes()); // It is the "invert" macro because we convert from pointer-per-inchs to points cinfo.Y_density = INCH_TO_POINT(image->yRes()); // It is the "invert" macro because we convert from pointer-per-inchs to points cinfo.density_unit = 1; cinfo.write_JFIF_header = (boolean)true; // Start compression jpeg_start_compress(&cinfo, (boolean)true); // Save exif and iptc information if any available if (metaData && !metaData->empty()) { metaData->applyFilters(options.filters); // Save EXIF if (options.exif) { dbgFile << "Trying to save exif information"; KisMetaData::IOBackend* exifIO = KisMetaData::IOBackendRegistry::instance()->value("exif"); Q_ASSERT(exifIO); QBuffer buffer; exifIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::JpegHeader); dbgFile << "Exif information size is" << buffer.data().size(); QByteArray data = buffer.data(); if (data.size() < MAX_DATA_BYTES_IN_MARKER) { jpeg_write_marker(&cinfo, JPEG_APP0 + 1, (const JOCTET*)data.data(), data.size()); } else { dbgFile << "EXIF information could not be saved."; // TODO: warn the user ? } } // Save IPTC if (options.iptc) { dbgFile << "Trying to save exif information"; KisMetaData::IOBackend* iptcIO = KisMetaData::IOBackendRegistry::instance()->value("iptc"); Q_ASSERT(iptcIO); QBuffer buffer; iptcIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::JpegHeader); dbgFile << "IPTC information size is" << buffer.data().size(); QByteArray data = buffer.data(); if (data.size() < MAX_DATA_BYTES_IN_MARKER) { jpeg_write_marker(&cinfo, JPEG_APP0 + 13, (const JOCTET*)data.data(), data.size()); } else { dbgFile << "IPTC information could not be saved."; // TODO: warn the user ? } } // Save XMP if (options.xmp) { dbgFile << "Trying to save XMP information"; KisMetaData::IOBackend* xmpIO = KisMetaData::IOBackendRegistry::instance()->value("xmp"); Q_ASSERT(xmpIO); QBuffer buffer; xmpIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::JpegHeader); dbgFile << "XMP information size is" << buffer.data().size(); QByteArray data = buffer.data(); if (data.size() < MAX_DATA_BYTES_IN_MARKER) { jpeg_write_marker(&cinfo, JPEG_APP0 + 14, (const JOCTET*)data.data(), data.size()); } else { dbgFile << "XMP information could not be saved."; // TODO: warn the user ? } } } KisPaintDeviceSP dev = new KisPaintDevice(layer->colorSpace()); KoColor c(options.transparencyFillColor, layer->colorSpace()); dev->fill(QRect(0, 0, width, height), c); KisPainter gc(dev); gc.bitBlt(QPoint(0, 0), layer->paintDevice(), QRect(0, 0, width, height)); gc.end(); if (options.saveProfile) { const KoColorProfile* colorProfile = layer->colorSpace()->profile(); QByteArray colorProfileData = colorProfile->rawData(); write_icc_profile(& cinfo, (uchar*) colorProfileData.data(), colorProfileData.size()); } // Write data information JSAMPROW row_pointer = new JSAMPLE[width*cinfo.input_components]; int color_nb_bits = 8 * layer->paintDevice()->pixelSize() / layer->paintDevice()->channelCount(); for (; cinfo.next_scanline < height;) { KisHLineConstIteratorSP it = dev->createHLineConstIteratorNG(0, cinfo.next_scanline, width); quint8 *dst = row_pointer; switch (color_type) { case JCS_GRAYSCALE: if (color_nb_bits == 16) { do { //const quint16 *d = reinterpret_cast(it->oldRawData()); const quint8 *d = it->oldRawData(); *(dst++) = cs->scaleToU8(d, 0);//d[0] / quint8_MAX; } while (it->nextPixel()); } else { do { const quint8 *d = it->oldRawData(); *(dst++) = d[0]; } while (it->nextPixel()); } break; case JCS_RGB: if (color_nb_bits == 16) { do { //const quint16 *d = reinterpret_cast(it->oldRawData()); const quint8 *d = it->oldRawData(); *(dst++) = cs->scaleToU8(d, 2); //d[2] / quint8_MAX; *(dst++) = cs->scaleToU8(d, 1); //d[1] / quint8_MAX; *(dst++) = cs->scaleToU8(d, 0); //d[0] / quint8_MAX; } while (it->nextPixel()); } else { do { const quint8 *d = it->oldRawData(); *(dst++) = d[2]; *(dst++) = d[1]; *(dst++) = d[0]; } while (it->nextPixel()); } break; case JCS_CMYK: if (color_nb_bits == 16) { do { //const quint16 *d = reinterpret_cast(it->oldRawData()); const quint8 *d = it->oldRawData(); *(dst++) = quint8_MAX - cs->scaleToU8(d, 0);//quint8_MAX - d[0] / quint8_MAX; *(dst++) = quint8_MAX - cs->scaleToU8(d, 1);//quint8_MAX - d[1] / quint8_MAX; *(dst++) = quint8_MAX - cs->scaleToU8(d, 2);//quint8_MAX - d[2] / quint8_MAX; *(dst++) = quint8_MAX - cs->scaleToU8(d, 3);//quint8_MAX - d[3] / quint8_MAX; } while (it->nextPixel()); } else { do { const quint8 *d = it->oldRawData(); *(dst++) = quint8_MAX - d[0]; *(dst++) = quint8_MAX - d[1]; *(dst++) = quint8_MAX - d[2]; *(dst++) = quint8_MAX - d[3]; } while (it->nextPixel()); } break; default: delete [] row_pointer; jpeg_destroy_compress(&cinfo); return KisImageBuilder_RESULT_UNSUPPORTED; } jpeg_write_scanlines(&cinfo, &row_pointer, 1); } // Writing is over jpeg_finish_compress(&cinfo); delete [] row_pointer; // Free memory jpeg_destroy_compress(&cinfo); return KisImageBuilder_RESULT_OK; } void KisJPEGConverter::cancel() { m_d->stop = true; } diff --git a/plugins/impex/jpeg/kis_jpeg_export.cc b/plugins/impex/jpeg/kis_jpeg_export.cc index 8c1fc1510c..bc9abb6100 100644 --- a/plugins/impex/jpeg/kis_jpeg_export.cc +++ b/plugins/impex/jpeg/kis_jpeg_export.cc @@ -1,302 +1,297 @@ /* * Copyright (c) 2005 Cyrille Berger * * 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_jpeg_export.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 #include #include #include #include #include "kis_jpeg_converter.h" class KisExternalLayer; K_PLUGIN_FACTORY_WITH_JSON(KisJPEGExportFactory, "krita_jpeg_export.json", registerPlugin();) KisJPEGExport::KisJPEGExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent) { } KisJPEGExport::~KisJPEGExport() { } KisImportExportFilter::ConversionStatus KisJPEGExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration) { KisImageSP image = document->savingImage(); Q_CHECK_PTR(image); // An extra option to pass to the config widget to set the state correctly, this isn't saved const KoColorSpace* cs = image->projection()->colorSpace(); bool sRGB = cs->profile()->name().contains(QLatin1String("srgb"), Qt::CaseInsensitive); configuration->setProperty("is_sRGB", sRGB); KisJPEGOptions options; options.progressive = configuration->getBool("progressive", false); options.quality = configuration->getInt("quality", 80); options.forceSRGB = configuration->getBool("forceSRGB", false); options.saveProfile = configuration->getBool("saveProfile", true); options.optimize = configuration->getBool("optimize", true); options.smooth = configuration->getInt("smoothing", 0); options.baseLineJPEG = configuration->getBool("baseline", true); options.subsampling = configuration->getInt("subsampling", 0); options.exif = configuration->getBool("exif", true); options.iptc = configuration->getBool("iptc", true); options.xmp = configuration->getBool("xmp", true); KoColor c(KoColorSpaceRegistry::instance()->rgb8()); c.fromQColor(Qt::white); options.transparencyFillColor = configuration->getColor("transparencyFillcolor", c).toQColor(); KisMetaData::FilterRegistryModel m; m.setEnabledFilters(configuration->getString("filters").split(",")); options.filters = m.enabledFilters(); options.storeAuthor = configuration->getBool("storeAuthor", false); options.storeDocumentMetaData = configuration->getBool("storeMetaData", false); KisPaintDeviceSP pd = new KisPaintDevice(*image->projection()); KisJPEGConverter kpc(document, batchMode()); KisPaintLayerSP l = new KisPaintLayer(image, "projection", OPACITY_OPAQUE_U8, pd); - KisExifInfoVisitor eIV; - eIV.visit(image->rootLayer().data()); + KisExifInfoVisitor exivInfoVisitor; + exivInfoVisitor.visit(image->rootLayer().data()); - KisMetaData::Store* eI = 0; - if (eIV.countPaintLayer() == 1) { - eI = eIV.exifInfo(); + QScopedPointer metaDataStore; + if (exivInfoVisitor.metaDataCount() == 1) { + metaDataStore.reset(new KisMetaData::Store(*exivInfoVisitor.exifInfo())); } - if (eI) { - KisMetaData::Store* copy = new KisMetaData::Store(*eI); - eI = copy; - } - if (!eI) { - eI = new KisMetaData::Store(); + else { + metaDataStore.reset(new KisMetaData::Store()); } //add extra meta-data here const KisMetaData::Schema* dcSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::DublinCoreSchemaUri); Q_ASSERT(dcSchema); if (options.storeDocumentMetaData) { QString title = document->documentInfo()->aboutInfo("title"); if (!title.isEmpty()) { - if (eI->containsEntry("title")) { - eI->removeEntry("title"); + if (metaDataStore->containsEntry("title")) { + metaDataStore->removeEntry("title"); } - eI->addEntry(KisMetaData::Entry(dcSchema, "title", KisMetaData::Value(QVariant(title)))); + metaDataStore->addEntry(KisMetaData::Entry(dcSchema, "title", KisMetaData::Value(QVariant(title)))); } QString description = document->documentInfo()->aboutInfo("subject"); if (description.isEmpty()) { description = document->documentInfo()->aboutInfo("abstract"); } if (!description.isEmpty()) { QString keywords = document->documentInfo()->aboutInfo("keyword"); if (!keywords.isEmpty()) { description = description + " keywords: " + keywords; } - if (eI->containsEntry("description")) { - eI->removeEntry("description"); + if (metaDataStore->containsEntry("description")) { + metaDataStore->removeEntry("description"); } - eI->addEntry(KisMetaData::Entry(dcSchema, "description", KisMetaData::Value(QVariant(description)))); + metaDataStore->addEntry(KisMetaData::Entry(dcSchema, "description", KisMetaData::Value(QVariant(description)))); } QString license = document->documentInfo()->aboutInfo("license"); if (!license.isEmpty()) { - if (eI->containsEntry("rights")) { - eI->removeEntry("rights"); + if (metaDataStore->containsEntry("rights")) { + metaDataStore->removeEntry("rights"); } - eI->addEntry(KisMetaData::Entry(dcSchema, "rights", KisMetaData::Value(QVariant(license)))); + metaDataStore->addEntry(KisMetaData::Entry(dcSchema, "rights", KisMetaData::Value(QVariant(license)))); } QString date = document->documentInfo()->aboutInfo("date"); - if (!date.isEmpty() && !eI->containsEntry("rights")) { - eI->addEntry(KisMetaData::Entry(dcSchema, "date", KisMetaData::Value(QVariant(date)))); + if (!date.isEmpty() && !metaDataStore->containsEntry("rights")) { + metaDataStore->addEntry(KisMetaData::Entry(dcSchema, "date", KisMetaData::Value(QVariant(date)))); } } if (options.storeAuthor) { QString author = document->documentInfo()->authorInfo("creator"); if (!author.isEmpty()) { if (!document->documentInfo()->authorContactInfo().isEmpty()) { QString contact = document->documentInfo()->authorContactInfo().at(0); if (!contact.isEmpty()) { author = author+"("+contact+")"; } } - if (eI->containsEntry("creator")) { - eI->removeEntry("creator"); + if (metaDataStore->containsEntry("creator")) { + metaDataStore->removeEntry("creator"); } - eI->addEntry(KisMetaData::Entry(dcSchema, "creator", KisMetaData::Value(QVariant(author)))); + metaDataStore->addEntry(KisMetaData::Entry(dcSchema, "creator", KisMetaData::Value(QVariant(author)))); } } - KisImageBuilder_Result res = kpc.buildFile(io, l, options, eI); + KisImageBuilder_Result res = kpc.buildFile(io, l, options, metaDataStore.data()); if (res == KisImageBuilder_RESULT_OK) { - delete eI; return KisImportExportFilter::OK; } - delete eI; dbgFile << " Result =" << res; return KisImportExportFilter::InternalError; } KisPropertiesConfigurationSP KisJPEGExport::defaultConfiguration(const QByteArray &/*from*/, const QByteArray &/*to*/) const { KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration(); cfg->setProperty("progressive", false); cfg->setProperty("quality", 80); cfg->setProperty("forceSRGB", false); cfg->setProperty("saveProfile", true); cfg->setProperty("optimize", true); cfg->setProperty("smoothing", 0); cfg->setProperty("baseline", true); cfg->setProperty("subsampling", 0); cfg->setProperty("exif", true); cfg->setProperty("iptc", true); cfg->setProperty("xmp", true); cfg->setProperty("storeAuthor", false); cfg->setProperty("storeMetaData", false); KoColor fill_color(KoColorSpaceRegistry::instance()->rgb8()); fill_color = KoColor(); fill_color.fromQColor(Qt::white); QVariant v; v.setValue(fill_color); cfg->setProperty("transparencyFillcolor", v); cfg->setProperty("filters", ""); return cfg; } KisConfigWidget *KisJPEGExport::createConfigurationWidget(QWidget *parent, const QByteArray &/*from*/, const QByteArray &/*to*/) const { return new KisWdgOptionsJPEG(parent); } void KisJPEGExport::initializeCapabilities() { addCapability(KisExportCheckRegistry::instance()->get("sRGBProfileCheck")->create(KisExportCheckBase::SUPPORTED)); addCapability(KisExportCheckRegistry::instance()->get("ExifCheck")->create(KisExportCheckBase::SUPPORTED)); QList > supportedColorModels; supportedColorModels << QPair() << QPair(RGBAColorModelID, Integer8BitsColorDepthID) << QPair(GrayAColorModelID, Integer8BitsColorDepthID) << QPair(CMYKAColorModelID, Integer8BitsColorDepthID); addSupportedColorModels(supportedColorModels, "JPEG"); } KisWdgOptionsJPEG::KisWdgOptionsJPEG(QWidget *parent) : KisConfigWidget(parent) { setupUi(this); metaDataFilters->setModel(&m_filterRegistryModel); qualityLevel->setRange(0, 100, 0); qualityLevel->setSuffix("%"); smoothLevel->setRange(0, 100, 0); smoothLevel->setSuffix("%"); } void KisWdgOptionsJPEG::setConfiguration(const KisPropertiesConfigurationSP cfg) { progressive->setChecked(cfg->getBool("progressive", false)); qualityLevel->setValue(cfg->getInt("quality", 80)); optimize->setChecked(cfg->getBool("optimize", true)); smoothLevel->setValue(cfg->getInt("smoothing", 0)); baseLineJPEG->setChecked(cfg->getBool("baseline", true)); subsampling->setCurrentIndex(cfg->getInt("subsampling", 0)); exif->setChecked(cfg->getBool("exif", true)); iptc->setChecked(cfg->getBool("iptc", true)); xmp->setChecked(cfg->getBool("xmp", true)); chkForceSRGB->setVisible(cfg->getBool("is_sRGB")); chkForceSRGB->setChecked(cfg->getBool("forceSRGB", false)); chkSaveProfile->setChecked(cfg->getBool("saveProfile", true)); KoColor background(KoColorSpaceRegistry::instance()->rgb8()); background.fromQColor(Qt::white); bnTransparencyFillColor->setDefaultColor(background); bnTransparencyFillColor->setColor(cfg->getColor("transparencyFillcolor", background)); chkAuthor->setChecked(cfg->getBool("storeAuthor", false)); chkMetaData->setChecked(cfg->getBool("storeMetaData", false)); m_filterRegistryModel.setEnabledFilters(cfg->getString("filters").split(',')); } KisPropertiesConfigurationSP KisWdgOptionsJPEG::configuration() const { KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration(); QVariant transparencyFillcolor; transparencyFillcolor.setValue(bnTransparencyFillColor->color()); cfg->setProperty("progressive", progressive->isChecked()); cfg->setProperty("quality", (int)qualityLevel->value()); cfg->setProperty("forceSRGB", chkForceSRGB->isChecked()); cfg->setProperty("saveProfile", chkSaveProfile->isChecked()); cfg->setProperty("optimize", optimize->isChecked()); cfg->setProperty("smoothing", (int)smoothLevel->value()); cfg->setProperty("baseline", baseLineJPEG->isChecked()); cfg->setProperty("subsampling", subsampling->currentIndex()); cfg->setProperty("exif", exif->isChecked()); cfg->setProperty("iptc", iptc->isChecked()); cfg->setProperty("xmp", xmp->isChecked()); cfg->setProperty("transparencyFillcolor", transparencyFillcolor); cfg->setProperty("storeAuthor", chkAuthor->isChecked()); cfg->setProperty("storeMetaData", chkMetaData->isChecked()); QString enabledFilters; Q_FOREACH (const KisMetaData::Filter* filter, m_filterRegistryModel.enabledFilters()) { enabledFilters = enabledFilters + filter->id() + ','; } cfg->setProperty("filters", enabledFilters); return cfg; } #include diff --git a/plugins/impex/png/kis_png_export.cc b/plugins/impex/png/kis_png_export.cc index 73ea449867..56fe0b88f9 100644 --- a/plugins/impex/png/kis_png_export.cc +++ b/plugins/impex/png/kis_png_export.cc @@ -1,230 +1,230 @@ /* * Copyright (c) 2005 Cyrille Berger * * 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_png_export.h" #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 K_PLUGIN_FACTORY_WITH_JSON(KisPNGExportFactory, "krita_png_export.json", registerPlugin();) KisPNGExport::KisPNGExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent) { } KisPNGExport::~KisPNGExport() { } KisImportExportFilter::ConversionStatus KisPNGExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration) { KisImageSP image = document->savingImage(); KisPNGOptions options; options.alpha = configuration->getBool("alpha", true); options.interlace = configuration->getBool("interlaced", false); options.compression = configuration->getInt("compression", 3); options.tryToSaveAsIndexed = configuration->getBool("indexed", false); KoColor c(KoColorSpaceRegistry::instance()->rgb8()); c.fromQColor(Qt::white); options.transparencyFillColor = configuration->getColor("transparencyFillcolor", c).toQColor(); options.saveSRGBProfile = configuration->getBool("saveSRGBProfile", false); options.forceSRGB = configuration->getBool("forceSRGB", true); options.storeAuthor = configuration->getBool("storeAuthor", false); options.storeMetaData = configuration->getBool("storeMetaData", false); vKisAnnotationSP_it beginIt = image->beginAnnotations(); vKisAnnotationSP_it endIt = image->endAnnotations(); KisExifInfoVisitor eIV; eIV.visit(image->rootLayer().data()); KisMetaData::Store *eI = 0; - if (eIV.countPaintLayer() == 1) { + if (eIV.metaDataCount() == 1) { eI = eIV.exifInfo(); } if (eI) { KisMetaData::Store* copy = new KisMetaData::Store(*eI); eI = copy; } KisPNGConverter pngConverter(document); KisImageBuilder_Result res = pngConverter.buildFile(io, image->bounds(), image->xRes(), image->yRes(), image->projection(), beginIt, endIt, options, eI); if (res == KisImageBuilder_RESULT_OK) { delete eI; return KisImportExportFilter::OK; } delete eI; dbgFile << " Result =" << res; return KisImportExportFilter::InternalError; } KisPropertiesConfigurationSP KisPNGExport::defaultConfiguration(const QByteArray &, const QByteArray &) const { KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration(); cfg->setProperty("alpha", true); cfg->setProperty("indexed", false); cfg->setProperty("compression", 3); cfg->setProperty("interlaced", false); KoColor fill_color(KoColorSpaceRegistry::instance()->rgb8()); fill_color = KoColor(); fill_color.fromQColor(Qt::white); QVariant v; v.setValue(fill_color); cfg->setProperty("transparencyFillcolor", v); cfg->setProperty("saveSRGBProfile", false); cfg->setProperty("forceSRGB", true); cfg->setProperty("storeMetaData", false); cfg->setProperty("storeAuthor", false); return cfg; } KisConfigWidget *KisPNGExport::createConfigurationWidget(QWidget *parent, const QByteArray &, const QByteArray &) const { return new KisWdgOptionsPNG(parent); } void KisPNGExport::initializeCapabilities() { addCapability(KisExportCheckRegistry::instance()->get("sRGBProfileCheck")->create(KisExportCheckBase::SUPPORTED)); QList > supportedColorModels; supportedColorModels << QPair() << QPair(RGBAColorModelID, Integer8BitsColorDepthID) << QPair(RGBAColorModelID, Integer16BitsColorDepthID) << QPair(GrayAColorModelID, Integer8BitsColorDepthID) << QPair(GrayAColorModelID, Integer16BitsColorDepthID); addSupportedColorModels(supportedColorModels, "PNG"); } void KisWdgOptionsPNG::setConfiguration(const KisPropertiesConfigurationSP cfg) { // the export manager should have prepared some info for us! KIS_SAFE_ASSERT_RECOVER_NOOP(cfg->hasProperty(KisImportExportFilter::ImageContainsTransparencyTag)); KIS_SAFE_ASSERT_RECOVER_NOOP(cfg->hasProperty(KisImportExportFilter::ColorModelIDTag)); KIS_SAFE_ASSERT_RECOVER_NOOP(cfg->hasProperty(KisImportExportFilter::sRGBTag)); const bool isThereAlpha = cfg->getBool(KisImportExportFilter::ImageContainsTransparencyTag); alpha->setChecked(cfg->getBool("alpha", isThereAlpha)); bnTransparencyFillColor->setEnabled(!alpha->isChecked()); if (cfg->getString(KisImportExportFilter::ColorModelIDTag) == RGBAColorModelID.id()) { tryToSaveAsIndexed->setVisible(true); if (alpha->isChecked()) { tryToSaveAsIndexed->setChecked(false); } else { tryToSaveAsIndexed->setChecked(cfg->getBool("indexed", false)); } } else { tryToSaveAsIndexed->setVisible(false); } interlacing->setChecked(cfg->getBool("interlaced", false)); compressionLevel->setValue(cfg->getInt("compression", 3)); compressionLevel->setRange(1, 9, 0); tryToSaveAsIndexed->setVisible(!isThereAlpha); //const bool sRGB = cfg->getBool(KisImportExportFilter::sRGBTag, false); //chkSRGB->setEnabled(sRGB); chkSRGB->setChecked(cfg->getBool("saveSRGBProfile", true)); //chkForceSRGB->setEnabled(!sRGB); chkForceSRGB->setChecked(cfg->getBool("forceSRGB", false)); chkAuthor->setChecked(cfg->getBool("storeAuthor", false)); chkMetaData->setChecked(cfg->getBool("storeMetaData", false)); KoColor background(KoColorSpaceRegistry::instance()->rgb8()); background.fromQColor(Qt::white); bnTransparencyFillColor->setDefaultColor(background); bnTransparencyFillColor->setColor(cfg->getColor("transparencyFillcolor", background)); } KisPropertiesConfigurationSP KisWdgOptionsPNG::configuration() const { KisPropertiesConfigurationSP cfg(new KisPropertiesConfiguration()); bool alpha = this->alpha->isChecked(); bool interlace = interlacing->isChecked(); int compression = (int)compressionLevel->value(); bool tryToSaveAsIndexed = this->tryToSaveAsIndexed->isChecked(); bool saveSRGB = chkSRGB->isChecked(); bool forceSRGB = chkForceSRGB->isChecked(); bool storeAuthor = chkAuthor->isChecked(); bool storeMetaData = chkMetaData->isChecked(); QVariant transparencyFillcolor; transparencyFillcolor.setValue(bnTransparencyFillColor->color()); cfg->setProperty("alpha", alpha); cfg->setProperty("indexed", tryToSaveAsIndexed); cfg->setProperty("compression", compression); cfg->setProperty("interlaced", interlace); cfg->setProperty("transparencyFillcolor", transparencyFillcolor); cfg->setProperty("saveSRGBProfile", saveSRGB); cfg->setProperty("forceSRGB", forceSRGB); cfg->setProperty("storeAuthor", storeAuthor); cfg->setProperty("storeMetaData", storeMetaData); return cfg; } void KisWdgOptionsPNG::on_alpha_toggled(bool checked) { bnTransparencyFillColor->setEnabled(!checked); } #include "kis_png_export.moc" diff --git a/plugins/impex/psd/krita_psd.desktop b/plugins/impex/psd/krita_psd.desktop index 0f67842cc5..e3113e396b 100644 --- a/plugins/impex/psd/krita_psd.desktop +++ b/plugins/impex/psd/krita_psd.desktop @@ -1,74 +1,74 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; Exec=krita %u Icon=calligrakrita -MimeType=image/x-psd;image/photoshop;image/x-photoshop;image/x-vnd.adobe.photoshop;image/vnd.adobe.photoshop; +MimeType=image/x-psd;image/photoshop;image/x-photoshop;image/vnd.adobe.photoshop; Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita NoDisplay=true StartupNotify=true Terminal=false Type=Application X-KDE-SubstituteUID=false X-KDE-Username= diff --git a/plugins/impex/psd/krita_psd_export.json b/plugins/impex/psd/krita_psd_export.json index 7426f82c74..cbf1a37391 100644 --- a/plugins/impex/psd/krita_psd_export.json +++ b/plugins/impex/psd/krita_psd_export.json @@ -1,13 +1,13 @@ { "Icon": "", "Id": "Krita PhotoShop Export Filter", "NoDisplay": "true", "Type": "Service", - "X-KDE-Export": "image/x-psd,image/photoshop,image/x-photoshop,image/x-vnd.adobe.photoshop,image/vnd.adobe.photoshop", + "X-KDE-Export": "image/x-psd,image/photoshop,image/x-photoshop,image/vnd.adobe.photoshop", "X-KDE-Library": "kritapsdexport", "X-KDE-ServiceTypes": [ "Krita/FileFilter" ], "X-KDE-Weight": "1", "X-KDE-Extensions" : "psd" } diff --git a/plugins/impex/psd/krita_psd_import.json b/plugins/impex/psd/krita_psd_import.json index 2ff783d165..11fe11f609 100644 --- a/plugins/impex/psd/krita_psd_import.json +++ b/plugins/impex/psd/krita_psd_import.json @@ -1,13 +1,13 @@ { "Icon": "", "Id": "Krita PhotoShop Import Filter", "NoDisplay": "true", "Type": "Service", - "X-KDE-Import": "image/x-psd,image/photoshop,image/x-photoshop,image/x-vnd.adobe.photoshop,image/vnd.adobe.photoshop,image/x-psb", + "X-KDE-Import": "image/x-psd,image/photoshop,image/x-photoshop,image/vnd.adobe.photoshop,image/x-psb", "X-KDE-Library": "kritapsdimport", "X-KDE-ServiceTypes": [ "Krita/FileFilter" ], "X-KDE-Weight": "1", "X-KDE-Extensions" : "psd,psb" } diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawsettingswidget.cpp b/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawsettingswidget.cpp index 8157920dbf..a7d64ce10b 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawsettingswidget.cpp +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawsettingswidget.cpp @@ -1,1324 +1,1324 @@ /** =========================================================== * @file * * This file is a part of digiKam project * http://www.digikam.org * * @date 2006-09-13 * @brief LibRaw settings widgets * * @author Copyright (C) 2006-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2006-2011 by Marcel Wiesweg * marcel dot wiesweg at gmx dot de * @author Copyright (C) 2007-2008 by Guillaume Castagnino * casta at xwing dot info * * 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, 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. * * ============================================================ */ #include "dcrawsettingswidget.h" // C++ includes #include // Qt includes #include #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "kdcraw.h" #include "rnuminput.h" #include "rcombobox.h" #include "rexpanderbox.h" #include "libkdcraw_debug.h" #include namespace KDcrawIface { class Q_DECL_HIDDEN DcrawSettingsWidget::Private { public: Private() { autoBrightnessBox = 0; sixteenBitsImage = 0; fourColorCheckBox = 0; brightnessLabel = 0; brightnessSpinBox = 0; blackPointCheckBox = 0; blackPointSpinBox = 0; whitePointCheckBox = 0; whitePointSpinBox = 0; whiteBalanceComboBox = 0; whiteBalanceLabel = 0; customWhiteBalanceSpinBox = 0; customWhiteBalanceLabel = 0; customWhiteBalanceGreenSpinBox = 0; customWhiteBalanceGreenLabel = 0; unclipColorLabel = 0; dontStretchPixelsCheckBox = 0; RAWQualityComboBox = 0; RAWQualityLabel = 0; noiseReductionComboBox = 0; NRSpinBox1 = 0; NRSpinBox2 = 0; NRLabel1 = 0; NRLabel2 = 0; enableCACorrectionBox = 0; autoCACorrectionBox = 0; caRedMultSpinBox = 0; caBlueMultSpinBox = 0; caRedMultLabel = 0; caBlueMultLabel = 0; unclipColorComboBox = 0; reconstructLabel = 0; reconstructSpinBox = 0; outputColorSpaceLabel = 0; outputColorSpaceComboBox = 0; demosaicingSettings = 0; whiteBalanceSettings = 0; correctionsSettings = 0; colormanSettings = 0; medianFilterPassesSpinBox = 0; medianFilterPassesLabel = 0; inIccUrlEdit = 0; outIccUrlEdit = 0; inputColorSpaceLabel = 0; inputColorSpaceComboBox = 0; fixColorsHighlightsBox = 0; refineInterpolationBox = 0; noiseReductionLabel = 0; expoCorrectionBox = 0; expoCorrectionShiftSpinBox = 0; expoCorrectionHighlightSpinBox = 0; expoCorrectionShiftLabel = 0; expoCorrectionHighlightLabel = 0; } /** Convert Exposure correction shift E.V value from GUI to Linear value needs by libraw decoder. */ double shiftExpoFromEvToLinear(double ev) const { // From GUI : -2.0EV => 0.25 // +3.0EV => 8.00 return (1.55*ev + 3.35); } /** Convert Exposure correction shift Linear value from liraw decoder to E.V value needs by GUI. */ double shiftExpoFromLinearToEv(double lin) const { // From GUI : 0.25 => -2.0EV // 8.00 => +3.0EV return ((lin-3.35) / 1.55); } public: QWidget* demosaicingSettings; QWidget* whiteBalanceSettings; QWidget* correctionsSettings; QWidget* colormanSettings; QLabel* whiteBalanceLabel; QLabel* customWhiteBalanceLabel; QLabel* customWhiteBalanceGreenLabel; QLabel* brightnessLabel; QLabel* RAWQualityLabel; QLabel* NRLabel1; QLabel* NRLabel2; QLabel* caRedMultLabel; QLabel* caBlueMultLabel; QLabel* unclipColorLabel; QLabel* reconstructLabel; QLabel* inputColorSpaceLabel; QLabel* outputColorSpaceLabel; QLabel* medianFilterPassesLabel; QLabel* noiseReductionLabel; QLabel* expoCorrectionShiftLabel; QLabel* expoCorrectionHighlightLabel; QCheckBox* blackPointCheckBox; QCheckBox* whitePointCheckBox; QCheckBox* sixteenBitsImage; QCheckBox* autoBrightnessBox; QCheckBox* fourColorCheckBox; QCheckBox* dontStretchPixelsCheckBox; QCheckBox* enableCACorrectionBox; QCheckBox* autoCACorrectionBox; QCheckBox* fixColorsHighlightsBox; QCheckBox* refineInterpolationBox; QCheckBox* expoCorrectionBox; RFileSelector* inIccUrlEdit; RFileSelector* outIccUrlEdit; RComboBox* noiseReductionComboBox; RComboBox* whiteBalanceComboBox; RComboBox* RAWQualityComboBox; RComboBox* unclipColorComboBox; RComboBox* inputColorSpaceComboBox; RComboBox* outputColorSpaceComboBox; RIntNumInput* customWhiteBalanceSpinBox; RIntNumInput* reconstructSpinBox; RIntNumInput* blackPointSpinBox; RIntNumInput* whitePointSpinBox; RIntNumInput* NRSpinBox1; RIntNumInput* NRSpinBox2; RIntNumInput* medianFilterPassesSpinBox; RDoubleNumInput* customWhiteBalanceGreenSpinBox; RDoubleNumInput* caRedMultSpinBox; RDoubleNumInput* caBlueMultSpinBox; RDoubleNumInput* brightnessSpinBox; RDoubleNumInput* expoCorrectionShiftSpinBox; RDoubleNumInput* expoCorrectionHighlightSpinBox; }; DcrawSettingsWidget::DcrawSettingsWidget(QWidget* const parent, int advSettings) : RExpanderBox(parent), d(new Private) { setup(advSettings); } void DcrawSettingsWidget::setup(int advSettings) { setObjectName( QLatin1String("DCRawSettings Expander" )); // --------------------------------------------------------------- // DEMOSAICING Settings panel d->demosaicingSettings = new QWidget(this); QGridLayout* const demosaicingLayout = new QGridLayout(d->demosaicingSettings); int line = 0; d->sixteenBitsImage = new QCheckBox(i18nc("@option:check", "16 bits color depth"), d->demosaicingSettings); d->sixteenBitsImage->setToolTip(i18nc("@info:whatsthis", "

    If enabled, all RAW files will " "be decoded in 16-bit color depth using a linear gamma curve. To " "prevent dark picture rendering in the editor, it is recommended to " "use Color Management in this mode.

    " "

    If disabled, all RAW files will be decoded in 8-bit color " "depth with a BT.709 gamma curve and a 99th-percentile white point. " "This mode is faster than 16-bit decoding.

    ")); demosaicingLayout->addWidget(d->sixteenBitsImage, 0, 0, 1, 2); if (advSettings & SIXTEENBITS) { d->sixteenBitsImage->show(); line = 1; } else { d->sixteenBitsImage->hide(); } d->fourColorCheckBox = new QCheckBox(i18nc("@option:check", "Interpolate RGB as four colors"), d->demosaicingSettings); d->fourColorCheckBox->setToolTip(i18nc("@info:whatsthis", "Interpolate RGB as four " "colors" "

    The default is to assume that all green pixels are the same. " "If even-row green pixels are more sensitive to ultraviolet light " "than odd-row this difference causes a mesh pattern in the output; " "using this option solves this problem with minimal loss of detail.

    " "

    To resume, this option blurs the image a little, but it " "eliminates false 2x2 mesh patterns with VNG quality method or " "mazes with AHD quality method.

    ")); demosaicingLayout->addWidget(d->fourColorCheckBox, line, 0, 1, line == 0 ? 2 : 3); line++; QLabel* const dcrawVersion = new QLabel(d->demosaicingSettings); dcrawVersion->setAlignment(Qt::AlignRight); dcrawVersion->setToolTip(i18nc("@info:tooltip", "Visit LibRaw project website")); dcrawVersion->setOpenExternalLinks(true); dcrawVersion->setTextFormat(Qt::RichText); dcrawVersion->setTextInteractionFlags(Qt::LinksAccessibleByMouse); dcrawVersion->setText(QString::fromLatin1("%2") .arg(QLatin1String("http://www.libraw.org")) .arg(QString::fromLatin1("libraw %1").arg(KDcraw::librawVersion()))); demosaicingLayout->addWidget(dcrawVersion, 0, 2, 1, 1); d->dontStretchPixelsCheckBox = new QCheckBox(i18nc("@option:check", "Do not stretch or rotate pixels"), d->demosaicingSettings); d->dontStretchPixelsCheckBox->setToolTip(i18nc("@info:whatsthis", "Do not stretch or rotate pixels" "

    For Fuji Super CCD cameras, show the image tilted 45 degrees. " "For cameras with non-square pixels, do not stretch the image to " "its correct aspect ratio. In any case, this option guarantees that " "each output pixel corresponds to one RAW pixel.

    ")); demosaicingLayout->addWidget(d->dontStretchPixelsCheckBox, line, 0, 1, 3); line++; d->RAWQualityLabel = new QLabel(i18nc("@label:listbox", "Quality:"), d->demosaicingSettings); d->RAWQualityComboBox = new RComboBox(d->demosaicingSettings); // Original dcraw demosaicing methods d->RAWQualityComboBox->insertItem(RawDecodingSettings::BILINEAR, i18nc("@item:inlistbox Quality", "Bilinear")); d->RAWQualityComboBox->insertItem(RawDecodingSettings::VNG, i18nc("@item:inlistbox Quality", "VNG")); d->RAWQualityComboBox->insertItem(RawDecodingSettings::PPG, i18nc("@item:inlistbox Quality", "PPG")); d->RAWQualityComboBox->insertItem(RawDecodingSettings::AHD, i18nc("@item:inlistbox Quality", "AHD")); // Extended demosaicing method from GPL2 pack d->RAWQualityComboBox->insertItem(RawDecodingSettings::DCB, i18nc("@item:inlistbox Quality", "DCB")); d->RAWQualityComboBox->insertItem(RawDecodingSettings::PL_AHD, i18nc("@item:inlistbox Quality", "AHD v2")); d->RAWQualityComboBox->insertItem(RawDecodingSettings::AFD, i18nc("@item:inlistbox Quality", "AFD")); d->RAWQualityComboBox->insertItem(RawDecodingSettings::VCD, i18nc("@item:inlistbox Quality", "VCD")); d->RAWQualityComboBox->insertItem(RawDecodingSettings::VCD_AHD, i18nc("@item:inlistbox Quality", "VCD & AHD")); d->RAWQualityComboBox->insertItem(RawDecodingSettings::LMMSE, i18nc("@item:inlistbox Quality", "LMMSE")); // Extended demosaicing method from GPL3 pack d->RAWQualityComboBox->insertItem(RawDecodingSettings::AMAZE, i18nc("@item:inlistbox Quality", "AMaZE")); // If Libraw do not support GPL2 pack, disable entries relevant. if (!KDcraw::librawUseGPL2DemosaicPack()) { for (int i=RawDecodingSettings::DCB ; i <=RawDecodingSettings::LMMSE ; ++i) d->RAWQualityComboBox->combo()->setItemData(i, false, Qt::UserRole-1); } // If Libraw do not support GPL3 pack, disable entries relevant. if (!KDcraw::librawUseGPL3DemosaicPack()) { d->RAWQualityComboBox->combo()->setItemData(RawDecodingSettings::AMAZE, false, Qt::UserRole-1); } d->RAWQualityComboBox->setDefaultIndex(RawDecodingSettings::BILINEAR); d->RAWQualityComboBox->setCurrentIndex(RawDecodingSettings::BILINEAR); d->RAWQualityComboBox->setToolTip(i18nc("@info:whatsthis", "Quality (interpolation)" "

    Select here the demosaicing method to use when decoding RAW " "images. A demosaicing algorithm is a digital image process used to " "interpolate a complete image from the partial raw data received " "from the color-filtered image sensor, internal to many digital " "cameras, in form of a matrix of colored pixels. Also known as CFA " "interpolation or color reconstruction, another common spelling is " "demosaicing. The following methods are available for demosaicing " "RAW images:

    " // Original dcraw demosaicing methods "

    • Bilinear: use " "high-speed but low-quality bilinear interpolation (default - for " "slow computers). In this method, the red value of a non-red pixel " "is computed as the average of the adjacent red pixels, and similarly " "for blue and green.
    • " "
    • VNG: use Variable Number " "of Gradients interpolation. This method computes gradients near " "the pixel of interest and uses the lower gradients (representing " "smoother and more similar parts of the image) to make an estimate.
    • " "
    • PPG: use Patterned-Pixel-" "Grouping interpolation. Pixel Grouping uses assumptions about " "natural scenery in making estimates. It has fewer color artifacts " "on natural images than the Variable Number of Gradients method.
    • " "
    • AHD: use Adaptive " "Homogeneity-Directed interpolation. This method selects the " "direction of interpolation so as to maximize a homogeneity metric, " "thus typically minimizing color artifacts.
    • " // Extended demosaicing method "
    • DCB: DCB interpolation from " "linuxphoto.org project.
    • " "
    • AHD v2: modified AHD " "interpolation using Variance of Color Differences method.
    • " "
    • AFD: Adaptive Filtered " "Demosaicing interpolation through 5 pass median filter from PerfectRaw " "project.
    • " "
    • VCD: Variance of Color " "Differences interpolation.
    • " "
    • VCD & AHD: Mixed demosaicing " "between VCD and AHD.
    • " "
    • LMMSE: color demosaicing via " "directional linear minimum mean-square error estimation interpolation " "from PerfectRaw.
    • " "
    • AMaZE: Aliasing Minimization " "interpolation and Zipper Elimination to apply color aberration removal " "from RawTherapee project.

    " "

    Note: some methods can be unavailable if RAW decoder have been built " "without extension packs.

    ")); demosaicingLayout->addWidget(d->RAWQualityLabel, line, 0, 1, 1); demosaicingLayout->addWidget(d->RAWQualityComboBox, line, 1, 1, 2); line++; d->medianFilterPassesSpinBox = new RIntNumInput(d->demosaicingSettings); d->medianFilterPassesSpinBox->setRange(0, 10, 1); d->medianFilterPassesSpinBox->setDefaultValue(0); d->medianFilterPassesLabel = new QLabel(i18nc("@label:slider", "Pass:"), d->whiteBalanceSettings); d->medianFilterPassesSpinBox->setToolTip( i18nc("@info:whatsthis", "Pass" "

    Set here the passes used by the median filter applied after " "interpolation to Red-Green and Blue-Green channels.

    " "

    This setting is only available for specific Quality options: " "Bilinear, " "VNG, PPG, " "AHD, " "DCB, and VCD & AHD.

    ")); demosaicingLayout->addWidget(d->medianFilterPassesLabel, line, 0, 1, 1); demosaicingLayout->addWidget(d->medianFilterPassesSpinBox, line, 1, 1, 2); line++; d->refineInterpolationBox = new QCheckBox(i18nc("@option:check", "Refine interpolation"), d->demosaicingSettings); d->refineInterpolationBox->setToolTip(i18nc("@info:whatsthis", "Refine interpolation" "

    This setting is available only for few Quality options:

    " "

    • DCB: turn on " "the enhance interpolated colors filter.
    • " "
    • VCD & AHD: turn on the " "enhanced effective color interpolation (EECI) refine to improve " "sharpness.

    ")); demosaicingLayout->addWidget(d->refineInterpolationBox, line, 0, 1, 2); // If Libraw do not support GPL2 pack, disable options relevant. if (!KDcraw::librawUseGPL2DemosaicPack()) { d->medianFilterPassesLabel->setEnabled(false); d->medianFilterPassesSpinBox->setEnabled(false); d->refineInterpolationBox->setEnabled(false); } addItem(d->demosaicingSettings, KisIconUtils::loadIcon("kdcraw").pixmap(16, 16), i18nc("@label", "Demosaicing"), QString("demosaicing"), true); // --------------------------------------------------------------- // WHITE BALANCE Settings Panel d->whiteBalanceSettings = new QWidget(this); QGridLayout* const whiteBalanceLayout = new QGridLayout(d->whiteBalanceSettings); d->whiteBalanceLabel = new QLabel(i18nc("@label:listbox", "Method:"), d->whiteBalanceSettings); d->whiteBalanceComboBox = new RComboBox(d->whiteBalanceSettings); d->whiteBalanceComboBox->insertItem(RawDecodingSettings::NONE, i18nc("@item:inlistbox", "Default D65")); d->whiteBalanceComboBox->insertItem(RawDecodingSettings::CAMERA, i18nc("@item:inlistbox", "Camera")); d->whiteBalanceComboBox->insertItem(RawDecodingSettings::AUTO, i18nc("@item:inlistbox set while balance automatically", "Automatic")); d->whiteBalanceComboBox->insertItem(RawDecodingSettings::CUSTOM, i18nc("@item:inlistbox set white balance manually", "Manual")); d->whiteBalanceComboBox->setDefaultIndex(RawDecodingSettings::CAMERA); d->whiteBalanceComboBox->setToolTip(i18nc("@info:whatsthis", "White Balance" "

    Configure the raw white balance:

    " "

    • Default D65: " "Use a standard daylight D65 white balance.
    • " "
    • Camera: Use the white " "balance specified by the camera. If not available, reverts to " "default neutral white balance.
    • " "
    • Automatic: Calculates an " "automatic white balance averaging the entire image.
    • " "
    • Manual: Set a custom " "temperature and green level values.

    ")); d->customWhiteBalanceSpinBox = new RIntNumInput(d->whiteBalanceSettings); d->customWhiteBalanceSpinBox->setRange(2000, 12000, 10); d->customWhiteBalanceSpinBox->setDefaultValue(6500); d->customWhiteBalanceLabel = new QLabel(i18nc("@label:slider", "T(K):"), d->whiteBalanceSettings); d->customWhiteBalanceSpinBox->setToolTip( i18nc("@info:whatsthis", "Temperature" "

    Set here the color temperature in Kelvin.

    ")); d->customWhiteBalanceGreenSpinBox = new RDoubleNumInput(d->whiteBalanceSettings); d->customWhiteBalanceGreenSpinBox->setDecimals(2); d->customWhiteBalanceGreenSpinBox->setRange(0.2, 2.5, 0.01); d->customWhiteBalanceGreenSpinBox->setDefaultValue(1.0); d->customWhiteBalanceGreenLabel = new QLabel(i18nc("@label:slider Green component", "Green:"), d->whiteBalanceSettings); d->customWhiteBalanceGreenSpinBox->setToolTip(i18nc("@info:whatsthis", "

    Set here the " "green component to set magenta color cast removal level.

    ")); d->unclipColorLabel = new QLabel(i18nc("@label:listbox", "Highlights:"), d->whiteBalanceSettings); d->unclipColorComboBox = new RComboBox(d->whiteBalanceSettings); d->unclipColorComboBox->insertItem(0, i18nc("@item:inlistbox", "Solid white")); d->unclipColorComboBox->insertItem(1, i18nc("@item:inlistbox", "Unclip")); d->unclipColorComboBox->insertItem(2, i18nc("@item:inlistbox", "Blend")); d->unclipColorComboBox->insertItem(3, i18nc("@item:inlistbox", "Rebuild")); d->unclipColorComboBox->setDefaultIndex(0); d->unclipColorComboBox->setToolTip(i18nc("@info:whatsthis", "Highlights" "

    Select here the highlight clipping method:

    " "

    • Solid white: " "clip all highlights to solid white
    • " "
    • Unclip: leave highlights " "unclipped in various shades of pink
    • " "
    • Blend:Blend clipped and " "unclipped values together for a gradual fade to white
    • " "
    • Rebuild: reconstruct " "highlights using a level value

    ")); d->reconstructLabel = new QLabel(i18nc("@label:slider Highlight reconstruct level", "Level:"), d->whiteBalanceSettings); d->reconstructSpinBox = new RIntNumInput(d->whiteBalanceSettings); d->reconstructSpinBox->setRange(0, 6, 1); d->reconstructSpinBox->setDefaultValue(0); d->reconstructSpinBox->setToolTip(i18nc("@info:whatsthis", "Level" "

    Specify the reconstruct highlight level. Low values favor " "whites and high values favor colors.

    ")); d->expoCorrectionBox = new QCheckBox(i18nc("@option:check", "Exposure Correction (E.V)"), d->whiteBalanceSettings); d->expoCorrectionBox->setToolTip(i18nc("@info:whatsthis", "

    Turn on the exposure " "correction before interpolation.

    ")); d->expoCorrectionShiftLabel = new QLabel(i18nc("@label:slider", "Linear Shift:"), d->whiteBalanceSettings); d->expoCorrectionShiftSpinBox = new RDoubleNumInput(d->whiteBalanceSettings); d->expoCorrectionShiftSpinBox->setDecimals(2); d->expoCorrectionShiftSpinBox->setRange(-2.0, 3.0, 0.01); d->expoCorrectionShiftSpinBox->setDefaultValue(0.0); d->expoCorrectionShiftSpinBox->setToolTip(i18nc("@info:whatsthis", "Shift" "

    Linear Shift of exposure correction before interpolation in E.V

    ")); d->expoCorrectionHighlightLabel = new QLabel(i18nc("@label:slider", "Highlight:"), d->whiteBalanceSettings); d->expoCorrectionHighlightSpinBox = new RDoubleNumInput(d->whiteBalanceSettings); d->expoCorrectionHighlightSpinBox->setDecimals(2); d->expoCorrectionHighlightSpinBox->setRange(0.0, 1.0, 0.01); d->expoCorrectionHighlightSpinBox->setDefaultValue(0.0); d->expoCorrectionHighlightSpinBox->setToolTip(i18nc("@info:whatsthis", "Highlight" "

    Amount of highlight preservation for exposure correction " "before interpolation in E.V. Only take effect if Shift Correction is > 1.0 E.V

    ")); d->fixColorsHighlightsBox = new QCheckBox(i18nc("@option:check", "Correct false colors in highlights"), d->whiteBalanceSettings); d->fixColorsHighlightsBox->setToolTip(i18nc("@info:whatsthis", "

    If enabled, images with " "overblown channels are processed much more accurately, without " "'pink clouds' (and blue highlights under tungsten lamps).

    ")); d->autoBrightnessBox = new QCheckBox(i18nc("@option:check", "Auto Brightness"), d->whiteBalanceSettings); d->autoBrightnessBox->setToolTip(i18nc("@info:whatsthis", "

    If disable, use a fixed white level " "and ignore the image histogram to adjust brightness.

    ")); d->brightnessLabel = new QLabel(i18nc("@label:slider", "Brightness:"), d->whiteBalanceSettings); d->brightnessSpinBox = new RDoubleNumInput(d->whiteBalanceSettings); d->brightnessSpinBox->setDecimals(2); d->brightnessSpinBox->setRange(0.0, 10.0, 0.01); d->brightnessSpinBox->setDefaultValue(1.0); d->brightnessSpinBox->setToolTip(i18nc("@info:whatsthis", "Brightness" "

    Specify the brightness level of output image. The default " "value is 1.0 (works in 8-bit mode only).

    ")); if (! (advSettings & POSTPROCESSING)) { d->brightnessLabel->hide(); d->brightnessSpinBox->hide(); } d->blackPointCheckBox = new QCheckBox(i18nc("@option:check Black point", "Black:"), d->whiteBalanceSettings); d->blackPointCheckBox->setToolTip(i18nc("@info:whatsthis", "Black point" "

    Use a specific black point value to decode RAW pictures. If " "you set this option to off, the Black Point value will be " "automatically computed.

    ")); d->blackPointSpinBox = new RIntNumInput(d->whiteBalanceSettings); d->blackPointSpinBox->setRange(0, 1000, 1); d->blackPointSpinBox->setDefaultValue(0); d->blackPointSpinBox->setToolTip(i18nc("@info:whatsthis", "Black point value" "

    Specify specific black point value of the output image.

    ")); d->whitePointCheckBox = new QCheckBox(i18nc("@option:check White point", "White:"), d->whiteBalanceSettings); d->whitePointCheckBox->setToolTip(i18nc("@info:whatsthis", "White point" "

    Use a specific white point value to decode RAW pictures. If " "you set this option to off, the White Point value will be " "automatically computed.

    ")); d->whitePointSpinBox = new RIntNumInput(d->whiteBalanceSettings); d->whitePointSpinBox->setRange(0, 20000, 1); d->whitePointSpinBox->setDefaultValue(0); d->whitePointSpinBox->setToolTip(i18nc("@info:whatsthis", "White point value" "

    Specify specific white point value of the output image.

    ")); if (! (advSettings & BLACKWHITEPOINTS)) { d->blackPointCheckBox->hide(); d->blackPointSpinBox->hide(); d->whitePointCheckBox->hide(); d->whitePointSpinBox->hide(); } whiteBalanceLayout->addWidget(d->whiteBalanceLabel, 0, 0, 1, 1); whiteBalanceLayout->addWidget(d->whiteBalanceComboBox, 0, 1, 1, 2); whiteBalanceLayout->addWidget(d->customWhiteBalanceLabel, 1, 0, 1, 1); whiteBalanceLayout->addWidget(d->customWhiteBalanceSpinBox, 1, 1, 1, 2); whiteBalanceLayout->addWidget(d->customWhiteBalanceGreenLabel, 2, 0, 1, 1); whiteBalanceLayout->addWidget(d->customWhiteBalanceGreenSpinBox, 2, 1, 1, 2); whiteBalanceLayout->addWidget(d->unclipColorLabel, 3, 0, 1, 1); whiteBalanceLayout->addWidget(d->unclipColorComboBox, 3, 1, 1, 2); whiteBalanceLayout->addWidget(d->reconstructLabel, 4, 0, 1, 1); whiteBalanceLayout->addWidget(d->reconstructSpinBox, 4, 1, 1, 2); whiteBalanceLayout->addWidget(d->expoCorrectionBox, 5, 0, 1, 2); whiteBalanceLayout->addWidget(d->expoCorrectionShiftLabel, 6, 0, 1, 1); whiteBalanceLayout->addWidget(d->expoCorrectionShiftSpinBox, 6, 1, 1, 2); whiteBalanceLayout->addWidget(d->expoCorrectionHighlightLabel, 7, 0, 1, 1); whiteBalanceLayout->addWidget(d->expoCorrectionHighlightSpinBox, 7, 1, 1, 2); whiteBalanceLayout->addWidget(d->fixColorsHighlightsBox, 8, 0, 1, 3); whiteBalanceLayout->addWidget(d->autoBrightnessBox, 9, 0, 1, 3); whiteBalanceLayout->addWidget(d->brightnessLabel, 10, 0, 1, 1); whiteBalanceLayout->addWidget(d->brightnessSpinBox, 10, 1, 1, 2); whiteBalanceLayout->addWidget(d->blackPointCheckBox, 11, 0, 1, 1); whiteBalanceLayout->addWidget(d->blackPointSpinBox, 11, 1, 1, 2); whiteBalanceLayout->addWidget(d->whitePointCheckBox, 12, 0, 1, 1); whiteBalanceLayout->addWidget(d->whitePointSpinBox, 12, 1, 1, 2); whiteBalanceLayout->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); whiteBalanceLayout->setMargin(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); addItem(d->whiteBalanceSettings, KisIconUtils::loadIcon("kdcraw").pixmap(16, 16), i18nc("@label", "White Balance"), QString("whitebalance"), true); // --------------------------------------------------------------- // CORRECTIONS Settings panel d->correctionsSettings = new QWidget(this); QGridLayout* const correctionsLayout = new QGridLayout(d->correctionsSettings); d->noiseReductionLabel = new QLabel(i18nc("@label:listbox", "Noise reduction:"), d->correctionsSettings); d->noiseReductionComboBox = new RComboBox(d->colormanSettings); d->noiseReductionComboBox->insertItem(RawDecodingSettings::NONR, i18nc("@item:inlistbox Noise Reduction", "None")); d->noiseReductionComboBox->insertItem(RawDecodingSettings::WAVELETSNR, i18nc("@item:inlistbox Noise Reduction", "Wavelets")); d->noiseReductionComboBox->insertItem(RawDecodingSettings::FBDDNR, i18nc("@item:inlistbox Noise Reduction", "FBDD")); d->noiseReductionComboBox->insertItem(RawDecodingSettings::LINENR, i18nc("@item:inlistbox Noise Reduction", "CFA Line Denoise")); d->noiseReductionComboBox->insertItem(RawDecodingSettings::IMPULSENR, i18nc("@item:inlistbox Noise Reduction", "Impulse Denoise")); d->noiseReductionComboBox->setDefaultIndex(RawDecodingSettings::NONR); d->noiseReductionComboBox->setToolTip(i18nc("@info:whatsthis", "Noise Reduction" "

    Select here the noise reduction method to apply during RAW " "decoding.

    " "

    • None: no " "noise reduction.
    • " "
    • Wavelets: wavelets " "correction to erase noise while preserving real detail. It's " "applied after interpolation.
    • " "
    • FBDD: Fake Before " "Demosaicing Denoising noise reduction. It's applied before " "interpolation.
    • " "
    • CFA Line Denoise: Banding " "noise suppression. It's applied after interpolation.
    • " "
    • Impulse Denoise: Impulse " "noise suppression. It's applied after interpolation.

    ")); d->NRSpinBox1 = new RIntNumInput(d->correctionsSettings); d->NRSpinBox1->setRange(100, 1000, 1); d->NRSpinBox1->setDefaultValue(100); d->NRLabel1 = new QLabel(d->correctionsSettings); d->NRSpinBox2 = new RIntNumInput(d->correctionsSettings); d->NRSpinBox2->setRange(100, 1000, 1); d->NRSpinBox2->setDefaultValue(100); d->NRLabel2 = new QLabel(d->correctionsSettings); d->enableCACorrectionBox = new QCheckBox(i18nc("@option:check", "Enable Chromatic Aberration correction"), d->correctionsSettings); d->enableCACorrectionBox->setToolTip(i18nc("@info:whatsthis", "Enable Chromatic " "Aberration correction" "

    Enlarge the raw red-green and blue-yellow axis by the given " "factors (automatic by default).

    ")); d->autoCACorrectionBox = new QCheckBox(i18nc("@option:check", "Automatic color axis adjustments"), d->correctionsSettings); d->autoCACorrectionBox->setToolTip(i18nc("@info:whatsthis", "Automatic Chromatic " "Aberration correction" "

    If this option is turned on, it will try to shift image " "channels slightly and evaluate Chromatic Aberration change. Note " "that if you shot blue-red pattern, the method may fail. In this " "case, disable this option and tune manually color factors.

    ")); d->caRedMultLabel = new QLabel(i18nc("@label:slider", "Red-Green:"), d->correctionsSettings); d->caRedMultSpinBox = new RDoubleNumInput(d->correctionsSettings); d->caRedMultSpinBox->setDecimals(1); d->caRedMultSpinBox->setRange(-4.0, 4.0, 0.1); d->caRedMultSpinBox->setDefaultValue(0.0); d->caRedMultSpinBox->setToolTip(i18nc("@info:whatsthis", "Red-Green multiplier" "

    Set here the amount of correction on red-green axis

    ")); d->caBlueMultLabel = new QLabel(i18nc("@label:slider", "Blue-Yellow:"), d->correctionsSettings); d->caBlueMultSpinBox = new RDoubleNumInput(d->correctionsSettings); d->caBlueMultSpinBox->setDecimals(1); d->caBlueMultSpinBox->setRange(-4.0, 4.0, 0.1); d->caBlueMultSpinBox->setDefaultValue(0.0); d->caBlueMultSpinBox->setToolTip(i18nc("@info:whatsthis", "Blue-Yellow multiplier" "

    Set here the amount of correction on blue-yellow axis

    ")); correctionsLayout->addWidget(d->noiseReductionLabel, 0, 0, 1, 1); correctionsLayout->addWidget(d->noiseReductionComboBox, 0, 1, 1, 2); correctionsLayout->addWidget(d->NRLabel1, 1, 0, 1, 1); correctionsLayout->addWidget(d->NRSpinBox1, 1, 1, 1, 2); correctionsLayout->addWidget(d->NRLabel2, 2, 0, 1, 1); correctionsLayout->addWidget(d->NRSpinBox2, 2, 1, 1, 2); correctionsLayout->addWidget(d->enableCACorrectionBox, 3, 0, 1, 3); correctionsLayout->addWidget(d->autoCACorrectionBox, 4, 0, 1, 3); correctionsLayout->addWidget(d->caRedMultLabel, 5, 0, 1, 1); correctionsLayout->addWidget(d->caRedMultSpinBox, 5, 1, 1, 2); correctionsLayout->addWidget(d->caBlueMultLabel, 6, 0, 1, 1); correctionsLayout->addWidget(d->caBlueMultSpinBox, 6, 1, 1, 2); correctionsLayout->setRowStretch(7, 10); correctionsLayout->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); correctionsLayout->setMargin(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); addItem(d->correctionsSettings, KisIconUtils::loadIcon("kdcraw").pixmap(16, 16), i18nc("@label", "Corrections"), QString("corrections"), false); // --------------------------------------------------------------- // COLOR MANAGEMENT Settings panel d->colormanSettings = new QWidget(this); QGridLayout* const colormanLayout = new QGridLayout(d->colormanSettings); d->inputColorSpaceLabel = new QLabel(i18nc("@label:listbox", "Camera Profile:"), d->colormanSettings); d->inputColorSpaceComboBox = new RComboBox(d->colormanSettings); d->inputColorSpaceComboBox->insertItem(RawDecodingSettings::NOINPUTCS, i18nc("@item:inlistbox Camera Profile", "None")); d->inputColorSpaceComboBox->insertItem(RawDecodingSettings::EMBEDDED, i18nc("@item:inlistbox Camera Profile", "Embedded")); d->inputColorSpaceComboBox->insertItem(RawDecodingSettings::CUSTOMINPUTCS, i18nc("@item:inlistbox Camera Profile", "Custom")); d->inputColorSpaceComboBox->setDefaultIndex(RawDecodingSettings::NOINPUTCS); d->inputColorSpaceComboBox->setToolTip(i18nc("@info:whatsthis", "Camera Profile" "

    Select here the input color space used to decode RAW data.

    " "

    • None: no " "input color profile is used during RAW decoding.
    • " "
    • Embedded: use embedded " "color profile from RAW file, if it exists.
    • " "
    • Custom: use a custom " "input color space profile.

    ")); d->inIccUrlEdit = new RFileSelector(d->colormanSettings); d->inIccUrlEdit->setFileDlgMode(QFileDialog::ExistingFile); d->inIccUrlEdit->setFileDlgFilter(i18n("ICC Files (*.icc; *.icm)")); d->outputColorSpaceLabel = new QLabel(i18nc("@label:listbox", "Workspace:"), d->colormanSettings); d->outputColorSpaceComboBox = new RComboBox( d->colormanSettings ); d->outputColorSpaceComboBox->insertItem(RawDecodingSettings::RAWCOLOR, i18nc("@item:inlistbox Workspace", "Raw (no profile)")); d->outputColorSpaceComboBox->insertItem(RawDecodingSettings::SRGB, i18nc("@item:inlistbox Workspace", "sRGB")); d->outputColorSpaceComboBox->insertItem(RawDecodingSettings::ADOBERGB, i18nc("@item:inlistbox Workspace", "Adobe RGB")); d->outputColorSpaceComboBox->insertItem(RawDecodingSettings::WIDEGAMMUT, i18nc("@item:inlistbox Workspace", "Wide Gamut")); d->outputColorSpaceComboBox->insertItem(RawDecodingSettings::PROPHOTO, i18nc("@item:inlistbox Workspace", "Pro-Photo")); d->outputColorSpaceComboBox->insertItem(RawDecodingSettings::CUSTOMOUTPUTCS, i18nc("@item:inlistbox Workspace", "Custom")); d->outputColorSpaceComboBox->setDefaultIndex(RawDecodingSettings::SRGB); d->outputColorSpaceComboBox->setToolTip(i18nc("@info:whatsthis", "Workspace" "

    Select here the output color space used to decode RAW data.

    " - "

    • Raw (linear): " + "

      • Raw (no profile): " "in this mode, no output color space is used during RAW decoding.
      • " "
      • sRGB: this is an RGB " "color space, created cooperatively by Hewlett-Packard and " "Microsoft. It is the best choice for images destined for the Web " "and portrait photography.
      • " "
      • Adobe RGB: this color " "space is an extended RGB color space, developed by Adobe. It is " "used for photography applications such as advertising and fine " "art.
      • " "
      • Wide Gamut: this color " "space is an expanded version of the Adobe RGB color space.
      • " "
      • Pro-Photo: this color " "space is an RGB color space, developed by Kodak, that offers an " "especially large gamut designed for use with photographic outputs " "in mind.
      • " "
      • Custom: use a custom " "output color space profile.

      ")); d->outIccUrlEdit = new RFileSelector(d->colormanSettings); d->outIccUrlEdit->setFileDlgMode(QFileDialog::ExistingFile); d->outIccUrlEdit->setFileDlgFilter(i18n("ICC Files (*.icc; *.icm)")); colormanLayout->addWidget(d->inputColorSpaceLabel, 0, 0, 1, 1); colormanLayout->addWidget(d->inputColorSpaceComboBox, 0, 1, 1, 2); colormanLayout->addWidget(d->inIccUrlEdit, 1, 0, 1, 3); colormanLayout->addWidget(d->outputColorSpaceLabel, 2, 0, 1, 1); colormanLayout->addWidget(d->outputColorSpaceComboBox, 2, 1, 1, 2); colormanLayout->addWidget(d->outIccUrlEdit, 3, 0, 1, 3); colormanLayout->setRowStretch(4, 10); colormanLayout->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); colormanLayout->setMargin(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); addItem(d->colormanSettings, KisIconUtils::loadIcon("kdcraw").pixmap(16, 16), i18nc("@label", "Color Management"), QString("colormanagement"), false); if (! (advSettings & COLORSPACE)) removeItem(COLORMANAGEMENT); addStretch(); // --------------------------------------------------------------- connect(d->unclipColorComboBox, static_cast(&RComboBox::activated), this, &DcrawSettingsWidget::slotUnclipColorActivated); connect(d->whiteBalanceComboBox, static_cast(&RComboBox::activated), this, &DcrawSettingsWidget::slotWhiteBalanceToggled); connect(d->noiseReductionComboBox, static_cast(&RComboBox::activated), this, &DcrawSettingsWidget::slotNoiseReductionChanged); connect(d->enableCACorrectionBox, &QCheckBox::toggled, this, &DcrawSettingsWidget::slotCACorrectionToggled); connect(d->autoCACorrectionBox, &QCheckBox::toggled, this, &DcrawSettingsWidget::slotAutoCAToggled); connect(d->blackPointCheckBox, SIGNAL(toggled(bool)), d->blackPointSpinBox, SLOT(setEnabled(bool))); connect(d->whitePointCheckBox, SIGNAL(toggled(bool)), d->whitePointSpinBox, SLOT(setEnabled(bool))); connect(d->sixteenBitsImage, &QCheckBox::toggled, this, &DcrawSettingsWidget::slotsixteenBitsImageToggled); connect(d->inputColorSpaceComboBox, static_cast(&RComboBox::activated), this, &DcrawSettingsWidget::slotInputColorSpaceChanged); connect(d->outputColorSpaceComboBox, static_cast(&RComboBox::activated), this, &DcrawSettingsWidget::slotOutputColorSpaceChanged); connect(d->expoCorrectionBox, &QCheckBox::toggled, this, &DcrawSettingsWidget::slotExposureCorrectionToggled); connect(d->expoCorrectionShiftSpinBox, &RDoubleNumInput::valueChanged, this, &DcrawSettingsWidget::slotExpoCorrectionShiftChanged); // Wrapper to emit signal when something is changed in settings. connect(d->inIccUrlEdit->lineEdit(), &QLineEdit::textChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->outIccUrlEdit->lineEdit(), &QLineEdit::textChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->whiteBalanceComboBox, static_cast(&RComboBox::activated), this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->RAWQualityComboBox, static_cast(&RComboBox::activated), this, &DcrawSettingsWidget::slotRAWQualityChanged); connect(d->unclipColorComboBox, static_cast(&RComboBox::activated), this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->inputColorSpaceComboBox, static_cast(&RComboBox::activated), this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->outputColorSpaceComboBox, static_cast(&RComboBox::activated), this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->blackPointCheckBox, &QCheckBox::toggled, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->whitePointCheckBox, &QCheckBox::toggled, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->sixteenBitsImage, &QCheckBox::toggled, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->fixColorsHighlightsBox, &QCheckBox::toggled, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->autoBrightnessBox, &QCheckBox::toggled, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->fourColorCheckBox, &QCheckBox::toggled, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->dontStretchPixelsCheckBox, &QCheckBox::toggled, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->refineInterpolationBox, &QCheckBox::toggled, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->customWhiteBalanceSpinBox, &RIntNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->reconstructSpinBox, &RIntNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->blackPointSpinBox, &RIntNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->whitePointSpinBox, &RIntNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->NRSpinBox1, &RIntNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->NRSpinBox2, &RIntNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->medianFilterPassesSpinBox, &RIntNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->customWhiteBalanceGreenSpinBox, &RDoubleNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->caRedMultSpinBox, &RDoubleNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->caBlueMultSpinBox, &RDoubleNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->brightnessSpinBox, &RDoubleNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->expoCorrectionHighlightSpinBox, &RDoubleNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); } DcrawSettingsWidget::~DcrawSettingsWidget() { delete d; } void DcrawSettingsWidget::updateMinimumWidth() { int width = 0; for (int i = 0; i < count(); i++) { if (widget(i)->width() > width) width = widget(i)->width(); } setMinimumWidth(width); } RFileSelector* DcrawSettingsWidget::inputProfileUrlEdit() const { return d->inIccUrlEdit; } RFileSelector* DcrawSettingsWidget::outputProfileUrlEdit() const { return d->outIccUrlEdit; } void DcrawSettingsWidget::resetToDefault() { setSettings(RawDecodingSettings()); } void DcrawSettingsWidget::slotsixteenBitsImageToggled(bool b) { setEnabledBrightnessSettings(!b); emit signalSixteenBitsImageToggled(d->sixteenBitsImage->isChecked()); } void DcrawSettingsWidget::slotWhiteBalanceToggled(int v) { if (v == 3) { d->customWhiteBalanceSpinBox->setEnabled(true); d->customWhiteBalanceGreenSpinBox->setEnabled(true); d->customWhiteBalanceLabel->setEnabled(true); d->customWhiteBalanceGreenLabel->setEnabled(true); } else { d->customWhiteBalanceSpinBox->setEnabled(false); d->customWhiteBalanceGreenSpinBox->setEnabled(false); d->customWhiteBalanceLabel->setEnabled(false); d->customWhiteBalanceGreenLabel->setEnabled(false); } } void DcrawSettingsWidget::slotUnclipColorActivated(int v) { if (v == 3) // Reconstruct Highlight method { d->reconstructLabel->setEnabled(true); d->reconstructSpinBox->setEnabled(true); } else { d->reconstructLabel->setEnabled(false); d->reconstructSpinBox->setEnabled(false); } } void DcrawSettingsWidget::slotNoiseReductionChanged(int item) { d->NRSpinBox1->setEnabled(true); d->NRLabel1->setEnabled(true); d->NRSpinBox2->setEnabled(true); d->NRLabel2->setEnabled(true); d->NRLabel1->setText(i18nc("@label", "Threshold:")); d->NRSpinBox1->setToolTip(i18nc("@info:whatsthis", "Threshold" "

      Set here the noise reduction threshold value to use.

      ")); switch(item) { case RawDecodingSettings::WAVELETSNR: case RawDecodingSettings::FBDDNR: case RawDecodingSettings::LINENR: d->NRSpinBox2->setVisible(false); d->NRLabel2->setVisible(false); break; case RawDecodingSettings::IMPULSENR: d->NRLabel1->setText(i18nc("@label", "Luminance:")); d->NRSpinBox1->setToolTip(i18nc("@info:whatsthis", "Luminance" "

      Amount of Luminance impulse noise reduction.

      ")); d->NRLabel2->setText(i18nc("@label", "Chrominance:")); d->NRSpinBox2->setToolTip(i18nc("@info:whatsthis", "Chrominance" "

      Amount of Chrominance impulse noise reduction.

      ")); d->NRSpinBox2->setVisible(true); d->NRLabel2->setVisible(true); break; default: d->NRSpinBox1->setEnabled(false); d->NRLabel1->setEnabled(false); d->NRSpinBox2->setEnabled(false); d->NRLabel2->setEnabled(false); d->NRSpinBox2->setVisible(false); d->NRLabel2->setVisible(false); break; } emit signalSettingsChanged(); } void DcrawSettingsWidget::slotCACorrectionToggled(bool b) { d->autoCACorrectionBox->setEnabled(b); slotAutoCAToggled(d->autoCACorrectionBox->isChecked()); } void DcrawSettingsWidget::slotAutoCAToggled(bool b) { if (b) { d->caRedMultSpinBox->setValue(0.0); d->caBlueMultSpinBox->setValue(0.0); } bool mult = (!b) && (d->autoCACorrectionBox->isEnabled()); d->caRedMultSpinBox->setEnabled(mult); d->caBlueMultSpinBox->setEnabled(mult); d->caRedMultLabel->setEnabled(mult); d->caBlueMultLabel->setEnabled(mult); emit signalSettingsChanged(); } void DcrawSettingsWidget::slotExposureCorrectionToggled(bool b) { d->expoCorrectionShiftLabel->setEnabled(b); d->expoCorrectionShiftSpinBox->setEnabled(b); d->expoCorrectionHighlightLabel->setEnabled(b); d->expoCorrectionHighlightSpinBox->setEnabled(b); slotExpoCorrectionShiftChanged(d->expoCorrectionShiftSpinBox->value()); } void DcrawSettingsWidget::slotExpoCorrectionShiftChanged(double ev) { // Only enable Highligh exposure correction if Shift correction is >= 1.0, else this settings do not take effect. bool b = (ev >= 1.0); d->expoCorrectionHighlightLabel->setEnabled(b); d->expoCorrectionHighlightSpinBox->setEnabled(b); emit signalSettingsChanged(); } void DcrawSettingsWidget::slotInputColorSpaceChanged(int item) { d->inIccUrlEdit->setEnabled(item == RawDecodingSettings::CUSTOMINPUTCS); } void DcrawSettingsWidget::slotOutputColorSpaceChanged(int item) { d->outIccUrlEdit->setEnabled(item == RawDecodingSettings::CUSTOMOUTPUTCS); } void DcrawSettingsWidget::slotRAWQualityChanged(int quality) { switch(quality) { case RawDecodingSettings::DCB: case RawDecodingSettings::VCD_AHD: // These options can be only available if Libraw use GPL2 pack. d->medianFilterPassesLabel->setEnabled(KDcraw::librawUseGPL2DemosaicPack()); d->medianFilterPassesSpinBox->setEnabled(KDcraw::librawUseGPL2DemosaicPack()); d->refineInterpolationBox->setEnabled(KDcraw::librawUseGPL2DemosaicPack()); break; case RawDecodingSettings::PL_AHD: case RawDecodingSettings::AFD: case RawDecodingSettings::VCD: case RawDecodingSettings::LMMSE: case RawDecodingSettings::AMAZE: d->medianFilterPassesLabel->setEnabled(false); d->medianFilterPassesSpinBox->setEnabled(false); d->refineInterpolationBox->setEnabled(false); break; default: // BILINEAR, VNG, PPG, AHD d->medianFilterPassesLabel->setEnabled(true); d->medianFilterPassesSpinBox->setEnabled(true); d->refineInterpolationBox->setEnabled(false); break; } emit signalSettingsChanged(); } void DcrawSettingsWidget::setEnabledBrightnessSettings(bool b) { d->brightnessLabel->setEnabled(b); d->brightnessSpinBox->setEnabled(b); } bool DcrawSettingsWidget::brightnessSettingsIsEnabled() const { return d->brightnessSpinBox->isEnabled(); } void DcrawSettingsWidget::setSettings(const RawDecodingSettings& settings) { d->sixteenBitsImage->setChecked(settings.sixteenBitsImage); switch(settings.whiteBalance) { case RawDecodingSettings::CAMERA: d->whiteBalanceComboBox->setCurrentIndex(1); break; case RawDecodingSettings::AUTO: d->whiteBalanceComboBox->setCurrentIndex(2); break; case RawDecodingSettings::CUSTOM: d->whiteBalanceComboBox->setCurrentIndex(3); break; default: d->whiteBalanceComboBox->setCurrentIndex(0); break; } slotWhiteBalanceToggled(d->whiteBalanceComboBox->currentIndex()); d->customWhiteBalanceSpinBox->setValue(settings.customWhiteBalance); d->customWhiteBalanceGreenSpinBox->setValue(settings.customWhiteBalanceGreen); d->fourColorCheckBox->setChecked(settings.RGBInterpolate4Colors); d->autoBrightnessBox->setChecked(settings.autoBrightness); d->fixColorsHighlightsBox->setChecked(settings.fixColorsHighlights); switch(settings.unclipColors) { case 0: d->unclipColorComboBox->setCurrentIndex(0); break; case 1: d->unclipColorComboBox->setCurrentIndex(1); break; case 2: d->unclipColorComboBox->setCurrentIndex(2); break; default: // Reconstruct Highlight method d->unclipColorComboBox->setCurrentIndex(3); d->reconstructSpinBox->setValue(settings.unclipColors-3); break; } slotUnclipColorActivated(d->unclipColorComboBox->currentIndex()); d->dontStretchPixelsCheckBox->setChecked(settings.DontStretchPixels); d->brightnessSpinBox->setValue(settings.brightness); d->blackPointCheckBox->setChecked(settings.enableBlackPoint); d->blackPointSpinBox->setEnabled(settings.enableBlackPoint); d->blackPointSpinBox->setValue(settings.blackPoint); d->whitePointCheckBox->setChecked(settings.enableWhitePoint); d->whitePointSpinBox->setEnabled(settings.enableWhitePoint); d->whitePointSpinBox->setValue(settings.whitePoint); int q = settings.RAWQuality; // If Libraw do not support GPL2 pack, reset to BILINEAR. if (!KDcraw::librawUseGPL2DemosaicPack()) { for (int i=RawDecodingSettings::DCB ; i <=RawDecodingSettings::LMMSE ; ++i) { if (q == i) { q = RawDecodingSettings::BILINEAR; qCDebug(LIBKDCRAW_LOG) << "Libraw GPL2 pack not available. Raw quality set to Bilinear"; break; } } } // If Libraw do not support GPL3 pack, reset to BILINEAR. if (!KDcraw::librawUseGPL3DemosaicPack() && (q == RawDecodingSettings::AMAZE)) { q = RawDecodingSettings::BILINEAR; qCDebug(LIBKDCRAW_LOG) << "Libraw GPL3 pack not available. Raw quality set to Bilinear"; } d->RAWQualityComboBox->setCurrentIndex(q); switch(q) { case RawDecodingSettings::DCB: d->medianFilterPassesSpinBox->setValue(settings.dcbIterations); d->refineInterpolationBox->setChecked(settings.dcbEnhanceFl); break; case RawDecodingSettings::VCD_AHD: d->medianFilterPassesSpinBox->setValue(settings.eeciRefine); d->refineInterpolationBox->setChecked(settings.eeciRefine); break; default: d->medianFilterPassesSpinBox->setValue(settings.medianFilterPasses); d->refineInterpolationBox->setChecked(false); // option not used. break; } slotRAWQualityChanged(q); d->inputColorSpaceComboBox->setCurrentIndex((int)settings.inputColorSpace); slotInputColorSpaceChanged((int)settings.inputColorSpace); d->outputColorSpaceComboBox->setCurrentIndex((int)settings.outputColorSpace); slotOutputColorSpaceChanged((int)settings.outputColorSpace); d->noiseReductionComboBox->setCurrentIndex(settings.NRType); slotNoiseReductionChanged(settings.NRType); d->NRSpinBox1->setValue(settings.NRThreshold); d->NRSpinBox2->setValue(settings.NRChroThreshold); d->enableCACorrectionBox->setChecked(settings.enableCACorrection); d->caRedMultSpinBox->setValue(settings.caMultiplier[0]); d->caBlueMultSpinBox->setValue(settings.caMultiplier[1]); d->autoCACorrectionBox->setChecked((settings.caMultiplier[0] == 0.0) && (settings.caMultiplier[1] == 0.0)); slotCACorrectionToggled(settings.enableCACorrection); d->expoCorrectionBox->setChecked(settings.expoCorrection); slotExposureCorrectionToggled(settings.expoCorrection); d->expoCorrectionShiftSpinBox->setValue(d->shiftExpoFromLinearToEv(settings.expoCorrectionShift)); d->expoCorrectionHighlightSpinBox->setValue(settings.expoCorrectionHighlight); d->inIccUrlEdit->lineEdit()->setText(settings.inputProfile); d->outIccUrlEdit->lineEdit()->setText(settings.outputProfile); } RawDecodingSettings DcrawSettingsWidget::settings() const { RawDecodingSettings prm; prm.sixteenBitsImage = d->sixteenBitsImage->isChecked(); switch(d->whiteBalanceComboBox->currentIndex()) { case 1: prm.whiteBalance = RawDecodingSettings::CAMERA; break; case 2: prm.whiteBalance = RawDecodingSettings::AUTO; break; case 3: prm.whiteBalance = RawDecodingSettings::CUSTOM; break; default: prm.whiteBalance = RawDecodingSettings::NONE; break; } prm.customWhiteBalance = d->customWhiteBalanceSpinBox->value(); prm.customWhiteBalanceGreen = d->customWhiteBalanceGreenSpinBox->value(); prm.RGBInterpolate4Colors = d->fourColorCheckBox->isChecked(); prm.autoBrightness = d->autoBrightnessBox->isChecked(); prm.fixColorsHighlights = d->fixColorsHighlightsBox->isChecked(); switch(d->unclipColorComboBox->currentIndex()) { case 0: prm.unclipColors = 0; break; case 1: prm.unclipColors = 1; break; case 2: prm.unclipColors = 2; break; default: // Reconstruct Highlight method prm.unclipColors = d->reconstructSpinBox->value()+3; break; } prm.DontStretchPixels = d->dontStretchPixelsCheckBox->isChecked(); prm.brightness = d->brightnessSpinBox->value(); prm.enableBlackPoint = d->blackPointCheckBox->isChecked(); prm.blackPoint = d->blackPointSpinBox->value(); prm.enableWhitePoint = d->whitePointCheckBox->isChecked(); prm.whitePoint = d->whitePointSpinBox->value(); prm.RAWQuality = (RawDecodingSettings::DecodingQuality)d->RAWQualityComboBox->currentIndex(); switch(prm.RAWQuality) { case RawDecodingSettings::DCB: prm.dcbIterations = d->medianFilterPassesSpinBox->value(); prm.dcbEnhanceFl = d->refineInterpolationBox->isChecked(); break; case RawDecodingSettings::VCD_AHD: prm.esMedPasses = d->medianFilterPassesSpinBox->value(); prm.eeciRefine = d->refineInterpolationBox->isChecked(); break; default: prm.medianFilterPasses = d->medianFilterPassesSpinBox->value(); break; } prm.NRType = (RawDecodingSettings::NoiseReduction)d->noiseReductionComboBox->currentIndex(); switch (prm.NRType) { case RawDecodingSettings::NONR: { prm.NRThreshold = 0; prm.NRChroThreshold = 0; break; } case RawDecodingSettings::WAVELETSNR: case RawDecodingSettings::FBDDNR: case RawDecodingSettings::LINENR: { prm.NRThreshold = d->NRSpinBox1->value(); prm.NRChroThreshold = 0; break; } default: // IMPULSENR { prm.NRThreshold = d->NRSpinBox1->value(); prm.NRChroThreshold = d->NRSpinBox2->value(); break; } } prm.enableCACorrection = d->enableCACorrectionBox->isChecked(); prm.caMultiplier[0] = d->caRedMultSpinBox->value(); prm.caMultiplier[1] = d->caBlueMultSpinBox->value(); prm.expoCorrection = d->expoCorrectionBox->isChecked(); prm.expoCorrectionShift = d->shiftExpoFromEvToLinear(d->expoCorrectionShiftSpinBox->value()); prm.expoCorrectionHighlight = d->expoCorrectionHighlightSpinBox->value(); prm.inputColorSpace = (RawDecodingSettings::InputColorSpace)(d->inputColorSpaceComboBox->currentIndex()); prm.outputColorSpace = (RawDecodingSettings::OutputColorSpace)(d->outputColorSpaceComboBox->currentIndex()); prm.inputProfile = d->inIccUrlEdit->lineEdit()->text(); prm.outputProfile = d->outIccUrlEdit->lineEdit()->text(); return prm; } void DcrawSettingsWidget::writeSettings(KConfigGroup& group) { RawDecodingSettings prm = settings(); prm.writeSettings(group); RExpanderBox::writeSettings(group); } void DcrawSettingsWidget::readSettings(KConfigGroup& group) { RawDecodingSettings prm; prm.readSettings(group); setSettings(prm); RExpanderBox::readSettings(group); } } // NameSpace KDcrawIface diff --git a/plugins/impex/raw/krita_raw_import.json b/plugins/impex/raw/krita_raw_import.json index b41b26a460..4c263f8430 100644 --- a/plugins/impex/raw/krita_raw_import.json +++ b/plugins/impex/raw/krita_raw_import.json @@ -1,12 +1,12 @@ { "Id": "Krita RAW Import Filter", "NoDisplay": "true", "Type": "Service", "X-KDE-Import": "image/x-krita-raw", "X-KDE-Library": "krita_raw_import", "X-KDE-ServiceTypes": [ "Krita/FileFilter" ], "X-KDE-Weight": "1", - "X-KDE-Extensions" : "rw2,nef,cr2,sr2,crw,pef,x3f,kdc,mrw,arw,k25,dcr,orf,raw,raw,raf,srf,dng" + "X-KDE-Extensions": "bay,bmq,cr2,crw,cs1,dc2,dcr,dng,erf,fff,hdr,k25,kdc,mdc,mos,mrw,nef,orf,pef,pxn,raf,raw,rdc,sr2,srf,x3f,arw,3fr,cine,ia,kc2,mef,nrw,qtk,rw2,sti,rwl,srw" } diff --git a/plugins/impex/video/video_saver.cpp b/plugins/impex/video/video_saver.cpp index d0564943a9..4521d300dc 100644 --- a/plugins/impex/video/video_saver.cpp +++ b/plugins/impex/video/video_saver.cpp @@ -1,361 +1,365 @@ /* * 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 #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(&ffmpegProcess, SIGNAL(error(QProcess::ProcessError)), SLOT(quit())); loop.connect(&watcher, SIGNAL(sigProgressChanged(int)), &progress, SLOT(setValue(int))); if (ffmpegProcess.state() != QProcess::NotRunning) { loop.exec(); // wait for some erroneous 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; 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("height")); KIS_SAFE_ASSERT_RECOVER_NOOP(configuration->hasProperty("width")); KIS_SAFE_ASSERT_RECOVER_NOOP(configuration->hasProperty("include_audio")); KIS_SAFE_ASSERT_RECOVER_NOOP(configuration->hasProperty("directory")); KIS_SAFE_ASSERT_RECOVER_NOOP(configuration->hasProperty("framerate")); KisImageAnimationInterface *animation = m_image->animationInterface(); const KisTimeRange fullRange = animation->fullClipRange(); const int frameRate = configuration->getInt("framerate", animation->framerate()); - const KisTimeRange clipRange(configuration->getInt("first_frame", fullRange.start()), configuration->getInt("last_frame", fullRange.end())); + const int sequenceNumberingOffset = configuration->getInt("sequence_start", 0); + const KisTimeRange clipRange(sequenceNumberingOffset + configuration->getInt("first_frame", fullRange.start()), + sequenceNumberingOffset + configuration->getInt("last_frame", fullRange.end()) + ); + const bool includeAudio = configuration->getBool("include_audio", true); const int exportHeight = configuration->getInt("height", int(m_image->height())); const int exportWidth = configuration->getInt("width", int(m_image->width())); // export dimensions could be off a little bit, so the last force option tweaks the pixels for the export to work const QString exportDimensions = QString("scale=w=").append(QString::number(exportWidth)).append(":h=") .append(QString::number(exportHeight)).append(":force_original_aspect_ratio=decrease"); 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) << "-start_number" << QString::number(clipRange.start()) << "-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()); if (result != KisImageBuilder_RESULT_OK) { return result; } } { QStringList args; args << "-r" << QString::number(frameRate) << "-start_number" << QString::number(clipRange.start()) << "-i" << savedFilesMask << "-i" << palettePath << "-lavfi" << "[0:v][1:v] paletteuse" << additionalOptionsList << "-y" << resultFile; // if we are exporting out at a different image size, we apply scaling filter if (m_image->height() != exportHeight || m_image->width() != exportWidth) { args << "-vf" << exportDimensions; } dbgFile << "savedFilesMask" << savedFilesMask << "start" << QString::number(clipRange.start()) << "duration" << clipRange.duration(); KisImageBuilder_Result result = m_runner->runFFMpeg(args, i18n("Encoding frames..."), framesDir.filePath("log_encode_gif.log"), clipRange.duration()); if (result != KisImageBuilder_RESULT_OK) { return result; } } } else { QStringList args; args << "-r" << QString::number(frameRate) << "-start_number" << QString::number(clipRange.start()) << "-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(); } // if we are exporting out at a different image size, we apply scaling filter // export options HAVE to go after input options, so make sure this is after the audio import if (m_image->height() != exportHeight || m_image->width() != exportWidth) { args << "-vf" << exportDimensions; } args << additionalOptionsList << "-y" << resultFile; result = m_runner->runFFMpeg(args, i18n("Encoding frames..."), framesDir.filePath("log_encode.log"), clipRange.duration()); } return result; } void VideoSaver::cancel() { m_runner->cancel(); } #include "video_saver.moc" diff --git a/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings_widget.cpp b/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings_widget.cpp index f9537ea5d1..abc24cf935 100644 --- a/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings_widget.cpp +++ b/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings_widget.cpp @@ -1,92 +1,92 @@ /* * Copyright (C) 2011 Silvio Heinrich * * 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_colorsmudgeop_settings_widget.h" #include "kis_brush_based_paintop_settings.h" #include "kis_overlay_mode_option.h" #include "kis_rate_option.h" #include "kis_smudge_option_widget.h" #include "kis_smudge_radius_option.h" #include #include #include #include #include #include #include #include #include #include #include #include "kis_texture_option.h" #include "kis_curve_option_widget.h" #include #include "kis_pressure_texture_strength_option.h" #include "kis_pressure_hsv_option.h" #include "kis_colorsmudgeop_settings.h" KisColorSmudgeOpSettingsWidget::KisColorSmudgeOpSettingsWidget(QWidget* parent): KisBrushBasedPaintopOptionWidget(parent) { setObjectName("brush option widget"); setPrecisionEnabled(true); addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size")); addPaintOpOption(new KisPressureSpacingOptionWidget(), i18n("Spacing")); addPaintOpOption(new KisPressureMirrorOptionWidget(), i18n("Mirror")); m_smudgeOptionWidget = new KisSmudgeOptionWidget(); addPaintOpOption(m_smudgeOptionWidget, i18n("Smudge Length")); addPaintOpOption(new KisCurveOptionWidget(new KisSmudgeRadiusOption(), i18n("0.0"), i18n("1.0")), i18n("Smudge Radius")); addPaintOpOption(new KisCurveOptionWidget(new KisRateOption("ColorRate", KisPaintOpOption::GENERAL, false), i18n("0.0"), i18n("1.0")), i18n("Color Rate")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation")); addPaintOpOption(new KisPressureScatterOptionWidget(), i18n("Scatter")); addPaintOpOption(new KisOverlayModeOptionWidget(), i18n("Overlay Mode")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureGradientOption(), i18n("0%"), i18n("100%")), i18n("Gradient")); addPaintOpOption(new KisCurveOptionWidget(KisPressureHSVOption::createHueOption(), KisPressureHSVOption::hueMinLabel(), KisPressureHSVOption::huemaxLabel()), i18n("Hue")); addPaintOpOption(new KisCurveOptionWidget(KisPressureHSVOption::createSaturationOption(), KisPressureHSVOption::saturationMinLabel(), KisPressureHSVOption::saturationmaxLabel()), i18n("Saturation")); - addPaintOpOption(new KisCurveOptionWidget(KisPressureHSVOption::createValueOption(), KisPressureHSVOption::valueMinLabel(), KisPressureHSVOption::valuemaxLabel()), i18n("Value")); + addPaintOpOption(new KisCurveOptionWidget(KisPressureHSVOption::createValueOption(), KisPressureHSVOption::valueMinLabel(), KisPressureHSVOption::valuemaxLabel()), i18nc("HSV Value", "Value")); addPaintOpOption(new KisTextureOption(), i18n("Pattern")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureTextureStrengthOption(), i18n("Weak"), i18n("Strong")), i18n("Strength")); } KisColorSmudgeOpSettingsWidget::~KisColorSmudgeOpSettingsWidget() { } KisPropertiesConfigurationSP KisColorSmudgeOpSettingsWidget::configuration() const { KisColorSmudgeOpSettingsSP config = new KisColorSmudgeOpSettings(); config->setOptionsWidget(const_cast(this)); config->setProperty("paintop", "colorsmudge"); writeConfiguration(config); return config; } void KisColorSmudgeOpSettingsWidget::notifyPageChanged() { KisBrushSP brush = this->brush(); bool pierced = brush ? brush->isPiercedApprox() : false; m_smudgeOptionWidget->updateBrushPierced(pierced); } diff --git a/plugins/paintops/curvebrush/kis_curve_line_option.cpp b/plugins/paintops/curvebrush/kis_curve_line_option.cpp index 30da5ecd99..fb042d6821 100644 --- a/plugins/paintops/curvebrush/kis_curve_line_option.cpp +++ b/plugins/paintops/curvebrush/kis_curve_line_option.cpp @@ -1,86 +1,86 @@ /* * Copyright (c) 2011 Lukáš Tvrdý * * 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_curve_line_option.h" #include "ui_wdgcurveoptions.h" class KisCurveOpOptionsWidget: public QWidget, public Ui::WdgCurveOptions { public: KisCurveOpOptionsWidget(QWidget *parent = 0) : QWidget(parent) { setupUi(this); historySizeSlider->setRange(2, 300); historySizeSlider->setValue(30); lineWidthSlider->setRange(1, 100); lineWidthSlider->setValue(1); lineWidthSlider->setSuffix(i18n(" px")); curvesOpacitySlider->setRange(0.0, 1.0, 2); curvesOpacitySlider->setValue(1.0); } }; KisCurveOpOption::KisCurveOpOption() : KisPaintOpOption(KisPaintOpOption::GENERAL, false) { m_checkable = false; m_options = new KisCurveOpOptionsWidget(); connect(m_options->connectionCHBox, SIGNAL(toggled(bool)), this, SLOT(emitSettingChanged())); connect(m_options->smoothingCHBox, SIGNAL(toggled(bool)), this, SLOT(emitSettingChanged())); connect(m_options->historySizeSlider, SIGNAL(valueChanged(qreal)), this, SLOT(emitSettingChanged())); connect(m_options->lineWidthSlider, SIGNAL(valueChanged(qreal)), this, SLOT(emitSettingChanged())); connect(m_options->curvesOpacitySlider, SIGNAL(valueChanged(qreal)), this, SLOT(emitSettingChanged())); setConfigurationPage(m_options); setObjectName("KisCurveOpOption"); } KisCurveOpOption::~KisCurveOpOption() { } void KisCurveOpOption::writeOptionSetting(KisPropertiesConfigurationSP config) const { - CurveOption op; + KisCurveOptionProperties op; op.curve_paint_connection_line = m_options->connectionCHBox->isChecked(); op.curve_smoothing = m_options->smoothingCHBox->isChecked(); op.curve_stroke_history_size = m_options->historySizeSlider->value(); op.curve_line_width = m_options->lineWidthSlider->value(); op.curve_curves_opacity = m_options->curvesOpacitySlider->value(); op.writeOptionSetting(config); } void KisCurveOpOption::readOptionSetting(const KisPropertiesConfigurationSP config) { - CurveOption op; + KisCurveOptionProperties op; op.readOptionSetting(config); m_options->connectionCHBox->setChecked(op.curve_paint_connection_line); m_options->smoothingCHBox->setChecked(op.curve_smoothing); m_options->historySizeSlider->setValue(op.curve_stroke_history_size); m_options->lineWidthSlider->setValue(op.curve_line_width); m_options->curvesOpacitySlider->setValue(op.curve_curves_opacity); } diff --git a/plugins/paintops/curvebrush/kis_curve_line_option.h b/plugins/paintops/curvebrush/kis_curve_line_option.h index bda1c4bb9f..7aec7bd24e 100644 --- a/plugins/paintops/curvebrush/kis_curve_line_option.h +++ b/plugins/paintops/curvebrush/kis_curve_line_option.h @@ -1,72 +1,72 @@ /* * Copyright (c) 2011 Lukáš Tvrdý * * 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_CURVE_LINE_OPTION_H #define KIS_CURVE_LINE_OPTION_H #include class KisCurveOpOptionsWidget; // new rewrite const QString CURVE_LINE_WIDTH = "Curve/lineWidth"; // same as in sketch const QString CURVE_PAINT_CONNECTION_LINE = "Curve/makeConnection"; // same as in sketch const QString CURVE_STROKE_HISTORY_SIZE = "Curve/strokeHistorySize"; const QString CURVE_SMOOTHING = "Curve/smoothing"; const QString CURVE_CURVES_OPACITY = "Curve/curvesOpacity"; class KisCurveOpOption : public KisPaintOpOption { public: KisCurveOpOption(); ~KisCurveOpOption() override; void writeOptionSetting(KisPropertiesConfigurationSP setting) const override; void readOptionSetting(const KisPropertiesConfigurationSP setting) override; private: KisCurveOpOptionsWidget * m_options; }; -class CurveOption : public KisBaseOption +class KisCurveOptionProperties : public KisPaintopPropertiesBase { public: bool curve_paint_connection_line; bool curve_smoothing; int curve_stroke_history_size; int curve_line_width; qreal curve_curves_opacity; void readOptionSettingImpl(const KisPropertiesConfiguration *config) override { curve_paint_connection_line = config->getBool(CURVE_PAINT_CONNECTION_LINE); curve_smoothing = config->getBool(CURVE_SMOOTHING); curve_stroke_history_size = config->getInt(CURVE_STROKE_HISTORY_SIZE); curve_line_width = config->getInt(CURVE_LINE_WIDTH); curve_curves_opacity = config->getDouble(CURVE_CURVES_OPACITY); } void writeOptionSettingImpl(KisPropertiesConfiguration *config) const override { config->setProperty(CURVE_PAINT_CONNECTION_LINE, curve_paint_connection_line); config->setProperty(CURVE_SMOOTHING, curve_smoothing); config->setProperty(CURVE_STROKE_HISTORY_SIZE, curve_stroke_history_size); config->setProperty(CURVE_LINE_WIDTH, curve_line_width); config->setProperty(CURVE_CURVES_OPACITY, curve_curves_opacity); } }; #endif diff --git a/plugins/paintops/curvebrush/kis_curve_paintop.h b/plugins/paintops/curvebrush/kis_curve_paintop.h index a4ada05ce6..be1a811779 100644 --- a/plugins/paintops/curvebrush/kis_curve_paintop.h +++ b/plugins/paintops/curvebrush/kis_curve_paintop.h @@ -1,64 +1,64 @@ /* * Copyright (c) 2008-2011 Lukáš Tvrdý * * 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_CURVEPAINTOP_H_ #define KIS_CURVEPAINTOP_H_ #include #include #include "curve_brush.h" #include "kis_curve_line_option.h" #include "kis_curve_paintop_settings.h" #include #include "kis_linewidth_option.h" #include "kis_curves_opacity_option.h" class KisPainter; class KisCurvePaintOp : public KisPaintOp { public: KisCurvePaintOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image); ~KisCurvePaintOp() override; void paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) override; protected: KisSpacingInformation paintAt(const KisPaintInformation& info) override; KisSpacingInformation updateSpacingImpl(const KisPaintInformation &info) const override; private: void paintLine(KisPaintDeviceSP dab, const KisPaintInformation &pi1, const KisPaintInformation &pi2); private: KisPaintDeviceSP m_dab; KisPaintDeviceSP m_dev; - CurveOption m_curveProperties; + KisCurveOptionProperties m_curveProperties; KisPressureOpacityOption m_opacityOption; KisLineWidthOption m_lineWidthOption; KisCurvesOpacityOption m_curvesOpacityOption; QList m_points; KisPainter * m_painter; }; #endif // KIS_CURVEPAINTOP_H_ diff --git a/plugins/paintops/curvebrush/kis_curve_paintop_settings.cpp b/plugins/paintops/curvebrush/kis_curve_paintop_settings.cpp index 269a6d87dd..2dcd0d00b4 100644 --- a/plugins/paintops/curvebrush/kis_curve_paintop_settings.cpp +++ b/plugins/paintops/curvebrush/kis_curve_paintop_settings.cpp @@ -1,203 +1,203 @@ /* * Copyright (c) 2008,2009 Lukáš Tvrdý * * 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 #include #include "kis_curve_line_option.h" struct KisCurvePaintOpSettings::Private { QList uniformProperties; }; KisCurvePaintOpSettings::KisCurvePaintOpSettings() : m_d(new Private) { } KisCurvePaintOpSettings::~KisCurvePaintOpSettings() { } void KisCurvePaintOpSettings::setPaintOpSize(qreal value) { - CurveOption option; + KisCurveOptionProperties option; option.readOptionSetting(this); option.curve_line_width = value; option.writeOptionSetting(this); } qreal KisCurvePaintOpSettings::paintOpSize() const { - CurveOption option; + KisCurveOptionProperties option; option.readOptionSetting(this); return option.curve_line_width; } bool KisCurvePaintOpSettings::paintIncremental() { return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP; } #include #include "kis_paintop_preset.h" #include "kis_paintop_settings_update_proxy.h" #include "kis_standard_uniform_properties_factory.h" QList KisCurvePaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props = listWeakToStrong(m_d->uniformProperties); if (props.isEmpty()) { { KisIntSliderBasedPaintOpPropertyCallback *prop = new KisIntSliderBasedPaintOpPropertyCallback( KisIntSliderBasedPaintOpPropertyCallback::Int, "curve_linewidth", i18n("Line Width"), settings, 0); prop->setRange(1, 100); prop->setSingleStep(1); prop->setSuffix(i18n(" px")); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { - CurveOption option; + KisCurveOptionProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.curve_line_width); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { - CurveOption option; + KisCurveOptionProperties option; option.readOptionSetting(prop->settings().data()); option.curve_line_width = prop->value().toInt(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisIntSliderBasedPaintOpPropertyCallback *prop = new KisIntSliderBasedPaintOpPropertyCallback( KisIntSliderBasedPaintOpPropertyCallback::Int, "curve_historysize", i18n("History Size"), settings, 0); prop->setRange(2, 300); prop->setSingleStep(1); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { - CurveOption option; + KisCurveOptionProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.curve_stroke_history_size); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { - CurveOption option; + KisCurveOptionProperties option; option.readOptionSetting(prop->settings().data()); option.curve_stroke_history_size = prop->value().toInt(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisDoubleSliderBasedPaintOpPropertyCallback *prop = new KisDoubleSliderBasedPaintOpPropertyCallback( KisDoubleSliderBasedPaintOpPropertyCallback::Double, "curve_lineopacity", i18n("Line Opacity"), settings, 0); prop->setRange(0, 100.0); prop->setSingleStep(0.01); prop->setDecimals(2); prop->setSuffix(i18n("%")); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { - CurveOption option; + KisCurveOptionProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.curve_curves_opacity * 100.0); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { - CurveOption option; + KisCurveOptionProperties option; option.readOptionSetting(prop->settings().data()); option.curve_curves_opacity = prop->value().toReal() / 100.0; option.writeOptionSetting(prop->settings().data()); }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisUniformPaintOpPropertyCallback *prop = new KisUniformPaintOpPropertyCallback( KisUniformPaintOpPropertyCallback::Bool, "curve_connectionline", i18n("Connection Line"), settings, 0); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { - CurveOption option; + KisCurveOptionProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.curve_paint_connection_line); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { - CurveOption option; + KisCurveOptionProperties option; option.readOptionSetting(prop->settings().data()); option.curve_paint_connection_line = prop->value().toBool(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } } { using namespace KisStandardUniformPropertiesFactory; Q_FOREACH (KisUniformPaintOpPropertySP prop, KisPaintOpSettings::uniformProperties(settings)) { if (prop->id() == opacity.id()) { props.prepend(prop); } } } return props; } diff --git a/plugins/paintops/curvebrush/kis_curve_paintop_settings_widget.cpp b/plugins/paintops/curvebrush/kis_curve_paintop_settings_widget.cpp index 4af175405b..dd75f2ca82 100644 --- a/plugins/paintops/curvebrush/kis_curve_paintop_settings_widget.cpp +++ b/plugins/paintops/curvebrush/kis_curve_paintop_settings_widget.cpp @@ -1,55 +1,55 @@ /* * Copyright (c) 2008,2010 Lukáš Tvrdý * * 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 #include #include #include "kis_curve_line_option.h" #include #include #include #include #include #include "kis_curves_opacity_option.h" KisCurvePaintOpSettingsWidget:: KisCurvePaintOpSettingsWidget(QWidget* parent) : KisPaintOpSettingsWidget(parent) { m_curveOption = new KisCurveOpOption(); - addPaintOpOption(m_curveOption, i18n("Value")); + addPaintOpOption(m_curveOption, i18nc("Brush settings curve value", "Value")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity")); addPaintOpOption(new KisCurveOptionWidget(new KisLineWidthOption(), i18n("0%"), i18n("100%")), i18n("Line width")); addPaintOpOption(new KisCurveOptionWidget(new KisCurvesOpacityOption(), i18n("0%"), i18n("100%")), i18n("Curves opacity")); addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode")); addPaintOpOption(new KisPaintActionTypeOption(), i18n("Painting Mode")); } KisCurvePaintOpSettingsWidget::~ KisCurvePaintOpSettingsWidget() { } KisPropertiesConfigurationSP KisCurvePaintOpSettingsWidget::configuration() const { KisCurvePaintOpSettings* config = new KisCurvePaintOpSettings(); config->setOptionsWidget(const_cast(this)); config->setProperty("paintop", "curvebrush"); // XXX: make this a const id string writeConfiguration(config); return config; } diff --git a/plugins/paintops/defaultpaintops/brush/kis_brushop.cpp b/plugins/paintops/defaultpaintops/brush/kis_brushop.cpp index 8583157ea3..8b91b464bd 100644 --- a/plugins/paintops/defaultpaintops/brush/kis_brushop.cpp +++ b/plugins/paintops/defaultpaintops/brush/kis_brushop.cpp @@ -1,426 +1,426 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004-2008 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2004 Adrian Page * Copyright (c) 2004 Cyrille Berger * Copyright (c) 2010 Lukáš Tvrdý * * 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_brushop.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "krita_utils.h" #include #include "kis_algebra_2d.h" #include #include #include #include "KisBrushOpResources.h" #include #include #include #include #include "kis_image_config.h" #include "kis_wrapped_rect.h" KisBrushOp::KisBrushOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image) : KisBrushBasedPaintOp(settings, painter) , m_opacityOption(node) , m_avgSpacing(50) , m_avgNumDabs(50) , m_avgUpdateTimePerDab(50) , m_idealNumRects(KisImageConfig().maxNumberOfThreads()) , m_minUpdatePeriod(10) , m_maxUpdatePeriod(100) { Q_UNUSED(image); Q_ASSERT(settings); /** * We do our own threading here, so we need to forbid the brushes * to do threading internally */ m_brush->setThreadingAllowed(false); - m_airbrushOptionWidget.readOptionSetting(settings); + m_airbrushOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_flowOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); m_ratioOption.readOptionSetting(settings); m_spacingOption.readOptionSetting(settings); m_rateOption.readOptionSetting(settings); m_softnessOption.readOptionSetting(settings); m_rotationOption.readOptionSetting(settings); m_scatterOption.readOptionSetting(settings); m_sharpnessOption.readOptionSetting(settings); m_opacityOption.resetAllSensors(); m_flowOption.resetAllSensors(); m_sizeOption.resetAllSensors(); m_ratioOption.resetAllSensors(); m_rateOption.resetAllSensors(); m_softnessOption.resetAllSensors(); m_sharpnessOption.resetAllSensors(); m_rotationOption.resetAllSensors(); m_scatterOption.resetAllSensors(); m_sharpnessOption.resetAllSensors(); m_rotationOption.applyFanCornersInfo(this); KisBrushSP baseBrush = m_brush; auto resourcesFactory = [baseBrush, settings, painter] () { KisDabCacheUtils::DabRenderingResources *resources = new KisBrushOpResources(settings, painter); resources->brush = baseBrush->clone(); return resources; }; m_dabExecutor.reset( new KisDabRenderingExecutor( painter->device()->compositionSourceColorSpace(), resourcesFactory, painter->runnableStrokeJobsInterface(), &m_mirrorOption, &m_precisionOption)); } KisBrushOp::~KisBrushOp() { } KisSpacingInformation KisBrushOp::paintAt(const KisPaintInformation& info) { if (!painter()->device()) return KisSpacingInformation(1.0); KisBrushSP brush = m_brush; Q_ASSERT(brush); if (!brush) return KisSpacingInformation(1.0); if (!brush->canPaintFor(info)) return KisSpacingInformation(1.0); qreal scale = m_sizeOption.apply(info); scale *= KisLodTransform::lodToScale(painter()->device()); if (checkSizeTooSmall(scale)) return KisSpacingInformation(); qreal rotation = m_rotationOption.apply(info); qreal ratio = m_ratioOption.apply(info); KisDabShape shape(scale, ratio, rotation); QPointF cursorPos = m_scatterOption.apply(info, brush->maskWidth(shape, 0, 0, info), brush->maskHeight(shape, 0, 0, info)); m_opacityOption.setFlow(m_flowOption.apply(info)); quint8 dabOpacity = OPACITY_OPAQUE_U8; quint8 dabFlow = OPACITY_OPAQUE_U8; m_opacityOption.apply(info, &dabOpacity, &dabFlow); KisDabCacheUtils::DabRequestInfo request(painter()->paintColor(), cursorPos, shape, info, m_softnessOption.apply(info)); m_dabExecutor->addDab(request, qreal(dabOpacity) / 255.0, qreal(dabFlow) / 255.0); KisSpacingInformation spacingInfo = - effectiveSpacing(scale, rotation, &m_airbrushOptionWidget, &m_spacingOption, info); + effectiveSpacing(scale, rotation, &m_airbrushOption, &m_spacingOption, info); // gather statistics about dabs m_avgSpacing(spacingInfo.scalarApprox()); return spacingInfo; } struct KisBrushOp::UpdateSharedState { // rendering data KisPainter *painter = 0; QList dabsQueue; // speed metrics QVector dabPoints; QElapsedTimer dabRenderingTimer; // final report QVector allDirtyRects; }; void KisBrushOp::addMirroringJobs(Qt::Orientation direction, QVector &rects, UpdateSharedStateSP state, QVector &jobs) { jobs.append(new KisRunnableStrokeJobData(0, KisStrokeJobData::SEQUENTIAL)); for (KisRenderedDab &dab : state->dabsQueue) { jobs.append( new KisRunnableStrokeJobData( [state, &dab, direction] () { state->painter->mirrorDab(direction, &dab); }, KisStrokeJobData::CONCURRENT)); } jobs.append(new KisRunnableStrokeJobData(0, KisStrokeJobData::SEQUENTIAL)); for (QRect &rc : rects) { state->painter->mirrorRect(direction, &rc); jobs.append( new KisRunnableStrokeJobData( [rc, state] () { state->painter->bltFixed(rc, state->dabsQueue); }, KisStrokeJobData::CONCURRENT)); } state->allDirtyRects.append(rects); } std::pair KisBrushOp::doAsyncronousUpdate(QVector &jobs) { bool someDabsAreStillInQueue = false; const bool hasPreparedDabsAtStart = m_dabExecutor->hasPreparedDabs(); if (!m_updateSharedState && hasPreparedDabsAtStart) { m_updateSharedState = toQShared(new UpdateSharedState()); UpdateSharedStateSP state = m_updateSharedState; state->painter = painter(); { const qreal dabRenderingTime = m_dabExecutor->averageDabRenderingTime(); const qreal totalRenderingTimePerDab = dabRenderingTime + m_avgUpdateTimePerDab.rollingMeanSafe(); // we limit the number of fetched dabs to fit the maximum update period and not // make visual hiccups const int dabsLimit = totalRenderingTimePerDab > 0 ? qMax(10, int(m_maxUpdatePeriod / totalRenderingTimePerDab * m_idealNumRects)) : -1; state->dabsQueue = m_dabExecutor->takeReadyDabs(painter()->hasMirroring(), dabsLimit, &someDabsAreStillInQueue); } KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!state->dabsQueue.isEmpty(), std::make_pair(m_currentUpdatePeriod, false)); const int diameter = m_dabExecutor->averageDabSize(); const qreal spacing = m_avgSpacing.rollingMean(); const int idealNumRects = m_idealNumRects; QVector rects; // wrap the dabs if needed if (painter()->device()->defaultBounds()->wrapAroundMode()) { /** * In WA mode we do two things: * * 1) We ensure that the parallel threads do not access the same are on * the image. For normal updates that is ensured by the code in KisImage * and the scheduler. Here we should do that manually by adjusting 'rects' * so that they would not intersect in the wrapped space. * * 2) We duplicate dabs, to ensure that all the pieces of dabs are painted * inside the wrapped rect. No pieces are dabs are painted twice, because * we paint only the parts intersecting the wrap rect. */ const QRect wrapRect = painter()->device()->defaultBounds()->bounds(); QList wrappedDabs; Q_FOREACH (const KisRenderedDab &dab, state->dabsQueue) { const QVector normalizationOrigins = KisWrappedRect::normalizationOriginsForRect(dab.realBounds(), wrapRect); Q_FOREACH(const QPoint &pt, normalizationOrigins) { KisRenderedDab newDab = dab; newDab.offset = pt; rects.append(newDab.realBounds() & wrapRect); wrappedDabs.append(newDab); } } state->dabsQueue = wrappedDabs; } else { // just get all rects Q_FOREACH (const KisRenderedDab &dab, state->dabsQueue) { rects.append(dab.realBounds()); } } // split/merge rects into non-overlapping areas rects = KisPaintOpUtils::splitDabsIntoRects(rects, idealNumRects, diameter, spacing); state->allDirtyRects = rects; Q_FOREACH (const KisRenderedDab &dab, state->dabsQueue) { state->dabPoints.append(dab.realBounds().center()); } state->dabRenderingTimer.start(); Q_FOREACH (const QRect &rc, rects) { jobs.append( new KisRunnableStrokeJobData( [rc, state] () { state->painter->bltFixed(rc, state->dabsQueue); }, KisStrokeJobData::CONCURRENT)); } /** * After the dab has been rendered once, we should mirror it either one * (h __or__ v) or three (h __and__ v) times. This sequence of 'if's achieves * the goal without any extra copying. Please note that it has __no__ 'else' * branches, which is done intentionally! */ if (state->painter->hasHorizontalMirroring()) { addMirroringJobs(Qt::Horizontal, rects, state, jobs); } if (state->painter->hasVerticalMirroring()) { addMirroringJobs(Qt::Vertical, rects, state, jobs); } if (state->painter->hasHorizontalMirroring() && state->painter->hasVerticalMirroring()) { addMirroringJobs(Qt::Horizontal, rects, state, jobs); } jobs.append( new KisRunnableStrokeJobData( [state, this, someDabsAreStillInQueue] () { Q_FOREACH(const QRect &rc, state->allDirtyRects) { state->painter->addDirtyRect(rc); } state->painter->setAverageOpacity(state->dabsQueue.last().averageOpacity); const int updateRenderingTime = state->dabRenderingTimer.elapsed(); const qreal dabRenderingTime = m_dabExecutor->averageDabRenderingTime(); m_avgNumDabs(state->dabsQueue.size()); const qreal currentUpdateTimePerDab = qreal(updateRenderingTime) / state->dabsQueue.size(); m_avgUpdateTimePerDab(currentUpdateTimePerDab); /** * NOTE: using currentUpdateTimePerDab in the calculation for the next update time instead * of the average one makes rendering speed about 40% faster. It happens because the * adaptation period is shorter than if it used */ const qreal totalRenderingTimePerDab = dabRenderingTime + currentUpdateTimePerDab; const int approxDabRenderingTime = qreal(totalRenderingTimePerDab) * m_avgNumDabs.rollingMean() / m_idealNumRects; m_currentUpdatePeriod = someDabsAreStillInQueue ? m_minUpdatePeriod : qBound(m_minUpdatePeriod, int(1.5 * approxDabRenderingTime), m_maxUpdatePeriod); { // debug chunk // ENTER_FUNCTION() << ppVar(state->allDirtyRects.size()) << ppVar(state->dabsQueue.size()) << ppVar(dabRenderingTime) << ppVar(updateRenderingTime); // ENTER_FUNCTION() << ppVar(m_currentUpdatePeriod) << ppVar(someDabsAreStillInQueue); } // release all the dab devices state->dabsQueue.clear(); m_updateSharedState.clear(); }, KisStrokeJobData::SEQUENTIAL)); } else if (m_updateSharedState && hasPreparedDabsAtStart) { someDabsAreStillInQueue = true; } return std::make_pair(m_currentUpdatePeriod, someDabsAreStillInQueue); } KisSpacingInformation KisBrushOp::updateSpacingImpl(const KisPaintInformation &info) const { const qreal scale = m_sizeOption.apply(info) * KisLodTransform::lodToScale(painter()->device()); qreal rotation = m_rotationOption.apply(info); - return effectiveSpacing(scale, rotation, &m_airbrushOptionWidget, &m_spacingOption, info); + return effectiveSpacing(scale, rotation, &m_airbrushOption, &m_spacingOption, info); } KisTimingInformation KisBrushOp::updateTimingImpl(const KisPaintInformation &info) const { - return KisPaintOpPluginUtils::effectiveTiming(&m_airbrushOptionWidget, &m_rateOption, info); + return KisPaintOpPluginUtils::effectiveTiming(&m_airbrushOption, &m_rateOption, info); } void KisBrushOp::paintLine(const KisPaintInformation& pi1, const KisPaintInformation& pi2, KisDistanceInformation *currentDistance) { if (m_sharpnessOption.isChecked() && m_brush && (m_brush->width() == 1) && (m_brush->height() == 1)) { if (!m_lineCacheDevice) { m_lineCacheDevice = source()->createCompositionSourceDevice(); } else { m_lineCacheDevice->clear(); } KisPainter p(m_lineCacheDevice); p.setPaintColor(painter()->paintColor()); p.drawDDALine(pi1.pos(), pi2.pos()); QRect rc = m_lineCacheDevice->extent(); painter()->bitBlt(rc.x(), rc.y(), m_lineCacheDevice, rc.x(), rc.y(), rc.width(), rc.height()); //fixes Bug 338011 painter()->renderMirrorMask(rc, m_lineCacheDevice); } else { KisPaintOp::paintLine(pi1, pi2, currentDistance); } } diff --git a/plugins/paintops/defaultpaintops/brush/kis_brushop.h b/plugins/paintops/defaultpaintops/brush/kis_brushop.h index 936ef68261..f7156905de 100644 --- a/plugins/paintops/defaultpaintops/brush/kis_brushop.h +++ b/plugins/paintops/defaultpaintops/brush/kis_brushop.h @@ -1,107 +1,107 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004-2008 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2004 Adrian Page * Copyright (c) 2004 Cyrille Berger * * 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_BRUSHOP_H_ #define KIS_BRUSHOP_H_ #include "kis_brush_based_paintop.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include class KisPainter; class KisColorSource; class KisDabRenderingExecutor; class KisRenderedDab; class KisRunnableStrokeJobData; class KisBrushOp : public KisBrushBasedPaintOp { public: KisBrushOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image); ~KisBrushOp() override; void paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) override; std::pair doAsyncronousUpdate(QVector &jobs) override; protected: KisSpacingInformation paintAt(const KisPaintInformation& info) override; KisSpacingInformation updateSpacingImpl(const KisPaintInformation &info) const override; KisTimingInformation updateTimingImpl(const KisPaintInformation &info) const override; struct UpdateSharedState; typedef QSharedPointer UpdateSharedStateSP; void addMirroringJobs(Qt::Orientation direction, QVector &rects, UpdateSharedStateSP state, QVector &jobs); UpdateSharedStateSP m_updateSharedState; private: - KisAirbrushOptionWidget m_airbrushOptionWidget; + KisAirbrushOptionProperties m_airbrushOption; KisPressureSizeOption m_sizeOption; KisPressureRatioOption m_ratioOption; KisPressureSpacingOption m_spacingOption; KisPressureRateOption m_rateOption; KisPressureFlowOption m_flowOption; KisFlowOpacityOption m_opacityOption; KisPressureSoftnessOption m_softnessOption; KisPressureSharpnessOption m_sharpnessOption; KisPressureRotationOption m_rotationOption; KisPressureScatterOption m_scatterOption; KisPaintDeviceSP m_lineCacheDevice; QScopedPointer m_dabExecutor; qreal m_currentUpdatePeriod = 20.0; KisRollingMeanAccumulatorWrapper m_avgSpacing; KisRollingMeanAccumulatorWrapper m_avgNumDabs; KisRollingMeanAccumulatorWrapper m_avgUpdateTimePerDab; const int m_idealNumRects; const int m_minUpdatePeriod; const int m_maxUpdatePeriod; }; #endif // KIS_BRUSHOP_H_ diff --git a/plugins/paintops/defaultpaintops/brush/kis_brushop_settings_widget.cpp b/plugins/paintops/defaultpaintops/brush/kis_brushop_settings_widget.cpp index 28bdd79433..cad21bdfc1 100644 --- a/plugins/paintops/defaultpaintops/brush/kis_brushop_settings_widget.cpp +++ b/plugins/paintops/defaultpaintops/brush/kis_brushop_settings_widget.cpp @@ -1,165 +1,165 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004-2008 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2004 Adrian Page * Copyright (c) 2004 Cyrille Berger * * 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_brushop_settings_widget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_texture_option.h" #include "kis_curve_option_widget.h" #include #include "kis_pressure_texture_strength_option.h" #include #include #include KisBrushOpSettingsWidget::KisBrushOpSettingsWidget(QWidget* parent) : KisBrushBasedPaintopOptionWidget(parent) { setObjectName("brush option widget"); setPrecisionEnabled(true); // Brush tip options addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode")); addPaintOpOption(new KisFlowOpacityOptionWidget(), i18n("Opacity")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureFlowOption(), i18n("0%"), i18n("100%")), i18n("Flow")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureRatioOption(), i18n("0%"), i18n("100%")), i18n("Ratio")); addPaintOpOption(new KisPressureSpacingOptionWidget(), i18n("Spacing")); addPaintOpOption(new KisPressureMirrorOptionWidget(), i18n("Mirror")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureSoftnessOption(), i18n("Soft"), i18n("Hard")), i18n("Softness")); addPaintOpOption(new KisPressureSharpnessOptionWidget(), i18n("Sharpness")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation")); addPaintOpOption(new KisPressureScatterOptionWidget(), i18n("Scatter")); // Colors options addPaintOpOption(new KisColorSourceOptionWidget(), i18n("Source")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureDarkenOption(), i18n("0.0"), i18n("1.0")), i18n("Darken")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureMixOption(), i18n("Foreground"), i18n("Background")), i18n("Mix")); addPaintOpOption(new KisCurveOptionWidget(KisPressureHSVOption::createHueOption(), KisPressureHSVOption::hueMinLabel(), KisPressureHSVOption::huemaxLabel()), i18n("Hue")); addPaintOpOption(new KisCurveOptionWidget(KisPressureHSVOption::createSaturationOption(), KisPressureHSVOption::saturationMinLabel(), KisPressureHSVOption::saturationmaxLabel()), i18n("Saturation")); - addPaintOpOption(new KisCurveOptionWidget(KisPressureHSVOption::createValueOption(), KisPressureHSVOption::valueMinLabel(), KisPressureHSVOption::valuemaxLabel()), i18n("Value")); + addPaintOpOption(new KisCurveOptionWidget(KisPressureHSVOption::createValueOption(), KisPressureHSVOption::valueMinLabel(), KisPressureHSVOption::valuemaxLabel()), i18nc("HSV Value", "Value")); addPaintOpOption(new KisAirbrushOptionWidget(false), i18n("Airbrush")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureRateOption(), i18n("0%"), i18n("100%")), i18n("Rate")); KisPaintActionTypeOption *actionTypeOption = new KisPaintActionTypeOption(); addPaintOpOption(actionTypeOption, i18n("Painting Mode")); addPaintOpOption(new KisTextureOption(), i18n("Pattern")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureTextureStrengthOption(), i18n("Weak"), i18n("Strong")), i18n("Strength")); KisMaskingBrushOption::MasterBrushSizeAdapter sizeAdapter = [this] () { return this->brush()->userEffectiveSize(); }; KisMaskingBrushOption *maskingOption = new KisMaskingBrushOption(sizeAdapter); addPaintOpOption(maskingOption, i18n("Brush Tip")); connect(maskingOption, SIGNAL(sigCheckedChanged(bool)), actionTypeOption, SLOT(slotForceWashMode(bool))); { KisCurveOption *maskingSizeOption = new KisPressureSizeOption(); maskingSizeOption->setChecked(false); addPaintOpOption( new KisPrefixedPaintOpOptionWrapper( KisPaintOpUtils::MaskingBrushPresetPrefix, maskingSizeOption, i18n("0%"), i18n("100%")), i18n("Size"), KisPaintOpOption::MASKING_BRUSH); } addPaintOpOption( new KisPrefixedPaintOpOptionWrapper( KisPaintOpUtils::MaskingBrushPresetPrefix), i18n("Opacity"), KisPaintOpOption::MASKING_BRUSH); KisCurveOption *maskingFlowOption = new KisPressureFlowOption(); maskingFlowOption->setChecked(false); KisCurveOption *maskingRatioOption = new KisPressureRatioOption(); maskingRatioOption->setChecked(false); addPaintOpOption( new KisPrefixedPaintOpOptionWrapper( KisPaintOpUtils::MaskingBrushPresetPrefix, maskingFlowOption, i18n("0%"), i18n("100%")), i18n("Flow"), KisPaintOpOption::MASKING_BRUSH); addPaintOpOption( new KisPrefixedPaintOpOptionWrapper( KisPaintOpUtils::MaskingBrushPresetPrefix, maskingRatioOption, i18n("0%"), i18n("100%")), i18n("Ratio"), KisPaintOpOption::MASKING_BRUSH); addPaintOpOption( new KisPrefixedPaintOpOptionWrapper( KisPaintOpUtils::MaskingBrushPresetPrefix), i18n("Mirror"), KisPaintOpOption::MASKING_BRUSH); addPaintOpOption( new KisPrefixedPaintOpOptionWrapper( KisPaintOpUtils::MaskingBrushPresetPrefix, new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation"), KisPaintOpOption::MASKING_BRUSH); addPaintOpOption( new KisPrefixedPaintOpOptionWrapper( KisPaintOpUtils::MaskingBrushPresetPrefix), i18n("Scatter"), KisPaintOpOption::MASKING_BRUSH); } KisBrushOpSettingsWidget::~KisBrushOpSettingsWidget() { } KisPropertiesConfigurationSP KisBrushOpSettingsWidget::configuration() const { KisBrushBasedPaintOpSettingsSP config = new KisBrushOpSettings(); config->setOptionsWidget(const_cast(this)); config->setProperty("paintop", "paintbrush"); // XXX: make this a const id string writeConfiguration(config); return config; } diff --git a/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_option.cpp b/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_option.cpp index 4b23f1009f..82d740fd0b 100644 --- a/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_option.cpp +++ b/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_option.cpp @@ -1,131 +1,131 @@ /* * Copyright (c) 2008 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 "kis_duplicateop_option.h" #include #include #include #include "ui_wdgduplicateop.h" #include class KisDuplicateOpOptionsWidget: public QWidget, public Ui::DuplicateOpOptionsWidget { public: KisDuplicateOpOptionsWidget(QWidget *parent = 0) : QWidget(parent) { setupUi(this); } KisImageWSP m_image; protected: void showEvent(QShowEvent* event) override { QWidget::showEvent(event); //cbPerspective->setEnabled(m_image && m_image->perspectiveGrid() && m_image->perspectiveGrid()->countSubGrids() == 1); cbPerspective->setVisible(false); // XXX: Until perspective cloning works again! } }; KisDuplicateOpOption::KisDuplicateOpOption() : KisPaintOpOption(KisPaintOpOption::COLOR, false) { setObjectName("KisDuplicateOpOption"); m_checkable = false; m_optionWidget = new KisDuplicateOpOptionsWidget(); connect(m_optionWidget->cbHealing, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_optionWidget->cbPerspective, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_optionWidget->cbSourcePoint, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_optionWidget->chkCloneProjection, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); setConfigurationPage(m_optionWidget); } KisDuplicateOpOption::~KisDuplicateOpOption() { } bool KisDuplicateOpOption::healing() const { return m_optionWidget->cbHealing->isChecked(); } void KisDuplicateOpOption::setHealing(bool healing) { m_optionWidget->cbHealing->setChecked(healing); } bool KisDuplicateOpOption::correctPerspective() const { return m_optionWidget->cbPerspective->isChecked(); } void KisDuplicateOpOption::setPerspective(bool perspective) { m_optionWidget->cbPerspective->setChecked(perspective); } bool KisDuplicateOpOption::moveSourcePoint() const { return m_optionWidget->cbSourcePoint->isChecked(); } void KisDuplicateOpOption::setMoveSourcePoint(bool move) { m_optionWidget->cbSourcePoint->setChecked(move); } bool KisDuplicateOpOption::cloneFromProjection() const { return m_optionWidget->chkCloneProjection->isChecked(); } void KisDuplicateOpOption::setCloneFromProjection(bool cloneFromProjection) { m_optionWidget->chkCloneProjection->setChecked(cloneFromProjection); } void KisDuplicateOpOption::writeOptionSetting(KisPropertiesConfigurationSP setting) const { - DuplicateOption op; + KisDuplicateOptionProperties op; op.duplicate_healing = healing(); op.duplicate_correct_perspective = correctPerspective(); op.duplicate_move_source_point = moveSourcePoint(); op.duplicate_clone_from_projection = cloneFromProjection(); op.writeOptionSetting(setting); } void KisDuplicateOpOption::readOptionSetting(const KisPropertiesConfigurationSP setting) { - DuplicateOption op; + KisDuplicateOptionProperties op; op.readOptionSetting(setting); m_optionWidget->cbHealing->setChecked(op.duplicate_healing); m_optionWidget->cbPerspective->setChecked(op.duplicate_correct_perspective); m_optionWidget->cbSourcePoint->setChecked(op.duplicate_move_source_point); m_optionWidget->chkCloneProjection->setChecked(op.duplicate_clone_from_projection); } void KisDuplicateOpOption::setImage(KisImageWSP image) { m_optionWidget->m_image = image; } diff --git a/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_option.h b/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_option.h index 32416ac65a..b16e6cad14 100644 --- a/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_option.h +++ b/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_option.h @@ -1,83 +1,83 @@ /* * Copyright (c) 2008 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 KIS_DUPLICATEOP_OPTION_H #define KIS_DUPLICATEOP_OPTION_H #include const QString DUPLICATE_HEALING = "Duplicateop/Healing"; const QString DUPLICATE_CORRECT_PERSPECTIVE = "Duplicateop/CorrectPerspective"; const QString DUPLICATE_MOVE_SOURCE_POINT = "Duplicateop/MoveSourcePoint"; const QString DUPLICATE_CLONE_FROM_PROJECTION = "Duplicateop/CloneFromProjection"; class KisDuplicateOpOptionsWidget; class KisDuplicateOpOption : public KisPaintOpOption { public: KisDuplicateOpOption(); ~KisDuplicateOpOption() override; private: bool healing() const; void setHealing(bool healing); bool correctPerspective() const; void setPerspective(bool perspective); bool moveSourcePoint() const; void setMoveSourcePoint(bool move); bool cloneFromProjection() const; void setCloneFromProjection(bool cloneFromProjection); public: void writeOptionSetting(KisPropertiesConfigurationSP setting) const override; void readOptionSetting(const KisPropertiesConfigurationSP setting) override; void setImage(KisImageWSP image) override; private: KisDuplicateOpOptionsWidget * m_optionWidget; }; -struct DuplicateOption : public KisBaseOption +struct KisDuplicateOptionProperties : public KisPaintopPropertiesBase { bool duplicate_healing; bool duplicate_correct_perspective; bool duplicate_move_source_point; bool duplicate_clone_from_projection; void readOptionSettingImpl(const KisPropertiesConfiguration* setting) override { duplicate_healing = setting->getBool(DUPLICATE_HEALING, false); duplicate_correct_perspective = setting->getBool(DUPLICATE_CORRECT_PERSPECTIVE, false); duplicate_move_source_point = setting->getBool(DUPLICATE_MOVE_SOURCE_POINT, true); duplicate_clone_from_projection = setting->getBool(DUPLICATE_CLONE_FROM_PROJECTION, false); } void writeOptionSettingImpl(KisPropertiesConfiguration *setting) const override { setting->setProperty(DUPLICATE_HEALING, duplicate_healing); setting->setProperty(DUPLICATE_CORRECT_PERSPECTIVE, duplicate_correct_perspective); setting->setProperty(DUPLICATE_MOVE_SOURCE_POINT, duplicate_move_source_point); setting->setProperty(DUPLICATE_CLONE_FROM_PROJECTION, duplicate_clone_from_projection); } }; #endif diff --git a/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.cpp b/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.cpp index fb11d336db..f95ce4f6e4 100644 --- a/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.cpp +++ b/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.cpp @@ -1,239 +1,239 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004-2008 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2004 Adrian Page * Copyright (c) 2004 Cyrille Berger * * 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_duplicateop_settings.h" #include "kis_duplicateop_option.h" #include "kis_duplicateop_settings_widget.h" #include #include #include #include #include #include #include #include #include #include #include #include KisDuplicateOpSettings::KisDuplicateOpSettings() : m_isOffsetNotUptodate(false) { } KisDuplicateOpSettings::~KisDuplicateOpSettings() { } bool KisDuplicateOpSettings::paintIncremental() { return false; } QString KisDuplicateOpSettings::indirectPaintingCompositeOp() const { return COMPOSITE_COPY; } QPointF KisDuplicateOpSettings::offset() const { return m_offset; } QPointF KisDuplicateOpSettings::position() const { return m_position; } bool KisDuplicateOpSettings::mousePressEvent(const KisPaintInformation &info, Qt::KeyboardModifiers modifiers, KisNodeWSP currentNode) { bool ignoreEvent = true; if (modifiers & Qt::ControlModifier) { if (!m_sourceNode || !(modifiers & Qt::AltModifier)) { m_sourceNode = currentNode; } m_position = info.pos(); m_isOffsetNotUptodate = true; ignoreEvent = false; } else { if (m_isOffsetNotUptodate) { m_offset = info.pos() - m_position; m_isOffsetNotUptodate = false; } ignoreEvent = true; } return ignoreEvent; } KisNodeWSP KisDuplicateOpSettings::sourceNode() const { return m_sourceNode; } void KisDuplicateOpSettings::activate() { } void KisDuplicateOpSettings::fromXML(const QDomElement& elt) { // First, call the parent class fromXML to make sure all the // properties are saved to the map KisPaintOpSettings::fromXML(elt); m_offset.setX(KisDomUtils::toDouble(elt.attribute("OffsetX", "0.0"))); m_offset.setY(KisDomUtils::toDouble(elt.attribute("OffsetY", "0.0"))); m_isOffsetNotUptodate = false; } void KisDuplicateOpSettings::toXML(QDomDocument& doc, QDomElement& rootElt) const { // Then call the parent class fromXML KisPropertiesConfiguration::toXML(doc, rootElt); rootElt.setAttribute("OffsetX", QString::number(m_offset.x())); rootElt.setAttribute("OffsetY", QString::number(m_offset.y())); } KisPaintOpSettingsSP KisDuplicateOpSettings::clone() const { KisPaintOpSettingsSP setting = KisBrushBasedPaintOpSettings::clone(); KisDuplicateOpSettings* s = dynamic_cast(setting.data()); s->m_offset = m_offset; s->m_isOffsetNotUptodate = m_isOffsetNotUptodate; s->m_position = m_position; return setting; } QPainterPath KisDuplicateOpSettings::brushOutline(const KisPaintInformation &info, OutlineMode mode) { QPainterPath path; // clone tool should always show an outline path = KisBrushBasedPaintOpSettings::brushOutlineImpl(info, mode, 1.0, true); QPainterPath copy(path); QRectF rect2 = copy.boundingRect(); if (m_isOffsetNotUptodate || !getBool(DUPLICATE_MOVE_SOURCE_POINT)) { copy.translate(m_position - info.pos()); } else { copy.translate(-m_offset); } path.addPath(copy); qreal dx = rect2.width() / 4.0; qreal dy = rect2.height() / 4.0; rect2.adjust(dx, dy, -dx, -dy); path.moveTo(rect2.topLeft()); path.lineTo(rect2.bottomRight()); path.moveTo(rect2.topRight()); path.lineTo(rect2.bottomLeft()); return path; } #include #include "kis_paintop_preset.h" #include "kis_paintop_settings_update_proxy.h" #include "kis_duplicateop_option.h" #include "kis_standard_uniform_properties_factory.h" QList KisDuplicateOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props = listWeakToStrong(m_uniformProperties); if (props.isEmpty()) { { KisUniformPaintOpPropertyCallback *prop = new KisUniformPaintOpPropertyCallback( KisUniformPaintOpPropertyCallback::Bool, "clone_healing", i18n("Healing"), settings, 0); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { - DuplicateOption option; + KisDuplicateOptionProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.duplicate_healing); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { - DuplicateOption option; + KisDuplicateOptionProperties option; option.readOptionSetting(prop->settings().data()); option.duplicate_healing = prop->value().toBool(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisUniformPaintOpPropertyCallback *prop = new KisUniformPaintOpPropertyCallback( KisUniformPaintOpPropertyCallback::Bool, "clone_movesource", i18n("Move Source"), settings, 0); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { - DuplicateOption option; + KisDuplicateOptionProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.duplicate_move_source_point); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { - DuplicateOption option; + KisDuplicateOptionProperties option; option.readOptionSetting(prop->settings().data()); option.duplicate_move_source_point = prop->value().toBool(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } } return KisPaintOpSettings::uniformProperties(settings) + props; } diff --git a/plugins/paintops/deform/deform_brush.h b/plugins/paintops/deform/deform_brush.h index 33d71abcb8..6b8481d5fe 100644 --- a/plugins/paintops/deform/deform_brush.h +++ b/plugins/paintops/deform/deform_brush.h @@ -1,252 +1,252 @@ /* * Copyright (c) 2008, 2010 Lukáš Tvrdý * * 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 _DEFORM_BRUSH_H_ #define _DEFORM_BRUSH_H_ #include #include #include #include #include #if defined(_WIN32) || defined(_WIN64) #define srand48 srand inline double drand48() { return double(rand()) / RAND_MAX; } #endif enum DeformModes {GROW, SHRINK, SWIRL_CW, SWIRL_CCW, MOVE, LENS_IN, LENS_OUT, DEFORM_COLOR}; class DeformProperties { public: int action; qreal deformAmount; bool useBilinear; bool useCounter; bool useOldData; }; class DeformBase { public: DeformBase() {} virtual ~DeformBase() {} virtual void transform(qreal * x, qreal * y, qreal distance) { Q_UNUSED(x); Q_UNUSED(y); Q_UNUSED(distance); } }; /// Inverse weighted inverse scaling - grow&shrink class DeformScale : public DeformBase { public: void setFactor(qreal factor) { m_factor = factor; } qreal factor() { return m_factor; } void transform(qreal* x, qreal* y, qreal distance) override { qreal scaleFactor = (1.0 - distance) * m_factor + distance; *x = *x / scaleFactor; *y = *y / scaleFactor; } private: qreal m_factor; }; /// Inverse weighted rotation - swirlCW&&swirlCWW class DeformRotation : public DeformBase { public: void setAlpha(qreal alpha) { m_alpha = alpha; } void transform(qreal* maskX, qreal* maskY, qreal distance) override { distance = 1.0 - distance; qreal rotX = cos(-m_alpha * distance) * (*maskX) - sin(-m_alpha * distance) * (*maskY); qreal rotY = sin(-m_alpha * distance) * (*maskX) + cos(-m_alpha * distance) * (*maskY); *maskX = rotX; *maskY = rotY; } private: qreal m_alpha; }; /// Inverse move class DeformMove : public DeformBase { public: void setFactor(qreal factor) { m_factor = factor; } void setDistance(qreal dx, qreal dy) { m_dx = dx; m_dy = dy; } void transform(qreal* maskX, qreal* maskY, qreal distance) override { *maskX -= m_dx * m_factor * (1.0 - distance); *maskY -= m_dy * m_factor * (1.0 - distance); } private: qreal m_dx; qreal m_dy; qreal m_factor; }; /// Inverse lens distortion class DeformLens : public DeformBase { public: void setLensFactor(qreal k1, qreal k2) { m_k1 = k1; m_k2 = k2; } void setMaxDistance(qreal maxX, qreal maxY) { m_maxX = maxX; m_maxY = maxY; } void setMode(bool out) { m_out = out; } void transform(qreal* maskX, qreal* maskY, qreal distance) override { Q_UNUSED(distance); //normalize qreal normX = *maskX / m_maxX; qreal normY = *maskY / m_maxY; qreal radius_2 = normX * normX + normY * normY; qreal radius_4 = radius_2 * radius_2; if (m_out) { *maskX = normX * (1.0 + m_k1 * radius_2 + m_k2 * radius_4); *maskY = normY * (1.0 + m_k1 * radius_2 + m_k2 * radius_4); } else { *maskX = normX / (1.0 + m_k1 * radius_2 + m_k2 * radius_4); *maskY = normY / (1.0 + m_k1 * radius_2 + m_k2 * radius_4); } *maskX = m_maxX * (*maskX); *maskY = m_maxY * (*maskY); } private: qreal m_k1, m_k2; qreal m_maxX, m_maxY; bool m_out; }; /// Randomly disturb the pixels class DeformColor : public DeformBase { public: DeformColor() { srand48(time(0)); } void setFactor(qreal factor) { m_factor = factor; } void transform(qreal* x, qreal* y, qreal distance) override { Q_UNUSED(distance); qreal randomX = m_factor * ((drand48() * 2.0) - 1.0); qreal randomY = m_factor * ((drand48() * 2.0) - 1.0); *x += randomX; *y += randomY; } private: qreal m_factor; }; class DeformBrush { public: DeformBrush(); ~DeformBrush(); KisFixedPaintDeviceSP paintMask(KisFixedPaintDeviceSP dab, KisPaintDeviceSP layer, qreal scale, qreal rotation, QPointF pos, qreal subPixelX, qreal subPixelY, int dabX, int dabY); - void setSizeProperties(BrushSizeOption * properties) { + void setSizeProperties(KisBrushSizeOptionProperties * properties) { m_sizeProperties = properties; } void setProperties(DeformOption * properties) { m_properties = properties; } void initDeformAction(); QPointF hotSpot(qreal scale, qreal rotation); private: // return true if can paint bool setupAction( DeformModes mode, const QPointF& pos, QTransform const& rotation); void debugColor(const quint8* data, KoColorSpace * cs); qreal maskWidth(qreal scale) { return m_sizeProperties->brush_diameter * scale; } qreal maskHeight(qreal scale) { return m_sizeProperties->brush_diameter * m_sizeProperties->brush_aspect * scale; } inline qreal norme(qreal x, qreal y) { return x * x + y * y; } private: KisRandomSubAccessorSP m_srcAcc; bool m_firstPaint; qreal m_prevX, m_prevY; int m_counter; QRectF m_maskRect; DeformBase * m_deformAction; DeformOption * m_properties; - BrushSizeOption * m_sizeProperties; + KisBrushSizeOptionProperties * m_sizeProperties; }; #endif diff --git a/plugins/paintops/deform/kis_brush_size_option.cpp b/plugins/paintops/deform/kis_brush_size_option.cpp index b1e10c8411..115f798fc9 100644 --- a/plugins/paintops/deform/kis_brush_size_option.cpp +++ b/plugins/paintops/deform/kis_brush_size_option.cpp @@ -1,154 +1,154 @@ /* * Copyright (c) 2009,2010 Lukáš Tvrdý * * 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_brush_size_option.h" #include #include #include #include "ui_wdgBrushSizeOptions.h" class KisBrushSizeOptionsWidget: public QWidget, public Ui::WdgBrushSizeOptions { public: KisBrushSizeOptionsWidget(QWidget *parent = 0) : QWidget(parent) { setupUi(this); } }; KisBrushSizeOption::KisBrushSizeOption() : KisPaintOpOption(KisPaintOpOption::GENERAL, false) { setObjectName("KisBrushSizeOption"); m_checkable = false; m_options = new KisBrushSizeOptionsWidget(); // init slider values m_options->diameter->setRange(1.0, 1000, 0); m_options->diameter->setValue(20); m_options->diameter->setExponentRatio(3.0); m_options->diameter->setSuffix(i18n(" px")); m_options->aspectBox->setRange(0.01, 2.0, 2); m_options->aspectBox->setValue(1.0); m_options->aspectBox->setExponentRatio(1.0); m_options->scale->setRange(0.01, 10.0, 2); m_options->scale->setValue(1.0); m_options->spacing->setRange(0.01, 5.0, 2); m_options->spacing->setValue(0.3); m_options->rotationBox->setRange(0.0, 360.0, 0); m_options->rotationBox->setValue(0.0); m_options->rotationBox->setSuffix(QChar(Qt::Key_degree)); m_options->densityBox->setRange(0.0, 100.0, 0); m_options->densityBox->setValue(100); m_options->densityBox->setSuffix("%"); m_options->jitterMove->setRange(0.0, 5.0, 2); m_options->jitterMove->setValue(0.0); connect(m_options->diameter, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->scale, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->aspectBox, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->spacing, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->rotationBox, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->densityBox, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->jitterMove, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->jitterMoveBox, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_options->jitterMoveBox, SIGNAL(toggled(bool)), m_options->jitterMove, SLOT(setEnabled(bool))); setConfigurationPage(m_options); } KisBrushSizeOption::~KisBrushSizeOption() { delete m_options; } int KisBrushSizeOption::diameter() const { return qRound(m_options->diameter->value()); } void KisBrushSizeOption::setDiameter(int diameter) { m_options->diameter->setValue(diameter); } qreal KisBrushSizeOption::brushAspect() const { return m_options->aspectBox->value(); } void KisBrushSizeOption::writeOptionSetting(KisPropertiesConfigurationSP setting) const { - BrushSizeOption op; + KisBrushSizeOptionProperties op; op.brush_diameter = m_options->diameter->value(); op.brush_aspect = m_options->aspectBox->value(); op.brush_rotation = m_options->rotationBox->value(); op.brush_scale = m_options->scale->value(); op.brush_spacing = m_options->spacing->value(); op.brush_density = m_options->densityBox->value() / 100.0; op.brush_jitter_movement = m_options->jitterMove->value(); op.brush_jitter_movement_enabled = m_options->jitterMoveBox->isChecked(); op.writeOptionSetting(setting); } void KisBrushSizeOption::readOptionSetting(const KisPropertiesConfigurationSP setting) { - BrushSizeOption op; + KisBrushSizeOptionProperties op; op.readOptionSetting(setting); m_options->diameter->setValue(op.brush_diameter); m_options->aspectBox->setValue(op.brush_aspect); m_options->rotationBox->setValue(op.brush_rotation); m_options->scale->setValue(op.brush_scale); m_options->spacing->setValue(op.brush_spacing); m_options->densityBox->setValue(op.brush_density * 100.0); m_options->jitterMove->setValue(op.brush_jitter_movement); m_options->jitterMoveBox->setChecked(op.brush_jitter_movement_enabled); } void KisBrushSizeOption::setSpacing(qreal spacing) { m_options->spacing->setValue(spacing); } qreal KisBrushSizeOption::spacing() const { return m_options->spacing->value(); } diff --git a/plugins/paintops/deform/kis_brush_size_option.h b/plugins/paintops/deform/kis_brush_size_option.h index edcec35ba4..429eb13e24 100644 --- a/plugins/paintops/deform/kis_brush_size_option.h +++ b/plugins/paintops/deform/kis_brush_size_option.h @@ -1,98 +1,98 @@ /* * Copyright (c) 2009,2010 Lukáš Tvrdý * * 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_SIZE_OPTION_H_ #define KIS_SIZE_OPTION_H_ #include #include #include class KisBrushSizeOptionsWidget; const QString BRUSH_SHAPE = "Brush/shape"; const QString BRUSH_DIAMETER = "Brush/diameter"; const QString BRUSH_ASPECT = "Brush/aspect"; const QString BRUSH_SCALE = "Brush/scale"; const QString BRUSH_ROTATION = "Brush/rotation"; const QString BRUSH_SPACING = "Brush/spacing"; const QString BRUSH_DENSITY = "Brush/density"; const QString BRUSH_JITTER_MOVEMENT = "Brush/jitterMovement"; const QString BRUSH_JITTER_MOVEMENT_ENABLED = "Brush/jitterMovementEnabled"; class KisBrushSizeOption : public KisPaintOpOption { public: KisBrushSizeOption(); ~KisBrushSizeOption() override; int diameter() const; void setDiameter(int diameter); void setSpacing(qreal spacing); qreal spacing() const; qreal brushAspect() const; void writeOptionSetting(KisPropertiesConfigurationSP setting) const override; void readOptionSetting(const KisPropertiesConfigurationSP setting) override; private: KisBrushSizeOptionsWidget * m_options; }; -class BrushSizeOption : public KisBaseOption +class KisBrushSizeOptionProperties : public KisPaintopPropertiesBase { public: qreal brush_diameter; qreal brush_aspect; qreal brush_rotation; qreal brush_scale; qreal brush_spacing; qreal brush_density; qreal brush_jitter_movement; bool brush_jitter_movement_enabled; public: void readOptionSettingImpl(const KisPropertiesConfiguration *setting) override { brush_diameter = setting->getDouble(BRUSH_DIAMETER); brush_aspect = setting->getDouble(BRUSH_ASPECT); brush_rotation = setting->getDouble(BRUSH_ROTATION); brush_scale = setting->getDouble(BRUSH_SCALE); brush_spacing = setting->getDouble(BRUSH_SPACING); brush_density = setting->getDouble(BRUSH_DENSITY); brush_jitter_movement = setting->getDouble(BRUSH_JITTER_MOVEMENT); brush_jitter_movement_enabled = setting->getBool(BRUSH_JITTER_MOVEMENT_ENABLED); } void writeOptionSettingImpl(KisPropertiesConfiguration *setting) const override { setting->setProperty(BRUSH_DIAMETER, brush_diameter); setting->setProperty(BRUSH_ASPECT, brush_aspect); setting->setProperty(BRUSH_ROTATION, brush_rotation); setting->setProperty(BRUSH_SCALE, brush_scale); setting->setProperty(BRUSH_SPACING, brush_spacing); setting->setProperty(BRUSH_DENSITY, brush_density); setting->setProperty(BRUSH_JITTER_MOVEMENT, brush_jitter_movement); setting->setProperty(BRUSH_JITTER_MOVEMENT_ENABLED, brush_jitter_movement_enabled); } }; #endif diff --git a/plugins/paintops/deform/kis_deform_paintop.cpp b/plugins/paintops/deform/kis_deform_paintop.cpp index b55dd59b52..1bde615ba4 100644 --- a/plugins/paintops/deform/kis_deform_paintop.cpp +++ b/plugins/paintops/deform/kis_deform_paintop.cpp @@ -1,160 +1,160 @@ /* * Copyright (c) 2008-2010 Lukáš Tvrdý * * 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_deform_paintop.h" #include "kis_deform_paintop_settings.h" #include #include #include #include #include #include "kis_global.h" #include "kis_paint_device.h" #include "kis_painter.h" #include "kis_selection.h" #include "kis_random_accessor_ng.h" #include "kis_lod_transform.h" #include #include "kis_deform_option.h" #include "kis_brush_size_option.h" #include "kis_paintop_plugin_utils.h" #include #include #ifdef Q_OS_WIN // quoting DRAND48(3) man-page: // These functions are declared obsolete by SVID 3, // which states that rand(3) should be used instead. #define drand48() (static_cast(qrand()) / static_cast(RAND_MAX)) #endif KisDeformPaintOp::KisDeformPaintOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image) : KisPaintOp(painter) { Q_UNUSED(image); Q_UNUSED(node); Q_ASSERT(settings); m_sizeProperties.readOptionSetting(settings); m_properties.readOptionSetting(settings); - m_airbrushOptionWidget.readOptionSetting(settings); + m_airbrushOption.readOptionSetting(settings); // sensors m_sizeOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_rotationOption.readOptionSetting(settings); m_rateOption.readOptionSetting(settings); m_sizeOption.resetAllSensors(); m_opacityOption.resetAllSensors(); m_rotationOption.resetAllSensors(); m_rateOption.resetAllSensors(); m_deformBrush.setProperties(&m_properties); m_deformBrush.setSizeProperties(&m_sizeProperties); m_deformBrush.initDeformAction(); m_dev = source(); if ((m_sizeProperties.brush_diameter * 0.5) > 1) { m_ySpacing = m_xSpacing = m_sizeProperties.brush_diameter * 0.5 * m_sizeProperties.brush_spacing; } else { m_ySpacing = m_xSpacing = 1.0; } m_spacing = m_xSpacing; } KisDeformPaintOp::~KisDeformPaintOp() { } KisSpacingInformation KisDeformPaintOp::paintAt(const KisPaintInformation& info) { if (!painter()) return KisSpacingInformation(m_spacing); if (!m_dev) return KisSpacingInformation(m_spacing); KisFixedPaintDeviceSP dab = cachedDab(source()->compositionSourceColorSpace()); qint32 x; qreal subPixelX; qint32 y; qreal subPixelY; QPointF pt = info.pos(); if (m_sizeProperties.brush_jitter_movement_enabled) { pt.setX(pt.x() + ((m_sizeProperties.brush_diameter * drand48()) - m_sizeProperties.brush_diameter * 0.5) * m_sizeProperties.brush_jitter_movement); pt.setY(pt.y() + ((m_sizeProperties.brush_diameter * drand48()) - m_sizeProperties.brush_diameter * 0.5) * m_sizeProperties.brush_jitter_movement); } qreal rotation = m_rotationOption.apply(info); // Deform Brush is capable of working with zero scale, // so no additional checks for 'zero'ness are needed qreal scale = m_sizeOption.apply(info); rotation += m_sizeProperties.brush_rotation; scale *= m_sizeProperties.brush_scale; QPointF pos = pt - m_deformBrush.hotSpot(scale, rotation); splitCoordinate(pos.x(), &x, &subPixelX); splitCoordinate(pos.y(), &y, &subPixelY); KisFixedPaintDeviceSP mask = m_deformBrush.paintMask(dab, m_dev, scale, rotation, info.pos(), subPixelX, subPixelY, x, y ); // this happens for the first dab of the move mode, we need more information for being able to move if (!mask) { return updateSpacingImpl(info); } quint8 origOpacity = m_opacityOption.apply(painter(), info); painter()->bltFixedWithFixedSelection(x, y, dab, mask, mask->bounds().width() , mask->bounds().height()); painter()->renderMirrorMask(QRect(QPoint(x, y), QSize(mask->bounds().width() , mask->bounds().height())), dab, mask); painter()->setOpacity(origOpacity); return updateSpacingImpl(info); } KisSpacingInformation KisDeformPaintOp::updateSpacingImpl(const KisPaintInformation &info) const { return KisPaintOpPluginUtils::effectiveSpacing(1.0, 1.0, true, 0.0, false, m_spacing, false, 1.0, KisLodTransform::lodToScale(painter()->device()), - &m_airbrushOptionWidget, nullptr, info); + &m_airbrushOption, nullptr, info); } KisTimingInformation KisDeformPaintOp::updateTimingImpl(const KisPaintInformation &info) const { - return KisPaintOpPluginUtils::effectiveTiming(&m_airbrushOptionWidget, &m_rateOption, info); + return KisPaintOpPluginUtils::effectiveTiming(&m_airbrushOption, &m_rateOption, info); } diff --git a/plugins/paintops/deform/kis_deform_paintop.h b/plugins/paintops/deform/kis_deform_paintop.h index 5bd9a34669..0f40de3aa5 100644 --- a/plugins/paintops/deform/kis_deform_paintop.h +++ b/plugins/paintops/deform/kis_deform_paintop.h @@ -1,73 +1,73 @@ /* * Copyright (c) 2008,2010 Lukáš Tvrdý * * 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_DEFORMPAINTOP_H_ #define KIS_DEFORMPAINTOP_H_ #include #include #include #include #include #include #include #include "deform_brush.h" #include "kis_deform_paintop_settings.h" #include "kis_deform_option.h" class KisPainter; class KisDeformPaintOp : public KisPaintOp { public: KisDeformPaintOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image); ~KisDeformPaintOp() override; protected: KisSpacingInformation paintAt(const KisPaintInformation& info) override; KisSpacingInformation updateSpacingImpl(const KisPaintInformation &info) const override; KisTimingInformation updateTimingImpl(const KisPaintInformation &info) const override; private: KisPaintDeviceSP m_dab; KisPaintDeviceSP m_dev; DeformBrush m_deformBrush; DeformOption m_properties; - BrushSizeOption m_sizeProperties; + KisBrushSizeOptionProperties m_sizeProperties; - KisAirbrushOptionWidget m_airbrushOptionWidget; + KisAirbrushOptionProperties m_airbrushOption; KisPressureSizeOption m_sizeOption; KisPressureOpacityOption m_opacityOption; KisPressureRotationOption m_rotationOption; KisPressureRateOption m_rateOption; qreal m_xSpacing; qreal m_ySpacing; qreal m_spacing; }; #endif // KIS_DEFORMPAINTOP_H_ diff --git a/plugins/paintops/deform/kis_deform_paintop_settings.cpp b/plugins/paintops/deform/kis_deform_paintop_settings.cpp index c18383be0b..45f1518e0b 100644 --- a/plugins/paintops/deform/kis_deform_paintop_settings.cpp +++ b/plugins/paintops/deform/kis_deform_paintop_settings.cpp @@ -1,235 +1,235 @@ /* * Copyright (c) 2008,2009,2010 Lukáš Tvrdý * * 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 #include #include #include #include struct KisDeformPaintOpSettings::Private { QList uniformProperties; }; KisDeformPaintOpSettings::KisDeformPaintOpSettings() : KisOutlineGenerationPolicy(KisCurrentOutlineFetcher::SIZE_OPTION | KisCurrentOutlineFetcher::ROTATION_OPTION), m_d(new Private) { } KisDeformPaintOpSettings::~KisDeformPaintOpSettings() { } void KisDeformPaintOpSettings::setPaintOpSize(qreal value) { - BrushSizeOption option; + KisBrushSizeOptionProperties option; option.readOptionSetting(this); option.brush_diameter = value; option.writeOptionSetting(this); } qreal KisDeformPaintOpSettings::paintOpSize() const { - BrushSizeOption option; + KisBrushSizeOptionProperties option; option.readOptionSetting(this); return option.brush_diameter; } bool KisDeformPaintOpSettings::paintIncremental() { return true; } bool KisDeformPaintOpSettings::isAirbrushing() const { // version 2.3 if (hasProperty(AIRBRUSH_ENABLED)) { return getBool(AIRBRUSH_ENABLED); } else { return getBool(DEFORM_USE_MOVEMENT_PAINT); } } QPainterPath KisDeformPaintOpSettings::brushOutline(const KisPaintInformation &info, OutlineMode mode) { QPainterPath path; if (mode == CursorIsOutline || mode == CursorIsCircleOutline || mode == CursorTiltOutline) { qreal width = getInt(BRUSH_DIAMETER); qreal height = getInt(BRUSH_DIAMETER) * getDouble(BRUSH_ASPECT); path = ellipseOutline(width, height, getDouble(BRUSH_SCALE), getDouble(BRUSH_ROTATION)); QPainterPath tiltLine; QLineF tiltAngle(QPointF(0.0,0.0), QPointF(0.0,width)); tiltAngle.setLength(qMax(width*0.5, 50.0) * (1 - info.tiltElevation(info, 60.0, 60.0, true))); tiltAngle.setAngle((360.0 - fmod(KisPaintInformation::tiltDirection(info, true) * 360.0 + 270.0, 360.0))-3.0); tiltLine.moveTo(tiltAngle.p1()); tiltLine.lineTo(tiltAngle.p2()); tiltAngle.setAngle((360.0 - fmod(KisPaintInformation::tiltDirection(info, true) * 360.0 + 270.0, 360.0))+3.0); tiltLine.lineTo(tiltAngle.p2()); tiltLine.lineTo(tiltAngle.p1()); path = outlineFetcher()->fetchOutline(info, this, path); if (mode == CursorTiltOutline) { QPainterPath tiltLine = makeTiltIndicator(info, QPointF(0.0, 0.0), width * 0.5, 3.0); path.addPath(outlineFetcher()->fetchOutline(info, this, tiltLine, 1.0, 0.0, true, 0, 0)); } } return path; } #include #include #include "kis_paintop_preset.h" #include "kis_paintop_settings_update_proxy.h" #include "kis_brush_size_option.h" #include "kis_deform_option.h" #include "kis_standard_uniform_properties_factory.h" QList KisDeformPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props = listWeakToStrong(m_d->uniformProperties); if (props.isEmpty()) { { KisDoubleSliderBasedPaintOpPropertyCallback *prop = new KisDoubleSliderBasedPaintOpPropertyCallback( KisDoubleSliderBasedPaintOpPropertyCallback::Double, "deform_amount", i18n("Amount"), settings, 0); prop->setRange(0.01, 1.0); prop->setSingleStep(0.01); prop->setDecimals(2); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { DeformOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.deform_amount); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { DeformOption option; option.readOptionSetting(prop->settings().data()); option.deform_amount = prop->value().toReal(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisComboBasedPaintOpPropertyCallback *prop = new KisComboBasedPaintOpPropertyCallback( "deform_mode", i18n("Deform Mode"), settings, 0); QList modes; modes << i18n("Grow"); modes << i18n("Shrink"); modes << i18n("Swirl CW"); modes << i18n("Swirl CCW"); modes << i18n("Move"); modes << i18n("Lens Zoom In"); modes << i18n("Lens Zoom Out"); modes << i18n("Color Deformation"); prop->setItems(modes); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { DeformOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(int(option.deform_action - 1)); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { DeformOption option; option.readOptionSetting(prop->settings().data()); option.deform_action = prop->value().toInt() + 1; option.writeOptionSetting(prop->settings().data()); }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisIntSliderBasedPaintOpPropertyCallback *prop = new KisIntSliderBasedPaintOpPropertyCallback( KisIntSliderBasedPaintOpPropertyCallback::Int, "deform_angle", i18n("Angle"), settings, 0); const QString degree = QChar(Qt::Key_degree); prop->setRange(0, 360); prop->setSingleStep(1); prop->setSuffix(degree); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { - BrushSizeOption option; + KisBrushSizeOptionProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(int(option.brush_rotation)); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { - BrushSizeOption option; + KisBrushSizeOptionProperties option; option.readOptionSetting(prop->settings().data()); option.brush_rotation = prop->value().toInt(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } } { using namespace KisStandardUniformPropertiesFactory; Q_FOREACH (KisUniformPaintOpPropertySP prop, KisPaintOpSettings::uniformProperties(settings)) { if (prop->id() == opacity.id() || prop->id() == size.id()) { props.prepend(prop); } } } return props; } diff --git a/plugins/paintops/gridbrush/kis_grid_paintop_settings.cpp b/plugins/paintops/gridbrush/kis_grid_paintop_settings.cpp index a7d2235050..967abec3ae 100644 --- a/plugins/paintops/gridbrush/kis_grid_paintop_settings.cpp +++ b/plugins/paintops/gridbrush/kis_grid_paintop_settings.cpp @@ -1,139 +1,139 @@ /* * Copyright (c) 2009,2010 Lukáš Tvrdý (lukast.dev@gmail.com) * * 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 #include "kis_grid_paintop_settings.h" #include "kis_grid_paintop_settings_widget.h" #include "kis_gridop_option.h" #include "kis_grid_shape_option.h" #include struct KisGridPaintOpSettings::Private { QList uniformProperties; }; KisGridPaintOpSettings::KisGridPaintOpSettings() : KisOutlineGenerationPolicy(KisCurrentOutlineFetcher::NO_OPTION), m_d(new Private) { } void KisGridPaintOpSettings::setPaintOpSize(qreal value) { - GridOption option; + KisGridOpProperties option; option.readOptionSetting(this); option.grid_width = value; option.grid_height = value; option.writeOptionSetting(this); } qreal KisGridPaintOpSettings::paintOpSize() const { - GridOption option; + KisGridOpProperties option; option.readOptionSetting(this); return option.grid_width; } KisGridPaintOpSettings::~KisGridPaintOpSettings() { } bool KisGridPaintOpSettings::paintIncremental() { return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP; } QPainterPath KisGridPaintOpSettings::brushOutline(const KisPaintInformation &info, OutlineMode mode) { QPainterPath path; if (mode == CursorIsOutline || mode == CursorIsCircleOutline || mode == CursorTiltOutline) { qreal sizex = getInt(GRID_WIDTH) * getDouble(GRID_SCALE); qreal sizey = getInt(GRID_HEIGHT) * getDouble(GRID_SCALE); QRectF rc(0, 0, sizex, sizey); rc.translate(-rc.center()); path.addRect(rc); QPainterPath tiltLine; QLineF tiltAngle(QPointF(0.0,0.0), QPointF(0.0,sizex)); tiltAngle.setLength(qMax(sizex*0.5, 50.0) * (1 - info.tiltElevation(info, 60.0, 60.0, true))); tiltAngle.setAngle((360.0 - fmod(KisPaintInformation::tiltDirection(info, true) * 360.0 + 270.0, 360.0))-3.0); tiltLine.moveTo(tiltAngle.p1()); tiltLine.lineTo(tiltAngle.p2()); tiltAngle.setAngle((360.0 - fmod(KisPaintInformation::tiltDirection(info, true) * 360.0 + 270.0, 360.0))+3.0); tiltLine.lineTo(tiltAngle.p2()); tiltLine.lineTo(tiltAngle.p1()); path = outlineFetcher()->fetchOutline(info, this, path); if (mode == CursorTiltOutline) { QPainterPath tiltLine = makeTiltIndicator(info, QPointF(0.0, 0.0), sizex * 0.5, 3.0); path.addPath(outlineFetcher()->fetchOutline(info, this, tiltLine, 1.0, 0.0, true, 0, 0)); } } return path; } #include #include "kis_paintop_preset.h" #include "kis_paintop_settings_update_proxy.h" QList KisGridPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props = listWeakToStrong(m_d->uniformProperties); if (props.isEmpty()) { { KisIntSliderBasedPaintOpPropertyCallback *prop = new KisIntSliderBasedPaintOpPropertyCallback( KisIntSliderBasedPaintOpPropertyCallback::Int, "grid_divisionlevel", i18n("Division Level"), settings, 0); prop->setRange(1, 25); prop->setSingleStep(1); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { - GridOption option; + KisGridOpProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(int(option.grid_division_level)); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { - GridOption option; + KisGridOpProperties option; option.readOptionSetting(prop->settings().data()); option.grid_division_level = prop->value().toInt(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } } return KisPaintOpSettings::uniformProperties(settings) + props; } diff --git a/plugins/paintops/gridbrush/kis_gridop_option.cpp b/plugins/paintops/gridbrush/kis_gridop_option.cpp index b6bbd79261..f40bab00bd 100644 --- a/plugins/paintops/gridbrush/kis_gridop_option.cpp +++ b/plugins/paintops/gridbrush/kis_gridop_option.cpp @@ -1,175 +1,175 @@ /* * Copyright (c) 2009,2010 Lukáš Tvrdý (lukast.dev@gmail.com) * * 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_gridop_option.h" #include #include "ui_wdggridoptions.h" class KisGridOpOptionsWidget: public QWidget, public Ui::WdgGridOptions { public: KisGridOpOptionsWidget(QWidget *parent = 0) : QWidget(parent) { setupUi(this); } }; KisGridOpOption::KisGridOpOption() : KisPaintOpOption(KisPaintOpOption::GENERAL, false) { setObjectName("KisGridOpOption"); m_checkable = false; m_options = new KisGridOpOptionsWidget(); // initialize slider values m_options->gridWidthSPBox->setRange(1, 999, 0); m_options->gridWidthSPBox->setValue(25); m_options->gridWidthSPBox->setSuffix(i18n(" px")); m_options->gridWidthSPBox->setExponentRatio(3.0); m_options->gridHeightSPBox->setRange(1, 999, 0); m_options->gridHeightSPBox->setValue(25); m_options->gridHeightSPBox->setSuffix(i18n(" px")); m_options->gridHeightSPBox->setExponentRatio(3.0); m_options->divisionLevelSPBox->setRange(0, 25, 0); m_options->divisionLevelSPBox->setValue(2); m_options->scaleDSPBox->setRange(0.1, 10.0, 2); m_options->scaleDSPBox->setValue(1.0); m_options->scaleDSPBox->setExponentRatio(3.0); m_options->vertBorderDSPBox->setRange(0, 100, 2); m_options->vertBorderDSPBox->setValue(0.0); m_options->horizBorderDSPBox->setRange(0, 100, 2); m_options->vertBorderDSPBox->setValue(0.0); connect(m_options->gridWidthSPBox, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->gridHeightSPBox, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->divisionLevelSPBox, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->divisionPressureCHBox, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_options->scaleDSPBox, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->vertBorderDSPBox, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->horizBorderDSPBox, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->jitterBorderCHBox, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); setConfigurationPage(m_options); } KisGridOpOption::~KisGridOpOption() { delete m_options; } int KisGridOpOption::divisionLevel() const { return m_options->divisionLevelSPBox->value(); } int KisGridOpOption::gridWidth() const { return m_options->gridWidthSPBox->value(); } void KisGridOpOption::setWidth(int width) const { m_options->gridWidthSPBox->setValue(width); } int KisGridOpOption::gridHeight() const { return m_options->gridHeightSPBox->value(); } void KisGridOpOption::setHeight(int height) const { m_options->gridHeightSPBox->setValue(height); } bool KisGridOpOption::pressureDivision() const { return m_options->divisionPressureCHBox->isChecked(); } qreal KisGridOpOption::horizBorder() const { return m_options->vertBorderDSPBox->value(); } qreal KisGridOpOption::vertBorder() const { return m_options->horizBorderDSPBox->value(); } bool KisGridOpOption::randomBorder() const { return m_options->jitterBorderCHBox->isChecked(); } qreal KisGridOpOption::scale() const { return m_options->scaleDSPBox->value(); } void KisGridOpOption::writeOptionSetting(KisPropertiesConfigurationSP setting) const { - GridOption op; + KisGridOpProperties op; op.grid_width = gridWidth(); op.grid_height = gridHeight(); op.grid_division_level = divisionLevel(); op.grid_pressure_division = pressureDivision(); op.grid_scale = scale(); op.grid_vertical_border = vertBorder(); op.grid_horizontal_border = horizBorder(); op.grid_random_border = randomBorder(); op.writeOptionSetting(setting); } void KisGridOpOption::readOptionSetting(const KisPropertiesConfigurationSP setting) { - GridOption op; + KisGridOpProperties op; op.readOptionSetting(setting); m_options->gridWidthSPBox->setValue(op.grid_width); m_options->gridHeightSPBox->setValue(op.grid_height); m_options->divisionLevelSPBox->setValue(op.grid_division_level); m_options->divisionPressureCHBox->setChecked(op.grid_pressure_division); m_options->scaleDSPBox->setValue(op.grid_scale); m_options->vertBorderDSPBox->setValue(op.grid_vertical_border); m_options->horizBorderDSPBox->setValue(op.grid_horizontal_border); m_options->jitterBorderCHBox->setChecked(op.grid_random_border); } diff --git a/plugins/paintops/gridbrush/kis_gridop_option.h b/plugins/paintops/gridbrush/kis_gridop_option.h index 63d772b979..51b73104b0 100644 --- a/plugins/paintops/gridbrush/kis_gridop_option.h +++ b/plugins/paintops/gridbrush/kis_gridop_option.h @@ -1,100 +1,100 @@ /* * Copyright (c) 2009,2010 Lukáš Tvrdý (lukast.dev@gmail.com) * * 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_GRIDOP_OPTION_H #define KIS_GRIDOP_OPTION_H #include const QString GRID_WIDTH = "Grid/gridWidth"; const QString GRID_HEIGHT = "Grid/gridHeight"; const QString GRID_DIVISION_LEVEL = "Grid/divisionLevel"; const QString GRID_PRESSURE_DIVISION = "Grid/pressureDivision"; const QString GRID_SCALE = "Grid/scale"; const QString GRID_VERTICAL_BORDER = "Grid/verticalBorder"; const QString GRID_HORIZONTAL_BORDER = "Grid/horizontalBorder"; const QString GRID_RANDOM_BORDER = "Grid/randomBorder"; class KisGridOpOptionsWidget; class KisGridOpOption : public KisPaintOpOption { public: KisGridOpOption(); ~KisGridOpOption() override; int gridWidth() const; void setWidth(int width) const; int gridHeight() const; void setHeight(int height) const; int divisionLevel() const; bool pressureDivision() const; qreal scale() const; qreal vertBorder() const; qreal horizBorder() const; bool randomBorder() const; void writeOptionSetting(KisPropertiesConfigurationSP setting) const override; void readOptionSetting(const KisPropertiesConfigurationSP setting) override; private: KisGridOpOptionsWidget * m_options; }; -struct GridOption : public KisBaseOption +struct KisGridOpProperties : public KisPaintopPropertiesBase { int grid_width; int grid_height; int grid_division_level; bool grid_pressure_division; qreal grid_scale; qreal grid_vertical_border; qreal grid_horizontal_border; bool grid_random_border; void readOptionSettingImpl(const KisPropertiesConfiguration *setting) override { grid_width = qMax(1, setting->getInt(GRID_WIDTH)); grid_height = qMax(1, setting->getInt(GRID_HEIGHT)); grid_division_level = setting->getInt(GRID_DIVISION_LEVEL); grid_pressure_division = setting->getBool(GRID_PRESSURE_DIVISION); grid_scale = setting->getDouble(GRID_SCALE); grid_vertical_border = setting->getDouble(GRID_VERTICAL_BORDER); grid_horizontal_border = setting->getDouble(GRID_HORIZONTAL_BORDER); grid_random_border = setting->getBool(GRID_RANDOM_BORDER); } void writeOptionSettingImpl(KisPropertiesConfiguration *setting) const override { setting->setProperty(GRID_WIDTH, qMax(1, grid_width)); setting->setProperty(GRID_HEIGHT, qMax(1, grid_height)); setting->setProperty(GRID_DIVISION_LEVEL, grid_division_level); setting->setProperty(GRID_PRESSURE_DIVISION, grid_pressure_division); setting->setProperty(GRID_SCALE, grid_scale); setting->setProperty(GRID_VERTICAL_BORDER, grid_vertical_border); setting->setProperty(GRID_HORIZONTAL_BORDER, grid_horizontal_border); setting->setProperty(GRID_RANDOM_BORDER, grid_random_border); } }; #endif diff --git a/plugins/paintops/hairy/kis_hairy_paintop.cpp b/plugins/paintops/hairy/kis_hairy_paintop.cpp index c2f09c672b..4008b502cc 100644 --- a/plugins/paintops/hairy/kis_hairy_paintop.cpp +++ b/plugins/paintops/hairy/kis_hairy_paintop.cpp @@ -1,167 +1,167 @@ /* * Copyright (c) 2008-2010 Lukáš Tvrdý * * 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_hairy_paintop.h" #include "kis_hairy_paintop_settings.h" #include #include #include #include #include "kis_paint_device.h" #include "kis_painter.h" #include #include #include #include #include #include #include #include #include "kis_brush.h" KisHairyPaintOp::KisHairyPaintOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image) : KisPaintOp(painter) { Q_UNUSED(image) Q_ASSERT(settings); m_dev = node ? node->paintDevice() : 0; - KisBrushOption brushOption; + KisBrushOptionProperties brushOption; brushOption.readOptionSetting(settings); KisBrushSP brush = brushOption.brush(); KisFixedPaintDeviceSP dab = cachedDab(painter->device()->compositionSourceColorSpace()); // properly initialize fake paint information to avoid warnings KisPaintInformation fakePaintInformation; fakePaintInformation.setRandomSource(new KisRandomSource()); fakePaintInformation.setPerStrokeRandomSource(new KisPerStrokeRandomSource()); if (brush->brushType() == IMAGE || brush->brushType() == PIPE_IMAGE) { dab = brush->paintDevice(source()->colorSpace(), KisDabShape(), fakePaintInformation); } else { brush->mask(dab, painter->paintColor(), KisDabShape(), fakePaintInformation); } m_brush.fromDabWithDensity(dab, settings->getDouble(HAIRY_BRISTLE_DENSITY) * 0.01); m_brush.setInkColor(painter->paintColor()); loadSettings(static_cast(settings.data())); m_brush.setProperties(&m_properties); m_rotationOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); m_rotationOption.resetAllSensors(); m_opacityOption.resetAllSensors(); m_sizeOption.resetAllSensors(); } void KisHairyPaintOp::loadSettings(const KisBrushBasedPaintOpSettings *settings) { m_properties.inkAmount = settings->getInt(HAIRY_INK_AMOUNT); //TODO: wait for the transfer function with variable size m_properties.inkDepletionCurve = settings->getCubicCurve(HAIRY_INK_DEPLETION_CURVE).floatTransfer(m_properties.inkAmount); m_properties.inkDepletionEnabled = settings->getBool(HAIRY_INK_DEPLETION_ENABLED); m_properties.useSaturation = settings->getBool(HAIRY_INK_USE_SATURATION); m_properties.useOpacity = settings->getBool(HAIRY_INK_USE_OPACITY); m_properties.useWeights = settings->getBool(HAIRY_INK_USE_WEIGHTS); m_properties.pressureWeight = settings->getDouble(HAIRY_INK_PRESSURE_WEIGHT) / 100.0; m_properties.bristleLengthWeight = settings->getDouble(HAIRY_INK_BRISTLE_LENGTH_WEIGHT) / 100.0; m_properties.bristleInkAmountWeight = settings->getDouble(HAIRY_INK_BRISTLE_INK_AMOUNT_WEIGHT) / 100.0; m_properties.inkDepletionWeight = settings->getDouble(HAIRY_INK_DEPLETION_WEIGHT); m_properties.useSoakInk = settings->getBool(HAIRY_INK_SOAK); m_properties.useMousePressure = settings->getBool(HAIRY_BRISTLE_USE_MOUSEPRESSURE); m_properties.shearFactor = settings->getDouble(HAIRY_BRISTLE_SHEAR); m_properties.randomFactor = settings->getDouble(HAIRY_BRISTLE_RANDOM); m_properties.scaleFactor = settings->getDouble(HAIRY_BRISTLE_SCALE); m_properties.threshold = settings->getBool(HAIRY_BRISTLE_THRESHOLD); m_properties.antialias = settings->getBool(HAIRY_BRISTLE_ANTI_ALIASING); m_properties.useCompositing = settings->getBool(HAIRY_BRISTLE_USE_COMPOSITING); m_properties.connectedPath = settings->getBool(HAIRY_BRISTLE_CONNECTED); } KisSpacingInformation KisHairyPaintOp::paintAt(const KisPaintInformation& info) { return updateSpacingImpl(info); } KisSpacingInformation KisHairyPaintOp::updateSpacingImpl(const KisPaintInformation &info) const { Q_UNUSED(info); return KisSpacingInformation(0.5); } void KisHairyPaintOp::paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) { Q_UNUSED(currentDistance); if (!painter()) return; if (!m_dab) { m_dab = source()->createCompositionSourceDevice(); } else { m_dab->clear(); } /** * Even though we don't use spacing in hairy brush, we should still * initialize its distance information to ensure drawing angle and * other history-based sensors work fine. */ KisPaintInformation pi(pi2); KisPaintInformation::DistanceInformationRegistrar r = pi.registerDistanceInformation(currentDistance); // Hairy Brush is capable of working with zero scale, // so no additional checks for 'zero'ness are needed qreal scale = m_sizeOption.apply(pi); scale *= KisLodTransform::lodToScale(painter()->device()); qreal rotation = m_rotationOption.apply(pi); quint8 origOpacity = m_opacityOption.apply(painter(), pi); // we don't use spacing here (the brush itself is used only once // during initialization), so we should just skip the distance info // update m_brush.paintLine(m_dab, m_dev, pi1, pi, scale * m_properties.scaleFactor, rotation); //QRect rc = m_dab->exactBounds(); QRect rc = m_dab->extent(); painter()->bitBlt(rc.topLeft(), m_dab, rc); painter()->renderMirrorMask(rc, m_dab); painter()->setOpacity(origOpacity); // we don't use spacing in hairy brush, but history is // still important for us currentDistance->registerPaintedDab(pi, KisSpacingInformation(), KisTimingInformation()); } diff --git a/plugins/paintops/libpaintop/KisMaskingBrushOptionProperties.cpp b/plugins/paintops/libpaintop/KisMaskingBrushOptionProperties.cpp index ba3f7d410d..739bb3e701 100644 --- a/plugins/paintops/libpaintop/KisMaskingBrushOptionProperties.cpp +++ b/plugins/paintops/libpaintop/KisMaskingBrushOptionProperties.cpp @@ -1,87 +1,87 @@ /* * Copyright (c) 2017 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 "KisMaskingBrushOptionProperties.h" #include "kis_brush_option.h" #include #include KisMaskingBrushOptionProperties::KisMaskingBrushOptionProperties() : compositeOpId(COMPOSITE_MULT) { } void KisMaskingBrushOptionProperties::write(KisPropertiesConfiguration *setting, qreal masterBrushSize) const { setting->setProperty(KisPaintOpUtils::MaskingBrushEnabledTag, isEnabled); setting->setProperty(KisPaintOpUtils::MaskingBrushCompositeOpTag, compositeOpId); setting->setProperty(KisPaintOpUtils::MaskingBrushUseMasterSizeTag, useMasterSize); const qreal masterSizeCoeff = brush && masterBrushSize > 0 ? brush->userEffectiveSize() / masterBrushSize : 1.0; setting->setProperty(KisPaintOpUtils::MaskingBrushMasterSizeCoeffTag, masterSizeCoeff); // TODO: skip saving in some cases? // if (!isEnabled) return; if (brush) { KisPropertiesConfigurationSP embeddedConfig = new KisPropertiesConfiguration(); { - KisBrushOption option; + KisBrushOptionProperties option; option.setBrush(brush); option.writeOptionSetting(embeddedConfig); } setting->setPrefixedProperties(KisPaintOpUtils::MaskingBrushPresetPrefix, embeddedConfig); const QString brushFileName = brush->shortFilename(); if (!brushFileName.isEmpty()) { QStringList requiredFiles = setting->getStringList(KisPaintOpUtils::RequiredBrushFilesListTag); requiredFiles << brushFileName; setting->setProperty(KisPaintOpUtils::RequiredBrushFilesListTag, requiredFiles); } } } void KisMaskingBrushOptionProperties::read(const KisPropertiesConfiguration *setting, qreal masterBrushSize) { isEnabled = setting->getBool(KisPaintOpUtils::MaskingBrushEnabledTag); compositeOpId = setting->getString(KisPaintOpUtils::MaskingBrushCompositeOpTag, COMPOSITE_MULT); useMasterSize = setting->getBool(KisPaintOpUtils::MaskingBrushUseMasterSizeTag, true); KisPropertiesConfigurationSP embeddedConfig = new KisPropertiesConfiguration(); setting->getPrefixedProperties(KisPaintOpUtils::MaskingBrushPresetPrefix, embeddedConfig); - KisBrushOption option; + KisBrushOptionProperties option; option.readOptionSetting(embeddedConfig); brush = option.brush(); if (brush && useMasterSize) { const qreal masterSizeCoeff = setting->getDouble(KisPaintOpUtils::MaskingBrushMasterSizeCoeffTag, 1.0); brush->setUserEffectiveSize(masterSizeCoeff * masterBrushSize); } } diff --git a/plugins/paintops/libpaintop/Mainpage.dox b/plugins/paintops/libpaintop/Mainpage.dox index 2c103102d3..e06fcd30c4 100644 --- a/plugins/paintops/libpaintop/Mainpage.dox +++ b/plugins/paintops/libpaintop/Mainpage.dox @@ -1,33 +1,53 @@ /* * Copyright (C) 2009 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. */ /** \mainpage libpaintop: shared functionality for brush engines libpaintop is meant to share common classes used by Krita brush engines, suchs as certain configuration widgets. * Options A paintop can make use of the predefined options provided here. An option provides a graphical view for editing that option (as a page in an option popup) and possible also the code necessary for applying the effects of that option to the paint device as part of painting. +The following classes are important: + +
        +
      • KisPaintOp: this is the engine for a certain kind of brush. It will + be created in non-gui threads and keeps its state in a KisPaintOpSettings + object. +
      • KisPaintOpSettings: this is a KisPropertiesConfiguration subclass that + stores the settings for all the options that make up a paintop. +
      • Classes derived from KisPaintOpOption. These classes create a configuration + widget. That means that you cannot construct any those objects in a + KisPaintOp. KisPaintOps are created in non-gui threads. +
      • Classes derived from KisCurveOption. KisCurveOption classes have a generic GUI widget, + KisCurveOptionWidget. So, in contrast to KisPaintOpOption classes, KisCurveOption + instances can and will be created in the constructor of KisPaintOp paintops. + This class can manage to read and write its settings directly. +
      • Classes derived from KisPaintopPropertiesBase. These classes are non-gui, + and can read and write KisPropertiesConfiguration objects created by + KisPaintOpOption. +
      + */ // DOXYGEN_SET_IGNORE_PREFIX = Kis Ko K diff --git a/plugins/paintops/libpaintop/kis_airbrush_option_widget.cpp b/plugins/paintops/libpaintop/kis_airbrush_option_widget.cpp index eb421be299..ad7dda7411 100644 --- a/plugins/paintops/libpaintop/kis_airbrush_option_widget.cpp +++ b/plugins/paintops/libpaintop/kis_airbrush_option_widget.cpp @@ -1,139 +1,157 @@ /* * Copyright (c) 2008 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 "kis_airbrush_option_widget.h" #include "kis_paintop_settings.h" #include #include #include #include "ui_wdgairbrush.h" const qreal MINIMUM_RATE = 1.0; const qreal MAXIMUM_RATE = 1000.0; const int RATE_NUM_DECIMALS = 2; const qreal RATE_EXPONENT_RATIO = 2.0; const qreal RATE_SINGLE_STEP = 1.0; const qreal DEFAULT_RATE = 20.0; class KisAirbrushWidget: public QWidget, public Ui::WdgAirbrush { public: KisAirbrushWidget(QWidget *parent = 0, bool canIgnoreSpacing = true) : QWidget(parent) { setupUi(this); sliderRate->setRange(MINIMUM_RATE, MAXIMUM_RATE, RATE_NUM_DECIMALS); sliderRate->setExponentRatio(RATE_EXPONENT_RATIO); sliderRate->setSingleStep(RATE_SINGLE_STEP); sliderRate->setValue(DEFAULT_RATE); checkBoxIgnoreSpacing->setVisible(canIgnoreSpacing); checkBoxIgnoreSpacing->setEnabled(canIgnoreSpacing); } }; struct KisAirbrushOptionWidget::Private { public: bool ignoreSpacing; // We store the airbrush interval (in milliseconds) instead of the rate because the interval is // likely to be accessed more often. qreal airbrushInterval; QScopedPointer configPage; }; KisAirbrushOptionWidget::KisAirbrushOptionWidget(bool enabled, bool canIgnoreSpacing) : KisPaintOpOption(KisPaintOpOption::COLOR, enabled) , m_d(new Private()) { setObjectName("KisAirbrushOption"); // Initialize GUI. m_checkable = true; m_d->configPage.reset(new KisAirbrushWidget(nullptr, canIgnoreSpacing)); connect(m_d->configPage->sliderRate, SIGNAL(valueChanged(qreal)), SLOT(slotIntervalChanged())); connect(m_d->configPage->checkBoxIgnoreSpacing, SIGNAL(toggled(bool)), SLOT(slotIgnoreSpacingChanged())); setConfigurationPage(m_d->configPage.data()); // Read initial configuration from the GUI. updateIgnoreSpacing(); updateInterval(); } KisAirbrushOptionWidget::~KisAirbrushOptionWidget() { delete m_d; } void KisAirbrushOptionWidget::writeOptionSetting(KisPropertiesConfigurationSP setting) const { KIS_SAFE_ASSERT_RECOVER (m_d->airbrushInterval > 0.0) { m_d->airbrushInterval = 1.0; } setting->setProperty(AIRBRUSH_ENABLED, isChecked()); setting->setProperty(AIRBRUSH_RATE, 1000.0 / m_d->airbrushInterval); + + qDebug() << "writeOptionSetting. Interval:" << m_d->airbrushInterval << "Calculated rate" << 1000.0 / m_d->airbrushInterval << "Rate in widget" << m_d->configPage->sliderRate->value(); + setting->setProperty(AIRBRUSH_IGNORE_SPACING, m_d->ignoreSpacing); } void KisAirbrushOptionWidget::readOptionSetting(const KisPropertiesConfigurationSP setting) { setChecked(setting->getBool(AIRBRUSH_ENABLED)); // Update settings in the widget. The widget's signals should cause the changes to be propagated // to this->m_d as well. m_d->configPage->sliderRate->setValue(setting->getDouble(AIRBRUSH_RATE, DEFAULT_RATE)); - m_d->configPage->checkBoxIgnoreSpacing->setChecked(setting->getBool(AIRBRUSH_IGNORE_SPACING, - false)); + + qDebug() << "readOptionSetting. Interval:" << m_d->airbrushInterval << "Rate from settings" << setting->getDouble(AIRBRUSH_RATE, DEFAULT_RATE) << "Rate in widget" << m_d->configPage->sliderRate->value(); + m_d->configPage->checkBoxIgnoreSpacing->setChecked(setting->getBool(AIRBRUSH_IGNORE_SPACING, false)); } qreal KisAirbrushOptionWidget::airbrushInterval() const { return m_d->airbrushInterval; } bool KisAirbrushOptionWidget::ignoreSpacing() const { return m_d->ignoreSpacing; } void KisAirbrushOptionWidget::slotIntervalChanged() { updateInterval(); emitSettingChanged(); } void KisAirbrushOptionWidget::slotIgnoreSpacingChanged() { updateIgnoreSpacing(); emitSettingChanged(); } void KisAirbrushOptionWidget::updateInterval() { // Get rate in dabs per second, then convert to interval in milliseconds. qreal rate = m_d->configPage->sliderRate->value(); KIS_SAFE_ASSERT_RECOVER(rate > 0.0) { rate = 1.0; } m_d->airbrushInterval = 1000.0 / rate; + qDebug() << "updateInterval();. Interval:" << m_d->airbrushInterval << "Rate in widget" << m_d->configPage->sliderRate->value(); } void KisAirbrushOptionWidget::updateIgnoreSpacing() { m_d->ignoreSpacing = m_d->configPage->checkBoxIgnoreSpacing->isChecked(); } + + +void KisAirbrushOptionProperties::readOptionSettingImpl(const KisPropertiesConfiguration *setting){ + enabled = setting->getBool(AIRBRUSH_ENABLED); + airbrushInterval = 1000.0 / setting->getDouble(AIRBRUSH_RATE, DEFAULT_RATE); + ignoreSpacing = setting->getBool(AIRBRUSH_IGNORE_SPACING, false); +} + +void KisAirbrushOptionProperties::writeOptionSettingImpl(KisPropertiesConfiguration *setting) const { + setting->setProperty(AIRBRUSH_ENABLED, enabled); + setting->setProperty(AIRBRUSH_RATE, airbrushInterval > 0 ? 1000.0 / airbrushInterval : 1.0); + setting->setProperty(AIRBRUSH_IGNORE_SPACING, ignoreSpacing); +} diff --git a/plugins/paintops/libpaintop/kis_airbrush_option_widget.h b/plugins/paintops/libpaintop/kis_airbrush_option_widget.h index 4008044ac2..334364d3ed 100644 --- a/plugins/paintops/libpaintop/kis_airbrush_option_widget.h +++ b/plugins/paintops/libpaintop/kis_airbrush_option_widget.h @@ -1,66 +1,78 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * 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_AIRBRUSH_OPTION_H #define KIS_AIRBRUSH_OPTION_H #include #include /** * Allows the user to activate airbrushing of the brush mask (brush is painted at the same position over and over) * Rate is set in milliseconds. */ class PAINTOP_EXPORT KisAirbrushOptionWidget : public KisPaintOpOption { Q_OBJECT public: KisAirbrushOptionWidget(bool enabled = true, bool canIgnoreSpacing = true); ~KisAirbrushOptionWidget() override; void writeOptionSetting(KisPropertiesConfigurationSP setting) const override; void readOptionSetting(const KisPropertiesConfigurationSP setting) override; /** * Returns the airbrushing interval, in milliseconds. This value should be ignored if the * KisAirbrushOption is not checked according to isChecked(). */ qreal airbrushInterval() const; /** * Returns true if regular distance-based spacing should be ignored and overridden by time-based * spacing. This value should be ignored if the KisAirbrushOption is not checked according to * isChecked(). */ bool ignoreSpacing() const; private Q_SLOTS: void slotIntervalChanged(); void slotIgnoreSpacingChanged(); private: // Reads the airbrush interval from the GUI. void updateInterval(); // Reads the "ignore spacing" setting from the GUI. void updateIgnoreSpacing(); struct Private; Private *const m_d; }; +class PAINTOP_EXPORT KisAirbrushOptionProperties : public KisPaintopPropertiesBase +{ +public: + bool enabled {false}; + qreal airbrushInterval {1000.0 / 20.0}; + bool ignoreSpacing {false}; +protected: + void readOptionSettingImpl(const KisPropertiesConfiguration *settings) override; + void writeOptionSettingImpl(KisPropertiesConfiguration *settings) const override; +}; + + #endif diff --git a/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp b/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp index 1e1115ffd6..109e29b9ae 100644 --- a/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp +++ b/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp @@ -1,181 +1,181 @@ /* * Copyright (c) 2008 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 "kis_brush_based_paintop.h" #include "kis_properties_configuration.h" #include #include "kis_brush_option.h" #include #include #include "kis_painter.h" #include #include "kis_paintop_utils.h" #include "kis_paintop_plugin_utils.h" #include #include #include #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND Q_GLOBAL_STATIC(TextBrushInitializationWorkaround, s_instance) TextBrushInitializationWorkaround *TextBrushInitializationWorkaround::instance() { return s_instance; } void TextBrushInitializationWorkaround::preinitialize(KisPropertiesConfigurationSP settings) { - if (KisBrushOption::isTextBrush(settings.data())) { - KisBrushOption brushOption; + if (KisBrushOptionProperties::isTextBrush(settings.data())) { + KisBrushOptionProperties brushOption; brushOption.readOptionSetting(settings); m_brush = brushOption.brush(); m_settings = settings; } else { m_brush = 0; m_settings = 0; } } KisBrushSP TextBrushInitializationWorkaround::tryGetBrush(const KisPropertiesConfigurationSP settings) { return (settings && settings == m_settings ? m_brush : 0); } TextBrushInitializationWorkaround::TextBrushInitializationWorkaround() : m_settings(0) {} TextBrushInitializationWorkaround::~TextBrushInitializationWorkaround() {} void KisBrushBasedPaintOp::preinitializeOpStatically(KisPaintOpSettingsSP settings) { TextBrushInitializationWorkaround::instance()->preinitialize(settings); } #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ KisBrushBasedPaintOp::KisBrushBasedPaintOp(const KisPropertiesConfigurationSP settings, KisPainter* painter) : KisPaintOp(painter), m_textureProperties(painter->device()->defaultBounds()->currentLevelOfDetail()) { Q_ASSERT(settings); #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND m_brush = TextBrushInitializationWorkaround::instance()->tryGetBrush(settings); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ if (!m_brush) { - KisBrushOption brushOption; + KisBrushOptionProperties brushOption; brushOption.readOptionSetting(settings); m_brush = brushOption.brush(); } m_brush->notifyStrokeStarted(); m_precisionOption.readOptionSetting(settings); m_dabCache = new KisDabCache(m_brush); m_dabCache->setPrecisionOption(&m_precisionOption); m_mirrorOption.readOptionSetting(settings); m_dabCache->setMirrorPostprocessing(&m_mirrorOption); m_textureProperties.fillProperties(settings); m_dabCache->setTexturePostprocessing(&m_textureProperties); } KisBrushBasedPaintOp::~KisBrushBasedPaintOp() { delete m_dabCache; } bool KisBrushBasedPaintOp::checkSizeTooSmall(qreal scale) { scale *= m_brush->scale(); return KisPaintOpUtils::checkSizeTooSmall(scale, m_brush->width(), m_brush->height()); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale) const { // we parse dab rotation separately, so don't count it QSizeF metric = m_brush->characteristicSize(KisDabShape(scale, 1.0, 0)); return effectiveSpacing(metric.width(), metric.height(), 1.0, false, 0.0, false); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation, const KisPaintInformation &pi) const { return effectiveSpacing(scale, rotation, nullptr, nullptr, pi); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation, const KisPressureSpacingOption &spacingOption, const KisPaintInformation &pi) const { return effectiveSpacing(scale, rotation, nullptr, &spacingOption, pi); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation, - const KisAirbrushOptionWidget *airbrushOption, + const KisAirbrushOptionProperties *airbrushOption, const KisPressureSpacingOption *spacingOption, const KisPaintInformation &pi) const { bool isotropicSpacing = spacingOption && spacingOption->isotropicSpacing(); MirrorProperties prop = m_mirrorOption.apply(pi); const bool implicitFlipped = prop.horizontalMirror != prop.verticalMirror; // we parse dab rotation separately, so don't count it QSizeF metric = m_brush->characteristicSize(KisDabShape(scale, 1.0, 0)); return KisPaintOpPluginUtils::effectiveSpacing(metric.width(), metric.height(), isotropicSpacing, rotation, implicitFlipped, m_brush->spacing(), m_brush->autoSpacingActive(), m_brush->autoSpacingCoeff(), KisLodTransform::lodToScale(painter()->device()), airbrushOption, spacingOption, pi); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal dabWidth, qreal dabHeight, qreal extraScale, bool isotropicSpacing, qreal rotation, bool axesFlipped) const { return KisPaintOpUtils::effectiveSpacing(dabWidth, dabHeight, extraScale, true, isotropicSpacing, rotation, axesFlipped, m_brush->spacing(), m_brush->autoSpacingActive(), m_brush->autoSpacingCoeff(), KisLodTransform::lodToScale(painter()->device())); } bool KisBrushBasedPaintOp::canPaint() const { return m_brush != 0; } diff --git a/plugins/paintops/libpaintop/kis_brush_based_paintop.h b/plugins/paintops/libpaintop/kis_brush_based_paintop.h index 0123cd658a..c884c3be46 100644 --- a/plugins/paintops/libpaintop/kis_brush_based_paintop.h +++ b/plugins/paintops/libpaintop/kis_brush_based_paintop.h @@ -1,103 +1,103 @@ /* * Copyright (c) 2008 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 KIS_BRUSH_BASED_PAINTOP_H #define KIS_BRUSH_BASED_PAINTOP_H #include "kritapaintop_export.h" #include #include "kis_dab_cache.h" #include "kis_brush.h" #include "kis_texture_option.h" #include "kis_precision_option.h" #include "kis_airbrush_option_widget.h" #include "kis_pressure_mirror_option.h" #include class KisPropertiesConfiguration; class KisPressureSpacingOption; class KisPressureRateOption; class KisDabCache; /// Internal class TextBrushInitializationWorkaround { public: TextBrushInitializationWorkaround(); ~TextBrushInitializationWorkaround(); static TextBrushInitializationWorkaround* instance(); void preinitialize(KisPropertiesConfigurationSP settings); KisBrushSP tryGetBrush(const KisPropertiesConfigurationSP settings); private: KisBrushSP m_brush; KisPropertiesConfigurationSP m_settings; }; /** * This is a base class for paintops that use a KisBrush or derived * brush to paint with. This is mainly important for the spacing * generation. */ class PAINTOP_EXPORT KisBrushBasedPaintOp : public KisPaintOp { public: KisBrushBasedPaintOp(const KisPropertiesConfigurationSP settings, KisPainter* painter); ~KisBrushBasedPaintOp() override; bool checkSizeTooSmall(qreal scale); KisSpacingInformation effectiveSpacing(qreal scale) const; KisSpacingInformation effectiveSpacing(qreal scale, qreal rotation, const KisPaintInformation &pi) const; KisSpacingInformation effectiveSpacing(qreal scale, qreal rotation, const KisPressureSpacingOption &spacingOption, const KisPaintInformation &pi) const; KisSpacingInformation effectiveSpacing(qreal scale, qreal rotation, - const KisAirbrushOptionWidget *airbrushOption, + const KisAirbrushOptionProperties *airbrushOption, const KisPressureSpacingOption *spacingOption, const KisPaintInformation &pi) const; ///Reimplemented, false if brush is 0 bool canPaint() const override; #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND typedef int needs_preinitialization; static void preinitializeOpStatically(KisPaintOpSettingsSP settings); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ private: KisSpacingInformation effectiveSpacing(qreal dabWidth, qreal dabHeight, qreal extraScale, bool isotropicSpacing, qreal rotation, bool axesFlipped) const; protected: // XXX: make private! KisDabCache *m_dabCache; KisBrushSP m_brush; private: KisTextureProperties m_textureProperties; protected: KisPressureMirrorOption m_mirrorOption; KisPrecisionOption m_precisionOption; }; #endif diff --git a/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.cpp b/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.cpp index f89c36f9b0..c2a23f773b 100644 --- a/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.cpp +++ b/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.cpp @@ -1,336 +1,336 @@ /* * Copyright (c) 2010 Sven Langkamp * * 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_brush_based_paintop_settings.h" #include #include #include "kis_brush_based_paintop_options_widget.h" #include #include "kis_brush_server.h" #include #include "kis_signals_blocker.h" #include "kis_brush_option.h" #include struct BrushReader { BrushReader(const KisBrushBasedPaintOpSettings *parent) : m_parent(parent) { m_option.readOptionSetting(m_parent); } KisBrushSP brush() { return m_option.brush(); } const KisBrushBasedPaintOpSettings *m_parent; - KisBrushOption m_option; + KisBrushOptionProperties m_option; }; struct BrushWriter { BrushWriter(KisBrushBasedPaintOpSettings *parent) : m_parent(parent) { m_option.readOptionSetting(m_parent); } ~BrushWriter() { m_option.writeOptionSetting(m_parent); } KisBrushSP brush() { return m_option.brush(); } KisBrushBasedPaintOpSettings *m_parent; - KisBrushOption m_option; + KisBrushOptionProperties m_option; }; KisBrushBasedPaintOpSettings::KisBrushBasedPaintOpSettings() : KisOutlineGenerationPolicy(KisCurrentOutlineFetcher::SIZE_OPTION | KisCurrentOutlineFetcher::ROTATION_OPTION | KisCurrentOutlineFetcher::MIRROR_OPTION) { } bool KisBrushBasedPaintOpSettings::paintIncremental() { if (hasProperty("PaintOpAction")) { return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP; } return true; } KisPaintOpSettingsSP KisBrushBasedPaintOpSettings::clone() const { KisPaintOpSettingsSP _settings = KisOutlineGenerationPolicy::clone(); KisBrushBasedPaintOpSettingsSP settings = dynamic_cast(_settings.data()); settings->m_savedBrush = 0; return settings; } KisBrushSP KisBrushBasedPaintOpSettings::brush() const { KisBrushSP brush = m_savedBrush; if (!brush) { BrushReader w(this); brush = w.brush(); m_savedBrush = brush; } return brush; } QPainterPath KisBrushBasedPaintOpSettings::brushOutlineImpl(const KisPaintInformation &info, OutlineMode mode, qreal additionalScale, bool forceOutline) { QPainterPath path; if (forceOutline || mode == CursorIsOutline || mode == CursorIsCircleOutline || mode == CursorTiltOutline) { KisBrushSP brush = this->brush(); if (!brush) return path; qreal finalScale = brush->scale() * additionalScale; QPainterPath realOutline = brush->outline(); if (mode == CursorIsCircleOutline || mode == CursorTiltOutline || (forceOutline && mode == CursorNoOutline)) { QPainterPath ellipse; ellipse.addEllipse(realOutline.boundingRect()); realOutline = ellipse; } path = outlineFetcher()->fetchOutline(info, this, realOutline, finalScale, brush->angle()); if (mode == CursorTiltOutline) { QPainterPath tiltLine = makeTiltIndicator(info, realOutline.boundingRect().center(), realOutline.boundingRect().width() * 0.5, 3.0); path.addPath(outlineFetcher()->fetchOutline(info, this, tiltLine, finalScale, 0.0, true, realOutline.boundingRect().center().x(), realOutline.boundingRect().center().y())); } } return path; } QPainterPath KisBrushBasedPaintOpSettings::brushOutline(const KisPaintInformation &info, OutlineMode mode) { return brushOutlineImpl(info, mode, 1.0); } bool KisBrushBasedPaintOpSettings::isValid() const { QStringList files = getStringList(KisPaintOpUtils::RequiredBrushFilesListTag); files << getString(KisPaintOpUtils::RequiredBrushFileTag); Q_FOREACH (const QString &file, files) { if (!file.isEmpty()) { KisBrushSP brush = KisBrushServer::instance()->brushServer()->resourceByFilename(file); if (!brush) { return false; } } } return true; } bool KisBrushBasedPaintOpSettings::isLoadable() { return (KisBrushServer::instance()->brushServer()->resources().count() > 0); } void KisBrushBasedPaintOpSettings::setAngle(qreal value) { BrushWriter w(this); if (!w.brush()) return; w.brush()->setAngle(value); } qreal KisBrushBasedPaintOpSettings::angle() { return this->brush()->angle(); } void KisBrushBasedPaintOpSettings::setSpacing(qreal value) { BrushWriter w(this); if (!w.brush()) return; w.brush()->setSpacing(value); } qreal KisBrushBasedPaintOpSettings::spacing() { return this->brush()->spacing(); } void KisBrushBasedPaintOpSettings::setAutoSpacing(bool active, qreal coeff) { BrushWriter w(this); if (!w.brush()) return; w.brush()->setAutoSpacing(active, coeff); } bool KisBrushBasedPaintOpSettings::autoSpacingActive() { return this->brush()->autoSpacingActive(); } qreal KisBrushBasedPaintOpSettings::autoSpacingCoeff() { return this->brush()->autoSpacingCoeff(); } void KisBrushBasedPaintOpSettings::setPaintOpSize(qreal value) { BrushWriter w(this); if (!w.brush()) return; w.brush()->setUserEffectiveSize(value); } qreal KisBrushBasedPaintOpSettings::paintOpSize() const { return this->brush()->userEffectiveSize(); } #include #include "kis_paintop_preset.h" #include "kis_paintop_settings_update_proxy.h" QList KisBrushBasedPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props = listWeakToStrong(m_uniformProperties); if (props.isEmpty()) { { KisIntSliderBasedPaintOpPropertyCallback *prop = new KisIntSliderBasedPaintOpPropertyCallback( KisIntSliderBasedPaintOpPropertyCallback::Int, "angle", "Angle", settings, 0); prop->setRange(0, 360); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { KisBrushBasedPaintOpSettings *s = dynamic_cast(prop->settings().data()); const qreal angleResult = kisRadiansToDegrees(s->angle()); prop->setValue(angleResult); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { KisBrushBasedPaintOpSettings *s = dynamic_cast(prop->settings().data()); s->setAngle(kisDegreesToRadians(prop->value().toReal())); }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisUniformPaintOpPropertyCallback *prop = new KisUniformPaintOpPropertyCallback( KisUniformPaintOpPropertyCallback::Bool, "auto_spacing", "Auto Spacing", settings, 0); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { KisBrushBasedPaintOpSettings *s = dynamic_cast(prop->settings().data()); prop->setValue(s->autoSpacingActive()); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { KisBrushBasedPaintOpSettings *s = dynamic_cast(prop->settings().data()); s->setAutoSpacing(prop->value().toBool(), s->autoSpacingCoeff()); }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisDoubleSliderBasedPaintOpPropertyCallback *prop = new KisDoubleSliderBasedPaintOpPropertyCallback( KisDoubleSliderBasedPaintOpPropertyCallback::Double, "spacing", "Spacing", settings, 0); prop->setRange(0.01, 10); prop->setSingleStep(0.01); prop->setExponentRatio(3.0); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { KisBrushBasedPaintOpSettings *s = dynamic_cast(prop->settings().data()); const qreal value = s->autoSpacingActive() ? s->autoSpacingCoeff() : s->spacing(); prop->setValue(value); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { KisBrushBasedPaintOpSettings *s = dynamic_cast(prop->settings().data()); if (s->autoSpacingActive()) { s->setAutoSpacing(true, prop->value().toReal()); } else { s->setSpacing(prop->value().toReal()); } }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } } return KisPaintOpSettings::uniformProperties(settings) + props; } void KisBrushBasedPaintOpSettings::onPropertyChanged() { m_savedBrush.clear(); KisOutlineGenerationPolicy::onPropertyChanged(); } diff --git a/plugins/paintops/libpaintop/kis_brush_option.cpp b/plugins/paintops/libpaintop/kis_brush_option.cpp index 6d350d4bc3..1e85893227 100644 --- a/plugins/paintops/libpaintop/kis_brush_option.cpp +++ b/plugins/paintops/libpaintop/kis_brush_option.cpp @@ -1,104 +1,104 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2008 * Copyright (C) Sven Langkamp , (C) 2008 * * 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_brush_option.h" #include #include #include "kis_properties_configuration.h" #include -void KisBrushOption::writeOptionSettingImpl(KisPropertiesConfiguration *setting) const +void KisBrushOptionProperties::writeOptionSettingImpl(KisPropertiesConfiguration *setting) const { if (!m_brush) return; QDomDocument d; QDomElement e = d.createElement("Brush"); m_brush->toXML(d, e); d.appendChild(e); setting->setProperty("brush_definition", d.toString()); QString brushFileName = !m_brush->filename().isEmpty() ? m_brush->shortFilename() : QString(); setting->setProperty(KisPaintOpUtils::RequiredBrushFileTag, brushFileName); { QStringList requiredFiles = setting->getStringList(KisPaintOpUtils::RequiredBrushFilesListTag); requiredFiles << brushFileName; setting->setProperty(KisPaintOpUtils::RequiredBrushFilesListTag, requiredFiles); } } QDomElement getBrushXMLElement(const KisPropertiesConfiguration *setting) { QDomElement element; QString brushDefinition = setting->getString("brush_definition"); if (!brushDefinition.isEmpty()) { QDomDocument d; d.setContent(brushDefinition, false); element = d.firstChildElement("Brush"); } return element; } -void KisBrushOption::readOptionSettingImpl(const KisPropertiesConfiguration *setting) +void KisBrushOptionProperties::readOptionSettingImpl(const KisPropertiesConfiguration *setting) { QDomElement element = getBrushXMLElement(setting); if (!element.isNull()) { m_brush = KisBrush::fromXML(element); } } #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND #include "kis_text_brush_factory.h" -bool KisBrushOption::isTextBrush(const KisPropertiesConfiguration *setting) +bool KisBrushOptionProperties::isTextBrush(const KisPropertiesConfiguration *setting) { static QString textBrushId = KisTextBrushFactory().id(); QDomElement element = getBrushXMLElement(setting); QString brushType = element.attribute("type"); return brushType == textBrushId; } #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ -KisBrushSP KisBrushOption::brush() const +KisBrushSP KisBrushOptionProperties::brush() const { return m_brush; } -void KisBrushOption::setBrush(KisBrushSP brush) +void KisBrushOptionProperties::setBrush(KisBrushSP brush) { m_brush = brush; } diff --git a/plugins/paintops/libpaintop/kis_brush_option.h b/plugins/paintops/libpaintop/kis_brush_option.h index 26df674e76..ac34d1e30b 100644 --- a/plugins/paintops/libpaintop/kis_brush_option.h +++ b/plugins/paintops/libpaintop/kis_brush_option.h @@ -1,50 +1,50 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2008 * Copyright (C) Sven Langkamp , (C) 2008 * * 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_BRUSH_OPTION_H_ #define KIS_BRUSH_OPTION_H_ #include -#include +#include #include #include #include -class PAINTOP_EXPORT KisBrushOption : public KisBaseOption +class PAINTOP_EXPORT KisBrushOptionProperties : public KisPaintopPropertiesBase { public: void writeOptionSettingImpl(KisPropertiesConfiguration *setting) const override; void readOptionSettingImpl(const KisPropertiesConfiguration *setting) override; KisBrushSP brush() const; void setBrush(KisBrushSP brush); #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND static bool isTextBrush(const KisPropertiesConfiguration *setting); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ private: KisBrushSP m_brush; }; #endif diff --git a/plugins/paintops/libpaintop/kis_brush_option_widget.h b/plugins/paintops/libpaintop/kis_brush_option_widget.h index 9c88cef1d1..41b9e9667f 100644 --- a/plugins/paintops/libpaintop/kis_brush_option_widget.h +++ b/plugins/paintops/libpaintop/kis_brush_option_widget.h @@ -1,74 +1,74 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2008 * * 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_BRUSH_OPTION_H #define KIS_BRUSH_OPTION_H #include "kis_paintop_option.h" #include "kis_brush_option.h" #include #include "kis_brush.h" class KisBrushSelectionWidget; /** * The brush option allows the user to select a particular brush * footprint for suitable paintops */ class PAINTOP_EXPORT KisBrushOptionWidget : public KisPaintOpOption { Q_OBJECT public: KisBrushOptionWidget(); /** * @return the currently selected brush */ KisBrushSP brush() const; void setAutoBrush(bool on); void setPredefinedBrushes(bool on); void setCustomBrush(bool on); void setTextBrush(bool on); void setImage(KisImageWSP image) override; void setPrecisionEnabled(bool value); void writeOptionSetting(KisPropertiesConfigurationSP setting) const override; void readOptionSetting(const KisPropertiesConfigurationSP setting) override; void lodLimitations(KisPaintopLodLimitations *l) const override; bool presetIsValid(); void hideOptions(const QStringList &options); private Q_SLOTS: void brushChanged(); private: KisBrushSelectionWidget * m_brushSelectionWidget; - KisBrushOption m_brushOption; + KisBrushOptionProperties m_brushOption; }; #endif diff --git a/plugins/paintops/libpaintop/kis_curve_option.h b/plugins/paintops/libpaintop/kis_curve_option.h index d173ba7597..ba6fd35e0d 100644 --- a/plugins/paintops/libpaintop/kis_curve_option.h +++ b/plugins/paintops/libpaintop/kis_curve_option.h @@ -1,204 +1,209 @@ /* This file is part of the KDE project * Copyright (C) 2008 Boudewijn Rempt * Copyright (C) 2011 Silvio Heinrich * * 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_CURVE_OPTION_H #define KIS_CURVE_OPTION_H #include #include #include "kis_paintop_option.h" #include "kis_global.h" #include "kis_paintop_option.h" #include #include "kritapaintop_export.h" #include "kis_dynamic_sensor.h" class KisDynamicSensor; /** * KisCurveOption is the base class for paintop options that are * defined through one or more curves. * * Note: it is NOT a KisPaintOpOption, even though the API is pretty similar! * + * KisCurveOption classes have a generic GUI widget, KisCurveOptionWidget. So, + * in contrast to KisPaintOpOption classes, KisCurveOption instances can and + * will be created in the constructor of KisPaintOp paintops. This class can + * manage to read and write its settings directly. + * */ class PAINTOP_EXPORT KisCurveOption { public: KisCurveOption(const QString& name, KisPaintOpOption::PaintopCategory category, bool checked, qreal value = 1.0, qreal min = 0.0, qreal max = 1.0); virtual ~KisCurveOption(); virtual void writeOptionSetting(KisPropertiesConfigurationSP setting) const; virtual void readOptionSetting(KisPropertiesConfigurationSP setting); virtual void lodLimitations(KisPaintopLodLimitations *l) const; const QString& name() const; KisPaintOpOption::PaintopCategory category() const; qreal minValue() const; qreal maxValue() const; qreal value() const; void resetAllSensors(); KisDynamicSensorSP sensor(DynamicSensorType sensorType, bool active) const; void replaceSensor(KisDynamicSensorSP sensor); QList sensors(); QList activeSensors() const; bool isCheckable(); bool isChecked() const; bool isCurveUsed() const; bool isSameCurveUsed() const; bool isRandom() const; int getCurveMode() const; void setSeparateCurveValue(bool separateCurveValue); void setChecked(bool checked); void setCurveUsed(bool useCurve); void setCurve(DynamicSensorType sensorType, bool useSameCurve, const KisCubicCurve &curve); void setValue(qreal value); void setCurveMode(int mode); struct ValueComponents { ValueComponents() : constant(1.0), scaling(1.0), additive(0.0), absoluteOffset(0.0), hasAbsoluteOffset(false), hasScaling(false), hasAdditive(false) { } qreal constant; qreal scaling; qreal additive; qreal absoluteOffset; bool hasAbsoluteOffset; bool hasScaling; bool hasAdditive; qreal minSizeLikeValue; qreal maxSizeLikeValue; /** * @param normalizedBaseAngle canvas rotation alngle normalized to range [0; 1] * @param absoluteAxesFlipped true if underlying image coordinate system is flipped (horiz. mirror != vert. mirror) */ qreal rotationLikeValue(qreal normalizedBaseAngle, bool absoluteAxesFlipped) const { const qreal offset = !hasAbsoluteOffset ? normalizedBaseAngle : absoluteAxesFlipped ? 1.0 - absoluteOffset : absoluteOffset; const qreal realScalingPart = hasScaling ? KisDynamicSensor::scalingToAdditive(scaling) : 0.0; const qreal realAdditivePart = hasAdditive ? additive : 0; qreal value = wrapInRange(2 * offset + constant * realScalingPart + realAdditivePart, -1.0, 1.0); if (qIsNaN(value)) { qWarning() << "rotationLikeValue returns NaN!" << normalizedBaseAngle << absoluteAxesFlipped; value = 0; } return value; } qreal sizeLikeValue() const { const qreal offset = hasAbsoluteOffset ? absoluteOffset : 1.0; const qreal realScalingPart = hasScaling ? scaling : 1.0; const qreal realAdditivePart = hasAdditive ? KisDynamicSensor::additiveToScaling(additive) : 1.0; return qBound(minSizeLikeValue, constant * offset * realScalingPart * realAdditivePart, maxSizeLikeValue); } private: static inline qreal wrapInRange(qreal x, qreal min, qreal max) { const qreal range = max - min; x -= min; if (x < 0.0) { x = range + fmod(x, range); } if (x > range) { x = fmod(x, range); } return x + min; } }; /** * Uses the curves set on the sensors to compute a single * double value that can control the parameters of a brush. * * This value is derives from the falues stored in * ValuesComponents opject. */ ValueComponents computeValueComponents(const KisPaintInformation& info) const; qreal computeSizeLikeValue(const KisPaintInformation &info) const; qreal computeRotationLikeValue(const KisPaintInformation& info, qreal baseValue, bool absoluteAxesFlipped) const; protected: void setValueRange(qreal min, qreal max); /** * Read the option using the prefix in argument */ void readNamedOptionSetting(const QString& prefix, const KisPropertiesConfigurationSP setting); QString m_name; KisPaintOpOption::PaintopCategory m_category; bool m_checkable; bool m_checked; bool m_useCurve; bool m_useSameCurve; bool m_separateCurveValue; int m_curveMode; QMap m_sensorMap; QMap m_curveCache; private: qreal m_value; qreal m_minValue; qreal m_maxValue; }; #endif diff --git a/plugins/paintops/libpaintop/kis_paintop_plugin_utils.h b/plugins/paintops/libpaintop/kis_paintop_plugin_utils.h index 4419852bc0..2a8b0e839d 100644 --- a/plugins/paintops/libpaintop/kis_paintop_plugin_utils.h +++ b/plugins/paintops/libpaintop/kis_paintop_plugin_utils.h @@ -1,101 +1,101 @@ /* * 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_PAINTOP_PLUGIN_UTILS_H #define __KIS_PAINTOP_PLUGIN_UTILS_H #include "kis_paint_information.h" #include "kis_paintop_utils.h" #include "kis_paintop_settings.h" #include "kis_airbrush_option_widget.h" #include "kis_pressure_spacing_option.h" #include "kis_pressure_rate_option.h" namespace KisPaintOpPluginUtils { /** * Similar to KisPaintOpUtils::effectiveSpacing, but some of the required parameters are obtained * from the provided configuration options. This function assumes a common configuration where * spacing and airbrush settings are configured through a KisPressureSpacingOption and * KisAirbrushOption. This type of configuration is used by several different paintops. * @param airbrushOption - The airbrushing option. Can be null for paintops that don't support * airbrushing. * @param spacingOption - The pressure-curve spacing option. Can be null for paintops that don't * support pressure-based spacing. */ KisSpacingInformation effectiveSpacing(qreal dabWidth, qreal dabHeight, bool isotropicSpacing, qreal rotation, bool axesFlipped, qreal spacingVal, bool autoSpacingActive, qreal autoSpacingCoeff, qreal lodScale, - const KisAirbrushOptionWidget *airbrushOption, + const KisAirbrushOptionProperties *airbrushOption, const KisPressureSpacingOption *spacingOption, const KisPaintInformation &pi) { // Extract required parameters. bool distanceSpacingEnabled = true; - if (airbrushOption && airbrushOption->isChecked()) { - distanceSpacingEnabled = !airbrushOption->ignoreSpacing(); + if (airbrushOption && airbrushOption->enabled) { + distanceSpacingEnabled = !airbrushOption->ignoreSpacing; } qreal extraScale = 1.0; if (spacingOption && spacingOption->isChecked()) { extraScale = spacingOption->apply(pi); } return KisPaintOpUtils::effectiveSpacing(dabWidth, dabHeight, extraScale, distanceSpacingEnabled, isotropicSpacing, rotation, axesFlipped, spacingVal, autoSpacingActive, autoSpacingCoeff, lodScale); } /** * Similar to KisPaintOpUtils::effectiveTiming, but some of the required parameters are obtained * from the provided configuration options. This function assumes a common configuration where * airbrush settings are configured through a KisAirbrushOption and KisPressureRateOption. This type * of configuration is used by several different paintops. * @param airbrushOption - The airbrushing option. Can be null for paintops that don't support * airbrushing. * @param rateOption - The pressure-curve airbrush rate option. Can be null for paintops that don't * support a pressure-based airbrush rate. */ -KisTimingInformation effectiveTiming(const KisAirbrushOptionWidget *airbrushOption, +KisTimingInformation effectiveTiming(const KisAirbrushOptionProperties *airbrushOption, const KisPressureRateOption *rateOption, const KisPaintInformation &pi) { // Extract required parameters. bool timingEnabled = false; qreal timingInterval = LONG_TIME; if (airbrushOption) { - timingEnabled = airbrushOption->isChecked(); - timingInterval = airbrushOption->airbrushInterval(); + timingEnabled = airbrushOption->enabled; + timingInterval = airbrushOption->airbrushInterval; } qreal rateExtraScale = 1.0; if (rateOption && rateOption->isChecked()) { rateExtraScale = rateOption->apply(pi); } return KisPaintOpUtils::effectiveTiming(timingEnabled, timingInterval, rateExtraScale); } } #endif /* __KIS_PAINTOP_PLUGIN_UTILS_H */ diff --git a/plugins/paintops/libpaintop/kis_spacing_selection_widget.cpp b/plugins/paintops/libpaintop/kis_spacing_selection_widget.cpp index 4723ea69aa..c95b9855ac 100644 --- a/plugins/paintops/libpaintop/kis_spacing_selection_widget.cpp +++ b/plugins/paintops/libpaintop/kis_spacing_selection_widget.cpp @@ -1,130 +1,130 @@ /* * 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. */ #include "kis_spacing_selection_widget.h" #include #include #include "klocalizedstring.h" #include "kis_signals_blocker.h" #include "kis_slider_spin_box.h" struct KisSpacingSelectionWidget::Private { Private(KisSpacingSelectionWidget *_q) : q(_q), oldSliderValue(0.1) { } KisSpacingSelectionWidget *q; KisDoubleSliderSpinBox *slider; QCheckBox *autoButton; qreal oldSliderValue; void slotSpacingChanged(qreal value); void slotAutoSpacing(bool value); }; KisSpacingSelectionWidget::KisSpacingSelectionWidget(QWidget *parent) : QWidget(parent), m_d(new Private(this)) { m_d->slider = new KisDoubleSliderSpinBox(this); - m_d->slider->setRange(0.01, 10.0, 2); + m_d->slider->setRange(0.02, 10.0, 2); m_d->slider->setExponentRatio(3); m_d->slider->setSingleStep(0.01); m_d->slider->setValue(0.1); m_d->slider->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed)); m_d->autoButton = new QCheckBox(this); m_d->autoButton->setText(i18nc("@action:button", "Auto")); m_d->autoButton->setToolTip(i18nc("@info:tooltip", "In auto mode the spacing of the brush will be calculated automatically depending on its size")); m_d->autoButton->setCheckable(true); m_d->autoButton->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); QHBoxLayout *layout = new QHBoxLayout(this); layout->addWidget(m_d->autoButton); layout->addWidget(m_d->slider); layout->setMargin(0); connect(m_d->slider, SIGNAL(valueChanged(qreal)), SLOT(slotSpacingChanged(qreal))); connect(m_d->autoButton, SIGNAL(toggled(bool)), SLOT(slotAutoSpacing(bool))); } KisSpacingSelectionWidget::~KisSpacingSelectionWidget() { } qreal KisSpacingSelectionWidget::spacing() const { return autoSpacingActive() ? 0.1 : m_d->slider->value(); } bool KisSpacingSelectionWidget::autoSpacingActive() const { return m_d->autoButton->isChecked(); } qreal KisSpacingSelectionWidget::autoSpacingCoeff() const { return autoSpacingActive() ? m_d->slider->value() : 1.0; } void KisSpacingSelectionWidget::setSpacing(bool isAuto, qreal spacing) { KisSignalsBlocker b1(m_d->autoButton); KisSignalsBlocker b2(m_d->slider); m_d->autoButton->setChecked(isAuto); m_d->slider->setValue(spacing); } void KisSpacingSelectionWidget::Private::slotSpacingChanged(qreal value) { Q_UNUSED(value); emit q->sigSpacingChanged(); } void KisSpacingSelectionWidget::Private::slotAutoSpacing(bool value) { qreal newSliderValue = 0.0; if (value) { newSliderValue = 1.0; oldSliderValue = slider->value(); } else { newSliderValue = oldSliderValue; } { KisSignalsBlocker b(slider); slider->setValue(newSliderValue); } emit q->sigSpacingChanged(); } #include "moc_kis_spacing_selection_widget.moc" diff --git a/plugins/paintops/particle/kis_particle_paintop.cpp b/plugins/paintops/particle/kis_particle_paintop.cpp index 4aa6f5956b..54d9b233f2 100644 --- a/plugins/paintops/particle/kis_particle_paintop.cpp +++ b/plugins/paintops/particle/kis_particle_paintop.cpp @@ -1,123 +1,123 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * 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_particle_paintop.h" #include "kis_particle_paintop_settings.h" #include #include "kis_vec.h" #include #include #include #include #include #include #include #include #include #include #include #include "kis_particleop_option.h" #include "particle_brush.h" KisParticlePaintOp::KisParticlePaintOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image) : KisPaintOp(painter) { Q_UNUSED(image); Q_UNUSED(node); m_properties.particleCount = settings->getInt(PARTICLE_COUNT); m_properties.iterations = settings->getInt(PARTICLE_ITERATIONS); m_properties.gravity = settings->getDouble(PARTICLE_GRAVITY); m_properties.weight = settings->getDouble(PARTICLE_WEIGHT); m_properties.scale = QPointF(settings->getDouble(PARTICLE_SCALE_X), settings->getDouble(PARTICLE_SCALE_Y)); m_particleBrush.setProperties(&m_properties); m_particleBrush.initParticles(); - m_airbrushOptionWidget.readOptionSetting(settings); + m_airbrushOption.readOptionSetting(settings); m_rateOption.readOptionSetting(settings); m_rateOption.resetAllSensors(); m_first = true; } KisParticlePaintOp::~KisParticlePaintOp() { } KisSpacingInformation KisParticlePaintOp::paintAt(const KisPaintInformation& info) { doPaintLine(info, info); return updateSpacingImpl(info); } KisSpacingInformation KisParticlePaintOp::updateSpacingImpl(const KisPaintInformation &info) const { return KisPaintOpPluginUtils::effectiveSpacing(0.0, 0.0, true, 0.0, false, 0.0, false, 0.0, KisLodTransform::lodToScale(painter()->device()), - &m_airbrushOptionWidget, nullptr, info); + &m_airbrushOption, nullptr, info); } KisTimingInformation KisParticlePaintOp::updateTimingImpl(const KisPaintInformation &info) const { - return KisPaintOpPluginUtils::effectiveTiming(&m_airbrushOptionWidget, &m_rateOption, info); + return KisPaintOpPluginUtils::effectiveTiming(&m_airbrushOption, &m_rateOption, info); } void KisParticlePaintOp::paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) { // Use superclass behavior for lines of zero length. Otherwise, airbrushing can happen faster // than it is supposed to. if (pi1.pos() == pi2.pos()) { KisPaintOp::paintLine(pi1, pi2, currentDistance); } else { doPaintLine(pi1, pi2); } } void KisParticlePaintOp::doPaintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2) { if (!painter()) return; if (!m_dab) { m_dab = source()->createCompositionSourceDevice(); } else { m_dab->clear(); } if (m_first) { m_particleBrush.setInitialPosition(pi1.pos()); m_first = false; } m_particleBrush.draw(m_dab, painter()->paintColor(), pi2.pos()); QRect rc = m_dab->extent(); painter()->bitBlt(rc.x(), rc.y(), m_dab, rc.x(), rc.y(), rc.width(), rc.height()); painter()->renderMirrorMask(rc, m_dab); } diff --git a/plugins/paintops/particle/kis_particle_paintop.h b/plugins/paintops/particle/kis_particle_paintop.h index facbe2538a..b5c394987a 100644 --- a/plugins/paintops/particle/kis_particle_paintop.h +++ b/plugins/paintops/particle/kis_particle_paintop.h @@ -1,62 +1,62 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * 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_PARTICLE_PAINTOP_H_ #define KIS_PARTICLE_PAINTOP_H_ #include #include #include #include #include "kis_particle_paintop_settings.h" #include "particle_brush.h" class KisPainter; class KisPaintInformation; class KisParticlePaintOp : public KisPaintOp { public: KisParticlePaintOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image); ~KisParticlePaintOp() override; void paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) override; protected: KisSpacingInformation paintAt(const KisPaintInformation& info) override; KisSpacingInformation updateSpacingImpl(const KisPaintInformation &info) const override; KisTimingInformation updateTimingImpl(const KisPaintInformation &info) const override; private: void doPaintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2); private: KisParticleBrushProperties m_properties; KisPaintDeviceSP m_dab; ParticleBrush m_particleBrush; - KisAirbrushOptionWidget m_airbrushOptionWidget; + KisAirbrushOptionProperties m_airbrushOption; KisPressureRateOption m_rateOption; bool m_first; }; #endif // KIS_PARTICLE_PAINTOP_H_ diff --git a/plugins/paintops/sketch/kis_sketch_paintop.cpp b/plugins/paintops/sketch/kis_sketch_paintop.cpp index ddaca275b2..98a72c8f8c 100644 --- a/plugins/paintops/sketch/kis_sketch_paintop.cpp +++ b/plugins/paintops/sketch/kis_sketch_paintop.cpp @@ -1,325 +1,325 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2010 Ricardo Cabello * * 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_sketch_paintop.h" #include "kis_sketch_paintop_settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_lod_transform.h" #include /* * Based on Harmony project http://github.com/mrdoob/harmony/ */ // chrome : diff 0.2, sketchy : 0.3, fur: 0.5 // fur : distance / thresholdDistance // shaded: opacity per line :/ // ((1 - (d / 1000)) * 0.1 * BRUSH_PRESSURE), offset == 0 // chrome: color per line :/ //this.context.strokeStyle = "rgba(" + Math.floor(Math.random() * COLOR[0]) + ", " + Math.floor(Math.random() * COLOR[1]) + ", " + Math.floor(Math.random() * COLOR[2]) + ", " + 0.1 * BRUSH_PRESSURE + " )"; // long fur // from: count + offset * -random // to: i point - (offset * -random) + random * 2 // probability distance / thresholdDistnace // shaded: probabity : paint always - 0.0 density KisSketchPaintOp::KisSketchPaintOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image) : KisPaintOp(painter) { Q_UNUSED(image); Q_UNUSED(node); - m_airbrushOptionWidget.readOptionSetting(settings); + m_airbrushOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); m_rotationOption.readOptionSetting(settings); m_rateOption.readOptionSetting(settings); m_sketchProperties.readOptionSetting(settings); m_brushOption.readOptionSetting(settings); m_densityOption.readOptionSetting(settings); m_lineWidthOption.readOptionSetting(settings); m_offsetScaleOption.readOptionSetting(settings); m_brush = m_brushOption.brush(); m_dabCache = new KisDabCache(m_brush); m_opacityOption.resetAllSensors(); m_sizeOption.resetAllSensors(); m_rotationOption.resetAllSensors(); m_rateOption.resetAllSensors(); m_painter = 0; m_count = 0; } KisSketchPaintOp::~KisSketchPaintOp() { delete m_painter; delete m_dabCache; } void KisSketchPaintOp::drawConnection(const QPointF& start, const QPointF& end, double lineWidth) { if (lineWidth == 1.0) { m_painter->drawThickLine(start, end, lineWidth, lineWidth); } else { m_painter->drawLine(start, end, lineWidth, true); } } void KisSketchPaintOp::updateBrushMask(const KisPaintInformation& info, qreal scale, qreal rotation) { QRect dstRect; m_maskDab = m_dabCache->fetchDab(m_dab->colorSpace(), painter()->paintColor(), info.pos(), KisDabShape(scale, 1.0, rotation), info, 1.0, &dstRect); m_brushBoundingBox = dstRect; m_hotSpot = QPointF(0.5 * m_brushBoundingBox.width(), 0.5 * m_brushBoundingBox.height()); } void KisSketchPaintOp::paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) { // Use superclass behavior for lines of zero length. Otherwise, airbrushing can happen faster // than it is supposed to. if (pi1.pos() == pi2.pos()) { KisPaintOp::paintLine(pi1, pi2, currentDistance); } else { doPaintLine(pi1, pi2); } -} +} void KisSketchPaintOp::doPaintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2) { if (!m_brush || !painter()) return; if (!m_dab) { m_dab = source()->createCompositionSourceDevice(); m_painter = new KisPainter(m_dab); m_painter->setPaintColor(painter()->paintColor()); } else { m_dab->clear(); } QPointF prevMouse = pi1.pos(); QPointF mousePosition = pi2.pos(); m_points.append(mousePosition); const qreal lodAdditionalScale = KisLodTransform::lodToScale(painter()->device()); const qreal scale = lodAdditionalScale * m_sizeOption.apply(pi2); if ((scale * m_brush->width()) <= 0.01 || (scale * m_brush->height()) <= 0.01) return; const qreal currentLineWidth = qMax(0.9, lodAdditionalScale * m_lineWidthOption.apply(pi2, m_sketchProperties.lineWidth)); const qreal currentOffsetScale = m_offsetScaleOption.apply(pi2, m_sketchProperties.offset); const double rotation = m_rotationOption.apply(pi2); const double currentProbability = m_densityOption.apply(pi2, m_sketchProperties.probability); // shaded: does not draw this line, chrome does, fur does if (m_sketchProperties.makeConnection) { drawConnection(prevMouse, mousePosition, currentLineWidth); } qreal thresholdDistance = 0.0; // update the mask for simple mode only once // determine the radius if (m_count == 0 && m_sketchProperties.simpleMode) { updateBrushMask(pi2, 1.0, 0.0); //m_radius = qMax(m_maskDab->bounds().width(),m_maskDab->bounds().height()) * 0.5; m_radius = 0.5 * qMax(m_brush->width(), m_brush->height()); } if (!m_sketchProperties.simpleMode) { updateBrushMask(pi2, scale, rotation); m_radius = qMax(m_maskDab->bounds().width(), m_maskDab->bounds().height()) * 0.5; thresholdDistance = pow(m_radius, 2); } if (m_sketchProperties.simpleMode) { // update the radius according scale in simple mode thresholdDistance = pow(m_radius * scale, 2); } // determine density const qreal density = thresholdDistance * currentProbability; // probability behaviour qreal probability = 1.0 - currentProbability; QColor painterColor = painter()->paintColor().toQColor(); QColor randomColor; KoColor color(m_dab->colorSpace()); int w = m_maskDab->bounds().width(); quint8 opacityU8 = 0; quint8 * pixel; qreal distance; QPoint positionInMask; QPointF diff; int size = m_points.size(); // MAIN LOOP for (int i = 0; i < size; i++) { diff = m_points.at(i) - mousePosition; distance = diff.x() * diff.x() + diff.y() * diff.y(); // circle test bool makeConnection = false; if (m_sketchProperties.simpleMode) { if (distance < thresholdDistance) { makeConnection = true; } // mask test } else { if (m_brushBoundingBox.contains(m_points.at(i))) { positionInMask = (diff + m_hotSpot).toPoint(); uint pos = ((positionInMask.y() * w + positionInMask.x()) * m_maskDab->pixelSize()); if (pos < m_maskDab->allocatedPixels() * m_maskDab->pixelSize()) { pixel = m_maskDab->data() + pos; opacityU8 = m_maskDab->colorSpace()->opacityU8(pixel); if (opacityU8 != 0) { makeConnection = true; } } } } if (!makeConnection) { // check next point continue; } if (m_sketchProperties.distanceDensity) { probability = distance / density; } KisRandomSourceSP randomSource = pi2.randomSource(); // density check if (randomSource->generateNormalized() >= probability) { QPointF offsetPt = diff * currentOffsetScale; if (m_sketchProperties.randomRGB) { /** * Since the order of calculation of function * parameters is not defined by C++ standard, we * should generate values in an external code snippet * which has a definite order of execution. */ qreal r1 = randomSource->generateNormalized(); qreal r2 = randomSource->generateNormalized(); qreal r3 = randomSource->generateNormalized(); // some color transformation per line goes here randomColor.setRgbF(r1 * painterColor.redF(), r2 * painterColor.greenF(), r3 * painterColor.blueF()); color.fromQColor(randomColor); m_painter->setPaintColor(color); } // distance based opacity quint8 opacity = OPACITY_OPAQUE_U8; if (m_sketchProperties.distanceOpacity) { opacity *= qRound((1.0 - (distance / thresholdDistance))); } if (m_sketchProperties.randomOpacity) { opacity *= randomSource->generateNormalized(); } m_painter->setOpacity(opacity); if (m_sketchProperties.magnetify) { drawConnection(mousePosition + offsetPt, m_points.at(i) - offsetPt, currentLineWidth); } else { drawConnection(mousePosition + offsetPt, mousePosition - offsetPt, currentLineWidth); } } }// end of MAIN LOOP m_count++; QRect rc = m_dab->extent(); quint8 origOpacity = m_opacityOption.apply(painter(), pi2); painter()->bitBlt(rc.x(), rc.y(), m_dab, rc.x(), rc.y(), rc.width(), rc.height()); painter()->renderMirrorMask(rc, m_dab); painter()->setOpacity(origOpacity); } KisSpacingInformation KisSketchPaintOp::paintAt(const KisPaintInformation& info) { doPaintLine(info, info); return updateSpacingImpl(info); } KisSpacingInformation KisSketchPaintOp::updateSpacingImpl(const KisPaintInformation &info) const { return KisPaintOpPluginUtils::effectiveSpacing(0.0, 0.0, true, 0.0, false, 0.0, false, 0.0, KisLodTransform::lodToScale(painter()->device()), - &m_airbrushOptionWidget, nullptr, info); + &m_airbrushOption, nullptr, info); } KisTimingInformation KisSketchPaintOp::updateTimingImpl(const KisPaintInformation &info) const { - return KisPaintOpPluginUtils::effectiveTiming(&m_airbrushOptionWidget, &m_rateOption, info); + return KisPaintOpPluginUtils::effectiveTiming(&m_airbrushOption, &m_rateOption, info); } diff --git a/plugins/paintops/sketch/kis_sketch_paintop.h b/plugins/paintops/sketch/kis_sketch_paintop.h index e9ef92276c..f760983b95 100644 --- a/plugins/paintops/sketch/kis_sketch_paintop.h +++ b/plugins/paintops/sketch/kis_sketch_paintop.h @@ -1,94 +1,94 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * 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_SKETCH_PAINTOP_H_ #define KIS_SKETCH_PAINTOP_H_ #include #include #include "kis_density_option.h" #include "kis_sketchop_option.h" #include "kis_sketch_paintop_settings.h" #include "kis_painter.h" #include #include #include #include #include #include "kis_linewidth_option.h" #include "kis_offset_scale_option.h" class KisDabCache; class KisSketchPaintOp : public KisPaintOp { public: KisSketchPaintOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image); ~KisSketchPaintOp() override; void paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) override; protected: KisSpacingInformation paintAt(const KisPaintInformation& info) override; KisSpacingInformation updateSpacingImpl(const KisPaintInformation &info) const override; KisTimingInformation updateTimingImpl(const KisPaintInformation &info) const override; private: // pixel buffer KisPaintDeviceSP m_dab; // mask detection area KisFixedPaintDeviceSP m_maskDab; QRectF m_brushBoundingBox; QPointF m_hotSpot; // simple mode qreal m_radius; KisPressureOpacityOption m_opacityOption; KisPressureSizeOption m_sizeOption; KisPressureRotationOption m_rotationOption; KisPressureRateOption m_rateOption; KisDensityOption m_densityOption; KisLineWidthOption m_lineWidthOption; KisOffsetScaleOption m_offsetScaleOption; - KisAirbrushOptionWidget m_airbrushOptionWidget; + KisAirbrushOptionProperties m_airbrushOption; - KisBrushOption m_brushOption; + KisBrushOptionProperties m_brushOption; SketchProperties m_sketchProperties; QVector m_points; int m_count; KisPainter * m_painter; KisBrushSP m_brush; KisDabCache *m_dabCache; private: void drawConnection(const QPointF &start, const QPointF &end, double lineWidth); void updateBrushMask(const KisPaintInformation& info, qreal scale, qreal rotation); void doPaintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2); }; #endif // KIS_SKETCH_PAINTOP_H_ diff --git a/plugins/paintops/spray/kis_spray_paintop.cpp b/plugins/paintops/spray/kis_spray_paintop.cpp index d104069f4a..ff83355007 100644 --- a/plugins/paintops/spray/kis_spray_paintop.cpp +++ b/plugins/paintops/spray/kis_spray_paintop.cpp @@ -1,153 +1,153 @@ /* * Copyright (c) 2008-2012 Lukáš Tvrdý * * 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_spray_paintop.h" #include "kis_spray_paintop_settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include KisSprayPaintOp::KisSprayPaintOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image) : KisPaintOp(painter) , m_isPresetValid(true) , m_node(node) { Q_ASSERT(settings); Q_ASSERT(painter); Q_UNUSED(image); - m_airbrushOptionWidget.readOptionSetting(settings); + m_airbrushOption.readOptionSetting(settings); m_rotationOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); m_rateOption.readOptionSetting(settings); m_rotationOption.resetAllSensors(); m_opacityOption.resetAllSensors(); m_sizeOption.resetAllSensors(); m_rateOption.resetAllSensors(); m_brushOption.readOptionSetting(settings); m_colorProperties.fillProperties(settings); m_properties.readOptionSetting(settings); // first load tip properties as shape properties are dependent on diameter/scale/aspect m_shapeProperties.loadSettings(settings, m_properties.diameter * m_properties.scale, m_properties.diameter * m_properties.aspect * m_properties.scale); // TODO: what to do with proportional sizes? m_shapeDynamicsProperties.loadSettings(settings); if (!m_shapeProperties.enabled && !m_brushOption.brush()) { // in case the preset does not contain the definition for KisBrush m_isPresetValid = false; dbgKrita << "Preset is not valid. Painting is not possible. Use the preset editor to fix current brush engine preset."; } m_sprayBrush.setProperties(&m_properties, &m_colorProperties, &m_shapeProperties, &m_shapeDynamicsProperties, m_brushOption.brush()); m_sprayBrush.setFixedDab(cachedDab()); // spacing if ((m_properties.diameter * 0.5) > 1) { m_ySpacing = m_xSpacing = m_properties.diameter * 0.5 * m_properties.spacing; } else { m_ySpacing = m_xSpacing = 1.0; } m_spacing = m_xSpacing; } KisSprayPaintOp::~KisSprayPaintOp() { } KisSpacingInformation KisSprayPaintOp::paintAt(const KisPaintInformation& info) { if (!painter() || !m_isPresetValid) { return KisSpacingInformation(m_spacing); } if (!m_dab) { m_dab = source()->createCompositionSourceDevice(); } else { m_dab->clear(); } qreal rotation = m_rotationOption.apply(info); quint8 origOpacity = m_opacityOption.apply(painter(), info); // Spray Brush is capable of working with zero scale, // so no additional checks for 'zero'ness are needed const qreal scale = m_sizeOption.apply(info); const qreal lodScale = KisLodTransform::lodToScale(painter()->device()); m_sprayBrush.paint(m_dab, m_node->paintDevice(), info, rotation, scale, lodScale, painter()->paintColor(), painter()->backgroundColor()); QRect rc = m_dab->extent(); painter()->bitBlt(rc.topLeft(), m_dab, rc); painter()->renderMirrorMask(rc, m_dab); painter()->setOpacity(origOpacity); return computeSpacing(info, lodScale); } KisSpacingInformation KisSprayPaintOp::updateSpacingImpl(const KisPaintInformation &info) const { return computeSpacing(info, KisLodTransform::lodToScale(painter()->device())); } KisTimingInformation KisSprayPaintOp::updateTimingImpl(const KisPaintInformation &info) const { - return KisPaintOpPluginUtils::effectiveTiming(&m_airbrushOptionWidget, &m_rateOption, info); + return KisPaintOpPluginUtils::effectiveTiming(&m_airbrushOption, &m_rateOption, info); } KisSpacingInformation KisSprayPaintOp::computeSpacing(const KisPaintInformation &info, qreal lodScale) const { return KisPaintOpPluginUtils::effectiveSpacing(1.0, 1.0, true, 0.0, false, m_spacing * lodScale, false, 1.0, lodScale, - &m_airbrushOptionWidget, nullptr, info); + &m_airbrushOption, nullptr, info); } diff --git a/plugins/paintops/spray/kis_spray_paintop.h b/plugins/paintops/spray/kis_spray_paintop.h index 432e5a0b57..600f6bcf48 100644 --- a/plugins/paintops/spray/kis_spray_paintop.h +++ b/plugins/paintops/spray/kis_spray_paintop.h @@ -1,75 +1,75 @@ /* * Copyright (c) 2008-2012 Lukáš Tvrdý * * 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_SPRAY_PAINTOP_H_ #define KIS_SPRAY_PAINTOP_H_ #include #include #include "spray_brush.h" #include "kis_spray_paintop_settings.h" #include "kis_brush_option.h" #include #include #include #include #include class KisPainter; class KisSprayPaintOp : public KisPaintOp { public: KisSprayPaintOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image); ~KisSprayPaintOp() override; protected: KisSpacingInformation paintAt(const KisPaintInformation& info) override; KisSpacingInformation updateSpacingImpl(const KisPaintInformation &info) const override; KisTimingInformation updateTimingImpl(const KisPaintInformation &info) const override; private: KisSpacingInformation computeSpacing(const KisPaintInformation &info, qreal lodScale) const; private: KisShapeProperties m_shapeProperties; - KisSprayProperties m_properties; + KisSprayOptionProperties m_properties; KisShapeDynamicsProperties m_shapeDynamicsProperties; KisColorProperties m_colorProperties; - KisBrushOption m_brushOption; + KisBrushOptionProperties m_brushOption; KisPaintDeviceSP m_dab; SprayBrush m_sprayBrush; qreal m_xSpacing, m_ySpacing, m_spacing; bool m_isPresetValid; - KisAirbrushOptionWidget m_airbrushOptionWidget; + KisAirbrushOptionProperties m_airbrushOption; KisPressureRotationOption m_rotationOption; KisPressureSizeOption m_sizeOption; KisPressureOpacityOption m_opacityOption; KisPressureRateOption m_rateOption; KisNodeSP m_node; }; #endif // KIS_SPRAY_PAINTOP_H_ diff --git a/plugins/paintops/spray/kis_spray_paintop_settings.cpp b/plugins/paintops/spray/kis_spray_paintop_settings.cpp index 8435a5a021..d862eb45e4 100644 --- a/plugins/paintops/spray/kis_spray_paintop_settings.cpp +++ b/plugins/paintops/spray/kis_spray_paintop_settings.cpp @@ -1,229 +1,229 @@ /* * Copyright (c) 2008,2009,2010 Lukáš Tvrdý * * 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 #include #include #include "kis_spray_paintop_settings.h" #include "kis_sprayop_option.h" #include "kis_spray_shape_option.h" #include struct KisSprayPaintOpSettings::Private { QList uniformProperties; }; KisSprayPaintOpSettings::KisSprayPaintOpSettings() : KisOutlineGenerationPolicy(KisCurrentOutlineFetcher::SIZE_OPTION | KisCurrentOutlineFetcher::ROTATION_OPTION), m_d(new Private) { } KisSprayPaintOpSettings::~KisSprayPaintOpSettings() { } void KisSprayPaintOpSettings::setPaintOpSize(qreal value) { - KisSprayProperties option; + KisSprayOptionProperties option; option.readOptionSetting(this); option.diameter = value; option.writeOptionSetting(this); } qreal KisSprayPaintOpSettings::paintOpSize() const { - KisSprayProperties option; + KisSprayOptionProperties option; option.readOptionSetting(this); return option.diameter; } bool KisSprayPaintOpSettings::paintIncremental() { return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP; } QPainterPath KisSprayPaintOpSettings::brushOutline(const KisPaintInformation &info, OutlineMode mode) { QPainterPath path; if (mode == CursorIsOutline || mode == CursorIsCircleOutline || mode == CursorTiltOutline) { qreal width = getInt(SPRAY_DIAMETER); qreal height = getInt(SPRAY_DIAMETER) * getDouble(SPRAY_ASPECT); path = ellipseOutline(width, height, getDouble(SPRAY_SCALE), getDouble(SPRAY_ROTATION)); QPainterPath tiltLine; QLineF tiltAngle(QPointF(0.0,0.0), QPointF(0.0,width)); tiltAngle.setLength(qMax(width*0.5, 50.0) * (1 - info.tiltElevation(info, 60.0, 60.0, true))); tiltAngle.setAngle((360.0 - fmod(KisPaintInformation::tiltDirection(info, true) * 360.0 + 270.0, 360.0))-3.0); tiltLine.moveTo(tiltAngle.p1()); tiltLine.lineTo(tiltAngle.p2()); tiltAngle.setAngle((360.0 - fmod(KisPaintInformation::tiltDirection(info, true) * 360.0 + 270.0, 360.0))+3.0); tiltLine.lineTo(tiltAngle.p2()); tiltLine.lineTo(tiltAngle.p1()); path = outlineFetcher()->fetchOutline(info, this, path); if (mode == CursorTiltOutline) { QPainterPath tiltLine = makeTiltIndicator(info, QPointF(0.0, 0.0), width * 0.5, 3.0); path.addPath(outlineFetcher()->fetchOutline(info, this, tiltLine, 1.0, 0.0, true, 0, 0)); } } return path; } #include #include "kis_paintop_preset.h" #include "kis_paintop_settings_update_proxy.h" #include "kis_standard_uniform_properties_factory.h" QList KisSprayPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props = listWeakToStrong(m_d->uniformProperties); if (props.isEmpty()) { { KisDoubleSliderBasedPaintOpPropertyCallback *prop = new KisDoubleSliderBasedPaintOpPropertyCallback( KisDoubleSliderBasedPaintOpPropertyCallback::Double, "spacing", i18n("Spacing"), settings, 0); prop->setRange(0.01, 10); prop->setSingleStep(0.01); prop->setExponentRatio(3.0); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { - KisSprayProperties option; + KisSprayOptionProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.spacing); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { - KisSprayProperties option; + KisSprayOptionProperties option; option.readOptionSetting(prop->settings().data()); option.spacing = prop->value().toReal(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisIntSliderBasedPaintOpPropertyCallback *prop = new KisIntSliderBasedPaintOpPropertyCallback( KisIntSliderBasedPaintOpPropertyCallback::Int, "spray_particlecount", i18n("Particle Count"), settings, 0); prop->setRange(0, 1000); prop->setExponentRatio(3); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { - KisSprayProperties option; + KisSprayOptionProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(int(option.particleCount)); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { - KisSprayProperties option; + KisSprayOptionProperties option; option.readOptionSetting(prop->settings().data()); option.particleCount = prop->value().toInt(); option.writeOptionSetting(prop->settings().data()); }); prop->setIsVisibleCallback( [](const KisUniformPaintOpProperty *prop) { - KisSprayProperties option; + KisSprayOptionProperties option; option.readOptionSetting(prop->settings().data()); return !option.useDensity; }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisDoubleSliderBasedPaintOpPropertyCallback *prop = new KisDoubleSliderBasedPaintOpPropertyCallback( KisDoubleSliderBasedPaintOpPropertyCallback::Double, "spray_density", i18n("Density"), settings, 0); prop->setRange(0.1, 100); prop->setSingleStep(0.01); prop->setDecimals(2); prop->setExponentRatio(3); prop->setSuffix(i18n("%")); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { - KisSprayProperties option; + KisSprayOptionProperties option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.coverage); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { - KisSprayProperties option; + KisSprayOptionProperties option; option.readOptionSetting(prop->settings().data()); option.coverage = prop->value().toReal(); option.writeOptionSetting(prop->settings().data()); }); prop->setIsVisibleCallback( [](const KisUniformPaintOpProperty *prop) { - KisSprayProperties option; + KisSprayOptionProperties option; option.readOptionSetting(prop->settings().data()); return option.useDensity; }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } } { using namespace KisStandardUniformPropertiesFactory; Q_FOREACH (KisUniformPaintOpPropertySP prop, KisPaintOpSettings::uniformProperties(settings)) { if (prop->id() == opacity.id() || prop->id() == size.id()) { props.prepend(prop); } } } return props; } diff --git a/plugins/paintops/spray/kis_sprayop_option.cpp b/plugins/paintops/spray/kis_sprayop_option.cpp index 3509bef2f8..34c71c8ace 100644 --- a/plugins/paintops/spray/kis_sprayop_option.cpp +++ b/plugins/paintops/spray/kis_sprayop_option.cpp @@ -1,149 +1,149 @@ /* * Copyright (c) 2008,2009,2010 Lukáš Tvrdý * * 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_sprayop_option.h" #include #include "ui_wdgsprayoptions.h" class KisSprayOpOptionsWidget: public QWidget, public Ui::WdgSprayOptions { public: KisSprayOpOptionsWidget(QWidget *parent = 0) : QWidget(parent) { setupUi(this); } }; KisSprayOpOption::KisSprayOpOption() : KisPaintOpOption(KisPaintOpOption::GENERAL, false) { setObjectName("KisSprayOpOption"); m_checkable = false; m_options = new KisSprayOpOptionsWidget(); m_options->diameterSpinBox->setRange(1, 1000, 0); m_options->diameterSpinBox->setValue(100); m_options->diameterSpinBox->setExponentRatio(1.5); m_options->diameterSpinBox->setSuffix(i18n(" px")); m_options->aspectSPBox->setRange(0.0, 2.0, 2); m_options->aspectSPBox->setValue(1.0); m_options->rotationSPBox->setRange(0.0, 360.0, 0); m_options->rotationSPBox->setValue(0.0); m_options->rotationSPBox->setSuffix(QChar(Qt::Key_degree)); m_options->scaleSpin->setRange(0.0, 10.0, 2); m_options->scaleSpin->setValue(1.0); m_options->spacingSpin->setRange(0.0, 5.0, 2); m_options->spacingSpin->setValue(0.5); m_options->coverageSpin->setRange(0.001, 0.02, 3); m_options->coverageSpin->setValue(0.003); m_options->coverageSpin->setSuffix("%"); m_options->particlesSpinBox->setRange(1.0, 1000.0, 0); m_options->particlesSpinBox->setValue(12); m_options->particlesSpinBox->setExponentRatio(3.0); m_options->jitterMovementSpin->setRange(0.0,5.0, 1); m_options->jitterMovementSpin->setValue(1.0); connect(m_options->diameterSpinBox, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->coverageSpin, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->jitterMovementSpin, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->spacingSpin, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->scaleSpin, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->particlesSpinBox, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->countRadioButton, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_options->densityRadioButton, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_options->gaussianBox, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_options->aspectSPBox, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->rotationSPBox, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->jitterMoveBox, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_options->countRadioButton, SIGNAL(toggled(bool)), m_options->particlesSpinBox, SLOT(setEnabled(bool))); connect(m_options->densityRadioButton, SIGNAL(toggled(bool)), m_options->coverageSpin, SLOT(setEnabled(bool))); connect(m_options->jitterMoveBox, SIGNAL(toggled(bool)), m_options->jitterMovementSpin, SLOT(setEnabled(bool))); setConfigurationPage(m_options); } KisSprayOpOption::~KisSprayOpOption() { delete m_options; } void KisSprayOpOption::writeOptionSetting(KisPropertiesConfigurationSP setting) const { - KisSprayProperties op; + KisSprayOptionProperties op; op.diameter = m_options->diameterSpinBox->value(); op.particleCount = m_options->particlesSpinBox->value(); op.aspect = m_options->aspectSPBox->value(); op.coverage = m_options->coverageSpin->value(); op.amount = m_options->jitterMovementSpin->value(); op.spacing = m_options->spacingSpin->value(); op.scale = m_options->scaleSpin->value(); op.brushRotation = m_options->rotationSPBox->value(); op.jitterMovement = m_options->jitterMoveBox->isChecked(); op.useDensity = m_options->densityRadioButton->isChecked(); op.gaussian = m_options->gaussianBox->isChecked(); op.writeOptionSetting(setting); } void KisSprayOpOption::readOptionSetting(const KisPropertiesConfigurationSP setting) { - KisSprayProperties op; + KisSprayOptionProperties op; op.readOptionSetting(setting); m_options->diameterSpinBox->setValue(op.diameter); m_options->aspectSPBox->setValue(op.aspect); m_options->coverageSpin->setValue(op.coverage); m_options->scaleSpin->setValue(op.scale); m_options->rotationSPBox->setValue(op.brushRotation); m_options->particlesSpinBox->setValue(op.particleCount); m_options->jitterMovementSpin->setValue(op.amount); m_options->jitterMoveBox->setChecked(op.jitterMovement); m_options->spacingSpin->setValue(op.spacing); m_options->gaussianBox->setChecked(op.gaussian); //TODO: come on, do this nicer! e.g. button group or something bool useDensity = op.useDensity; m_options->densityRadioButton->setChecked(useDensity); m_options->countRadioButton->setChecked(!useDensity); } void KisSprayOpOption::setDiameter(int diameter) const { m_options->diameterSpinBox->setValue(diameter); } int KisSprayOpOption::diameter() const { return m_options->diameterSpinBox->value(); } qreal KisSprayOpOption::brushAspect() const { return m_options->aspectSPBox->value(); } diff --git a/plugins/paintops/spray/kis_sprayop_option.h b/plugins/paintops/spray/kis_sprayop_option.h index ead7483434..b02d61ea3d 100644 --- a/plugins/paintops/spray/kis_sprayop_option.h +++ b/plugins/paintops/spray/kis_sprayop_option.h @@ -1,106 +1,106 @@ /* * Copyright (c) 2008,2009,2010 Lukáš Tvrdý * * 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_SPRAYOP_OPTION_H #define KIS_SPRAYOP_OPTION_H #include const QString SPRAY_DIAMETER = "Spray/diameter"; const QString SPRAY_ASPECT = "Spray/aspect"; const QString SPRAY_COVERAGE = "Spray/coverage"; const QString SPRAY_SCALE = "Spray/scale"; const QString SPRAY_ROTATION = "Spray/rotation"; const QString SPRAY_PARTICLE_COUNT = "Spray/particleCount"; const QString SPRAY_JITTER_MOVE_AMOUNT = "Spray/jitterMoveAmount"; const QString SPRAY_JITTER_MOVEMENT = "Spray/jitterMovement"; const QString SPRAY_SPACING = "Spray/spacing"; const QString SPRAY_GAUSS_DISTRIBUTION = "Spray/gaussianDistribution"; const QString SPRAY_USE_DENSITY = "Spray/useDensity"; class KisSprayOpOptionsWidget; class KisSprayOpOption : public KisPaintOpOption { public: KisSprayOpOption(); ~KisSprayOpOption() override; void setDiameter(int diameter) const; int diameter() const; qreal brushAspect() const; void writeOptionSetting(KisPropertiesConfigurationSP setting) const override; void readOptionSetting(const KisPropertiesConfigurationSP setting) override; private: KisSprayOpOptionsWidget *m_options; }; -class KisSprayProperties : public KisBaseOption +class KisSprayOptionProperties : public KisPaintopPropertiesBase { public: quint16 diameter; quint16 particleCount; qreal aspect; qreal coverage; qreal amount; qreal spacing; qreal scale; qreal brushRotation; bool jitterMovement; bool useDensity; bool gaussian; int radius() const { return diameter / 2; } public: void readOptionSettingImpl(const KisPropertiesConfiguration *settings) override { diameter = settings->getInt(SPRAY_DIAMETER); aspect = settings->getDouble(SPRAY_ASPECT); particleCount = settings->getDouble(SPRAY_PARTICLE_COUNT); coverage = (settings->getDouble(SPRAY_COVERAGE) / 100.0); amount = settings->getDouble(SPRAY_JITTER_MOVE_AMOUNT); spacing = settings->getDouble(SPRAY_SPACING); scale = settings->getDouble(SPRAY_SCALE); brushRotation = settings->getDouble(SPRAY_ROTATION); jitterMovement = settings->getBool(SPRAY_JITTER_MOVEMENT); useDensity = settings->getBool(SPRAY_USE_DENSITY); gaussian = settings->getBool(SPRAY_GAUSS_DISTRIBUTION); } void writeOptionSettingImpl(KisPropertiesConfiguration *setting) const override { setting->setProperty(SPRAY_DIAMETER, diameter); setting->setProperty(SPRAY_ASPECT, aspect); setting->setProperty(SPRAY_COVERAGE, coverage * 100.0); setting->setProperty(SPRAY_SCALE, scale); setting->setProperty(SPRAY_ROTATION, brushRotation); setting->setProperty(SPRAY_PARTICLE_COUNT, particleCount); setting->setProperty(SPRAY_JITTER_MOVE_AMOUNT, amount); setting->setProperty(SPRAY_JITTER_MOVEMENT, jitterMovement); setting->setProperty(SPRAY_SPACING, spacing); setting->setProperty(SPRAY_GAUSS_DISTRIBUTION, gaussian); setting->setProperty(SPRAY_USE_DENSITY, useDensity); } }; #endif diff --git a/plugins/paintops/spray/spray_brush.cpp b/plugins/paintops/spray/spray_brush.cpp index 018af972eb..e0fe291868 100644 --- a/plugins/paintops/spray/spray_brush.cpp +++ b/plugins/paintops/spray/spray_brush.cpp @@ -1,533 +1,533 @@ /* * Copyright (c) 2008-2010 Lukáš Tvrdý * * 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 "spray_brush.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_spray_paintop_settings.h" #include #include #include SprayBrush::SprayBrush() { m_painter = 0; m_transfo = 0; } SprayBrush::~SprayBrush() { delete m_painter; delete m_transfo; } -void SprayBrush::setProperties(KisSprayProperties * properties, +void SprayBrush::setProperties(KisSprayOptionProperties * properties, KisColorProperties * colorProperties, KisShapeProperties * shapeProperties, KisShapeDynamicsProperties * shapeDynamicsProperties, KisBrushSP brush) { m_properties = properties; m_colorProperties = colorProperties; m_shapeProperties = shapeProperties; m_shapeDynamicsProperties = shapeDynamicsProperties; m_brush = brush; if (m_brush) { m_brush->notifyStrokeStarted(); } } qreal SprayBrush::rotationAngle(KisRandomSourceSP randomSource) { qreal rotation = 0.0; if (m_shapeDynamicsProperties->fixedRotation) { rotation = deg2rad(m_shapeDynamicsProperties->fixedAngle); } if (m_shapeDynamicsProperties->randomRotation) { qreal randomValue = 0.0; if (m_properties->gaussian) { randomValue = qBound(0.0, randomSource->generateGaussian(0.0, 0.5), 1.0); } else { randomValue = randomSource->generateNormalized(); } rotation = linearInterpolation(rotation , M_PI * 2.0 * randomValue, m_shapeDynamicsProperties->randomRotationWeight); } return rotation; } void SprayBrush::paint(KisPaintDeviceSP dab, KisPaintDeviceSP source, const KisPaintInformation& info, qreal rotation, qreal scale, qreal additionalScale, const KoColor &color, const KoColor &bgColor) { KisRandomSourceSP randomSource = info.randomSource(); // initializing painter if (!m_painter) { m_painter = new KisPainter(dab); m_painter->setFillStyle(KisPainter::FillStyleForegroundColor); m_painter->setMaskImageSize(m_shapeProperties->width, m_shapeProperties->height); m_dabPixelSize = dab->colorSpace()->pixelSize(); if (m_colorProperties->useRandomHSV) { m_transfo = dab->colorSpace()->createColorTransformation("hsv_adjustment", QHash()); } m_brushQImage = m_shapeProperties->image; if (!m_brushQImage.isNull()) { m_brushQImage = m_brushQImage.scaled(m_shapeProperties->width, m_shapeProperties->height); } m_imageDevice = new KisPaintDevice(dab->colorSpace()); } qreal x = info.pos().x(); qreal y = info.pos().y(); KisRandomAccessorSP accessor = dab->createRandomAccessorNG(qRound(x), qRound(y)); Q_ASSERT(color.colorSpace()->pixelSize() == dab->pixelSize()); m_inkColor = color; KisCrossDeviceColorPicker colorPicker(source, m_inkColor); // apply size sensor m_radius = m_properties->radius() * scale * additionalScale; // jitter movement if (m_properties->jitterMovement) { x = x + ((2 * m_radius * randomSource->generateNormalized()) - m_radius) * m_properties->amount; y = y + ((2 * m_radius * randomSource->generateNormalized()) - m_radius) * m_properties->amount; } // this is wrong for every shape except pixel and anti-aliased pixel if (m_properties->useDensity) { m_particlesCount = (m_properties->coverage * (M_PI * pow2(m_radius)) / pow2(additionalScale)); } else { m_particlesCount = m_properties->particleCount; } QHash params; qreal nx, ny; int ix, iy; qreal angle; qreal length; qreal rotationZ = 0.0; qreal particleScale = 1.0; bool shouldColor = true; if (m_colorProperties->fillBackground) { m_painter->setPaintColor(bgColor); paintCircle(m_painter, x, y, m_radius); } QTransform m; m.reset(); m.rotateRadians(-rotation + deg2rad(m_properties->brushRotation)); m.scale(m_properties->scale, m_properties->scale); for (quint32 i = 0; i < m_particlesCount; i++) { // generate random angle angle = randomSource->generateNormalized() * M_PI * 2; // generate random length if (m_properties->gaussian) { length = randomSource->generateGaussian(0.0, 0.5); } else { length = randomSource->generateNormalized(); } if (m_shapeDynamicsProperties->enabled) { // rotation rotationZ = rotationAngle(randomSource); if (m_shapeDynamicsProperties->followCursor) { rotationZ = linearInterpolation(rotationZ, angle, m_shapeDynamicsProperties->followCursorWeigth); } if (m_shapeDynamicsProperties->followDrawingAngle) { rotationZ = linearInterpolation(rotationZ, info.drawingAngle(), m_shapeDynamicsProperties->followDrawingAngleWeight); } // random size - scale if (m_shapeDynamicsProperties->randomSize) { particleScale = randomSource->generateNormalized(); } } // generate polar coordinate nx = (m_radius * cos(angle) * length); ny = (m_radius * sin(angle) * length); // compute the height of the ellipse ny *= m_properties->aspect; // transform m.map(nx, ny, &nx, &ny); // color transformation if (shouldColor) { if (m_colorProperties->sampleInputColor) { colorPicker.pickOldColor(nx + x, ny + y, m_inkColor.data()); } // mix the color with background color if (m_colorProperties->mixBgColor) { KoMixColorsOp * mixOp = dab->colorSpace()->mixColorsOp(); const quint8 *colors[2]; colors[0] = m_inkColor.data(); colors[1] = bgColor.data(); qint16 colorWeights[2]; int MAX_16BIT = 255; qreal blend = info.pressure(); colorWeights[0] = static_cast(blend * MAX_16BIT); colorWeights[1] = static_cast((1.0 - blend) * MAX_16BIT); mixOp->mixColors(colors, colorWeights, 2, m_inkColor.data()); } if (m_colorProperties->useRandomHSV && m_transfo) { params["h"] = (m_colorProperties->hue / 180.0) * randomSource->generateNormalized(); params["s"] = (m_colorProperties->saturation / 100.0) * randomSource->generateNormalized(); params["v"] = (m_colorProperties->value / 100.0) * randomSource->generateNormalized(); m_transfo->setParameters(params); m_transfo->setParameter(3, 1);//sets the type to HSV. For some reason 0 is not an option. m_transfo->setParameter(4, false);//sets the colorize to false. m_transfo->transform(m_inkColor.data(), m_inkColor.data() , 1); } if (m_colorProperties->useRandomOpacity) { quint8 alpha = qRound(randomSource->generateNormalized() * OPACITY_OPAQUE_U8); m_inkColor.setOpacity(alpha); m_painter->setOpacity(alpha); } if (!m_colorProperties->colorPerParticle) { shouldColor = false; } m_painter->setPaintColor(m_inkColor); } qreal jitteredWidth = qMax(1.0 * additionalScale, m_shapeProperties->width * particleScale * additionalScale); qreal jitteredHeight = qMax(1.0 * additionalScale, m_shapeProperties->height * particleScale * additionalScale); if (m_shapeProperties->enabled){ switch (m_shapeProperties->shape){ // ellipse case 0: { if (m_shapeProperties->width == m_shapeProperties->height){ paintCircle(m_painter, nx + x, ny + y, jitteredWidth * 0.5); } else { paintEllipse(m_painter, nx + x, ny + y, jitteredWidth * 0.5 , jitteredHeight * 0.5, rotationZ); } break; } // rectangle case 1: { paintRectangle(m_painter, nx + x, ny + y, qRound(jitteredWidth) , qRound(jitteredHeight), rotationZ); break; } // wu-particle case 2: { paintParticle(accessor, m_inkColor, nx + x, ny + y); break; } // pixel case 3: { ix = qRound(nx + x); iy = qRound(ny + y); accessor->moveTo(ix, iy); memcpy(accessor->rawData(), m_inkColor.data(), m_dabPixelSize); break; } case 4: { if (!m_brushQImage.isNull()) { QTransform m; m.rotate(rad2deg(rotationZ)); m.scale(additionalScale, additionalScale); if (m_shapeDynamicsProperties->randomSize) { m.scale(particleScale, particleScale); } m_transformed = m_brushQImage.transformed(m, Qt::SmoothTransformation); m_imageDevice->convertFromQImage(m_transformed, 0); KisRandomAccessorSP ac = m_imageDevice->createRandomAccessorNG(0, 0); QRect rc = m_transformed.rect(); if (m_colorProperties->useRandomHSV && m_transfo) { for (int y = rc.y(); y < rc.y() + rc.height(); y++) { for (int x = rc.x(); x < rc.x() + rc.width(); x++) { ac->moveTo(x, y); m_transfo->transform(ac->rawData(), ac->rawData() , 1); } } } ix = qRound(nx + x - rc.width() * 0.5); iy = qRound(ny + y - rc.height() * 0.5); m_painter->bitBlt(QPoint(ix, iy), m_imageDevice, rc); m_imageDevice->clear(); break; } } } // Auto-brush } else { KisDabShape shape(particleScale * additionalScale, 1.0, -rotationZ); QPointF hotSpot = m_brush->hotSpot(shape, info); QPointF pos(nx + x, ny + y); QPointF pt = pos - hotSpot; qint32 ix; qreal xFraction; qint32 iy; qreal yFraction; KisPaintOp::splitCoordinate(pt.x(), &ix, &xFraction); KisPaintOp::splitCoordinate(pt.y(), &iy, &yFraction); //KisFixedPaintDeviceSP dab; if (m_brush->brushType() == IMAGE || m_brush->brushType() == PIPE_IMAGE) { m_fixedDab = m_brush->paintDevice(m_fixedDab->colorSpace(), shape, info, xFraction, yFraction); if (m_colorProperties->useRandomHSV && m_transfo) { quint8 * dabPointer = m_fixedDab->data(); int pixelCount = m_fixedDab->bounds().width() * m_fixedDab->bounds().height(); m_transfo->transform(dabPointer, dabPointer, pixelCount); } } else { m_brush->mask(m_fixedDab, m_inkColor, shape, info, xFraction, yFraction); } m_painter->bltFixed(QPoint(ix, iy), m_fixedDab, m_fixedDab->bounds()); } if (m_colorProperties->colorPerParticle){ m_inkColor=color;//reset color// } } // recover from jittering of color, // m_inkColor.opacity is recovered with every paint } void SprayBrush::paintParticle(KisRandomAccessorSP &writeAccessor, const KoColor &color, qreal rx, qreal ry) { // opacity top left, right, bottom left, right KoColor pcolor(color); //int opacity = pcolor.opacityU8(); int ipx = int (rx); int ipy = int (ry); qreal fx = rx - ipx; qreal fy = ry - ipy; qreal btl = (1 - fx) * (1 - fy); qreal btr = (fx) * (1 - fy); qreal bbl = (1 - fx) * (fy); qreal bbr = (fx) * (fy); // this version overwrite pixels, e.g. when it sprays two particle next // to each other, the pixel with lower opacity can override other pixel. // Maybe some kind of compositing using here would be cool pcolor.setOpacity(btl); writeAccessor->moveTo(ipx , ipy); memcpy(writeAccessor->rawData(), pcolor.data(), m_dabPixelSize); pcolor.setOpacity(btr); writeAccessor->moveTo(ipx + 1, ipy); memcpy(writeAccessor->rawData(), pcolor.data(), m_dabPixelSize); pcolor.setOpacity(bbl); writeAccessor->moveTo(ipx, ipy + 1); memcpy(writeAccessor->rawData(), pcolor.data(), m_dabPixelSize); pcolor.setOpacity(bbr); writeAccessor->moveTo(ipx + 1, ipy + 1); memcpy(writeAccessor->rawData(), pcolor.data(), m_dabPixelSize); } void SprayBrush::paintCircle(KisPainter* painter, qreal x, qreal y, qreal radius) { QPainterPath path; path.addEllipse(QPointF(x,y),radius,radius); painter->fillPainterPath(path); } void SprayBrush::paintEllipse(KisPainter* painter, qreal x, qreal y, qreal a, qreal b, qreal angle) { QPainterPath path; path.addEllipse(QPointF(), a, b); QTransform t; t.translate(x, y); t.rotateRadians(angle); path = t.map(path); painter->fillPainterPath(path); } void SprayBrush::paintRectangle(KisPainter* painter, qreal x, qreal y, qreal width, qreal height, qreal angle) { QPainterPath path; path.addRect(QRectF(-0.5 * width, -0.5 * height, width, height)); QTransform t; t.translate(x, y); t.rotateRadians(angle); path = t.map(path); painter->fillPainterPath(path); } void SprayBrush::paintOutline(KisPaintDeviceSP dev , const KoColor &outlineColor, qreal posX, qreal posY, qreal radius) { QList antiPixels; KisRandomAccessorSP accessor = dev->createRandomAccessorNG(qRound(posX), qRound(posY)); for (int y = -radius + posY; y <= radius + posY; y++) { for (int x = -radius + posX; x <= radius + posX; x++) { accessor->moveTo(x, y); qreal alpha = dev->colorSpace()->opacityU8(accessor->rawData()); if (alpha != 0) { // top left accessor->moveTo(x - 1, y - 1); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x - 1, y - 1)); //continue; } // top accessor->moveTo(x, y - 1); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x, y - 1)); //continue; } // top right accessor->moveTo(x + 1, y - 1); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x + 1, y - 1)); //continue; } //left accessor->moveTo(x - 1, y); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x - 1, y)); //continue; } //right accessor->moveTo(x + 1, y); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x + 1, y)); //continue; } // bottom left accessor->moveTo(x - 1, y + 1); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x - 1, y + 1)); //continue; } // bottom accessor->moveTo(x, y + 1); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x, y + 1)); //continue; } // bottom right accessor->moveTo(x + 1, y + 1); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x + 1, y + 1)); //continue; } } } } // anti-alias it int size = antiPixels.size(); for (int i = 0; i < size; i++) { accessor->moveTo(antiPixels[i].x(), antiPixels[i].y()); memcpy(accessor->rawData(), outlineColor.data(), dev->colorSpace()->pixelSize()); } } void SprayBrush::setFixedDab(KisFixedPaintDeviceSP dab) { m_fixedDab = dab; } diff --git a/plugins/paintops/spray/spray_brush.h b/plugins/paintops/spray/spray_brush.h index bbec46d2f6..6526fccbdb 100644 --- a/plugins/paintops/spray/spray_brush.h +++ b/plugins/paintops/spray/spray_brush.h @@ -1,104 +1,104 @@ /* * Copyright (c) 2008-2010,2013 Lukáš Tvrdý * * 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 _SPRAY_BRUSH_H_ #define _SPRAY_BRUSH_H_ #include #include "kis_types.h" #include "kis_painter.h" #include #include "kis_color_option.h" #include "kis_spray_shape_option.h" #include "kis_spray_shape_dynamics.h" #include "kis_sprayop_option.h" #include #include class KisPaintInformation; class SprayBrush { public: SprayBrush(); ~SprayBrush(); void paint(KisPaintDeviceSP dab, KisPaintDeviceSP source, const KisPaintInformation& info, qreal rotation, qreal scale, qreal additionalScale, const KoColor &color, const KoColor &bgColor); - void setProperties(KisSprayProperties * properties, + void setProperties(KisSprayOptionProperties * properties, KisColorProperties * colorProperties, KisShapeProperties * shapeProperties, KisShapeDynamicsProperties * shapeDynamicsProperties, KisBrushSP brush); void setFixedDab(KisFixedPaintDeviceSP dab); private: KoColor m_inkColor; qreal m_radius; quint32 m_particlesCount; quint8 m_dabPixelSize; KisPainter * m_painter; KisPaintDeviceSP m_imageDevice; QImage m_brushQImage; QImage m_transformed; KoColorTransformation* m_transfo; - const KisSprayProperties * m_properties; + const KisSprayOptionProperties * m_properties; const KisColorProperties * m_colorProperties; const KisShapeProperties * m_shapeProperties; const KisShapeDynamicsProperties * m_shapeDynamicsProperties; KisBrushSP m_brush; KisFixedPaintDeviceSP m_fixedDab; private: /// rotation in radians according the settings (gauss distribution, uniform distribution or fixed angle) qreal rotationAngle(KisRandomSourceSP randomSource); /// Paints Wu Particle void paintParticle(KisRandomAccessorSP &writeAccessor, const KoColor &color, qreal rx, qreal ry); void paintCircle(KisPainter * painter, qreal x, qreal y, qreal radius); void paintEllipse(KisPainter * painter, qreal x, qreal y, qreal a, qreal b, qreal angle); void paintRectangle(KisPainter * painter, qreal x, qreal y, qreal width, qreal height, qreal angle); void paintOutline(KisPaintDeviceSP dev, const KoColor& painterColor, qreal posX, qreal posY, qreal radius); /// mix a with b.b mix with weight and a with 1.0 - weight inline qreal linearInterpolation(qreal a, qreal b, qreal weight) const { return (1.0 - weight) * a + weight * b; } // TODO: move this somewhere where I can reuse it /// convert radians to degrees inline qreal rad2deg(qreal rad) const { return rad * (180.0 / M_PI); } /// convert degrees to radians inline qreal deg2rad(quint16 deg) const { return deg * (M_PI / 180.0); } }; #endif diff --git a/plugins/paintops/tangentnormal/kis_tangent_normal_paintop.cpp b/plugins/paintops/tangentnormal/kis_tangent_normal_paintop.cpp index e0bcbfe695..6534935056 100644 --- a/plugins/paintops/tangentnormal/kis_tangent_normal_paintop.cpp +++ b/plugins/paintops/tangentnormal/kis_tangent_normal_paintop.cpp @@ -1,253 +1,253 @@ /* * Copyright (C) 2015 Wolthera van Hövell tot Westerflier * * 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_tangent_normal_paintop.h" #include #include #include #include #include #include #include #include #include #include #include #include KisTangentNormalPaintOp::KisTangentNormalPaintOp(const KisPaintOpSettingsSP settings, KisPainter* painter, KisNodeSP node, KisImageSP image): KisBrushBasedPaintOp(settings, painter), m_opacityOption(node), m_tempDev(painter->device()->createCompositionSourceDevice()) { Q_UNUSED(image); //Init, read settings, etc// m_tangentTiltOption.readOptionSetting(settings); - m_airbrushOptionWidget.readOptionSetting(settings); + m_airbrushOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_flowOption.readOptionSetting(settings); m_spacingOption.readOptionSetting(settings); m_rateOption.readOptionSetting(settings); m_softnessOption.readOptionSetting(settings); m_sharpnessOption.readOptionSetting(settings); m_rotationOption.readOptionSetting(settings); m_scatterOption.readOptionSetting(settings); m_sizeOption.resetAllSensors(); m_opacityOption.resetAllSensors(); m_flowOption.resetAllSensors(); m_spacingOption.resetAllSensors(); m_rateOption.resetAllSensors(); m_softnessOption.resetAllSensors(); m_sharpnessOption.resetAllSensors(); m_rotationOption.resetAllSensors(); m_scatterOption.resetAllSensors(); m_dabCache->setSharpnessPostprocessing(&m_sharpnessOption); m_rotationOption.applyFanCornersInfo(this); } KisTangentNormalPaintOp::~KisTangentNormalPaintOp() { //destroy things here// } KisSpacingInformation KisTangentNormalPaintOp::paintAt(const KisPaintInformation& info) { /* * For the color, the precision of tilt is only 60x60, and the precision of direction and rotation are 360 and 360*90. * You can't get more precise than 8bit. Therefore, we will check if the current space is RGB, * if so we request a profile with that space and 8bit bit depth, if not, just sRGB */ KoColor currentColor = painter()->paintColor(); QString currentSpace = currentColor.colorSpace()->colorModelId().id(); const KoColorSpace* rgbColorSpace = KoColorSpaceRegistry::instance()->rgb8(); if (currentSpace != "RGBA") { rgbColorSpace = KoColorSpaceRegistry::instance()->rgb8(); } else { rgbColorSpace = currentColor.colorSpace(); } QVector channelValues(4); qreal r, g, b; if (currentColor.colorSpace()->colorDepthId().id()=="F16" || currentColor.colorSpace()->colorDepthId().id()=="F32"){ channelValues[0] = 0.5;//red channelValues[1] = 0.5;//green channelValues[2] = 1.0;//blue channelValues[3] = 1.0;//alpha, leave alone. m_tangentTiltOption.apply(info, &r, &g, &b); channelValues[0] = r;//red channelValues[1] = g;//green channelValues[2] = b;//blue } else { channelValues[0] = 1.0;//blue channelValues[1] = 0.5;//green channelValues[2] = 0.5;//red channelValues[3] = 1.0;//alpha, leave alone. m_tangentTiltOption.apply(info, &r, &g, &b); channelValues[0] = b;//blue channelValues[1] = g;//green channelValues[2] = r;//red } quint8 data[4]; rgbColorSpace->fromNormalisedChannelsValue(data, channelValues); KoColor color(data, rgbColorSpace);//Should be default RGB(0.5,0.5,1.0) //draw stuff here, return kisspacinginformation. KisBrushSP brush = m_brush; if (!painter()->device() || !brush || !brush->canPaintFor(info)) { return KisSpacingInformation(1.0); } qreal scale = m_sizeOption.apply(info); scale *= KisLodTransform::lodToScale(painter()->device()); qreal rotation = m_rotationOption.apply(info); if (checkSizeTooSmall(scale)) return KisSpacingInformation(); KisDabShape shape(scale, 1.0, rotation); QPointF cursorPos = m_scatterOption.apply(info, brush->maskWidth(shape, 0, 0, info), brush->maskHeight(shape, 0, 0, info)); m_maskDab = m_dabCache->fetchDab(rgbColorSpace, color, cursorPos, shape, info, m_softnessOption.apply(info), &m_dstDabRect); if (m_dstDabRect.isEmpty()) return KisSpacingInformation(1.0); QRect dabRect = m_maskDab->bounds(); // sanity check Q_ASSERT(m_dstDabRect.size() == dabRect.size()); Q_UNUSED(dabRect); quint8 oldOpacity = painter()->opacity(); QString oldCompositeOpId = painter()->compositeOp()->id(); m_opacityOption.setFlow(m_flowOption.apply(info)); m_opacityOption.apply(painter(), info); //paint with the default color? Copied this from color smudge.// //painter()->setCompositeOp(COMPOSITE_COPY); //painter()->fill(0, 0, m_dstDabRect.width(), m_dstDabRect.height(), color); painter()->bltFixed(m_dstDabRect.topLeft(), m_maskDab, m_maskDab->bounds()); painter()->renderMirrorMaskSafe(m_dstDabRect, m_maskDab, !m_dabCache->needSeparateOriginal()); // restore original opacity and composite mode values painter()->setOpacity(oldOpacity); painter()->setCompositeOp(oldCompositeOpId); return computeSpacing(info, scale, rotation); } KisSpacingInformation KisTangentNormalPaintOp::updateSpacingImpl(const KisPaintInformation &info) const { qreal scale = m_sizeOption.apply(info) * KisLodTransform::lodToScale(painter()->device()); qreal rotation = m_rotationOption.apply(info); return computeSpacing(info, scale, rotation); } KisTimingInformation KisTangentNormalPaintOp::updateTimingImpl(const KisPaintInformation &info) const { - return KisPaintOpPluginUtils::effectiveTiming(&m_airbrushOptionWidget, &m_rateOption, info); + return KisPaintOpPluginUtils::effectiveTiming(&m_airbrushOption, &m_rateOption, info); } KisSpacingInformation KisTangentNormalPaintOp::computeSpacing(const KisPaintInformation &info, qreal scale, qreal rotation) const { - return effectiveSpacing(scale, rotation, &m_airbrushOptionWidget, &m_spacingOption, info); + return effectiveSpacing(scale, rotation, &m_airbrushOption, &m_spacingOption, info); } void KisTangentNormalPaintOp::paintLine(const KisPaintInformation& pi1, const KisPaintInformation& pi2, KisDistanceInformation *currentDistance) { if (m_sharpnessOption.isChecked() && m_brush && (m_brush->width() == 1) && (m_brush->height() == 1)) { if (!m_lineCacheDevice) { m_lineCacheDevice = m_tempDev; } else { m_lineCacheDevice->clear(); } KisPainter p(m_lineCacheDevice); KoColor currentColor = painter()->paintColor(); QString currentSpace = currentColor.colorSpace()->colorModelId().id(); const KoColorSpace* rgbColorSpace = KoColorSpaceRegistry::instance()->rgb8(); if (currentSpace != "RGBA") { rgbColorSpace = KoColorSpaceRegistry::instance()->rgb8(); } else { rgbColorSpace = currentColor.colorSpace(); } QVector channelValues(4); qreal r, g, b; if (currentColor.colorSpace()->colorDepthId().id()=="F16" || currentColor.colorSpace()->colorDepthId().id()=="F32"){ channelValues[0] = 0.5;//red channelValues[1] = 0.5;//green channelValues[2] = 1.0;//blue channelValues[3] = 1.0;//alpha, leave alone. m_tangentTiltOption.apply(pi2, &r, &g, &b); channelValues[0] = r;//red channelValues[1] = g;//green channelValues[2] = b;//blue } else { channelValues[0] = 1.0;//blue channelValues[1] = 0.5;//green channelValues[2] = 0.5;//red channelValues[3] = 1.0;//alpha, leave alone. m_tangentTiltOption.apply(pi2, &r, &g, &b); channelValues[0] = b;//blue channelValues[1] = g;//green channelValues[2] = r;//red } quint8 data[4]; rgbColorSpace->fromNormalisedChannelsValue(data, channelValues); KoColor color(data, rgbColorSpace); p.setPaintColor(color); p.drawDDALine(pi1.pos(), pi2.pos()); QRect rc = m_lineCacheDevice->extent(); painter()->bitBlt(rc.x(), rc.y(), m_lineCacheDevice, rc.x(), rc.y(), rc.width(), rc.height()); painter()->renderMirrorMask(rc, m_lineCacheDevice); } else { KisPaintOp::paintLine(pi1, pi2, currentDistance); } } diff --git a/plugins/paintops/tangentnormal/kis_tangent_normal_paintop.h b/plugins/paintops/tangentnormal/kis_tangent_normal_paintop.h index 943594f021..58e53ee966 100644 --- a/plugins/paintops/tangentnormal/kis_tangent_normal_paintop.h +++ b/plugins/paintops/tangentnormal/kis_tangent_normal_paintop.h @@ -1,85 +1,85 @@ /* * Copyright (C) 2015 Wolthera van Hövell tot Westerflier * * 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_TANGENTNORMALPAINTOP_H_ #define _KIS_TANGENTNORMALPAINTOP_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include class KisBrushBasedPaintOpSettings; class KisPainter; class KisTangentNormalPaintOp: public KisBrushBasedPaintOp { public: //public functions// /* Create a Tangent Normal Brush Operator*/ KisTangentNormalPaintOp(const KisPaintOpSettingsSP settings, KisPainter* painter, KisNodeSP node, KisImageSP image); ~KisTangentNormalPaintOp() override; void paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) override; protected: /*paint the dabs*/ KisSpacingInformation paintAt(const KisPaintInformation& info) override; KisSpacingInformation updateSpacingImpl(const KisPaintInformation &info) const override; KisTimingInformation updateTimingImpl(const KisPaintInformation &info) const override; private: KisSpacingInformation computeSpacing(const KisPaintInformation &info, qreal scale, qreal rotation) const; private: //private functions// KisPressureSizeOption m_sizeOption; KisFlowOpacityOption m_opacityOption; KisPressureSpacingOption m_spacingOption; KisPressureRateOption m_rateOption; KisPressureRotationOption m_rotationOption; KisPressureScatterOption m_scatterOption; KisTangentTiltOption m_tangentTiltOption; - KisAirbrushOptionWidget m_airbrushOptionWidget; + KisAirbrushOptionProperties m_airbrushOption; KisPressureSoftnessOption m_softnessOption; KisPressureSharpnessOption m_sharpnessOption; KisPressureFlowOption m_flowOption; KisFixedPaintDeviceSP m_maskDab; KisPaintDeviceSP m_tempDev; QRect m_dstDabRect; KisPaintDeviceSP m_lineCacheDevice; }; #endif // _KIS_TANGENTNORMALPAINTOP_H_ diff --git a/plugins/paintops/tangentnormal/kis_tangent_tilt_option.h b/plugins/paintops/tangentnormal/kis_tangent_tilt_option.h index 67a7ee2437..53f84cb31b 100644 --- a/plugins/paintops/tangentnormal/kis_tangent_tilt_option.h +++ b/plugins/paintops/tangentnormal/kis_tangent_tilt_option.h @@ -1,68 +1,68 @@ /* This file is part of the KDE project * * Copyright (C) 2015 Wolthera van Hövell tot Westerflier * * 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_TANGENT_TILT_OPTION_H #define KIS_TANGENT_TILT_OPTION_H #include #include #include const QString TANGENT_RED = "Tangent/swizzleRed"; const QString TANGENT_GREEN = "Tangent/swizzleGreen"; const QString TANGENT_BLUE = "Tangent/swizzleBlue"; const QString TANGENT_TYPE = "Tangent/directionType"; const QString TANGENT_EV_SEN = "Tangent/elevationSensitivity"; const QString TANGENT_MIX_VAL = "Tangent/mixValue"; //const QString TANGENT_DIR_MIN = "Tangent/directionMinimum"; //const QString TANGENT_DIR_MAX = "Tangent/directionMaximum"; class KisPropertiesConfiguration; class KisTangentTiltOptionWidget; -class KisTangentTiltOption: public KisPaintOpOption//not really// +class KisTangentTiltOption: public KisPaintOpOption //not really// { public: KisTangentTiltOption(); ~KisTangentTiltOption() override; /*These three give away which the index of the combobox for a given channel*/ int redChannel() const; int greenChannel() const; int blueChannel() const; int directionType() const; double elevationSensitivity() const; double mixValue() const; qreal m_canvasAngle; bool m_canvasAxisXMirrored; bool m_canvasAxisYMirrored; /*This assigns the right axis to the component, based on index and maximum value*/ void swizzleAssign(qreal const horizontal, qreal const vertical, qreal const depth, qreal *component, int index, qreal maxvalue); //takes the RGB values and will deform them depending on tilt. void apply(const KisPaintInformation& info, qreal *r, qreal *g, qreal *b); void writeOptionSetting(KisPropertiesConfigurationSP setting) const override; void readOptionSetting(const KisPropertiesConfigurationSP setting) override; private: KisTangentTiltOptionWidget * m_options; }; #endif // KIS_TANGENT_TILT_OPTION_H diff --git a/plugins/python/assignprofiledialog/assignprofiledialog.py b/plugins/python/assignprofiledialog/assignprofiledialog.py index c4b92bec31..36e57198f5 100644 --- a/plugins/python/assignprofiledialog/assignprofiledialog.py +++ b/plugins/python/assignprofiledialog/assignprofiledialog.py @@ -1,59 +1,60 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' -import sys -from PyQt5.QtGui import * -from PyQt5.QtWidgets import * -from krita import * +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import (QDialogButtonBox, QDialog, + QMessageBox, QComboBox, QVBoxLayout) +from krita import Extension class AssignProfileDialog(Extension): def __init__(self, parent): - super().__init__(parent) + super(AssignProfileDialog, self).__init__(parent) def assignProfile(self): doc = Application.activeDocument() - if doc == None: + if doc is None: QMessageBox.information(Application.activeWindow().qwindow(), "Assign Profile", "There is no active document.") return self.dialog = QDialog(Application.activeWindow().qwindow()) self.cmbProfile = QComboBox(self.dialog) for profile in sorted(Application.profiles(doc.colorModel(), doc.colorDepth())): self.cmbProfile.addItem(profile) vbox = QVBoxLayout(self.dialog) vbox.addWidget(self.cmbProfile) self.buttonBox = QDialogButtonBox(self.dialog) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonBox.accepted.connect(self.dialog.accept) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.dialog.reject) vbox.addWidget(self.buttonBox) self.dialog.show() self.dialog.activateWindow() self.dialog.exec_() def accept(self): doc = Application.activeDocument() doc.setColorProfile(self.cmbProfile.currentText()) def setup(self): pass - + def createActions(self, window): action = window.createAction("assing_profile_to_image", "Assign Profile to Image") action.triggered.connect(self.assignProfile) + Scripter.addExtension(AssignProfileDialog(Application)) diff --git a/plugins/python/assignprofiledialog/kritapykrita_assignprofiledialog.desktop b/plugins/python/assignprofiledialog/kritapykrita_assignprofiledialog.desktop index 84d67afa61..4f3ef47654 100644 --- a/plugins/python/assignprofiledialog/kritapykrita_assignprofiledialog.desktop +++ b/plugins/python/assignprofiledialog/kritapykrita_assignprofiledialog.desktop @@ -1,47 +1,47 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=assignprofiledialog -X-Python-2-Compatible=false +X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Assign Profile to Image Name[ar]=إسناد اللاحات إلى الصّور Name[ca]=Assigna un perfil a una imatge Name[ca@valencia]=Assigna un perfil a una imatge Name[cs]=Přiřadit obrázku profil Name[el]=Αντιστοίχιση προφίλ σε εικόνα Name[en_GB]=Assign Profile to Image Name[es]=Asignar perfil a imagen Name[gl]=Asignar un perfil á imaxe Name[is]=Úthluta litasniði á myndina Name[it]=Assegna profilo a immagine Name[nl]=Profiel aan afbeelding toekennen Name[pl]=Przypisz profil do obrazu Name[pt]=Atribuir um Perfil à Imagem Name[pt_BR]=Atribuir perfil a imagem Name[sv]=Tilldela profil till bild Name[tr]=Görüntüye Profil Ata Name[uk]=Призначити профіль до зображення Name[x-test]=xxAssign Profile to Imagexx -Name[zh_CN]=应用配置到图像 +Name[zh_CN]=为图像指定色彩配置文件 Name[zh_TW]=指定設定檔到圖像 Comment=Assign a profile to an image without converting it. Comment[ar]=أسنِد لاحة إلى صورة دون تحويلها. Comment[ca]=Assigna un perfil a una imatge sense convertir-la. Comment[ca@valencia]=Assigna un perfil a una imatge sense convertir-la. Comment[el]=Αντιστοιχίζει ένα προφίλ σε μια εικόνα χωρίς μετατροπή. Comment[en_GB]=Assign a profile to an image without converting it. Comment[es]=Asignar un perfil a una imagen sin convertirla. Comment[gl]=Asignar un perfil a unha imaxe sen convertela. Comment[is]=Úthluta litasniði á myndina án þess að umbreyta henni. Comment[it]=Assegna un profilo a un'immagine senza convertirla. Comment[nl]=Een profiel aan een afbeelding toekennen zonder het te converteren. Comment[pl]=Przypisz profil do obrazu bez jego przekształcania. Comment[pt]=Atribui um perfil à imagem sem a converter. Comment[pt_BR]=Atribui um perfil para uma imagem sem convertê-la. Comment[sv]=Tilldela en profil till en bild utan att konvertera den. Comment[tr]=Bir görüntüye, görüntüyü değiştirmeden bir profil ata. Comment[uk]=Призначити профіль до зображення без його перетворення. Comment[x-test]=xxAssign a profile to an image without converting it.xx -Comment[zh_CN]=为图像设定配置而无需转换它。 +Comment[zh_CN]=仅为图像指定色彩配置文件,不转换其色彩空间 Comment[zh_TW]=將設定檔指定給圖像,而不進行轉換。 diff --git a/plugins/python/colorspace/colorspace.py b/plugins/python/colorspace/colorspace.py index 938326960b..3d4c4bd8ab 100644 --- a/plugins/python/colorspace/colorspace.py +++ b/plugins/python/colorspace/colorspace.py @@ -1,34 +1,34 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' import krita -from colorspace import uicolorspace +from . import uicolorspace class ColorSpaceExtension(krita.Extension): def __init__(self, parent): super(ColorSpaceExtension, self).__init__(parent) def setup(self): pass - + def createActions(self, window): action = window.createAction("color_space", "Color Space") action.setToolTip("Plugin to change color space to selected documents") action.triggered.connect(self.initialize) def initialize(self): self.uicolorspace = uicolorspace.UIColorSpace() self.uicolorspace.initialize() Scripter.addExtension(ColorSpaceExtension(krita.Krita.instance())) diff --git a/plugins/python/colorspace/kritapykrita_colorspace.desktop b/plugins/python/colorspace/kritapykrita_colorspace.desktop index 3d750fb64a..afa906b39f 100644 --- a/plugins/python/colorspace/kritapykrita_colorspace.desktop +++ b/plugins/python/colorspace/kritapykrita_colorspace.desktop @@ -1,49 +1,49 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=colorspace -X-Python-2-Compatible=false +X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Color Space Name[ar]=الفضاء اللونيّ Name[ca]=Espai de color Name[ca@valencia]=Espai de color Name[cs]=Barevný prostor Name[de]=Farbraum Name[el]=Χρωματικός χώρος Name[en_GB]=Colour Space Name[es]=Espacio de color Name[gl]=Espazo de cores Name[is]=Litrýmd Name[it]=Spazio dei colori Name[nl]=Kleurruimte Name[pl]=Przestrzeń barw Name[pt]=Espaço de Cores Name[pt_BR]=Espaço de cores Name[sk]=Farebný priestor Name[sv]=Färgrymd Name[tr]=Renk Aralığı Name[uk]=Простір кольорів Name[x-test]=xxColor Spacexx Name[zh_CN]=色彩空间 Name[zh_TW]=色彩空間 Comment=Plugin to change color space to selected documents Comment[ar]=ملحقة لتغيير الفضاء اللونيّ في المستندات المحدّدة Comment[ca]=Un connector per canviar l'espai de color dels documents seleccionats Comment[ca@valencia]=Un connector per canviar l'espai de color dels documents seleccionats Comment[cs]=Modul pro změnu rozsahu barvy pro vybrané dokumenty Comment[el]=Πρόσθετο αλλαγής χρωματικού χώρου σε επιλεγμένα έγγραφα Comment[en_GB]=Plugin to change colour space to selected documents Comment[es]=Complemento para cambiar el espacio de color de los documentos seleccionados Comment[gl]=Complemento para cambiar o espazo de cores dos documentos seleccionados. Comment[it]=Estensione per cambiare lo spazio dei colori ai documenti selezionati Comment[nl]=Plug-in om kleurruimte in geselecteerde documenten te wijzigen Comment[pl]=Wtyczka do zmiany przestrzeni barw wybranych dokumentów Comment[pt]='Plugin' para mudar o espaço de cores do documento seleccionado Comment[pt_BR]=Plug-in para alterar o espaço de cores em documentos selecionados Comment[sv]=Insticksprogram för att ändra färgrymd för valda dokument Comment[tr]=Seçili belgede renk aralığını değiştirmek için eklenti Comment[uk]=Додаток для зміни простору кольорів у позначених документах Comment[x-test]=xxPlugin to change color space to selected documentsxx -Comment[zh_CN]=将颜色空间改为所选文档的插件 +Comment[zh_CN]=用于更改选定文档色彩空间的插件 Comment[zh_TW]=用於變更色彩空間為選定文件的外掛程式 diff --git a/plugins/python/colorspace/uicolorspace.py b/plugins/python/colorspace/uicolorspace.py index accd3ab57e..f7c9a8378e 100644 --- a/plugins/python/colorspace/uicolorspace.py +++ b/plugins/python/colorspace/uicolorspace.py @@ -1,129 +1,129 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' -from colorspace import colorspacedialog -from colorspace.components import colormodelcombobox, colordepthcombobox, colorprofilecombobox +from . import colorspacedialog +from .components import colormodelcombobox, colordepthcombobox, colorprofilecombobox from PyQt5.QtCore import Qt from PyQt5.QtWidgets import (QFormLayout, QListWidget, QListWidgetItem, QAbstractItemView, QComboBox, QDialogButtonBox, QVBoxLayout, QFrame, QMessageBox, QPushButton, QHBoxLayout, QAbstractScrollArea) from PyQt5.QtGui import QIcon import krita -from colorspace import resources_rc +from . import resources_rc class UIColorSpace(object): def __init__(self): self.mainDialog = colorspacedialog.ColorSpaceDialog() self.mainLayout = QVBoxLayout(self.mainDialog) self.formLayout = QFormLayout() self.documentLayout = QVBoxLayout() self.refreshButton = QPushButton(QIcon(':/icons/refresh.svg'), "Refresh") self.widgetDocuments = QListWidget() self.colorModelComboBox = colormodelcombobox.ColorModelComboBox(self) self.colorDepthComboBox = colordepthcombobox.ColorDepthComboBox(self) self.colorProfileComboBox = colorprofilecombobox.ColorProfileComboBox(self) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.kritaInstance = krita.Krita.instance() self.documentsList = [] self.colorModelsList = [] self.colorDepthsList = [] self.colorProfilesList = [] self.refreshButton.clicked.connect(self.refreshButtonClicked) self.buttonBox.accepted.connect(self.confirmButton) self.buttonBox.rejected.connect(self.mainDialog.close) self.mainDialog.setWindowModality(Qt.NonModal) self.widgetDocuments.setSelectionMode(QAbstractItemView.MultiSelection) self.widgetDocuments.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents) def initialize(self): self.loadDocuments() self.loadColorModels() self.loadColorDepths() self.loadColorProfiles() self.documentLayout.addWidget(self.widgetDocuments) self.documentLayout.addWidget(self.refreshButton) self.formLayout.addRow('Documents', self.documentLayout) self.formLayout.addRow('Color Model', self.colorModelComboBox) self.formLayout.addRow('Color Depth', self.colorDepthComboBox) self.formLayout.addRow('Color Profile', self.colorProfileComboBox) self.line = QFrame() self.line.setFrameShape(QFrame.HLine) self.line.setFrameShadow(QFrame.Sunken) self.mainLayout.addLayout(self.formLayout) self.mainLayout.addWidget(self.line) self.mainLayout.addWidget(self.buttonBox) self.mainDialog.resize(500, 300) self.mainDialog.setWindowTitle("Color Space") self.mainDialog.setSizeGripEnabled(True) self.mainDialog.show() self.mainDialog.activateWindow() def loadColorModels(self): self.colorModelsList = sorted(self.kritaInstance.colorModels()) self.colorModelComboBox.addItems(self.colorModelsList) def loadColorDepths(self): self.colorDepthComboBox.clear() colorModel = self.colorModelComboBox.currentText() self.colorDepthsList = sorted(self.kritaInstance.colorDepths(colorModel)) self.colorDepthComboBox.addItems(self.colorDepthsList) def loadColorProfiles(self): self.colorProfileComboBox.clear() colorModel = self.colorModelComboBox.currentText() colorDepth = self.colorDepthComboBox.currentText() self.colorProfilesList = sorted(self.kritaInstance.profiles(colorModel, colorDepth)) self.colorProfileComboBox.addItems(self.colorProfilesList) def loadDocuments(self): self.widgetDocuments.clear() self.documentsList = [document for document in self.kritaInstance.documents() if document.fileName()] for document in self.documentsList: self.widgetDocuments.addItem(document.fileName()) def refreshButtonClicked(self): self.loadDocuments() def confirmButton(self): selectedPaths = [item.text() for item in self.widgetDocuments.selectedItems()] selectedDocuments = [document for document in self.documentsList for path in selectedPaths if path == document.fileName()] self.msgBox = QMessageBox(self.mainDialog) if selectedDocuments: self.convertColorSpace(selectedDocuments) self.msgBox.setText("The selected documents has been converted.") else: self.msgBox.setText("Select at least one document.") self.msgBox.exec_() def convertColorSpace(self, documents): for document in documents: document.setColorSpace(self.colorModelComboBox.currentText(), self.colorDepthComboBox.currentText(), self.colorProfileComboBox.currentText()) diff --git a/plugins/python/comics_project_management_tools/kritapykrita_comics_project_management_tools.desktop b/plugins/python/comics_project_management_tools/kritapykrita_comics_project_management_tools.desktop index 1a4d79327e..1a4760b94a 100644 --- a/plugins/python/comics_project_management_tools/kritapykrita_comics_project_management_tools.desktop +++ b/plugins/python/comics_project_management_tools/kritapykrita_comics_project_management_tools.desktop @@ -1,46 +1,46 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=comics_project_management_tools X-Krita-Manual=README.html X-Python-2-Compatible=false Name=Comics Project Management Tools Name[ar]=أدوات إدارة المشاريع الهزليّة Name[ca]=Eines per a la gestió dels projectes de còmics Name[ca@valencia]=Eines per a la gestió dels projectes de còmics Name[cs]=Nástroje pro správu projektů komixů Name[el]=Εργαλεία διαχείρισης έργων ιστοριών σε εικόνες Name[en_GB]=Comics Project Management Tools Name[es]=Herramientas de gestión de proyectos de cómics Name[gl]=Ferramentas de xestión de proxectos de cómics Name[is]=Verkefnisstjórn teiknimyndasögu Name[it]=Strumenti per la gestione dei progetti di fumetti Name[nl]=Hulpmiddelen voor projectbeheer van strips Name[pl]=Narzędzia do zarządzania projektami komiksów Name[pt]=Ferramentas de Gestão de Projectos de Banda Desenhada Name[sv]=Projekthanteringsverktyg för tecknade serier Name[tr]=Çizgi Roman Projesi Yönetimi Araçları Name[uk]=Інструменти для керування проектами коміксів Name[x-test]=xxComics Project Management Toolsxx Name[zh_CN]=漫画项目管理工具 Name[zh_TW]=漫畫專案管理工具 Comment=Tools for managing comics. Comment[ar]=أدوات لإدارة الهزليّات. Comment[ca]=Eines per a gestionar els còmics. Comment[ca@valencia]=Eines per a gestionar els còmics. Comment[cs]=Nástroje pro správu komixů. Comment[el]=Εργαλεία για τη διαχείριση ιστοριών σε εικόνες. Comment[en_GB]=Tools for managing comics. Comment[es]=Herramientas para gestionar cómics. Comment[gl]=Ferramentas para xestionar cómics. Comment[is]=Verkfæri til að stýra gerð teiknimyndasögu. Comment[it]=Strumenti per la gestione dei fumetti. Comment[nl]=Hulpmiddelen voor beheer van strips. Comment[pl]=Narzędzie do zarządzania komiksami. Comment[pt]=Ferramentas para gerir bandas desenhadas. Comment[sv]=Verktyg för att hantera tecknade serier. Comment[tr]=Çizgi romanları yönetmek için araçlar. Comment[uk]=Інструменти для керування коміксами Comment[x-test]=xxTools for managing comics.xx -Comment[zh_CN]=管理漫画的工具。 +Comment[zh_CN]=用来管理漫画的工具。 Comment[zh_TW]=管理漫畫的工具。 diff --git a/plugins/python/documenttools/documenttools.py b/plugins/python/documenttools/documenttools.py index 9432c9309e..cf498ef349 100644 --- a/plugins/python/documenttools/documenttools.py +++ b/plugins/python/documenttools/documenttools.py @@ -1,34 +1,34 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' import krita -from documenttools import uidocumenttools +from . import uidocumenttools class DocumentToolsExtension(krita.Extension): def __init__(self, parent): super(DocumentToolsExtension, self).__init__(parent) def setup(self): pass - + def createActions(self, window): action = window.createAction("document_tools", "Document Tools") action.setToolTip("Plugin to manipulate properties of selected documents") action.triggered.connect(self.initialize) def initialize(self): self.uidocumenttools = uidocumenttools.UIDocumentTools() self.uidocumenttools.initialize() Scripter.addExtension(DocumentToolsExtension(krita.Krita.instance())) diff --git a/plugins/python/documenttools/kritapykrita_documenttools.desktop b/plugins/python/documenttools/kritapykrita_documenttools.desktop index e656524b89..59007cae7c 100644 --- a/plugins/python/documenttools/kritapykrita_documenttools.desktop +++ b/plugins/python/documenttools/kritapykrita_documenttools.desktop @@ -1,46 +1,46 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=documenttools -X-Python-2-Compatible=false +X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Document Tools Name[ar]=أدوات المستندات Name[ca]=Eines de document Name[ca@valencia]=Eines de document Name[cs]=Dokumentové nástroje Name[el]=Εργαλεία για έγγραφα Name[en_GB]=Document Tools Name[es]=Herramientas de documentos Name[gl]=Ferramentas de documentos Name[it]=Strumenti per i documenti Name[nl]=Documenthulpmiddelen Name[pl]=Narzędzia dokumentu Name[pt]=Ferramentas de Documentos Name[pt_BR]=Ferramentas de documento Name[sv]=Dokumentverktyg Name[tr]=Belge Araçları Name[uk]=Засоби документа Name[x-test]=xxDocument Toolsxx Name[zh_CN]=文档工具 Name[zh_TW]=文件工具 Comment=Plugin to manipulate properties of selected documents Comment[ar]=ملحقة لتعديل خصائص المستندات المحدّدة Comment[ca]=Un connector per manipular propietats dels documents seleccionats Comment[ca@valencia]=Un connector per manipular propietats dels documents seleccionats Comment[cs]=Modul pro správu vlastností vybraných dokumentů Comment[el]=Πρόσθετο χειρισμού ιδιοτήτων σε επιλεγμένα έγγραφα Comment[en_GB]=Plugin to manipulate properties of selected documents Comment[es]=Complemento para manipular las propiedades de los documentos seleccionados Comment[gl]=Complemento para manipular as propiedades dos documentos seleccionados. Comment[it]=Estensione per manipolare le proprietà dei documenti selezionati Comment[nl]=Plug-in om eigenschappen van geselecteerde documenten te manipuleren Comment[pl]=Wtyczka do zmiany właściwości wybranych dokumentów Comment[pt]='Plugin' para manipular as propriedades dos documentos seleccionados Comment[pt_BR]=Plug-in para manipular as propriedades de documentos selecionados Comment[sv]=Insticksprogram för att ändra egenskaper för valda dokument Comment[tr]=Seçili belgelerin özelliklerini değiştirmek için eklenti Comment[uk]=Додаток для керування властивостями позначених документів Comment[x-test]=xxPlugin to manipulate properties of selected documentsxx -Comment[zh_CN]=操纵选中文档的属性的插件 +Comment[zh_CN]=用于编辑选定文档属性的插件 Comment[zh_TW]=用於修改所選文件屬性的外掛程式 diff --git a/plugins/python/documenttools/uidocumenttools.py b/plugins/python/documenttools/uidocumenttools.py index 41e627122d..eec5e51279 100644 --- a/plugins/python/documenttools/uidocumenttools.py +++ b/plugins/python/documenttools/uidocumenttools.py @@ -1,106 +1,107 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' -from documenttools import documenttoolsdialog +from . import documenttoolsdialog from PyQt5.QtCore import Qt from PyQt5.QtWidgets import (QFormLayout, QListWidget, QAbstractItemView, QDialogButtonBox, QVBoxLayout, QFrame, QTabWidget, QPushButton, QAbstractScrollArea, QMessageBox) import krita import importlib class UIDocumentTools(object): def __init__(self): self.mainDialog = documenttoolsdialog.DocumentToolsDialog() self.mainLayout = QVBoxLayout(self.mainDialog) self.formLayout = QFormLayout() self.documentLayout = QVBoxLayout() self.refreshButton = QPushButton("Refresh") self.widgetDocuments = QListWidget() self.tabTools = QTabWidget() self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.kritaInstance = krita.Krita.instance() self.documentsList = [] self.refreshButton.clicked.connect(self.refreshButtonClicked) self.buttonBox.accepted.connect(self.confirmButton) self.buttonBox.rejected.connect(self.mainDialog.close) self.mainDialog.setWindowModality(Qt.NonModal) self.widgetDocuments.setSelectionMode(QAbstractItemView.MultiSelection) self.widgetDocuments.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents) def initialize(self): self.loadDocuments() self.loadTools() self.documentLayout.addWidget(self.widgetDocuments) self.documentLayout.addWidget(self.refreshButton) self.formLayout.addRow('Documents', self.documentLayout) self.formLayout.addRow(self.tabTools) self.line = QFrame() self.line.setFrameShape(QFrame.HLine) self.line.setFrameShadow(QFrame.Sunken) self.mainLayout.addLayout(self.formLayout) self.mainLayout.addWidget(self.line) self.mainLayout.addWidget(self.buttonBox) self.mainDialog.resize(500, 300) self.mainDialog.setWindowTitle("Document Tools") self.mainDialog.setSizeGripEnabled(True) self.mainDialog.show() self.mainDialog.activateWindow() def loadTools(self): modulePath = 'documenttools.tools' toolsModule = importlib.import_module(modulePath) modules = [] for classPath in toolsModule.ToolClasses: - _module, _klass = classPath.rsplit('.', maxsplit=1) + _module = classPath[:classPath.rfind(".")] + _klass = classPath[classPath.rfind(".") + 1:] modules.append(dict(module='{0}.{1}'.format(modulePath, _module), klass=_klass)) for module in modules: m = importlib.import_module(module['module']) toolClass = getattr(m, module['klass']) obj = toolClass(self.mainDialog) self.tabTools.addTab(obj, obj.objectName()) def loadDocuments(self): self.widgetDocuments.clear() self.documentsList = [document for document in self.kritaInstance.documents() if document.fileName()] for document in self.documentsList: self.widgetDocuments.addItem(document.fileName()) def refreshButtonClicked(self): self.loadDocuments() def confirmButton(self): selectedPaths = [item.text() for item in self.widgetDocuments.selectedItems()] selectedDocuments = [document for document in self.documentsList for path in selectedPaths if path == document.fileName()] self.msgBox = QMessageBox(self.mainDialog) if selectedDocuments: widget = self.tabTools.currentWidget() widget.adjust(selectedDocuments) self.msgBox.setText("The selected documents has been modified.") else: self.msgBox.setText("Select at least one document.") self.msgBox.exec_() diff --git a/plugins/python/exportlayers/exportlayers.py b/plugins/python/exportlayers/exportlayers.py index de0b65af49..c6336f62a5 100644 --- a/plugins/python/exportlayers/exportlayers.py +++ b/plugins/python/exportlayers/exportlayers.py @@ -1,34 +1,34 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' import krita -from exportlayers import uiexportlayers +from . import uiexportlayers class ExportLayersExtension(krita.Extension): def __init__(self, parent): super(ExportLayersExtension, self).__init__(parent) def setup(self): pass - + def createActions(self, window): action = window.createAction("export_layers", "Export Layers") action.setToolTip("Plugin to export layers from a document") action.triggered.connect(self.initialize) def initialize(self): self.uiexportlayers = uiexportlayers.UIExportLayers() self.uiexportlayers.initialize() Scripter.addExtension(ExportLayersExtension(krita.Krita.instance())) diff --git a/plugins/python/exportlayers/kritapykrita_exportlayers.desktop b/plugins/python/exportlayers/kritapykrita_exportlayers.desktop index dcf6e5f520..2587174e00 100644 --- a/plugins/python/exportlayers/kritapykrita_exportlayers.desktop +++ b/plugins/python/exportlayers/kritapykrita_exportlayers.desktop @@ -1,48 +1,48 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=exportlayers X-Krita-Manual=Manual.html -X-Python-2-Compatible=false +X-Python-2-Compatible=true Name=Export Layers Name[ar]=تصدير الطّبقات Name[ca]=Exportació de capes Name[ca@valencia]=Exportació de capes Name[cs]=Exportovat vrstvy Name[de]=Ebenen exportieren Name[el]=Εξαγωγή επιπέδων Name[en_GB]=Export Layers Name[es]=Exportar capas Name[gl]=Exportar as capas Name[is]=Flytja út lög Name[it]=Esporta livelli Name[nl]=Lagen exporteren Name[pl]=Eksportuj warstwy Name[pt]=Exportar as Camadas Name[pt_BR]=Exportar camadas Name[sv]=Exportera lager Name[tr]=Katmanları Dışa Aktar Name[uk]=Експортувати шари Name[x-test]=xxExport Layersxx Name[zh_CN]=导出图层 Name[zh_TW]=匯出圖層 Comment=Plugin to export layers from a document Comment[ar]=ملحقة لتصدير الطّبقات من مستند Comment[ca]=Un connector per exportar capes d'un document Comment[ca@valencia]=Un connector per exportar capes d'un document Comment[cs]=Modul pro export vrstev z dokumentu Comment[el]=Πρόσθετο εξαγωγής επιπέδων από έγγραφο Comment[en_GB]=Plugin to export layers from a document Comment[es]=Complemento para exportar las capas de un documento Comment[gl]=Complemento para exportar as capas dun documento. Comment[it]=Estensione per esportare i livelli da un documento Comment[nl]=Plug-in om lagen uit een document te exporteren Comment[pl]=Wtyczka do eksportowania warstw z dokumentu Comment[pt]='Plugin' para exportar as camadas de um documento Comment[pt_BR]=Plug-in para exportar as camadas de um documento Comment[sv]=Insticksprogram för att exportera lager från ett dokument Comment[tr]=Belgenin katmanlarını dışa aktarmak için eklenti Comment[uk]=Додаток для експортування шарів з документа Comment[x-test]=xxPlugin to export layers from a documentxx -Comment[zh_CN]=从文档导出图层的插件 +Comment[zh_CN]=用于从文档导出图层的插件 Comment[zh_TW]=用於從文件匯出圖層的外掛程式 diff --git a/plugins/python/exportlayers/uiexportlayers.py b/plugins/python/exportlayers/uiexportlayers.py index 625fbfad26..9b8c3239d9 100644 --- a/plugins/python/exportlayers/uiexportlayers.py +++ b/plugins/python/exportlayers/uiexportlayers.py @@ -1,180 +1,184 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' -from exportlayers import exportlayersdialog +from . import exportlayersdialog from PyQt5.QtCore import Qt from PyQt5.QtWidgets import (QFormLayout, QListWidget, QHBoxLayout, QDialogButtonBox, QVBoxLayout, QFrame, QPushButton, QAbstractScrollArea, QLineEdit, QMessageBox, QFileDialog, QCheckBox, QSpinBox, QComboBox) import os import errno import krita class UIExportLayers(object): def __init__(self): self.mainDialog = exportlayersdialog.ExportLayersDialog() self.mainLayout = QVBoxLayout(self.mainDialog) self.formLayout = QFormLayout() self.documentLayout = QVBoxLayout() self.directorySelectorLayout = QHBoxLayout() self.optionsLayout = QVBoxLayout() self.resolutionLayout = QHBoxLayout() self.refreshButton = QPushButton("Refresh") self.widgetDocuments = QListWidget() self.directoryTextField = QLineEdit() self.directoryDialogButton = QPushButton("...") self.exportFilterLayersCheckBox = QCheckBox("Export filter layers") self.batchmodeCheckBox = QCheckBox("Export in batchmode") self.ignoreInvisibleLayersCheckBox = QCheckBox("Ignore invisible layers") self.xResSpinBox = QSpinBox() self.yResSpinBox = QSpinBox() self.formatsComboBox = QComboBox() self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.kritaInstance = krita.Krita.instance() self.documentsList = [] self.directoryTextField.setReadOnly(True) self.batchmodeCheckBox.setChecked(True) self.directoryDialogButton.clicked.connect(self._selectDir) self.widgetDocuments.currentRowChanged.connect(self._setResolution) self.refreshButton.clicked.connect(self.refreshButtonClicked) self.buttonBox.accepted.connect(self.confirmButton) self.buttonBox.rejected.connect(self.mainDialog.close) self.mainDialog.setWindowModality(Qt.NonModal) self.widgetDocuments.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents) def initialize(self): self.loadDocuments() self.xResSpinBox.setRange(1, 10000) self.yResSpinBox.setRange(1, 10000) self.formatsComboBox.addItem("jpeg") self.formatsComboBox.addItem("png") self.documentLayout.addWidget(self.widgetDocuments) self.documentLayout.addWidget(self.refreshButton) self.directorySelectorLayout.addWidget(self.directoryTextField) self.directorySelectorLayout.addWidget(self.directoryDialogButton) self.optionsLayout.addWidget(self.exportFilterLayersCheckBox) self.optionsLayout.addWidget(self.batchmodeCheckBox) self.optionsLayout.addWidget(self.ignoreInvisibleLayersCheckBox) self.resolutionLayout.addWidget(self.xResSpinBox) self.resolutionLayout.addWidget(self.yResSpinBox) self.formLayout.addRow('Documents', self.documentLayout) self.formLayout.addRow('Initial directory', self.directorySelectorLayout) self.formLayout.addRow('Export options', self.optionsLayout) self.formLayout.addRow('Resolution', self.resolutionLayout) self.formLayout.addRow('Images Extensions', self.formatsComboBox) self.line = QFrame() self.line.setFrameShape(QFrame.HLine) self.line.setFrameShadow(QFrame.Sunken) self.mainLayout.addLayout(self.formLayout) self.mainLayout.addWidget(self.line) self.mainLayout.addWidget(self.buttonBox) self.mainDialog.resize(500, 300) self.mainDialog.setWindowTitle("Export Layers") self.mainDialog.setSizeGripEnabled(True) self.mainDialog.show() self.mainDialog.activateWindow() def loadDocuments(self): self.widgetDocuments.clear() self.documentsList = [document for document in self.kritaInstance.documents() if document.fileName()] for document in self.documentsList: self.widgetDocuments.addItem(document.fileName()) def refreshButtonClicked(self): self.loadDocuments() def confirmButton(self): selectedPaths = [item.text() for item in self.widgetDocuments.selectedItems()] selectedDocuments = [document for document in self.documentsList for path in selectedPaths if path == document.fileName()] self.msgBox = QMessageBox(self.mainDialog) if not selectedDocuments: self.msgBox.setText("Select one document.") elif not self.directoryTextField.text(): self.msgBox.setText("Select the initial directory.") else: self.export(selectedDocuments[0]) self.msgBox.setText("All layers has been exported.") self.msgBox.exec_() def mkdir(self, directory): + target_directory = self.directoryTextField.text() + directory + if os.path.exists(target_directory) and os.path.isdir(target_directory): + return + try: - os.makedirs(self.directoryTextField.text() + directory) + os.makedirs(target_directory) except OSError as e: - if e.errno != errno.EEXIST: - raise + raise e def export(self, document): Application.setBatchmode(self.batchmodeCheckBox.isChecked()) documentName = document.fileName() if document.fileName() else 'Untitled' - fileName, extension = str(documentName).rsplit('/', maxsplit=1)[-1].split('.', maxsplit=1) + fileName, extension = os.path.splitext(os.path.basename(documentName)) self.mkdir('/' + fileName) self._exportLayers(document.rootNode(), self.formatsComboBox.currentText(), '/' + fileName) Application.setBatchmode(True) def _exportLayers(self, parentNode, fileFormat, parentDir): """ This method get all sub-nodes from the current node and export then in the defined format.""" for node in parentNode.childNodes(): newDir = '' if node.type() == 'grouplayer': - newDir = parentDir + '/' + node.name() + newDir = os.path.join(parentDir, node.name()) self.mkdir(newDir) elif not self.exportFilterLayersCheckBox.isChecked() and 'filter' in node.type(): continue elif self.ignoreInvisibleLayersCheckBox.isChecked() and not node.visible(): continue else: nodeName = node.name() _fileFormat = self.formatsComboBox.currentText() if '[jpeg]' in nodeName: _fileFormat = 'jpeg' elif '[png]' in nodeName: _fileFormat = 'png' - layerFileName = '{0}{1}/{2}.{3}'.format(self.directoryTextField.text(), parentDir, node.name(), _fileFormat) - teste = node.save(layerFileName, self.xResSpinBox.value(), self.yResSpinBox.value()) + layerFileName = '{0}{1}/{2}.{3}'.format(self.directoryTextField.text(), + parentDir, node.name(), _fileFormat) + node.save(layerFileName, self.xResSpinBox.value(), self.yResSpinBox.value()) if node.childNodes(): self._exportLayers(node, fileFormat, newDir) def _selectDir(self): directory = QFileDialog.getExistingDirectory(self.mainDialog, "Select a folder", os.path.expanduser("~"), QFileDialog.ShowDirsOnly) self.directoryTextField.setText(directory) def _setResolution(self, index): document = self.documentsList[index] self.xResSpinBox.setValue(document.width()) self.yResSpinBox.setValue(document.height()) diff --git a/plugins/python/filtermanager/components/filtermanagertreemodel.py b/plugins/python/filtermanager/components/filtermanagertreemodel.py index b6576871ca..b2bb646938 100644 --- a/plugins/python/filtermanager/components/filtermanagertreemodel.py +++ b/plugins/python/filtermanager/components/filtermanagertreemodel.py @@ -1,134 +1,134 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' from PyQt5.QtCore import QAbstractItemModel, QFile, QIODevice, QModelIndex, Qt from PyQt5.QtWidgets import QApplication, QTreeView -from filtermanager.components import filtermanagertreeitem +from . import filtermanagertreeitem from PyQt5.QtGui import QPixmap class FilterManagerTreeModel(QAbstractItemModel): TYPE_COLUMN = 1 NODE_COLUMN = 3 DOCUMENT_COLUMN = 4 def __init__(self, uiFilterManager, parent=None): super(FilterManagerTreeModel, self).__init__(parent) self.rootItem = filtermanagertreeitem.FilterManagerTreeItem(("Name", "Type", "Thumbnail")) self.uiFilterManager = uiFilterManager self._loadTreeModel(self.rootItem) def index(self, row, column, parent): if not self.hasIndex(row, column, parent): return QModelIndex() if parent.isValid(): parentItem = parent.internalPointer() else: parentItem = self.rootItem # It's a FilterManagerTreeItem childItem = parentItem.child(row) if childItem: return self.createIndex(row, column, childItem) else: return QModelIndex() def parent(self, index): if not index.isValid(): return QModelIndex() childItem = index.internalPointer() parentItem = childItem.parent() if parentItem == self.rootItem: return QModelIndex() return self.createIndex(parentItem.row(), 0, parentItem) def rowCount(self, parent): if parent.column() > 0: return 0 if not parent.isValid(): parentItem = self.rootItem else: parentItem = parent.internalPointer() return parentItem.childCount() def columnCount(self, parent): if parent.isValid(): return parent.internalPointer().columnCount() else: return self.rootItem.columnCount() def data(self, index, role): if not index.isValid(): return None item = index.internalPointer() if role == Qt.UserRole + 1: return item.data(self.NODE_COLUMN) if role == Qt.UserRole + 2: return item.data(self.DOCUMENT_COLUMN) if role == Qt.UserRole + 3: return item.data(self.TYPE_COLUMN) if role != Qt.DisplayRole and role != Qt.DecorationRole: return None return item.data(index.column()) def flags(self, index): if not index.isValid(): return Qt.NoItemFlags return Qt.ItemIsEnabled | Qt.ItemIsSelectable def headerData(self, section, orientation, role): if orientation == Qt.Horizontal and role == Qt.DisplayRole: return self.rootItem.data(section) return None def _loadTreeModel(self, parent): for index, document in enumerate(self.uiFilterManager.documents): rootNode = document.rootNode() columnData = (document.fileName(), "Document", QPixmap.fromImage(document.thumbnail(30, 30)), rootNode, index) item = filtermanagertreeitem.FilterManagerTreeItem(columnData, parent) parent.appendChild(item) childNodes = rootNode.childNodes() if len(childNodes): self._addSubNodes(childNodes[::-1], item, index) def _addSubNodes(self, nodes, parent, documentIndex): for node in nodes: nodeName = node.name() nodeType = node.type() columnData = ("Unnamed" if nodeName == '' else nodeName, "Untyped" if nodeType == '' else nodeType, QPixmap.fromImage(node.thumbnail(30, 30)), node, documentIndex) item = filtermanagertreeitem.FilterManagerTreeItem(columnData, parent) parent.appendChild(item) childNodes = node.childNodes() if len(childNodes): self._addSubNodes(childNodes[::-1], item, documentIndex) diff --git a/plugins/python/filtermanager/filtermanager.py b/plugins/python/filtermanager/filtermanager.py index 0e9e3fd8d5..63ea46f32b 100644 --- a/plugins/python/filtermanager/filtermanager.py +++ b/plugins/python/filtermanager/filtermanager.py @@ -1,34 +1,34 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' import krita -from filtermanager import uifiltermanager +from . import uifiltermanager class FilterManagerExtension(krita.Extension): def __init__(self, parent): super(FilterManagerExtension, self).__init__(parent) def setup(self): pass - + def createActions(self, window): action = window.createAction("filter_manager", "Filter Manager") action.setToolTip("Plugin to filters management") action.triggered.connect(self.initialize) def initialize(self): self.uifiltermanager = uifiltermanager.UIFilterManager() self.uifiltermanager.initialize() Scripter.addExtension(FilterManagerExtension(krita.Krita.instance())) diff --git a/plugins/python/filtermanager/kritapykrita_filtermanager.desktop b/plugins/python/filtermanager/kritapykrita_filtermanager.desktop index c63ba5fb04..ead386c720 100644 --- a/plugins/python/filtermanager/kritapykrita_filtermanager.desktop +++ b/plugins/python/filtermanager/kritapykrita_filtermanager.desktop @@ -1,48 +1,48 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=filtermanager -X-Python-2-Compatible=false +X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Filter Manager Name[ar]=مدير المرشّحات Name[ca]=Gestor de filtres Name[ca@valencia]=Gestor de filtres Name[cs]=Správce filtrů Name[de]=Filterverwaltung Name[el]=Διαχειριστής φίλτρων Name[en_GB]=Filter Manager Name[es]=Gestor de filtros Name[gl]=Xestor de filtros Name[it]=Gestore dei filtri Name[nl]=Beheerder van filters Name[pl]=Zarządzanie filtrami Name[pt]=Gestor de Filtros Name[pt_BR]=Gerenciador de filtros Name[sv]=Filterhantering Name[tr]=Süzgeç Yöneticisi Name[uk]=Керування фільтрами Name[x-test]=xxFilter Managerxx Name[zh_CN]=滤镜管理器 Name[zh_TW]=濾鏡管理器 Comment=Plugin to filters management Comment[ar]=ملحقة إدارة المرشّحات Comment[ca]=Un connector per gestionar filtres Comment[ca@valencia]=Un connector per gestionar filtres Comment[cs]=Modul pro správu filtrů Comment[de]=Modul zum Verwalten von Filtern Comment[el]=Πρόσθετο για τη διαχείριση φίλτρων Comment[en_GB]=Plugin to filters management Comment[es]=Complemento para la gestión de filtros Comment[gl]=Complemento para a xestión de filtros. Comment[it]=Estensione per la gestione dei filtri Comment[nl]=Plug-in voor beheer van filters Comment[pl]=Wtyczka do zarządzania filtrami Comment[pt]='Plugin' para a gestão de filtros Comment[pt_BR]=Plug-in para gerenciamento de filtros Comment[sv]=Insticksprogram för filterhantering Comment[tr]=Süzgeç yönetimi için eklenti Comment[uk]=Додаток для керування фільтрами Comment[x-test]=xxPlugin to filters managementxx Comment[zh_CN]=滤镜管理插件 Comment[zh_TW]=用於濾鏡管理的外掛程式 diff --git a/plugins/python/filtermanager/uifiltermanager.py b/plugins/python/filtermanager/uifiltermanager.py index 9e137e35a5..fa6b794686 100644 --- a/plugins/python/filtermanager/uifiltermanager.py +++ b/plugins/python/filtermanager/uifiltermanager.py @@ -1,107 +1,107 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' -from filtermanager import filtermanagerdialog -from filtermanager.components import (filtercombobox, filtermanagertreemodel) +from . import filtermanagerdialog +from .components import (filtercombobox, filtermanagertreemodel) from PyQt5.QtCore import Qt from PyQt5.QtWidgets import (QFormLayout, QAbstractItemView, QDialogButtonBox, QVBoxLayout, QFrame, QAbstractScrollArea, QWidget, QTreeView) import krita class UIFilterManager(object): def __init__(self): self.mainDialog = filtermanagerdialog.FilterManagerDialog() self.mainLayout = QVBoxLayout(self.mainDialog) self.formLayout = QFormLayout() self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.kritaInstance = krita.Krita.instance() self._filters = sorted(self.kritaInstance.filters()) self._documents = self.kritaInstance.documents() self.treeModel = filtermanagertreemodel.FilterManagerTreeModel(self) self.documentsTreeView = QTreeView() self.filterComboBox = filtercombobox.FilterComboBox(self) self.buttonBox.accepted.connect(self.confirmButton) self.buttonBox.rejected.connect(self.mainDialog.close) self.documentsTreeView.setSelectionMode(QAbstractItemView.SingleSelection) self.mainDialog.setWindowModality(Qt.NonModal) def initialize(self): self.documentsTreeView.setModel(self.treeModel) self.documentsTreeView.setWindowTitle("Document Tree Model") self.documentsTreeView.resizeColumnToContents(0) self.documentsTreeView.resizeColumnToContents(1) self.documentsTreeView.resizeColumnToContents(2) self.formLayout.addRow("Filters", self.filterComboBox) self.line = QFrame() self.line.setFrameShape(QFrame.HLine) self.line.setFrameShadow(QFrame.Sunken) self.mainLayout.addWidget(self.documentsTreeView) self.mainLayout.addLayout(self.formLayout) self.mainLayout.addWidget(self.line) self.mainLayout.addWidget(self.buttonBox) self.mainDialog.resize(500, 300) self.mainDialog.setWindowTitle("Filter Manager") self.mainDialog.setSizeGripEnabled(True) self.mainDialog.show() self.mainDialog.activateWindow() def confirmButton(self): documentsIndexes = [] selectionModel = self.documentsTreeView.selectionModel() for index in selectionModel.selectedRows(): node = self.treeModel.data(index, Qt.UserRole + 1) documentIndex = self.treeModel.data(index, Qt.UserRole + 2) _type = self.treeModel.data(index, Qt.UserRole + 3) if _type == 'Document': self.applyFilterOverDocument(self.documents[documentIndex]) else: self.applyFilterOverNode(node, self.documents[documentIndex]) documentsIndexes.append(documentIndex) self.refreshDocumentsProjections(set(documentsIndexes)) def refreshDocumentsProjections(self, indexes): for index in indexes: document = self.documents[index] document.refreshProjection() def applyFilterOverNode(self, node, document): _filter = self.kritaInstance.filter(self.filterComboBox.currentText()) _filter.apply(node, 0, 0, document.width(), document.height()) def applyFilterOverDocument(self, document): """ This method applies the selected filter just to topLevelNodes, then if topLevelNodes are GroupLayers, that filter will not be applied. """ for node in document.topLevelNodes(): self.applyFilterOverNode(node, document) @property def filters(self): return self._filters @property def documents(self): return self._documents diff --git a/plugins/python/hello/hello.py b/plugins/python/hello/hello.py index bd31a90068..677d85af87 100644 --- a/plugins/python/hello/hello.py +++ b/plugins/python/hello/hello.py @@ -1,88 +1,88 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' -""" -This is a simple example of a Python script for Krita. -It demonstrates how to set up a custom extension and a custom docker! -""" +# +# This is a simple example of a Python script for Krita. +# It demonstrates how to set up a custom extension and a custom docker! +# -from PyQt5.QtCore import qDebug from PyQt5.QtWidgets import QWidget, QLabel, QMessageBox -from krita import Krita, Extension, DockWidget, DockWidgetFactory, DockWidgetFactoryBase +from krita import (Krita, Extension, DockWidget, + DockWidgetFactory, DockWidgetFactoryBase) def hello(): """ Show a test message box. """ QMessageBox.information(QWidget(), i18n("Test"), i18n("Hello! This is Krita version %s") % Application.version()) class HelloExtension(Extension): """ HelloExtension is a small example extension demonstrating basic Python scripting support in Krita! """ def __init__(self, parent): """ Standard Krita Python extension constructor. Most of the initialization happens in :func:`setup` :param parent: Parent widget :type parent: :class:`QWidget` or None """ - super().__init__(parent) + super(HelloExtension, self).__init__(parent) def setup(self): pass def createActions(self, window): """ This is where most of the setup takes place! """ action = window.createAction("hello_python", "hello") action.triggered.connect(hello) # Initialize and add the extension Scripter.addExtension(HelloExtension(Krita.instance())) class HelloDocker(DockWidget): """ The HelloDocker is an example of a simple Python-based docker. """ def __init__(self): """ Constructs an instance of HelloDocker and the widget it contains """ - super().__init__() + super(HelloDocker, self).__init__() # The window title is also used in the Docker menu, # so it should be set to something sensible! self.setWindowTitle("HelloDocker") label = QLabel("Hello", self) self.setWidget(label) self._label = label def canvasChanged(self, canvas): """ Override canvasChanged from :class:`DockWidget`. This gets called when the canvas changes. You can also access the active canvas via :func:`DockWidget.canvas` Parameter `canvas` can be null if the last document is closed """ self._label.setText("HelloDocker: canvas changed") # Register the docker so Krita can use it! Application.addDockWidgetFactory(DockWidgetFactory("hello", DockWidgetFactoryBase.DockRight, HelloDocker)) diff --git a/plugins/python/hello/kritapykrita_hello.desktop b/plugins/python/hello/kritapykrita_hello.desktop index 48d1c42a36..2bd08fb74f 100644 --- a/plugins/python/hello/kritapykrita_hello.desktop +++ b/plugins/python/hello/kritapykrita_hello.desktop @@ -1,50 +1,50 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=hello X-Krita-Manual=Manual.html -X-Python-2-Compatible=false +X-Python-2-Compatible=true Name=Hello World Name[ar]=مرحبًا يا عالم Name[ca]=Hola món Name[ca@valencia]=Hola món Name[cs]=Hello World Name[de]=Hallo Welt Name[el]=Hello World Name[en_GB]=Hello World Name[es]=Hola mundo Name[fr]=Bonjour tout le monde Name[gl]=Ola mundo Name[is]=Halló Heimur Name[it]=Ciao mondo Name[nl]=Hallo wereld Name[pl]=Witaj świecie Name[pt]=Olá Mundo Name[pt_BR]=Olá mundo Name[sk]=Ahoj svet Name[sv]=Hello World Name[tr]=Merhaba Dünya Name[uk]=Привіт, світе Name[x-test]=xxHello Worldxx Name[zh_CN]=Hello World Name[zh_TW]=你好,世界 Comment=Basic plugin to test PyKrita Comment[ar]=ملحقة أساسيّة لاختبار PyKrita Comment[ca]=Connector bàsic per provar el PyKrita Comment[ca@valencia]=Connector bàsic per provar el PyKrita Comment[cs]=Základní modul pro testování PyKrita Comment[el]=Βασικό πρόσθετο δοκιμής PyKrita Comment[en_GB]=Basic plugin to test PyKrita Comment[es]=Complemento básico para probar PyKrita Comment[gl]=Complemento básico para probar PyKrita. Comment[it]=Estensione di base per provare PyKrita Comment[nl]=Basisplug-in om PyKrita te testen Comment[pl]=Podstawowa wtyczka do wypróbowania PyKrity Comment[pt]='Plugin' básico para testar o PyKrita Comment[pt_BR]=Plug-in básico para testar o PyKrita Comment[sv]=Enkelt insticksprogram för att utprova PyKrita Comment[tr]=PyKrita'yı test etmek için temel eklenti Comment[uk]=Базовий додаток для тестування PyKrita Comment[x-test]=xxBasic plugin to test PyKritaxx -Comment[zh_CN]=测试 PyKrita 的基本插件 +Comment[zh_CN]=用于测试 PyKrita 的简易插件 Comment[zh_TW]=測試 PyKrita 的基本外掛程式 diff --git a/plugins/python/krita_script_starter/kritapykrita_krita_script_starter.desktop b/plugins/python/krita_script_starter/kritapykrita_krita_script_starter.desktop index 77b2317394..ea20453e3f 100644 --- a/plugins/python/krita_script_starter/kritapykrita_krita_script_starter.desktop +++ b/plugins/python/krita_script_starter/kritapykrita_krita_script_starter.desktop @@ -1,33 +1,35 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=krita_script_starter X-Python-2-Compatible=false X-Krita-Manual=Manual.html Name=Krita Script Starter Name[ca]=Iniciador de scripts del Krita Name[ca@valencia]=Iniciador de scripts del Krita Name[cs]=Spouštěč skriptů Krita Name[en_GB]=Krita Script Starter Name[es]=Iniciador de guiones de Krita Name[gl]=Iniciador de scripts de Krita Name[it]=Iniziatore di script per Krita Name[nl]=Script-starter van Krita Name[pl]=Starter skryptów Krity Name[pt]=Inicialização do Programa do Krita Name[sv]=Krita skriptstart Name[uk]=Створення скрипту Krita Name[x-test]=xxKrita Script Starterxx +Name[zh_CN]=Krita 空脚本生成器 Comment=Create the metadata and file structure for a new Krita script Comment[ca]=Crea les metadades i l'estructura de fitxers d'un script nou del Krita Comment[ca@valencia]=Crea les metadades i l'estructura de fitxers d'un script nou del Krita Comment[en_GB]=Create the metadata and file structure for a new Krita script Comment[es]=Crear los metadatos y la estructura de archivos para un nuevo guion de Krita Comment[gl]=Crear os metadatos e a estrutura de ficheiros para un novo script de Krita. Comment[it]=Crea i metadati e la struttura dei file per un nuovo script di Krita Comment[nl]=Maak de metagegevens en bestandsstructuur voor een nieuw Krita-script Comment[pl]=Utwórz metadane i strukturę plików dla nowego skryptu Krity Comment[pt]=Cria os meta-dados e a estrutura de ficheiros para um novo programa do Krita Comment[sv]=Skapa metadata och filstruktur för ett nytt Krita-skript Comment[uk]=Створення метаданих і структури файлів для нового скрипту Krita Comment[x-test]=xxCreate the metadata and file structure for a new Krita scriptxx +Comment[zh_CN]=给一个新 Krita 脚本创建元数据及文件结构 diff --git a/plugins/python/lastdocumentsdocker/kritapykrita_lastdocumentsdocker.desktop b/plugins/python/lastdocumentsdocker/kritapykrita_lastdocumentsdocker.desktop index b4693e0383..2b2f195e63 100644 --- a/plugins/python/lastdocumentsdocker/kritapykrita_lastdocumentsdocker.desktop +++ b/plugins/python/lastdocumentsdocker/kritapykrita_lastdocumentsdocker.desktop @@ -1,42 +1,42 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=lastdocumentsdocker X-Python-2-Compatible=false X-Krita-Manual=Manual.html Name=Last Documents Docker Name[ar]=رصيف بآخر المستندات Name[ca]=Acoblador dels darrers documents Name[ca@valencia]=Acoblador dels darrers documents Name[el]=Προσάρτηση τελευταίων εγγράφοων Name[en_GB]=Last Documents Docker Name[es]=Panel de últimos documentos Name[gl]=Doca dos últimos documentos Name[it]=Area di aggancio Ultimi documenti Name[nl]=Vastzetter van laatste documenten Name[pl]=Dok ostatnich dokumentów Name[pt]=Área dos Últimos Documentos Name[sv]=Dockningsfönster för senaste dokument Name[tr]=Son Belgeler Doku Name[uk]=Бічна панель останніх документів Name[x-test]=xxLast Documents Dockerxx -Name[zh_CN]=最近文档坞窗 +Name[zh_CN]=最近文档工具面板 Name[zh_TW]=「最後文件」面板 Comment=A Python-based docker for show thumbnails to last ten documents Comment[ar]=رصيف بِ‍«پيثون» لعرض مصغّرات آخر ١٠ مستندات مفتوحة Comment[ca]=Un acoblador basant en el Python per mostrar miniatures dels darrers deu documents Comment[ca@valencia]=Un acoblador basant en el Python per mostrar miniatures dels darrers deu documents Comment[el]=Ένα εργαλείο προσάρτησης σε Python για την εμφάνιση επισκοπήσεων των δέκα τελευταίων εγγράφων Comment[en_GB]=A Python-based docker for show thumbnails to last ten documents Comment[es]=Un panel basado en Python para mostrar miniaturas de los últimos diez documentos Comment[gl]=Unha doca baseada en Python para mostrar as miniaturas dos últimos dez documentos. Comment[it]=Un'area di aggancio basata su Python per mostrare miniature degli ultimi dieci documenti. Comment[nl]=Een op Python gebaseerde vastzetter om miniaturen te tonen naar de laatste tien documenten. Comment[pl]=Dok oparty na pythonie do wyświetlania miniatur ostatnich dziesięciu dokumentów Comment[pt]=Uma área acoplável, feita em Python, para mostrar as miniaturas dos últimos dez documentos Comment[sv]=Ett Python-baserat dockningsfönster för att visa miniatyrbilder för de tio senaste dokumenten Comment[tr]=Son on belgenin küçük resmini göstermek için Python-tabanlı bir dok Comment[uk]=Бічна панель на основі Python для показу мініатюр останніх десяти документів Comment[x-test]=xxA Python-based docker for show thumbnails to last ten documentsxx -Comment[zh_CN]=基于 Python 的坞窗,展示最近十个文档的缩略图 +Comment[zh_CN]=基于 Python 的工具面板,显示最近十个文档的缩略图 Comment[zh_TW]=基於 Python 的面板,用於顯示最後 10 個文件縮圖 diff --git a/plugins/python/lastdocumentsdocker/lastdocumentsdocker.py b/plugins/python/lastdocumentsdocker/lastdocumentsdocker.py index 5f14d3d1eb..449a4ba278 100644 --- a/plugins/python/lastdocumentsdocker/lastdocumentsdocker.py +++ b/plugins/python/lastdocumentsdocker/lastdocumentsdocker.py @@ -1,47 +1,47 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' from PyQt5.QtWidgets import QWidget, QVBoxLayout, QListView, QPushButton import krita -from lastdocumentsdocker import lastdocumentslistmodel +from . import lastdocumentslistmodel class LastDocumentsDocker(krita.DockWidget): def __init__(self): super(LastDocumentsDocker, self).__init__() self.baseWidget = QWidget() self.layout = QVBoxLayout() self.listView = QListView() self.loadButton = QPushButton("Refresh") self.listModel = lastdocumentslistmodel.LastDocumentsListModel() self.listView.setModel(self.listModel) self.listView.setFlow(QListView.LeftToRight) self.layout.addWidget(self.listView) self.layout.addWidget(self.loadButton) self.baseWidget.setLayout(self.layout) self.setWidget(self.baseWidget) self.loadButton.clicked.connect(self.refreshRecentDocuments) self.setWindowTitle("Last Documents Docker") def canvasChanged(self, canvas): pass def refreshRecentDocuments(self): self.listModel.loadRecentDocuments() Application.addDockWidgetFactory(krita.DockWidgetFactory("lastdocumentsdocker", krita.DockWidgetFactoryBase.DockRight, LastDocumentsDocker)) diff --git a/plugins/python/palette_docker/kritapykrita_palette_docker.desktop b/plugins/python/palette_docker/kritapykrita_palette_docker.desktop index c7e661a26b..227913909d 100644 --- a/plugins/python/palette_docker/kritapykrita_palette_docker.desktop +++ b/plugins/python/palette_docker/kritapykrita_palette_docker.desktop @@ -1,45 +1,45 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=palette_docker -X-Python-2-Compatible=false +X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Palette docker Name[ar]=رصيف اللوحات Name[ca]=Acoblador de paletes Name[ca@valencia]=Acoblador de paletes Name[cs]=Dok palet Name[de]=Paletten-Docker Name[el]=Προσάρτηση παλέτας Name[en_GB]=Palette docker Name[es]=Panel de paleta Name[gl]=Doca de paleta Name[is]=Tengikví fyrir litaspjald Name[it]=Area di aggancio della tavolozza Name[nl]=Vastzetter van palet Name[pl]=Dok palety Name[pt]=Área acoplável da paleta Name[sv]=Dockningsfönster för palett Name[tr]=Palet doku Name[uk]=Панель палітри Name[x-test]=xxPalette dockerxx -Name[zh_CN]=调色板坞窗 +Name[zh_CN]=调色板工具面板 Name[zh_TW]=「調色盤」面板 Comment=A Python-based docker to edit color palettes. Comment[ar]=رصيف بِ‍«پيثون» لتحرير لوحات الألوان. Comment[ca]=Un acoblador basant en el Python per editar paletes de colors. Comment[ca@valencia]=Un acoblador basant en el Python per editar paletes de colors. Comment[el]=Ένα εργαλείο προσάρτησης σε Python για την επεξεργασία παλετών χρώματος. Comment[en_GB]=A Python-based docker to edit colour palettes. Comment[es]=Un panel basado en Python para editar paletas de colores. Comment[gl]=Unha doca baseada en Python para editar paletas de cores. Comment[it]=Un'area di aggancio per modificare le tavolozze di colori basata su Python. Comment[nl]=Een op Python gebaseerde vastzetter om kleurpaletten te bewerken. Comment[pl]=Dok oparty na pythonie do edytowania palet barw. Comment[pt]=Uma área acoplável, feita em Python, para editar paletas de cores. Comment[sv]=Ett Python-baserat dockningsfönster för att redigera färgpaletter. Comment[tr]=Renk paletlerini düzenlemek için Python-tabanlı bir dok. Comment[uk]=Бічна панель для редагування палітр кольорів на основі Python. Comment[x-test]=xxA Python-based docker to edit color palettes.xx -Comment[zh_CN]=基于 Python 的调色板编辑坞窗 +Comment[zh_CN]=基于 Python 的调色板编辑器工具面板 Comment[zh_TW]=基於 Python 的面板,用於編輯調色盤。 diff --git a/plugins/python/palette_docker/palette_docker.py b/plugins/python/palette_docker/palette_docker.py index bce52fb971..f097b33b63 100644 --- a/plugins/python/palette_docker/palette_docker.py +++ b/plugins/python/palette_docker/palette_docker.py @@ -1,259 +1,259 @@ ''' Description: A Python based docker that allows you to edit KPL color palettes. By Wolthera(originally) This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode @package palette_docker ''' # Importing the relevant dependencies: import sys from PyQt5.QtGui import QPixmap, QIcon, QImage, QPainter, QBrush, QPalette from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QComboBox, QAction, QTabWidget, QLineEdit, QSpinBox, QDialogButtonBox, QToolButton, QDialog, QPlainTextEdit, QCompleter, QMenu from PyQt5.Qt import Qt, pyqtSignal, pyqtSlot import math from krita import * # import the exporters from . import palette_exporter_gimppalette, palette_exporter_inkscapeSVG, palette_sortColors class Palette_Docker(DockWidget): # Init the docker def __init__(self): - super().__init__() + super(Palette_Docker, self).__init__() # make base-widget and layout widget = QWidget() layout = QVBoxLayout() buttonLayout = QHBoxLayout() widget.setLayout(layout) self.setWindowTitle("Python Palette Docker") # Make a combobox and add palettes self.cmb_palettes = QComboBox() allPalettes = Application.resources("palette") for palette_name in allPalettes: self.cmb_palettes.addItem(palette_name) self.cmb_palettes.model().sort(0) if len(allPalettes.keys()) > 0: self.currentPalette = Palette(allPalettes[list(allPalettes.keys())[0]]) else: self.currentPalette = None self.cmb_palettes.currentTextChanged.connect(self.slot_paletteChanged) layout.addWidget(self.cmb_palettes) # add combobox to the layout self.paletteView = PaletteView() self.paletteView.setPalette(self.currentPalette) layout.addWidget(self.paletteView) self.paletteView.entrySelectedForeGround.connect(self.slot_swatchSelected) self.colorComboBox = QComboBox() self.colorList = list() buttonLayout.addWidget(self.colorComboBox) self.addEntry = QAction(self) self.addEntry.setIconText("+") self.addEntry.triggered.connect(self.slot_add_entry) self.addGroup = QAction(self) self.addGroup.triggered.connect(self.slot_add_group) self.addGroup.setText("Add Group") self.addGroup.setIconText(str("\U0001F4C2")) self.removeEntry = QAction(self) self.removeEntry.setText("Remove Entry") self.removeEntry.setIconText("-") self.removeEntry.triggered.connect(self.slot_remove_entry) addEntryButton = QToolButton() addEntryButton.setDefaultAction(self.addEntry) buttonLayout.addWidget(addEntryButton) addGroupButton = QToolButton() addGroupButton.setDefaultAction(self.addGroup) buttonLayout.addWidget(addGroupButton) removeEntryButton = QToolButton() removeEntryButton.setDefaultAction(self.removeEntry) buttonLayout.addWidget(removeEntryButton) # QActions self.extra = QToolButton() self.editPaletteData = QAction(self) self.editPaletteData.setText("Edit Palette Settings") self.editPaletteData.triggered.connect(self.slot_edit_palette_data) self.extra.setDefaultAction(self.editPaletteData) buttonLayout.addWidget(self.extra) self.actionMenu = QMenu() self.exportToGimp = QAction(self) self.exportToGimp.setText("Export as GIMP palette file.") self.exportToGimp.triggered.connect(self.slot_export_to_gimp_palette) self.exportToInkscape = QAction(self) self.exportToInkscape.setText("Export as Inkscape SVG with swatches.") self.exportToInkscape.triggered.connect(self.slot_export_to_inkscape_svg) self.sortColors = QAction(self) self.sortColors.setText("Sort colors") self.sortColors.triggered.connect(self.slot_sort_colors) self.actionMenu.addAction(self.editPaletteData) self.actionMenu.addAction(self.exportToGimp) self.actionMenu.addAction(self.exportToInkscape) # self.actionMenu.addAction(self.sortColors) self.extra.setMenu(self.actionMenu) layout.addLayout(buttonLayout) self.slot_fill_combobox() self.setWidget(widget) # add widget to the docker def slot_paletteChanged(self, name): allPalettes = Application.resources("palette") if len(allPalettes) > 0 and name in allPalettes: self.currentPalette = Palette(Application.resources("palette")[name]) self.paletteView.setPalette(self.currentPalette) self.slot_fill_combobox() @pyqtSlot('PaletteEntry') def slot_swatchSelected(self, entry): print("entry " + entry.name()) if (self.canvas()) is not None: if (self.canvas().view()) is not None: name = entry.name() if len(entry.id) > 0: name = entry.id() + " - " + entry.name() if len(name) > 0: if name in self.colorList: self.colorComboBox.setCurrentIndex(self.colorList.index(name)) color = self.currentPalette.colorForEntry(entry) self.canvas().view().setForeGroundColor(color) ''' A function for making a combobox with the available colors. We use QCompleter on the colorComboBox so that people can type in the name of a color to select it. This is useful for people with carefully made palettes where the colors are named properly, which makes it easier for them to find colors. ''' def slot_fill_combobox(self): if self.currentPalette is None: pass palette = self.currentPalette self.colorComboBox.clear() - self.colorList.clear() + self.colorList = list() for i in range(palette.colorsCountTotal()): entry = palette.colorSetEntryByIndex(i) color = palette.colorForEntry(entry).colorForCanvas(self.canvas()) colorSquare = QPixmap(12, 12) if entry.spotColor() is True: img = colorSquare.toImage() circlePainter = QPainter() img.fill(self.colorComboBox.palette().color(QPalette.Base)) circlePainter.begin(img) brush = QBrush(Qt.SolidPattern) brush.setColor(color) circlePainter.setBrush(brush) circlePainter.pen().setWidth(0) circlePainter.drawEllipse(0, 0, 11, 11) circlePainter.end() colorSquare = QPixmap.fromImage(img) else: colorSquare.fill(color) name = entry.name() if len(entry.id()) > 0: name = entry.id() + " - " + entry.name() self.colorList.append(name) self.colorComboBox.addItem(QIcon(colorSquare), name) self.colorComboBox.setEditable(True) self.colorComboBox.setInsertPolicy(QComboBox.NoInsert) self.colorComboBox.completer().setCompletionMode(QCompleter.PopupCompletion) self.colorComboBox.completer().setCaseSensitivity(False) self.colorComboBox.completer().setFilterMode(Qt.MatchContains) self.colorComboBox.currentIndexChanged.connect(self.slot_get_color_from_combobox) def slot_get_color_from_combobox(self): if self.currentPalette is not None: entry = self.currentPalette.colorSetEntryByIndex(self.colorComboBox.currentIndex()) self.slot_swatchSelected(entry) def slot_add_entry(self): if (self.canvas()) is not None: if (self.canvas().view()) is not None: color = self.canvas().view().foregroundColor() success = self.paletteView.addEntryWithDialog(color) if success is True: self.slot_fill_combobox() def slot_add_group(self): success = self.paletteView.addGroupWithDialog() if success is True: self.slot_fill_combobox() def slot_remove_entry(self): success = self.paletteView.removeSelectedEntryWithDialog() if success is True: self.slot_fill_combobox() ''' A function for giving a gui to edit palette metadata... I also want this to be the way to edit the settings of the palette docker. ''' def slot_edit_palette_data(self): dialog = QDialog(self) tabWidget = QTabWidget() dialog.setWindowTitle("Edit Palette Data") dialog.setLayout(QVBoxLayout()) dialog.layout().addWidget(tabWidget) paletteWidget = QWidget() paletteWidget.setLayout(QVBoxLayout()) tabWidget.addTab(paletteWidget, "Palette Data") paletteName = QLineEdit() paletteName.setText(self.cmb_palettes.currentText()) paletteWidget.layout().addWidget(paletteName) paletteColumns = QSpinBox() paletteColumns.setValue(self.currentPalette.columnCount()) paletteWidget.layout().addWidget(paletteColumns) paletteComment = QPlainTextEdit() paletteComment.appendPlainText(self.currentPalette.comment()) paletteWidget.layout().addWidget(paletteComment) buttons = QDialogButtonBox(QDialogButtonBox.Ok) dialog.layout().addWidget(buttons) buttons.accepted.connect(dialog.accept) # buttons.rejected.connect(dialog.reject()) if dialog.exec_() == QDialog.Accepted: Resource = Application.resources("palette")[self.cmb_palettes.currentText()] Resource.setName(paletteName.text()) self.currentPalette = Palette(Resource) print(paletteColumns.value()) self.currentPalette.setColumnCount(paletteColumns.value()) self.paletteView.setPalette(self.currentPalette) self.slot_fill_combobox() self.currentPalette.setComment(paletteComment.toPlainText()) self.currentPalette.save() def slot_export_to_gimp_palette(self): palette_exporter_gimppalette.gimpPaletteExporter(self.cmb_palettes.currentText()) def slot_export_to_inkscape_svg(self): palette_exporter_inkscapeSVG.inkscapeSVGExporter(self.cmb_palettes.currentText()) def slot_sort_colors(self): colorSorter = palette_sortColors.sortColors(self.cmb_palettes.currentText()) self.paletteView.setPalette(colorSorter.palette()) def canvasChanged(self, canvas): self.cmb_palettes.clear() allPalettes = Application.resources("palette") for palette_name in allPalettes: self.cmb_palettes.addItem(palette_name) self.cmb_palettes.model().sort(0) if self.currentPalette == None and len(allPalettes.keys()) > 0: self.currentPalette = Palette(allPalettes[list(allPalettes.keys())[0]]) # Add docker to the application :) Application.addDockWidgetFactory(DockWidgetFactory("palette_docker", DockWidgetFactoryBase.DockRight, Palette_Docker)) diff --git a/plugins/python/palette_docker/palette_sortColors.py b/plugins/python/palette_docker/palette_sortColors.py index 4a4a36be5d..304ff9d6eb 100644 --- a/plugins/python/palette_docker/palette_sortColors.py +++ b/plugins/python/palette_docker/palette_sortColors.py @@ -1,80 +1,78 @@ ''' A script that sorts the colors in the group. By Wolthera(originally) This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode @package palette_docker ''' # Importing the relevant dependencies: -import sys -import math from krita import * -class sortColors: +class sortColors(object): def __init__(self, name): # We want people to select a palette... allPalettes = Application.resources("palette") self.paletteName = name self.currentPalette = Palette(allPalettes[self.paletteName]) self.sort_all_groups() def sort_all_groups(self): self.sort_color_by_name(str()) groupNames = self.currentPalette.groupNames() for groupName in groupNames: self.sort_color_by_name(groupName) def sort_color_by_name(self, groupName): l = {} colorCount = self.currentPalette.colorsCountGroup(groupName) for i in range(colorCount - 1, -1, -1): entry = self.currentPalette.colorSetEntryFromGroup((i), groupName) l[entry.name + str(i)] = entry self.currentPalette.removeEntry((i), groupName) for s in sorted(l): self.currentPalette.addEntry(l[s], groupName) def sort_color_by_id(self, groupName): l = {} colorCount = self.currentPalette.colorsCountGroup(groupName) for i in range(colorCount - 1, -1, -1): entry = self.currentPalette.colorSetEntryFromGroup((i), groupName) l[entry.id() + " " + str(i)] = entry self.currentPalette.removeEntry((i), groupName) for s in sorted(l): self.currentPalette.addEntry(l[s], groupName) def sort_by_value(self, groupName): l = {} colorCount = self.currentPalette.colorsCountGroup(groupName) for i in range(colorCount - 1, -1, -1): entry = self.currentPalette.colorSetEntryFromGroup((i), groupName) color = self.currentPalette.colorForEntry(entry) color.setColorSpace("RGBA", "U8", "sRGB built-in") l[color.components()[0] + color.components()[1] + color.components()[2]] = entry self.currentPalette.removeEntry((i), groupName) for s in sorted(l): self.currentPalette.addEntry(l[s], groupName) def sort_by_hue(self, stepsize, groupName): pass def palette(self): return self.currentPalette diff --git a/plugins/python/quick_settings_docker/kritapykrita_quick_settings_docker.desktop b/plugins/python/quick_settings_docker/kritapykrita_quick_settings_docker.desktop index a2ef8bc411..e2e5102e39 100644 --- a/plugins/python/quick_settings_docker/kritapykrita_quick_settings_docker.desktop +++ b/plugins/python/quick_settings_docker/kritapykrita_quick_settings_docker.desktop @@ -1,43 +1,43 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=quick_settings_docker -X-Python-2-Compatible=false +X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Quick Settings Docker Name[ar]=رصيف بإعدادات سريعة Name[ca]=Acoblador d'arranjament ràpid Name[ca@valencia]=Acoblador d'arranjament ràpid Name[cs]=Dok pro rychlé nastavení Name[el]=Προσάρτηση γρήγορων ρυθμίσεων Name[en_GB]=Quick Settings Docker Name[es]=Panel de ajustes rápidos Name[gl]=Doca de configuración rápida Name[it]=Area di aggancio delle impostazioni rapide Name[nl]=Docker voor snelle instellingen Name[pl]=Dok szybkich ustawień Name[pt]=Área de Configuração Rápida Name[sv]=Dockningspanel med snabbinställningar Name[tr]=Hızlı Ayarlar Doku Name[uk]=Панель швидких параметрів Name[x-test]=xxQuick Settings Dockerxx -Name[zh_CN]=快速设置坞窗 +Name[zh_CN]=快速设置工具面板 Name[zh_TW]=「快速設定」面板 Comment=A Python-based docker for quickly changing brush size and opacity. Comment[ar]=رصيف بِ‍«پيثون» لتغيير حجم الفرشاة وشفافيّتها بسرعة. Comment[ca]=Un acoblador basant en el Python per canviar ràpidament la mida del pinzell i l'opacitat. Comment[ca@valencia]=Un acoblador basant en el Python per canviar ràpidament la mida del pinzell i l'opacitat. Comment[el]=Ένα εργαλείο προσάρτησης σε Python για γρήγορη αλλαγή του μεγέθους και της αδιαφάνειας του πινέλου. Comment[en_GB]=A Python-based docker for quickly changing brush size and opacity. Comment[es]=Un panel basado en Python para cambiar rápidamente el tamaño y la opacidad del pincel. Comment[gl]=Unha doca baseada en Python para cambiar rapidamente a opacidade e o tamaño dos pinceis. Comment[it]=Un'area di aggancio basata su Python per cambiare rapidamente la dimensione del pennello e l'opacità. Comment[nl]=Een op Python gebaseerde docker voor snel wijzigen van penseelgrootte en dekking. Comment[pl]=Dok oparty na pythonie do szybkiej zmiany rozmiaru i nieprzezroczystości pędzla. Comment[pt]=Uma área acoplável em Python para mudar rapidamente o tamanho e opacidade do pincel. Comment[sv]=En Python-baserad dockningspanel för att snabbt ändra penselstorlek och ogenomskinlighet. Comment[tr]=Fırça boyutunu ve matlığını hızlıca değiştirmek için Python-tabanlı bir dok. Comment[uk]=Панель на основі мови програмування Python для швидкої зміни розміру та непрозорості пензля. Comment[x-test]=xxA Python-based docker for quickly changing brush size and opacity.xx -Comment[zh_CN]=基于 Python 的坞窗,用于快速更改笔刷尺寸和透明度。 +Comment[zh_CN]=基于 Python 的用于快速更改笔刷尺寸和透明度的工具面板。 Comment[zh_TW]=基於 Python 的面板,用於快速變更筆刷尺寸和不透明度。 diff --git a/plugins/python/quick_settings_docker/quick_settings_docker.py b/plugins/python/quick_settings_docker/quick_settings_docker.py index 24312be815..1add2d6a18 100644 --- a/plugins/python/quick_settings_docker/quick_settings_docker.py +++ b/plugins/python/quick_settings_docker/quick_settings_docker.py @@ -1,175 +1,175 @@ ''' Description: A Python based docker for quickly choosing the brushsize like similar dockers in other drawing programs. By Wolthera(originally) This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode @package quick_settings_docker ''' # Importing the relevant dependencies: import sys from PyQt5.QtCore import pyqtSlot, Qt, QPointF from PyQt5.QtGui import QStandardItem, QStandardItemModel, QPainter, QPalette, QPixmap, QImage, QBrush, QPen, QIcon from PyQt5.QtWidgets import QWidget, QTabWidget, QListView, QVBoxLayout from krita import * class QuickSettingsDocker(DockWidget): # Init the docker def __init__(self): - super().__init__() + super(QuickSettingsDocker, self).__init__() # make base-widget and layout widget = QWidget() layout = QVBoxLayout() widget.setLayout(layout) self.setWindowTitle("Quick Settings Docker") tabWidget = QTabWidget() self.brushSizeTableView = QListView() self.brushSizeTableView.setViewMode(QListView.IconMode) self.brushSizeTableView.setMovement(QListView.Static) self.brushSizeTableView.setResizeMode(QListView.Adjust) self.brushSizeTableView.setUniformItemSizes(True) self.brushSizeTableView.setSelectionMode(QListView.SingleSelection) self.brushOpacityTableView = QListView() self.brushOpacityTableView.setViewMode(QListView.IconMode) self.brushOpacityTableView.setMovement(QListView.Static) self.brushOpacityTableView.setResizeMode(QListView.Adjust) self.brushOpacityTableView.setUniformItemSizes(True) self.brushOpacityTableView.setSelectionMode(QListView.SingleSelection) self.brushFlowTableView = QListView() self.brushFlowTableView.setViewMode(QListView.IconMode) self.brushFlowTableView.setMovement(QListView.Static) self.brushFlowTableView.setResizeMode(QListView.Adjust) self.brushFlowTableView.setUniformItemSizes(True) self.brushFlowTableView.setSelectionMode(QListView.SingleSelection) tabWidget.addTab(self.brushSizeTableView, "Size") tabWidget.addTab(self.brushOpacityTableView, "Opacity") tabWidget.addTab(self.brushFlowTableView, "Flow") layout.addWidget(tabWidget) self.setWidget(widget) # Add the widget to the docker. # amount of columns in each row for all the tables. # We want a grid with possible options to select. # To do this, we'll make a ListView widget and use a standarditemmodel for the entries. # The entries are filled out based on the sizes and opacity lists. # Sizes and opacity lists. The former is half-way copied from ptsai, the latter is based on personal experience of useful opacities. self.sizesList = [0.7, 1.0, 1.5, 2, 2.5, 3, 3.5, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 20, 25, 30, 35, 40, 50, 60, 70, 80, 100, 120, 160, 200, 250, 300, 350, 400, 450, 500] self.opacityList = [0.1, 0.5, 1, 5, 10, 15, 20, 30, 40, 50, 60, 70, 80, 90, 100] self.brushSizeModel = QStandardItemModel() self.brushOpacityModel = QStandardItemModel() self.brushFlowModel = QStandardItemModel() self.fillSizesModel() self.fillOpacityModel() # Now we're done filling out our tables, we connect the views to the functions that'll change the settings. self.brushSizeTableView.clicked.connect(self.setBrushSize) self.brushOpacityTableView.clicked.connect(self.setBrushOpacity) self.brushFlowTableView.clicked.connect(self.setBrushFlow) def fillSizesModel(self): # First we empty the old model. We might wanna use this function in the future to fill it with the brushmask of the selected brush, but there's currently no API for recognising changes in the current brush nor is there a way to get its brushmask. self.brushSizeModel.clear() for s in range(len(self.sizesList)): # we're gonna itterate over our list, and make a new item for each entry. # We need to disable a bunch of stuff to make sure people won't do funny things to our entries. item = QStandardItem() item.setCheckable(False) item.setEditable(False) item.setDragEnabled(False) item.setText(str(self.sizesList[s])+" px") # And from here on we'll make an icon. brushImage = QPixmap(64, 64) img = QImage(64, 64, QImage.Format_RGBA8888) circlePainter = QPainter() img.fill(Qt.transparent) circlePainter.begin(img) brush = QBrush(Qt.SolidPattern) brush.setColor(self.brushSizeTableView.palette().color(QPalette.Text)) circlePainter.setBrush(brush) circlePainter.setPen(QPen(QBrush(Qt.transparent), 0)) brushSize = max(min(self.sizesList[s], 64), 1) brushSize = brushSize * 0.5 circlePainter.drawEllipse(QPointF(32, 32), brushSize, brushSize) circlePainter.end() brushImage = QPixmap.fromImage(img) # now we're done with drawing the icon, so we set it on the item. item.setIcon(QIcon(brushImage)) self.brushSizeModel.appendRow(item) self.brushSizeTableView.setModel(self.brushSizeModel) def fillOpacityModel(self): self.brushOpacityModel.clear() self.brushFlowModel.clear() for s in range(len(self.opacityList)): # we're gonna itterate over our list, and make a new item for each entry. item = QStandardItem() item.setCheckable(False) item.setEditable(False) item.setDragEnabled(False) item.setText(str(self.opacityList[s])+" %") brushImage = QPixmap(64, 64) img = QImage(64, 64, QImage.Format_RGBA8888) circlePainter = QPainter() img.fill(Qt.transparent) circlePainter.begin(img) brush = QBrush(Qt.SolidPattern) brush.setColor(self.brushSizeTableView.palette().color(QPalette.Text)) circlePainter.setBrush(brush) circlePainter.setPen(QPen(QBrush(Qt.transparent), 0)) - circlePainter.setOpacity(self.opacityList[s] / 100) + circlePainter.setOpacity(float(self.opacityList[s]) / 100.0) circlePainter.drawEllipse(QPointF(32, 32), 32, 32) circlePainter.end() brushImage = QPixmap.fromImage(img) item.setIcon(QIcon(brushImage)) # the flow and opacity models will use virtually the same items, but Qt would like us to make sure we understand # these are not really the same items, so hence the clone. itemFlow = item.clone() self.brushOpacityModel.appendRow(item) self.brushFlowModel.appendRow(itemFlow) self.brushOpacityTableView.setModel(self.brushOpacityModel) self.brushFlowTableView.setModel(self.brushFlowModel) def canvasChanged(self, canvas): pass @pyqtSlot('QModelIndex') def setBrushSize(self, index): i = index.row() brushSize = self.sizesList[i] if Application.activeWindow() and len(Application.activeWindow().views()) > 0: Application.activeWindow().views()[0].setBrushSize(brushSize) @pyqtSlot('QModelIndex') def setBrushOpacity(self, index): i = index.row() - brushOpacity = self.opacityList[i] / 100 + brushOpacity = float(self.opacityList[i]) / 100.0 if Application.activeWindow() and len(Application.activeWindow().views()) > 0: Application.activeWindow().views()[0].setPaintingOpacity(brushOpacity) @pyqtSlot('QModelIndex') def setBrushFlow(self, index): i = index.row() - brushOpacity = self.opacityList[i] / 100 + brushOpacity = float(self.opacityList[i]) / 100.0 if Application.activeWindow() and len(Application.activeWindow().views()) > 0: Application.activeWindow().views()[0].setPaintingFlow(brushOpacity) # Add docker to the application :) Application.addDockWidgetFactory(DockWidgetFactory("quick_settings_docker", DockWidgetFactoryBase.DockRight, QuickSettingsDocker)) diff --git a/plugins/python/scriptdocker/kritapykrita_scriptdocker.desktop b/plugins/python/scriptdocker/kritapykrita_scriptdocker.desktop index 90617de68d..7d86748428 100644 --- a/plugins/python/scriptdocker/kritapykrita_scriptdocker.desktop +++ b/plugins/python/scriptdocker/kritapykrita_scriptdocker.desktop @@ -1,42 +1,42 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=scriptdocker X-Python-2-Compatible=false Name=Script Docker Name[ar]=رصيف سكربتات Name[ca]=Acoblador de scripts Name[ca@valencia]=Acoblador de scripts Name[cs]=Dok skriptu Name[el]=Προσάρτηση σεναρίων Name[en_GB]=Script Docker Name[es]=Panel de guiones Name[gl]=Doca de scripts Name[it]=Area di aggancio degli script Name[nl]=Vastzetten van scripts Name[pl]=Dok skryptów Name[pt]=Área de Programas Name[sv]=Dockningsfönster för skript Name[tr]=Betik Doku Name[uk]=Бічна панель скриптів Name[x-test]=xxScript Dockerxx -Name[zh_CN]=脚本坞窗 +Name[zh_CN]=脚本工具面板 Name[zh_TW]=「腳本」面板 Comment=A Python-based docker for create actions and point to Python scripts Comment[ar]=رصيف بِ‍«پيثون» لإنشاء الإجراءات والإشارة إلى سكربتات «پيثون» Comment[ca]=Un acoblador basant en el Python per crear accions i apuntar a scripts del Python Comment[ca@valencia]=Un acoblador basant en el Python per crear accions i apuntar a scripts del Python Comment[el]=Ένα εργαλείο προσάρτησης σε Python για τη δημιουργία ενεργειών και τη δεικτοδότηση σεναρίων σε Python Comment[en_GB]=A Python-based docker for create actions and point to Python scripts Comment[es]=Un panel basado en Python para crear y apuntar a guiones de Python Comment[gl]=Unha doca escrita en Python para crear accións e apuntar a scripts escritos en Python. Comment[it]=Un'area di aggancio basata su Python per creare azioni e scegliere script Python Comment[nl]=Een op Python gebaseerde vastzetter voor aanmaakacties en wijzen naar Python-scripts Comment[pl]=Dok oparty na pythonie do tworzenia działań i punktów dla skryptów pythona Comment[pt]=Uma área acoplável, feita em Python, para criar acções e apontar para programas em Python Comment[sv]=Ett Python-baserat dockningsfönster för att skapa åtgärder och peka ut Python-skript Comment[tr]=Eylemler oluşturmak ve Python betiklerine yönlendirmek için Python-tabanlı bir dok Comment[uk]=Бічна панель для створення дій і керування скриптами на основі Python. Comment[x-test]=xxA Python-based docker for create actions and point to Python scriptsxx -Comment[zh_CN]=基于 Python 的坞窗,用于创建动作并指向 Python 脚本 +Comment[zh_CN]=基于 Python 的用于创建动作并指向 Python 脚本的工具面板 Comment[zh_TW]=基於 Python 的面板,用於建立動作並指向 Python 腳本 diff --git a/plugins/python/scripter/debugcontroller.py b/plugins/python/scripter/debugcontroller.py index b53a804be7..b497d7aa1d 100644 --- a/plugins/python/scripter/debugcontroller.py +++ b/plugins/python/scripter/debugcontroller.py @@ -1,99 +1,104 @@ """ Copyright (c) 2017 Eliakin Costa 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. """ -from scripter.debugger_scripter import debugger -import asyncio +from .debugger_scripter import debugger +import sys +if sys.version_info[0] > 2: + import asyncio +else: + # trollius is a port of asyncio for python2. + import trollius as asyncio class DebugController (object): def __init__(self, scripter): self._debugger = None self._cmd = None self.scripter = scripter def start(self, document): self.setCmd(compile(document.data, document.filePath, "exec")) self._debugger = debugger.Debugger(self.scripter, self._cmd) self._debugger.debugprocess.start() loop = asyncio.get_event_loop() loop.run_until_complete(self._debugger.start()) self.updateUIDebugger() def step(self): loop = asyncio.get_event_loop() loop.run_until_complete(self._debugger.step()) self.scripter.uicontroller.setStepped(True) self.updateUIDebugger() def stop(self): loop = asyncio.get_event_loop() loop.run_until_complete(self._debugger.stop()) self.updateUIDebugger() self._debugger = None def setCmd(self, cmd): self._cmd = cmd @property def isActive(self): try: if self._debugger: return self._debugger.debugprocess.is_alive() return False except Exception: return False @property def currentLine(self): try: if self._debugger: return int(self.debuggerData['code']['lineNumber']) except Exception: return 0 def updateUIDebugger(self): widget = self.scripter.uicontroller.findTabWidget('Debugger') exception = self._debuggerException() if exception: self.scripter.uicontroller.showException(exception) if not self.isActive or self._quitDebugger(): widget.disableToolbar(True) self.scripter.uicontroller.repaintDebugArea() widget.updateWidget() @property def debuggerData(self): try: if self._debugger: return self._debugger.application_data except Exception: return def _quitDebugger(self): try: return self.debuggerData['quit'] except Exception: return False def _debuggerException(self): try: return self.debuggerData['exception'] except Exception: return False diff --git a/plugins/python/scripter/debugger_scripter/debugger.py b/plugins/python/scripter/debugger_scripter/debugger.py index 85d192ae28..2ccdde60dc 100644 --- a/plugins/python/scripter/debugger_scripter/debugger.py +++ b/plugins/python/scripter/debugger_scripter/debugger.py @@ -1,120 +1,126 @@ """ Copyright (c) 2017 Eliakin Costa 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. """ import bdb import multiprocessing -import asyncio +import sys +if sys.version_info[0] > 2: + import asyncio +else: + import trollius as asyncio from . import debuggerformatter class Debugger(bdb.Bdb): def __init__(self, scripter, cmd): bdb.Bdb.__init__(self) self.quit = False self.debugq = multiprocessing.Queue() self.scripter = scripter self.applicationq = multiprocessing.Queue() self.filePath = self.scripter.documentcontroller.activeDocument.filePath self.application_data = {} self.exception_data = {} self.debugprocess = multiprocessing.Process(target=self._run, args=(self.filePath,)) self.currentLine = 0 bdb.Bdb.reset(self) def _run(self, filename): try: self.mainpyfile = self.canonic(filename) with open(filename, "rb") as fp: statement = "exec(compile(%r, %r, 'exec'))" % \ (fp.read(), self.mainpyfile) self.run(statement) except Exception as e: raise e def user_call(self, frame, args): name = frame.f_code.co_name or "" def user_line(self, frame): """Handler that executes with every line of code""" co = frame.f_code if self.filePath != co.co_filename: return self.currentLine = frame.f_lineno self.applicationq.put({"code": {"file": co.co_filename, "name": co.co_name, "lineNumber": str(frame.f_lineno) }, "frame": {"firstLineNumber": co.co_firstlineno, "locals": debuggerformatter.format_data(frame.f_locals), "globals": debuggerformatter.format_data(frame.f_globals) }, "trace": "line" }) if self.quit: return self.set_quit() if self.currentLine == 0: return else: cmd = self.debugq.get() if cmd == "step": return if cmd == "stop": return self.set_quit() def user_return(self, frame, value): name = frame.f_code.co_name or "" if name == '': self.applicationq.put({"quit": True}) def user_exception(self, frame, exception): self.applicationq.put({"exception": str(exception[1])}) @asyncio.coroutine def display(self): """Coroutine for updating the UI""" while True: if self.applicationq.empty(): - yield from asyncio.sleep(0.3) + # 'yield from' is not available in Python 2. + for i in asyncio.sleep(0.3): + yield i else: while not self.applicationq.empty(): self.application_data.update(self.applicationq.get()) self.scripter.uicontroller.repaintDebugArea() return @asyncio.coroutine def start(self): - yield from self.display() + yield self.display() @asyncio.coroutine def step(self): self.debugq.put("step") - yield from self.display() + yield self.display() @asyncio.coroutine def stop(self): self.debugq.put("stop") self.applicationq.put({"quit": True}) - yield from self.display() + yield self.display() diff --git a/plugins/python/scripter/documentcontroller.py b/plugins/python/scripter/documentcontroller.py index 6d5f14ae9f..85f7667de6 100644 --- a/plugins/python/scripter/documentcontroller.py +++ b/plugins/python/scripter/documentcontroller.py @@ -1,57 +1,57 @@ """ Copyright (c) 2017 Eliakin Costa 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. """ -from scripter.document_scripter import document +from .document_scripter import document class DocumentController(object): def __init__(self): self._activeDocument = None @property def activeDocument(self): return self._activeDocument def openDocument(self, filePath): if filePath: newDocument = document.Document(filePath) newDocument.open() self._activeDocument = newDocument return newDocument def saveDocument(self, data, filePath, save_as=False): """ data - data to be written filePath - file path to write data to save_as = boolean, is this call made from save_as functionality. If so, do not compare data against existing document before save. """ if save_as or not self._activeDocument: self._activeDocument = document.Document(filePath) text = str(data) if save_as or not self._activeDocument.compare(text): # compare is not evaluated if save_as is True self._activeDocument.data = text self._activeDocument.save() return self._activeDocument def clearActiveDocument(self): self._activeDocument = None diff --git a/plugins/python/scripter/kritapykrita_scripter.desktop b/plugins/python/scripter/kritapykrita_scripter.desktop index 03250df585..4ace62d5fd 100644 --- a/plugins/python/scripter/kritapykrita_scripter.desktop +++ b/plugins/python/scripter/kritapykrita_scripter.desktop @@ -1,41 +1,41 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=scripter -X-Python-2-Compatible=false +X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Scripter Name[ca]=Scripter Name[ca@valencia]=Scripter Name[de]=Scripter Name[el]=Σενάρια Name[en_GB]=Scripter Name[es]=Guionador Name[gl]=Executor de scripts Name[it]=Scripter Name[nl]=Scriptschrijver Name[pl]=Skrypter Name[pt]=Programador Name[sv]=Skriptgenerator Name[tr]=Betik yazarı Name[uk]=Скриптер Name[x-test]=xxScripterxx -Name[zh_CN]=脚本编写者 +Name[zh_CN]=脚本编写器 Name[zh_TW]=腳本編寫者 Comment=Plugin to execute ad-hoc Python code Comment[ca]=Connector per executar codi Python ad hoc Comment[ca@valencia]=Connector per executar codi Python ad hoc Comment[el]=Πρόσθετο για την εκτέλεση συγκεκριμένου κώδικα Python Comment[en_GB]=Plugin to execute ad-hoc Python code Comment[es]=Complemento para ejecutar código Python a medida Comment[gl]=Complemento para executar código de Python escrito no momento. Comment[it]=Estensione per eseguire ad-hoc codice Python Comment[nl]=Plug-in om ad-hoc Python code uit te voeren Comment[pl]=Wtyczka do wykonywania kodu Pythona ad-hoc Comment[pt]='Plugin' para executar código em Python arbitrário Comment[sv]=Insticksprogram för att köra godtycklig Python-kod Comment[tr]=Geçici Python kodu çalıştırmak için eklenti Comment[uk]=Додаток для виконання апріорного коду Python Comment[x-test]=xxPlugin to execute ad-hoc Python codexx -Comment[zh_CN]=执行特定 Python 代码的插件 +Comment[zh_CN]=执行现场编写的 Python 代码的插件 Comment[zh_TW]=外掛程式,用於執行特定 Python 程式碼 diff --git a/plugins/python/scripter/scripter.py b/plugins/python/scripter/scripter.py index 2653fe788e..35b63d1bd9 100644 --- a/plugins/python/scripter/scripter.py +++ b/plugins/python/scripter/scripter.py @@ -1,45 +1,44 @@ """ Copyright (c) 2017 Eliakin Costa 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. """ -from PyQt5.QtWidgets import QDialog from PyQt5.QtCore import QSettings, QStandardPaths -from krita import * -from scripter import uicontroller, documentcontroller, debugcontroller +from krita import Krita, Extension +from . import uicontroller, documentcontroller, debugcontroller class ScripterExtension(Extension): def __init__(self, parent): - super().__init__(parent) + super(ScripterExtension, self).__init__(parent) def setup(self): pass - + def createActions(self, window): action = window.createAction("python_scripter", "Scripter") action.triggered.connect(self.initialize) def initialize(self): configPath = QStandardPaths.writableLocation(QStandardPaths.GenericConfigLocation) self.settings = QSettings(configPath + '/krita-scripterrc', QSettings.IniFormat) self.uicontroller = uicontroller.UIController() self.documentcontroller = documentcontroller.DocumentController() self.debugcontroller = debugcontroller.DebugController(self) self.uicontroller.initialize(self) Krita.instance().addExtension(ScripterExtension(Krita.instance())) diff --git a/plugins/python/scripter/ui_scripter/actions/closeaction/closeaction.py b/plugins/python/scripter/ui_scripter/actions/closeaction/closeaction.py index 65b9e07788..064a889f53 100644 --- a/plugins/python/scripter/ui_scripter/actions/closeaction/closeaction.py +++ b/plugins/python/scripter/ui_scripter/actions/closeaction/closeaction.py @@ -1,54 +1,54 @@ """ Copyright (c) 2017 Eliakin Costa 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. """ from PyQt5.QtWidgets import QAction, QMessageBox from PyQt5.QtGui import QKeySequence from PyQt5.QtCore import Qt class CloseAction(QAction): def __init__(self, scripter, parent=None): super(CloseAction, self).__init__(parent) self.scripter = scripter self.triggered.connect(self.close) self.setText('Close') self.setObjectName('close') self.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_Q)) @property def parent(self): return 'File' def close(self): msgBox = QMessageBox(self.scripter.uicontroller.mainWidget) msgBox.setInformativeText("Do you want to save the current document?") msgBox.setStandardButtons(QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel) msgBox.setDefaultButton(QMessageBox.Save) - ret = msgBox.exec() + ret = msgBox.exec_() if ret == QMessageBox.Cancel: return if ret == QMessageBox.Save: if not self.scripter.uicontroller.invokeAction('save'): return self.scripter.uicontroller.closeScripter() diff --git a/plugins/python/scripter/ui_scripter/actions/newaction/newaction.py b/plugins/python/scripter/ui_scripter/actions/newaction/newaction.py index df7749bf5b..e5761f2b7e 100644 --- a/plugins/python/scripter/ui_scripter/actions/newaction/newaction.py +++ b/plugins/python/scripter/ui_scripter/actions/newaction/newaction.py @@ -1,56 +1,56 @@ """ Copyright (c) 2017 Eliakin Costa 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. """ from PyQt5.QtWidgets import QAction, QMessageBox from PyQt5.QtGui import QKeySequence from PyQt5.QtCore import Qt class NewAction(QAction): def __init__(self, scripter, parent=None): super(NewAction, self).__init__(parent) self.scripter = scripter self.triggered.connect(self.new) self.setText('New') self.setObjectName('new') self.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_N)) @property def parent(self): return 'File' def new(self): msgBox = QMessageBox(self.scripter.uicontroller.mainWidget) msgBox.setText("The document has been modified.") msgBox.setInformativeText("Do you want to save your changes?") msgBox.setStandardButtons(QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel) msgBox.setDefaultButton(QMessageBox.Save) - ret = msgBox.exec() + ret = msgBox.exec_() if ret == QMessageBox.Cancel: return if ret == QMessageBox.Save: self.scripter.uicontroller.invokeAction('save') self.scripter.documentcontroller.clearActiveDocument() self.scripter.uicontroller.setStatusBar() self.scripter.uicontroller.clearEditor() diff --git a/plugins/python/scripter/ui_scripter/actions/openaction/openaction.py b/plugins/python/scripter/ui_scripter/actions/openaction/openaction.py index 5baa4a6d14..209b371185 100644 --- a/plugins/python/scripter/ui_scripter/actions/openaction/openaction.py +++ b/plugins/python/scripter/ui_scripter/actions/openaction/openaction.py @@ -1,56 +1,57 @@ """ Copyright (c) 2017 Eliakin Costa 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. """ from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox from PyQt5.QtGui import QKeySequence from PyQt5.QtCore import Qt +import os + class OpenAction(QAction): def __init__(self, scripter, parent=None): super(OpenAction, self).__init__(parent) self.scripter = scripter self.triggered.connect(self.open) self.setText('Open') self.setObjectName('open') self.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_O)) @property def parent(self): return 'File' def open(self): dialog = QFileDialog(self.scripter.uicontroller.mainWidget) dialog.setNameFilter('Python files (*.py)') - if dialog.exec(): + if dialog.exec_(): try: selectedFile = dialog.selectedFiles()[0] - fileExtension = selectedFile.rsplit('.', maxsplit=1)[1] + _, fileExtension = os.path.splitext(selectedFile) - if fileExtension == 'py': + if fileExtension == '.py': document = self.scripter.documentcontroller.openDocument(selectedFile) self.scripter.uicontroller.setDocumentEditor(document) self.scripter.uicontroller.setStatusBar(document.filePath) - print("open is run") except Exception: QMessageBox.information(self.scripter.uicontroller.mainWidget, 'Invalid File', 'Open files with .py extension') diff --git a/plugins/python/scripter/ui_scripter/actions/reloadaction/reloadaction.py b/plugins/python/scripter/ui_scripter/actions/reloadaction/reloadaction.py index 6b3a57cc2b..7b6ebbb0e3 100644 --- a/plugins/python/scripter/ui_scripter/actions/reloadaction/reloadaction.py +++ b/plugins/python/scripter/ui_scripter/actions/reloadaction/reloadaction.py @@ -1,46 +1,62 @@ -from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox +""" +Copyright (c) 2017 Eliakin Costa + +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. +""" +from PyQt5.QtWidgets import QAction, QMessageBox from PyQt5.QtGui import QKeySequence from PyQt5.QtCore import Qt class ReloadAction(QAction): def __init__(self, scripter, parent=None): super(ReloadAction, self).__init__(parent) self.scripter = scripter self.editor = self.scripter.uicontroller.editor self.triggered.connect(self.reloadFile) self.setText('Reload File') self.setObjectName('reloadfile') self.setShortcut(QKeySequence(Qt.ALT + Qt.Key_R)) @property def parent(self): return 'File' def reloadFile(self): # get the currently open document's path curr_doc_fpath = '' document = self.scripter.documentcontroller._activeDocument if document is None: QMessageBox.critical(self.scripter.uicontroller.mainWidget, 'No existing document', 'Please specify a document by opening it before reloading') return else: curr_doc_fpath = document.filePath # clear the editor self.scripter.documentcontroller.clearActiveDocument() self.scripter.uicontroller.setStatusBar() self.scripter.uicontroller.clearEditor() # reload the document document = self.scripter.documentcontroller.openDocument(curr_doc_fpath) self.scripter.uicontroller.setDocumentEditor(document) self.scripter.uicontroller.setStatusBar(document.filePath) - print("reload is run") return document diff --git a/plugins/python/scripter/ui_scripter/actions/runaction/docwrapper.py b/plugins/python/scripter/ui_scripter/actions/runaction/docwrapper.py index 4709bcab79..a5519fbbb1 100644 --- a/plugins/python/scripter/ui_scripter/actions/runaction/docwrapper.py +++ b/plugins/python/scripter/ui_scripter/actions/runaction/docwrapper.py @@ -1,30 +1,30 @@ """ Copyright (c) 2017 Eliakin Costa 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. """ from PyQt5.QtGui import QTextCursor -class DocWrapper: +class DocWrapper(object): def __init__(self, textdocument): self.textdocument = textdocument def write(self, text, view=None): cursor = QTextCursor(self.textdocument) cursor.clearSelection() cursor.movePosition(QTextCursor.End) cursor.insertText(text) diff --git a/plugins/python/scripter/ui_scripter/actions/runaction/runaction.py b/plugins/python/scripter/ui_scripter/actions/runaction/runaction.py index b607d5a403..66f2d909d1 100644 --- a/plugins/python/scripter/ui_scripter/actions/runaction/runaction.py +++ b/plugins/python/scripter/ui_scripter/actions/runaction/runaction.py @@ -1,132 +1,155 @@ """ Copyright (c) 2017 Eliakin Costa 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. """ from PyQt5.QtWidgets import QAction from PyQt5.QtGui import QIcon, QKeySequence from PyQt5.QtCore import Qt import sys -from . import docwrapper -import importlib -from importlib.machinery import SourceFileLoader import traceback +import inspect +from . import docwrapper +if sys.version_info[0] > 2: + import importlib + from importlib.machinery import SourceFileLoader +else: + import imp +PYTHON27 = sys.version_info.major == 2 and sys.version_info.minor == 7 PYTHON33 = sys.version_info.major == 3 and sys.version_info.minor == 3 PYTHON34 = sys.version_info.major == 3 and sys.version_info.minor == 4 EXEC_NAMESPACE = "__main__" # namespace that user scripts will run in class RunAction(QAction): def __init__(self, scripter, parent=None): super(RunAction, self).__init__(parent) self.scripter = scripter self.editor = self.scripter.uicontroller.editor self.output = self.scripter.uicontroller.findTabWidget('OutPut', 'OutPutTextEdit') self.triggered.connect(self.run) self.setText('Run') self.setToolTip('Run Ctrl+R') self.setIcon(QIcon(':/icons/run.svg')) self.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_R)) @property def parent(self): return 'toolBar' def run(self): """ This method execute python code from an activeDocument (file) or direct from editor (ui_scripter/editor/pythoneditor.py). When executing code from a file, we use importlib to load this module/file and with "users_script" name. That's implementation seeks for a "main()" function in the script. When executing code from editor without creating a file, we compile this script to bytecode and we execute this in an empty scope. That's faster than use exec directly and cleaner, because we are using an empty scope. """ self.scripter.uicontroller.setActiveWidget('OutPut') stdout = sys.stdout stderr = sys.stderr output = docwrapper.DocWrapper(self.output.document()) if (self.editor._documentModified is True): output.write("==== Warning: Script not saved! ====\n") else: output.write("======================================\n") sys.stdout = output sys.stderr = output script = self.editor.document().toPlainText() document = self.scripter.documentcontroller.activeDocument try: if document and self.editor._documentModified is False: - spec = importlib.util.spec_from_file_location(EXEC_NAMESPACE, document.filePath) - try: - users_module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(users_module) - - except AttributeError as e: # no module from spec - if PYTHON34 or PYTHON33: - loader = SourceFileLoader(EXEC_NAMESPACE, document.filePath) - users_module = loader.load_module() - else: - raise e - - try: - # maybe script is to be execed, maybe main needs to be invoked - # if there is a main() then execute it, otherwise don't worry... + if PYTHON27: + users_module = self.run_py2_document(document) + else: + users_module = self.run_py3_document(document) + + # maybe script is to be execed, maybe main needs to be invoked + # if there is a main() then execute it, otherwise don't worry... + if hasattr(users_module, "main") and inspect.isfunction(users_module.main): users_module.main() - except AttributeError: - pass - else: code = compile(script, '', 'exec') globals_dict = {"__name__": EXEC_NAMESPACE} exec(code, globals_dict) - except Exception as e: - """Provide context (line number and text) for an error that is caught. - Ordinarily, syntax and Indent errors are caught during initial - compilation in exec(), and the traceback traces back to this file. - So these need to be treated separately. - Other errors trace back to the file/script being run. - """ + except Exception: + # Provide context (line number and text) for an error that is caught. + # Ordinarily, syntax and Indent errors are caught during initial + # compilation in exec(), and the traceback traces back to this file. + # So these need to be treated separately. + # Other errors trace back to the file/script being run. type_, value_, traceback_ = sys.exc_info() if type_ == SyntaxError: errorMessage = "%s\n%s" % (value_.text.rstrip(), " " * (value_.offset - 1) + "^") # rstrip to remove trailing \n, output needs to be fixed width font for the ^ to align correctly errorText = "Syntax Error on line %s" % value_.lineno elif type_ == IndentationError: # (no offset is provided for an IndentationError errorMessage = value_.text.rstrip() errorText = "Unexpected Indent on line %s" % value_.lineno else: errorText = traceback.format_exception_only(type_, value_)[0] format_string = "In file: {0}\nIn function: {2} at line: {1}. Line with error:\n{3}" tbList = traceback.extract_tb(traceback_) tb = tbList[-1] errorMessage = format_string.format(*tb) m = "\n**********************\n%s\n%s\n**********************\n" % (errorText, errorMessage) output.write(m) sys.stdout = stdout sys.stderr = stderr # scroll to bottom of output bottom = self.output.verticalScrollBar().maximum() self.output.verticalScrollBar().setValue(bottom) + + def run_py2_document(self, document): + """ Loads and executes an external script using Python 2 specific operations + and returns the loaded module for further execution if needed. + """ + try: + user_module = imp.load_source(EXEC_NAMESPACE, document.filePath) + except Exception as e: + raise e + + return user_module + + def run_py3_document(self, document): + """ Loads and executes an external script using Python 3 specific operations + and returns the loaded module for further execution if needed. + """ + spec = importlib.util.spec_from_file_location(EXEC_NAMESPACE, document.filePath) + try: + users_module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(users_module) + + except AttributeError as e: # no module from spec + if PYTHON34 or PYTHON33: + loader = SourceFileLoader(EXEC_NAMESPACE, document.filePath) + users_module = loader.load_module() + else: + raise e + + return users_module diff --git a/plugins/python/scripter/ui_scripter/actions/settingsaction/settingsaction.py b/plugins/python/scripter/ui_scripter/actions/settingsaction/settingsaction.py index b335a6e7b1..34f0f88b96 100644 --- a/plugins/python/scripter/ui_scripter/actions/settingsaction/settingsaction.py +++ b/plugins/python/scripter/ui_scripter/actions/settingsaction/settingsaction.py @@ -1,49 +1,49 @@ """ Copyright (c) 2017 Eliakin Costa 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. """ from PyQt5.QtWidgets import QAction from PyQt5.QtCore import Qt from . import settingsdialog class SettingsAction(QAction): def __init__(self, scripter, parent=None): super(SettingsAction, self).__init__(parent) self.scripter = scripter self.triggered.connect(self.openSettings) self.settingsDialog = settingsdialog.SettingsDialog(self.scripter) self.settingsDialog.setWindowModality(Qt.WindowModal) self.settingsDialog.setFixedSize(400, 250) self.setText('Settings') @property def parent(self): return 'File' def openSettings(self): self.settingsDialog.show() - self.settingsDialog.exec() + self.settingsDialog.exec_() def readSettings(self): self.settingsDialog.readSettings(self.scripter.settings) def writeSettings(self): self.settingsDialog.writeSettings(self.scripter.settings) diff --git a/plugins/python/scripter/uicontroller.py b/plugins/python/scripter/uicontroller.py index 27e18f3dd7..e02cabb330 100644 --- a/plugins/python/scripter/uicontroller.py +++ b/plugins/python/scripter/uicontroller.py @@ -1,262 +1,264 @@ """ Copyright (c) 2017 Eliakin Costa 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. """ -from PyQt5.QtGui import QTextCursor, QPalette, QFontInfo +from PyQt5.QtCore import Qt, QObject, QFileInfo, QRect +from PyQt5.QtGui import QTextCursor, QPalette from PyQt5.QtWidgets import (QToolBar, QMenuBar, QTabWidget, QLabel, QVBoxLayout, QMessageBox, QSplitter, QSizePolicy) -from PyQt5.QtCore import Qt, QObject, QFileInfo, pyqtSlot, QRect -from scripter.ui_scripter.syntax import syntax, syntaxstyles -from scripter.ui_scripter.editor import pythoneditor -from scripter import scripterdialog +from .ui_scripter.syntax import syntax, syntaxstyles +from .ui_scripter.editor import pythoneditor +from . import scripterdialog import importlib KEY_GEOMETRY = "geometry" DEFAULT_GEOMETRY = QRect(600, 200, 400, 500) # essentially randomly placed class Elided_Text_Label(QLabel): mainText = str() def __init__(self, parent=None): super(QLabel, self).__init__(parent) self.setMinimumWidth(self.fontMetrics().width("...")) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) def setMainText(self, text=str()): self.mainText = text self.elideText() def elideText(self): self.setText(self.fontMetrics().elidedText(self.mainText, Qt.ElideRight, self.width())) def resizeEvent(self, event): self.elideText() class UIController(object): documentModifiedText = "" documentStatusBarText = "untitled" def __init__(self): self.mainWidget = scripterdialog.ScripterDialog(self) self.actionToolbar = QToolBar('toolBar', self.mainWidget) self.menu_bar = QMenuBar(self.mainWidget) self.actionToolbar.setObjectName('toolBar') self.menu_bar.setObjectName('menuBar') self.actions = [] self.mainWidget.setWindowModality(Qt.NonModal) def initialize(self, scripter): self.editor = pythoneditor.CodeEditor(scripter) self.tabWidget = QTabWidget() self.statusBar = Elided_Text_Label() self.statusBar.setMainText('untitled') self.splitter = QSplitter() self.splitter.setOrientation(Qt.Vertical) self.highlight = syntax.PythonHighlighter(self.editor.document(), syntaxstyles.DefaultSyntaxStyle()) p = self.editor.palette() p.setColor(QPalette.Base, syntaxstyles.DefaultSyntaxStyle()['background'].foreground().color()) p.setColor(QPalette.Text, syntaxstyles.DefaultSyntaxStyle()['foreground'].foreground().color()) self.editor.setPalette(p) self.scripter = scripter self.loadMenus() self.loadWidgets() self.loadActions() self._readSettings() # sets window size vbox = QVBoxLayout(self.mainWidget) vbox.addWidget(self.menu_bar) vbox.addWidget(self.actionToolbar) self.splitter.addWidget(self.editor) self.splitter.addWidget(self.tabWidget) vbox.addWidget(self.splitter) vbox.addWidget(self.statusBar) self.mainWidget.setWindowTitle("Scripter") self.mainWidget.setSizeGripEnabled(True) self.mainWidget.show() self.mainWidget.activateWindow() self.editor.undoAvailable.connect(self.setStatusModified) def loadMenus(self): self.addMenu('File', 'File') def addMenu(self, menuName, parentName): parent = self.menu_bar.findChild(QObject, parentName) self.newMenu = None if parent: self.newMenu = parent.addMenu(menuName) else: self.newMenu = self.menu_bar.addMenu(menuName) self.newMenu.setObjectName(menuName) return self.newMenu def loadActions(self): module_path = 'scripter.ui_scripter.actions' actions_module = importlib.import_module(module_path) modules = [] for class_path in actions_module.action_classes: - _module, _klass = class_path.rsplit('.', maxsplit=1) + _module = class_path[:class_path.rfind(".")] + _klass = class_path[class_path.rfind(".") + 1:] modules.append(dict(module='{0}.{1}'.format(module_path, _module), klass=_klass)) for module in modules: m = importlib.import_module(module['module']) action_class = getattr(m, module['klass']) obj = action_class(self.scripter) parent = self.mainWidget.findChild(QObject, obj.parent) self.actions.append(dict(action=obj, parent=parent)) for action in self.actions: action['parent'].addAction(action['action']) def loadWidgets(self): modulePath = 'scripter.ui_scripter.tabwidgets' widgetsModule = importlib.import_module(modulePath) modules = [] - for classPath in widgetsModule.widgetClasses: - _module, _klass = classPath.rsplit('.', maxsplit=1) + for class_path in widgetsModule.widgetClasses: + _module = class_path[:class_path.rfind(".")] + _klass = class_path[class_path.rfind(".") + 1:] modules.append(dict(module='{0}.{1}'.format(modulePath, _module), klass=_klass)) for module in modules: m = importlib.import_module(module['module']) widgetClass = getattr(m, module['klass']) obj = widgetClass(self.scripter) self.tabWidget.addTab(obj, obj.objectName()) def invokeAction(self, actionName): for action in self.actions: if action['action'].objectName() == actionName: method = getattr(action['action'], actionName) if method: return method() def findTabWidget(self, widgetName, childName=''): for index in range(self.tabWidget.count()): widget = self.tabWidget.widget(index) if widget.objectName() == widgetName: if childName: widget = widget.findChild(QObject, childName) return widget def showException(self, exception): QMessageBox.critical(self.editor, "Error running script", str(exception)) def setDocumentEditor(self, document): self.editor.clear() self.editor.moveCursor(QTextCursor.Start) self.editor._documentModified = False self.editor.setPlainText(document.data) self.editor.moveCursor(QTextCursor.End) def setStatusBar(self, value='untitled'): self.documentStatusBarText = value self.statusBar.setMainText(self.documentStatusBarText + self.documentModifiedText) def setStatusModified(self): self.documentModifiedText = "" if (self.editor._documentModified is True): self.documentModifiedText = " [Modified]" self.statusBar.setMainText(self.documentStatusBarText + self.documentModifiedText) def setActiveWidget(self, widgetName): widget = self.findTabWidget(widgetName) if widget: self.tabWidget.setCurrentWidget(widget) def setStepped(self, status): self.editor.setStepped(status) def clearEditor(self): self.editor.clear() def repaintDebugArea(self): self.editor.repaintDebugArea() def closeScripter(self): self.mainWidget.close() def _writeSettings(self): """ _writeSettings is a method invoked when the scripter starts, making control inversion. Actions can implement a writeSettings method to save your own settings without this method to know about it. """ self.scripter.settings.beginGroup('scripter') document = self.scripter.documentcontroller.activeDocument if document: self.scripter.settings.setValue('activeDocumentPath', document.filePath) else: self.scripter.settings.setValue('activeDocumentPath', '') self.scripter.settings.setValue('editorFontSize', self.editor.fontInfo().pointSize()) for action in self.actions: writeSettings = getattr(action['action'], "writeSettings", None) if callable(writeSettings): writeSettings() # Window Geometry rect = self.mainWidget.geometry() self.scripter.settings.setValue(KEY_GEOMETRY, rect) self.scripter.settings.endGroup() def _readSettings(self): """ It's similar to _writeSettings, but reading the settings when the ScripterDialog is closed. """ self.scripter.settings.beginGroup('scripter') activeDocumentPath = self.scripter.settings.value('activeDocumentPath', '') if activeDocumentPath: if QFileInfo(activeDocumentPath).exists(): document = self.scripter.documentcontroller.openDocument(activeDocumentPath) self.setStatusBar(document.filePath) self.setDocumentEditor(document) for action in self.actions: readSettings = getattr(action['action'], "readSettings", None) if callable(readSettings): readSettings() pointSize = self.scripter.settings.value('editorFontSize', str(self.editor.fontInfo().pointSize())) self.editor.setFontSize(int(pointSize)) # Window Geometry rect = self.scripter.settings.value(KEY_GEOMETRY, DEFAULT_GEOMETRY) self.mainWidget.setGeometry(rect) self.scripter.settings.endGroup() def _saveSettings(self): self.scripter.settings.sync() diff --git a/plugins/python/selectionsbagdocker/kritapykrita_selectionsbagdocker.desktop b/plugins/python/selectionsbagdocker/kritapykrita_selectionsbagdocker.desktop index b00ac6d3fd..b19c9a320d 100644 --- a/plugins/python/selectionsbagdocker/kritapykrita_selectionsbagdocker.desktop +++ b/plugins/python/selectionsbagdocker/kritapykrita_selectionsbagdocker.desktop @@ -1,42 +1,42 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=selectionsbagdocker X-Python-2-Compatible=false Name=Selections Bag Docker Name[ar]=رصيف سلّة التّحديدات Name[ca]=Acoblador de bossa de seleccions Name[ca@valencia]=Acoblador de bossa de seleccions Name[el]=Προσάρτηση σάκου επιλογών Name[en_GB]=Selections Bag Docker Name[es]=Panel de selecciones Name[gl]=Doca de bolsa das seleccións Name[it]=Area di raccolta selezioni Name[nl]=Docker van zak met selecties Name[pl]=Dok worka zaznaczeń Name[pt]=Área de Selecções Name[sv]=Dockningspanel med markeringspåse Name[tr]=Seçim Çantası Doku Name[uk]=Бічна панель позначеного Name[x-test]=xxSelections Bag Dockerxx -Name[zh_CN]=选区包坞窗 +Name[zh_CN]=选区列表工具面板 Name[zh_TW]=「選取範圍收藏」面板 Comment=A docker that allow to store a list of selections Comment[ar]=رصيف يتيح تخزين قائمة تحديدات Comment[ca]=Un acoblador que permet emmagatzemar una llista de seleccions Comment[ca@valencia]=Un acoblador que permet emmagatzemar una llista de seleccions Comment[cs]=Dok umožňující uložit seznam výběrů Comment[el]=Ένα εργαλείο προσάρτησης που επιτρέπει την αποθήκευση μιας λίστας επιλογών Comment[en_GB]=A docker that allow to store a list of selections Comment[es]=Un panel que permite guardar una lista de selecciones Comment[gl]=Unha doca que permite almacenar unha lista de seleccións. Comment[it]=Un'area di aggancio che consente di memorizzare un elenco di selezioni Comment[nl]=Een docker die een lijst met selecties kan opslaan Comment[pl]=Dok, który umożliwia przechowywanie listy zaznaczeń Comment[pt]=Uma área acoplável que permite guardar uma lista de selecções Comment[sv]=En dockningspanel som gör det möjligt att lagra en lista över markeringar Comment[tr]=Seçimlerin bir listesini saklamayı sağlayan bir dok Comment[uk]=Бічна панель, на якій можна зберігати список позначеного Comment[x-test]=xxA docker that allow to store a list of selectionsxx -Comment[zh_CN]=存储选区列表的坞窗 +Comment[zh_CN]=用于保存一系列选区的工具面板 Comment[zh_TW]=允許儲存選取範圍列表的面板 diff --git a/plugins/python/tenbrushes/kritapykrita_tenbrushes.desktop b/plugins/python/tenbrushes/kritapykrita_tenbrushes.desktop index 43c4c4055c..827b65acf6 100644 --- a/plugins/python/tenbrushes/kritapykrita_tenbrushes.desktop +++ b/plugins/python/tenbrushes/kritapykrita_tenbrushes.desktop @@ -1,43 +1,43 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=tenbrushes -X-Python-2-Compatible=false +X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Ten Brushes Name[ar]=عشرُ فُرش Name[ca]=Deu pinzells Name[ca@valencia]=Deu pinzells Name[cs]=Deset štětců Name[el]=Δέκα πινέλα Name[en_GB]=Ten Brushes Name[es]=Diez pinceles Name[gl]=Dez pinceis Name[is]=Tíu penslar Name[it]=Dieci pennelli Name[nl]=Tien penselen Name[pl]=Dziesięć pędzli Name[pt]=Dez Pincéis Name[sv]=Tio penslar Name[tr]=On Fırça Name[uk]=Десять пензлів Name[x-test]=xxTen Brushesxx -Name[zh_CN]=十笔刷 +Name[zh_CN]=十大笔刷 Name[zh_TW]=10 個筆刷 Comment=Assign a preset to ctrl-1 to ctrl-0 Comment[ca]=Assigna una predefinició des de Ctrl-1 a Ctrl-0 Comment[ca@valencia]=Assigna una predefinició des de Ctrl-1 a Ctrl-0 Comment[el]=Αντιστοίχιση προκαθορισμένου από ctrl-1 στο ctrl-0 Comment[en_GB]=Assign a preset to ctrl-1 to ctrl-0 Comment[es]=Asignar una preselección a Ctrl-1 hasta Ctrl-0 Comment[gl]=Asigne unha predefinición do Ctrl+1 ao Ctrl+0. Comment[it]=Assegna una preimpostazione per ctrl-1 a ctrl-0 Comment[nl]=Een voorinstelling toekennen aan Ctrl-1 tot Ctrl-0 Comment[pl]=Przypisz nastawę do ctrl-1 lub ctrl-0 Comment[pt]=Atribuir uma predefinição de Ctrl-1 a Ctrl-0 Comment[sv]=Tilldela en förinställning för Ctrl+1 till Ctrl+0 Comment[tr]= ctrl-1 ve ctrl-0 için ayar atama Comment[uk]=Прив’язування наборів налаштувань до скорочень від ctrl-1 до ctrl-0 Comment[x-test]=xxAssign a preset to ctrl-1 to ctrl-0xx -Comment[zh_CN]=将预设分配给 Ctrl+1 到 Ctrl+0 +Comment[zh_CN]=将预设分配给 Ctrl+1 到 Ctrl+0 快捷键 Comment[zh_TW]=將預設指定給 ctrl-1 至 ctrl-0 diff --git a/plugins/python/tenbrushes/tenbrushes.py b/plugins/python/tenbrushes/tenbrushes.py index d21c66a6fb..0f296e8d69 100644 --- a/plugins/python/tenbrushes/tenbrushes.py +++ b/plugins/python/tenbrushes/tenbrushes.py @@ -1,69 +1,69 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' import krita -from tenbrushes import uitenbrushes +from . import uitenbrushes class TenBrushesExtension(krita.Extension): def __init__(self, parent): super(TenBrushesExtension, self).__init__(parent) self.actions = [] self.buttons = [] self.selectedPresets = [] def setup(self): self.readSettings() - + def createActions(self, window): action = window.createAction("ten_brushes", "Ten Brushes") action.setToolTip("Assign ten brush presets to ten shortcuts.") action.triggered.connect(self.initialize) self.loadActions(window) def initialize(self): self.uitenbrushes = uitenbrushes.UITenBrushes() self.uitenbrushes.initialize(self) def readSettings(self): self.selectedPresets = Application.readSetting("", "tenbrushes", "").split(',') def writeSettings(self): presets = [] for index, button in enumerate(self.buttons): self.actions[index].preset = button.preset presets.append(button.preset) Application.writeSetting("", "tenbrushes", ','.join(map(str, presets))) def loadActions(self, window): allPresets = Application.resources("preset") for index, item in enumerate(['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']): action = window.createAction("activate_preset_" + item, "Activate Brush Preset " + item, "") action.triggered.connect(self.activatePreset) if index < len(self.selectedPresets) and self.selectedPresets[index] in allPresets: action.preset = self.selectedPresets[index] else: action.preset = None self.actions.append(action) def activatePreset(self): allPresets = Application.resources("preset") if Application.activeWindow() and len(Application.activeWindow().views()) > 0 and self.sender().preset in allPresets: Application.activeWindow().views()[0].activateResource(allPresets[self.sender().preset]) Scripter.addExtension(TenBrushesExtension(Application)) diff --git a/plugins/python/tenbrushes/uitenbrushes.py b/plugins/python/tenbrushes/uitenbrushes.py index 8ad75f6103..72826374cc 100644 --- a/plugins/python/tenbrushes/uitenbrushes.py +++ b/plugins/python/tenbrushes/uitenbrushes.py @@ -1,76 +1,75 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' from PyQt5.QtCore import Qt, QSize from PyQt5.QtGui import QPixmap, QIcon from PyQt5.QtWidgets import (QDialogButtonBox, QLabel, QVBoxLayout, QHBoxLayout) -from tenbrushes import tenbrushesdialog, dropbutton +from . import tenbrushesdialog, dropbutton import krita class UITenBrushes(object): def __init__(self): self.kritaInstance = krita.Krita.instance() self.mainDialog = tenbrushesdialog.TenBrushesDialog(self, self.kritaInstance.activeWindow().qwindow()) self.buttonBox = QDialogButtonBox(self.mainDialog) self.vbox = QVBoxLayout(self.mainDialog) self.hbox = QHBoxLayout(self.mainDialog) self.buttonBox.accepted.connect(self.mainDialog.accept) self.buttonBox.rejected.connect(self.mainDialog.reject) self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.presetChooser = krita.PresetChooser(self.mainDialog) def initialize(self, tenbrushes): self.tenbrushes = tenbrushes self.loadButtons() self.vbox.addLayout(self.hbox) self.vbox.addWidget(QLabel("Select the brush preset, then click on the button you want to use to select the preset")) self.vbox.addWidget(self.presetChooser) self.vbox.addWidget(self.buttonBox) - self.mainDialog.show() self.mainDialog.activateWindow() self.mainDialog.exec_() def loadButtons(self): self.tenbrushes.buttons = [] allPresets = Application.resources("preset") for index, item in enumerate(['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']): buttonLayout = QVBoxLayout() button = dropbutton.DropButton(self.mainDialog) button.setObjectName(item) button.clicked.connect(button.selectPreset) button.presetChooser = self.presetChooser if self.tenbrushes.actions[index] and self.tenbrushes.actions[index].preset and self.tenbrushes.actions[index].preset in allPresets: p = allPresets[self.tenbrushes.actions[index].preset] button.preset = p.name() button.setIcon(QIcon(QPixmap.fromImage(p.image()))) buttonLayout.addWidget(button) label = QLabel(self.tenbrushes.actions[index].shortcut().toString()) label.setAlignment(Qt.AlignHCenter) buttonLayout.addWidget(label) self.hbox.addLayout(buttonLayout) self.tenbrushes.buttons.append(button) diff --git a/plugins/python/tenscripts/kritapykrita_tenscripts.desktop b/plugins/python/tenscripts/kritapykrita_tenscripts.desktop index 5ed96d1668..25cdadfd6e 100644 --- a/plugins/python/tenscripts/kritapykrita_tenscripts.desktop +++ b/plugins/python/tenscripts/kritapykrita_tenscripts.desktop @@ -1,39 +1,39 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=tenscripts -X-Python-2-Compatible=false +X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Ten Scripts Name[ar]=عشرُ سكربتات Name[ca]=Deu scripts Name[ca@valencia]=Deu scripts Name[en_GB]=Ten Scripts Name[es]=Diez guiones Name[gl]=Dez scripts Name[is]=Tíu skriftur Name[it]=Dieci script Name[nl]=Tien scripts Name[pl]=Skrypty Ten Name[pt]=Dez Programas Name[sv]=Tio skript Name[uk]=Десять скриптів Name[x-test]=xxTen Scriptsxx -Name[zh_CN]=十脚本 +Name[zh_CN]=十大脚本 Name[zh_TW]=10 個腳本 Comment=A Python-based plugin for creating ten actions and assign them to Python scripts Comment[ar]=ملحقة بِ‍«پيثون» لإنشاء ١٠ إجراءات وإسنادها إلى سكربتات «پيثون» Comment[ca]=Un connector basant en el Python per crear deu accions i assignar-les a scripts del Python Comment[ca@valencia]=Un connector basant en el Python per crear deu accions i assignar-les a scripts del Python Comment[en_GB]=A Python-based plugin for creating ten actions and assign them to Python scripts Comment[es]=Un complemento basado en Python para crear diez acciones y asignarlas a guiones de Python Comment[gl]=Un complemento escrito en Python para crear dez accións e asignalas a scripts escritos en Python. Comment[it]=Un'estensione basata su Python per creare dieci azioni e assegnarle a script Python Comment[nl]=Een op Python gebaseerde plug-in voor aanmaken van tien acties en ze dan toewijzen aan Python-scripts Comment[pl]=Wtyczka oparta na Pythonie do tworzenia działań i przypisywanie ich skryptom Pythona Comment[pt]=Um 'plugin' feito em Python para criar dez acções e atribuí-las a programas em Python Comment[sv]=Ett Python-baserat insticksprogram för att skapa tio åtgärder och tilldela dem till Python-skript Comment[uk]=Скрипт на основі Python для створення десяти дій і прив'язування до них скриптів Python Comment[x-test]=xxA Python-based plugin for creating ten actions and assign them to Python scriptsxx -Comment[zh_CN]=用于创建十个操作并将它们分配给 Python 脚本的插件 +Comment[zh_CN]=基于 Python 的可创建十个操作并将它们指定到其它 Python 脚本的插件 Comment[zh_TW]=基於 Python 的外掛程式,用於建立 10 個動作並且將它們指定給 Python 腳本 diff --git a/plugins/python/tenscripts/tenscripts.py b/plugins/python/tenscripts/tenscripts.py index b556580aae..983b977721 100644 --- a/plugins/python/tenscripts/tenscripts.py +++ b/plugins/python/tenscripts/tenscripts.py @@ -1,84 +1,93 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' +import sys + from PyQt5.QtWidgets import QMessageBox import krita -from tenscripts import uitenscripts -import importlib +from . import uitenscripts + +if sys.version_info[0] > 2: + import importlib +else: + import imp class TenScriptsExtension(krita.Extension): def __init__(self, parent): - super(TenScriptsExtension, self).__init__(parent) + super(TenScriptsExtension, self).__init__(parent) - self.actions = [] - self.scripts = [] + self.actions = [] + self.scripts = [] def setup(self): self.readSettings() - + def createActions(self, window): action = window.createAction("ten_scripts", "Ten Scripts") action.setToolTip("Assign ten scripts to ten shortcuts.") action.triggered.connect(self.initialize) self.loadActions(window) def initialize(self): self.uitenscripts = uitenscripts.UITenScripts() self.uitenscripts.initialize(self) def readSettings(self): self.scripts = Application.readSetting("tenscripts", "scripts", "").split(',') def writeSettings(self): saved_scripts = self.uitenscripts.saved_scripts() for index, script in enumerate(saved_scripts): self.actions[index].script = script Application.writeSetting("tenscripts", "scripts", ','.join(map(str, saved_scripts))) def loadActions(self, window): for index, item in enumerate(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']): action = window.createAction("execute_script_" + item, "Execute Script " + item, "") action.script = None action.triggered.connect(self._executeScript) if index < len(self.scripts): action.script = self.scripts[index] self.actions.append(action) def _executeScript(self): script = self.sender().script if script: try: - spec = importlib.util.spec_from_file_location("users_script", script) - users_module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(users_module) - + if sys.version_info[0] > 2: + spec = importlib.util.spec_from_file_location("users_script", script) + users_module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(users_module) + else: + users_module = imp.load_source("users_script", script) + if hasattr(users_module, 'main') and callable(users_module.main): users_module.main() self.showMessage('script {0} executed'.format(self.sender().script)) except Exception as e: self.showMessage(str(e)) else: self.showMessage("You didn't assign a script to that action") def showMessage(self, message): self.msgBox = QMessageBox(Application.activeWindow().qwindow()) self.msgBox.setText(message) self.msgBox.exec_() Scripter.addExtension(TenScriptsExtension(Application)) diff --git a/plugins/python/tenscripts/uitenscripts.py b/plugins/python/tenscripts/uitenscripts.py index b73d94670a..2eec3c15b6 100644 --- a/plugins/python/tenscripts/uitenscripts.py +++ b/plugins/python/tenscripts/uitenscripts.py @@ -1,109 +1,109 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' from PyQt5.QtCore import Qt from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLineEdit, QScrollArea, QGridLayout, QFileDialog, QLabel, QDialogButtonBox) -from tenscripts import tenscriptsdialog +from . import tenscriptsdialog import krita class UITenScripts(object): def __init__(self): self.kritaInstance = krita.Krita.instance() self.mainDialog = tenscriptsdialog.TenScriptsDialog(self, self.kritaInstance.activeWindow().qwindow()) self.buttonBox = QDialogButtonBox(self.mainDialog) self.layout = QVBoxLayout(self.mainDialog) self.baseWidget = QWidget() self.baseArea = QWidget() - self.scrollArea = QScrollArea() + self.scrollArea = QScrollArea() self.scriptsLayout = QGridLayout() self.buttonBox.accepted.connect(self.mainDialog.accept) self.buttonBox.rejected.connect(self.mainDialog.reject) self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.scrollArea.setWidgetResizable(True) def initialize(self, tenscripts): self.tenscripts = tenscripts self._loadGridLayout() self._fillScripts() self.baseArea.setLayout(self.scriptsLayout) self.scrollArea.setWidget(self.baseArea) self.layout.addWidget(self.scrollArea) self.layout.addWidget(self.buttonBox) self.mainDialog.show() self.mainDialog.activateWindow() self.mainDialog.exec_() def addNewRow(self, key): rowPosition = self.scriptsLayout.rowCount() rowLayout = QHBoxLayout() label = QLabel() directoryTextField = QLineEdit() directoryDialogButton = QPushButton("...") directoryTextField.setReadOnly(True) label.setText(self.tenscripts.actions[key].shortcut().toString()) directoryTextField.setToolTip("Selected Path") directoryDialogButton.setToolTip("Select the script") directoryDialogButton.clicked.connect(self._selectScript) self.scriptsLayout.addWidget(label, rowPosition, 0, Qt.AlignLeft|Qt.AlignTop) self.scriptsLayout.addWidget(directoryTextField, rowPosition, 1, Qt.AlignLeft|Qt.AlignTop) self.scriptsLayout.addWidget(directoryDialogButton, rowPosition, 2, Qt.AlignLeft|Qt.AlignTop) def saved_scripts(self): _saved_scripts = [] index = 0 for row in range(self.scriptsLayout.rowCount()-1): textField = self.scriptsLayout.itemAt(index + 1).widget() if textField.text(): _saved_scripts.append(textField.text()) index += 3 return _saved_scripts def _selectScript(self): dialog = QFileDialog(self.mainDialog) dialog.setNameFilter('Python files (*.py)') - if dialog.exec(): + if dialog.exec_(): selectedFile = dialog.selectedFiles()[0] obj = self.mainDialog.sender() textField = self.scriptsLayout.itemAt(self.scriptsLayout.indexOf(obj)-1).widget() textField.setText(selectedFile) def _loadGridLayout(self): for item in range(0, 10): self.addNewRow(item) def _fillScripts(self): scripts = self.tenscripts.scripts index = 0 for row in range(self.scriptsLayout.rowCount()-1): if row >= len(scripts): return textField = self.scriptsLayout.itemAt(index + 1).widget() textField.setText(scripts[row]) index += 3 diff --git a/plugins/tools/basictools/kis_tool_brush.cc b/plugins/tools/basictools/kis_tool_brush.cc index 01c45fc0f5..bd6cab3b76 100644 --- a/plugins/tools/basictools/kis_tool_brush.cc +++ b/plugins/tools/basictools/kis_tool_brush.cc @@ -1,469 +1,469 @@ /* * kis_tool_brush.cc - part of Krita * * Copyright (c) 2003-2004 Boudewijn Rempt * Copyright (c) 2015 Moritz Molch * * 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_tool_brush.h" #include #include #include #include #include #include #include #include #include #include #include "kis_cursor.h" #include "kis_config.h" #include "kis_slider_spin_box.h" #include "kundo2magicstring.h" #define MAXIMUM_SMOOTHNESS_DISTANCE 1000.0 // 0..1000.0 == weight in gui #define MAXIMUM_MAGNETISM 1000 void KisToolBrush::addSmoothingAction(int enumId, const QString &id, const QString &name, const QIcon &icon, KActionCollection *globalCollection) { /** * KisToolBrush is the base of several tools, but the actions * should be unique, so let's be careful with them */ if (!globalCollection->action(id)) { QAction *action = new QAction(name, globalCollection); action->setIcon(icon); globalCollection->addAction(id, action); } QAction *action = dynamic_cast(globalCollection->action(id)); addAction(id, action); connect(action, SIGNAL(triggered()), &m_signalMapper, SLOT(map())); m_signalMapper.setMapping(action, enumId); } KisToolBrush::KisToolBrush(KoCanvasBase * canvas) : KisToolFreehand(canvas, KisCursor::load("tool_freehand_cursor.png", 5, 5), kundo2_i18n("Freehand Brush Stroke")) { setObjectName("tool_brush"); connect(this, SIGNAL(smoothingTypeChanged()), this, SLOT(resetCursorStyle())); KActionCollection *collection = this->canvas()->canvasController()->actionCollection(); addSmoothingAction(KisSmoothingOptions::NO_SMOOTHING, "set_no_brush_smoothing", i18nc("@action", "Brush Smoothing: Disabled"), KisIconUtils::loadIcon("smoothing-no"), collection); addSmoothingAction(KisSmoothingOptions::SIMPLE_SMOOTHING, "set_simple_brush_smoothing", i18nc("@action", "Brush Smoothing: Basic"), KisIconUtils::loadIcon("smoothing-basic"), collection); addSmoothingAction(KisSmoothingOptions::WEIGHTED_SMOOTHING, "set_weighted_brush_smoothing", i18nc("@action", "Brush Smoothing: Weighted"), KisIconUtils::loadIcon("smoothing-weighted"), collection); addSmoothingAction(KisSmoothingOptions::STABILIZER, "set_stabilizer_brush_smoothing", i18nc("@action", "Brush Smoothing: Stabilizer"), KisIconUtils::loadIcon("smoothing-stabilizer"), collection); } KisToolBrush::~KisToolBrush() { } void KisToolBrush::activate(ToolActivation activation, const QSet &shapes) { KisToolFreehand::activate(activation, shapes); connect(&m_signalMapper, SIGNAL(mapped(int)), SLOT(slotSetSmoothingType(int)), Qt::UniqueConnection); m_configGroup = KSharedConfig::openConfig()->group(toolId()); } void KisToolBrush::deactivate() { disconnect(&m_signalMapper, 0, this, 0); KisToolFreehand::deactivate(); } int KisToolBrush::smoothingType() const { return smoothingOptions()->smoothingType(); } bool KisToolBrush::smoothPressure() const { return smoothingOptions()->smoothPressure(); } int KisToolBrush::smoothnessQuality() const { return smoothingOptions()->smoothnessDistance(); } qreal KisToolBrush::smoothnessFactor() const { return smoothingOptions()->tailAggressiveness(); } void KisToolBrush::slotSetSmoothingType(int index) { /** * The slot can also be called from smoothing-type-switching * action that would mean the combo box will not be synchronized */ if (m_cmbSmoothingType->currentIndex() != index) { m_cmbSmoothingType->setCurrentIndex(index); } switch (index) { case 0: smoothingOptions()->setSmoothingType(KisSmoothingOptions::NO_SMOOTHING); showControl(m_sliderSmoothnessDistance, false); showControl(m_sliderTailAggressiveness, false); showControl(m_chkSmoothPressure, false); showControl(m_chkUseScalableDistance, false); showControl(m_sliderDelayDistance, false); showControl(m_chkFinishStabilizedCurve, false); showControl(m_chkStabilizeSensors, false); break; case 1: smoothingOptions()->setSmoothingType(KisSmoothingOptions::SIMPLE_SMOOTHING); showControl(m_sliderSmoothnessDistance, false); showControl(m_sliderTailAggressiveness, false); showControl(m_chkSmoothPressure, false); showControl(m_chkUseScalableDistance, false); showControl(m_sliderDelayDistance, false); showControl(m_chkFinishStabilizedCurve, false); showControl(m_chkStabilizeSensors, false); break; case 2: smoothingOptions()->setSmoothingType(KisSmoothingOptions::WEIGHTED_SMOOTHING); showControl(m_sliderSmoothnessDistance, true); showControl(m_sliderTailAggressiveness, true); showControl(m_chkSmoothPressure, true); showControl(m_chkUseScalableDistance, true); showControl(m_sliderDelayDistance, false); showControl(m_chkFinishStabilizedCurve, false); showControl(m_chkStabilizeSensors, false); break; case 3: default: smoothingOptions()->setSmoothingType(KisSmoothingOptions::STABILIZER); showControl(m_sliderSmoothnessDistance, true); showControl(m_sliderTailAggressiveness, false); showControl(m_chkSmoothPressure, false); showControl(m_chkUseScalableDistance, true); showControl(m_sliderDelayDistance, true); showControl(m_chkFinishStabilizedCurve, true); showControl(m_chkStabilizeSensors, true); } emit smoothingTypeChanged(); } void KisToolBrush::slotSetSmoothnessDistance(qreal distance) { smoothingOptions()->setSmoothnessDistance(distance); emit smoothnessQualityChanged(); } void KisToolBrush::slotSetTailAgressiveness(qreal argh_rhhrr) { smoothingOptions()->setTailAggressiveness(argh_rhhrr); emit smoothnessFactorChanged(); } // used with weighted smoothing void KisToolBrush::setSmoothPressure(bool value) { smoothingOptions()->setSmoothPressure(value); } void KisToolBrush::slotSetMagnetism(int magnetism) { m_magnetism = expf(magnetism / (double)MAXIMUM_MAGNETISM) / expf(1.0); } bool KisToolBrush::useScalableDistance() const { return smoothingOptions()->useScalableDistance(); } // used with weighted smoothing void KisToolBrush::setUseScalableDistance(bool value) { smoothingOptions()->setUseScalableDistance(value); emit useScalableDistanceChanged(); } void KisToolBrush::resetCursorStyle() { KisConfig cfg; CursorStyle cursorStyle = cfg.newCursorStyle(); // When the stabilizer is in use, we avoid using the brush outline cursor, // because it would hide the real position of the cursor to the user, // yielding unexpected results. if (smoothingOptions()->smoothingType() == KisSmoothingOptions::STABILIZER && smoothingOptions()->useDelayDistance() && cursorStyle == CURSOR_STYLE_NO_CURSOR) { useCursor(KisCursor::roundCursor()); } else { KisToolFreehand::resetCursorStyle(); } overrideCursorIfNotEditable(); } // stabilizer brush settings bool KisToolBrush::useDelayDistance() const { return smoothingOptions()->useDelayDistance(); } qreal KisToolBrush::delayDistance() const { return smoothingOptions()->delayDistance(); } void KisToolBrush::setUseDelayDistance(bool value) { smoothingOptions()->setUseDelayDistance(value); m_sliderDelayDistance->setEnabled(value); enableControl(m_chkFinishStabilizedCurve, !value); emit useDelayDistanceChanged(); } void KisToolBrush::setDelayDistance(qreal value) { smoothingOptions()->setDelayDistance(value); emit delayDistanceChanged(); } void KisToolBrush::setFinishStabilizedCurve(bool value) { smoothingOptions()->setFinishStabilizedCurve(value); emit finishStabilizedCurveChanged(); } bool KisToolBrush::finishStabilizedCurve() const { return smoothingOptions()->finishStabilizedCurve(); } void KisToolBrush::setStabilizeSensors(bool value) { smoothingOptions()->setStabilizeSensors(value); emit stabilizeSensorsChanged(); } bool KisToolBrush::stabilizeSensors() const { return smoothingOptions()->stabilizeSensors(); } void KisToolBrush::updateSettingsViews() { m_cmbSmoothingType->setCurrentIndex(smoothingOptions()->smoothingType()); m_sliderSmoothnessDistance->setValue(smoothingOptions()->smoothnessDistance()); m_chkDelayDistance->setChecked(smoothingOptions()->useDelayDistance()); m_sliderDelayDistance->setValue(smoothingOptions()->delayDistance()); m_sliderTailAggressiveness->setValue(smoothingOptions()->tailAggressiveness()); m_chkSmoothPressure->setChecked(smoothingOptions()->smoothPressure()); m_chkUseScalableDistance->setChecked(smoothingOptions()->useScalableDistance()); m_cmbSmoothingType->setCurrentIndex((int)smoothingOptions()->smoothingType()); m_chkStabilizeSensors->setChecked(smoothingOptions()->stabilizeSensors()); emit smoothnessQualityChanged(); emit smoothnessFactorChanged(); emit smoothPressureChanged(); emit smoothingTypeChanged(); emit useScalableDistanceChanged(); emit useDelayDistanceChanged(); emit delayDistanceChanged(); emit finishStabilizedCurveChanged(); emit stabilizeSensorsChanged(); KisTool::updateSettingsViews(); } QWidget * KisToolBrush::createOptionWidget() { QWidget *optionsWidget = KisToolFreehand::createOptionWidget(); optionsWidget->setObjectName(toolId() + "option widget"); // See https://bugs.kde.org/show_bug.cgi?id=316896 QWidget *specialSpacer = new QWidget(optionsWidget); specialSpacer->setObjectName("SpecialSpacer"); specialSpacer->setFixedSize(0, 0); optionsWidget->layout()->addWidget(specialSpacer); // Line smoothing configuration m_cmbSmoothingType = new QComboBox(optionsWidget); m_cmbSmoothingType->addItems(QStringList() << i18n("None") << i18n("Basic") << i18n("Weighted") << i18n("Stabilizer")); connect(m_cmbSmoothingType, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSetSmoothingType(int))); addOptionWidgetOption(m_cmbSmoothingType, new QLabel(i18n("Brush Smoothing:"))); m_sliderSmoothnessDistance = new KisDoubleSliderSpinBox(optionsWidget); m_sliderSmoothnessDistance->setRange(3.0, MAXIMUM_SMOOTHNESS_DISTANCE, 1); m_sliderSmoothnessDistance->setEnabled(true); connect(m_sliderSmoothnessDistance, SIGNAL(valueChanged(qreal)), SLOT(slotSetSmoothnessDistance(qreal))); m_sliderSmoothnessDistance->setValue(smoothingOptions()->smoothnessDistance()); addOptionWidgetOption(m_sliderSmoothnessDistance, new QLabel(i18n("Distance:"))); // Finish stabilizer curve m_chkFinishStabilizedCurve = new QCheckBox(optionsWidget); m_chkFinishStabilizedCurve->setMinimumHeight(qMax(m_sliderSmoothnessDistance->sizeHint().height()-3, m_chkFinishStabilizedCurve->sizeHint().height())); connect(m_chkFinishStabilizedCurve, SIGNAL(toggled(bool)), this, SLOT(setFinishStabilizedCurve(bool))); m_chkFinishStabilizedCurve->setChecked(smoothingOptions()->finishStabilizedCurve()); // Delay Distance for Stabilizer QWidget* delayWidget = new QWidget(optionsWidget); QHBoxLayout* delayLayout = new QHBoxLayout(delayWidget); delayLayout->setContentsMargins(0,0,0,0); delayLayout->setSpacing(1); QLabel* delayLabel = new QLabel(i18n("Delay:"), optionsWidget); delayLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); delayLayout->addWidget(delayLabel); m_chkDelayDistance = new QCheckBox(optionsWidget); m_chkDelayDistance->setLayoutDirection(Qt::RightToLeft); delayWidget->setToolTip(i18n("Delay the brush stroke to make the line smoother")); connect(m_chkDelayDistance, SIGNAL(toggled(bool)), this, SLOT(setUseDelayDistance(bool))); delayLayout->addWidget(m_chkDelayDistance); m_sliderDelayDistance = new KisDoubleSliderSpinBox(optionsWidget); m_sliderDelayDistance->setToolTip(i18n("Radius where the brush is blocked")); m_sliderDelayDistance->setRange(0, 500); m_sliderDelayDistance->setSuffix(i18n(" px")); connect(m_sliderDelayDistance, SIGNAL(valueChanged(qreal)), SLOT(setDelayDistance(qreal))); addOptionWidgetOption(m_sliderDelayDistance, delayWidget); addOptionWidgetOption(m_chkFinishStabilizedCurve, new QLabel(i18n("Finish line:"))); m_sliderDelayDistance->setValue(smoothingOptions()->delayDistance()); m_chkDelayDistance->setChecked(smoothingOptions()->useDelayDistance()); // if the state is not flipped, then the previous line doesn't generate any signals setUseDelayDistance(m_chkDelayDistance->isChecked()); // Stabilize sensors m_chkStabilizeSensors = new QCheckBox(optionsWidget); m_chkStabilizeSensors->setMinimumHeight(qMax(m_sliderSmoothnessDistance->sizeHint().height()-3, m_chkStabilizeSensors->sizeHint().height())); connect(m_chkStabilizeSensors, SIGNAL(toggled(bool)), this, SLOT(setStabilizeSensors(bool))); m_chkStabilizeSensors->setChecked(smoothingOptions()->stabilizeSensors()); addOptionWidgetOption(m_chkStabilizeSensors, new QLabel(i18n("Stabilize Sensors:"))); m_sliderTailAggressiveness = new KisDoubleSliderSpinBox(optionsWidget); m_sliderTailAggressiveness->setRange(0.0, 1.0, 2); m_sliderTailAggressiveness->setEnabled(true); connect(m_sliderTailAggressiveness, SIGNAL(valueChanged(qreal)), SLOT(slotSetTailAgressiveness(qreal))); m_sliderTailAggressiveness->setValue(smoothingOptions()->tailAggressiveness()); addOptionWidgetOption(m_sliderTailAggressiveness, new QLabel(i18n("Stroke Ending:"))); m_chkSmoothPressure = new QCheckBox(optionsWidget); m_chkSmoothPressure->setMinimumHeight(qMax(m_sliderSmoothnessDistance->sizeHint().height()-3, m_chkSmoothPressure->sizeHint().height())); m_chkSmoothPressure->setChecked(smoothingOptions()->smoothPressure()); connect(m_chkSmoothPressure, SIGNAL(toggled(bool)), this, SLOT(setSmoothPressure(bool))); addOptionWidgetOption(m_chkSmoothPressure, new QLabel(QString("%1:").arg(i18n("Smooth Pressure")))); m_chkUseScalableDistance = new QCheckBox(optionsWidget); m_chkUseScalableDistance->setChecked(smoothingOptions()->useScalableDistance()); m_chkUseScalableDistance->setMinimumHeight(qMax(m_sliderSmoothnessDistance->sizeHint().height()-3, m_chkUseScalableDistance->sizeHint().height())); m_chkUseScalableDistance->setToolTip(i18nc("@info:tooltip", "Scalable distance takes zoom level " "into account and makes the distance " "be visually constant whatever zoom " "level is chosen")); connect(m_chkUseScalableDistance, SIGNAL(toggled(bool)), this, SLOT(setUseScalableDistance(bool))); addOptionWidgetOption(m_chkUseScalableDistance, new QLabel(QString("%1:").arg(i18n("Scalable Distance")))); // add a line spacer so we know that the next set of options are for different settings QFrame* line = new QFrame(optionsWidget); line->setObjectName(QString::fromUtf8("line")); line->setFrameShape(QFrame::HLine); addOptionWidgetOption(line); // Drawing assistant configuration QWidget* assistantWidget = new QWidget(optionsWidget); QGridLayout* assistantLayout = new QGridLayout(assistantWidget); assistantLayout->setContentsMargins(10,0,0,0); assistantLayout->setSpacing(5); m_chkAssistant = new QCheckBox(optionsWidget); m_chkAssistant->setText(i18n("Snap to Assistants")); - assistantWidget->setToolTip(i18n("You need to add Ruler Assistants before this tool will work.")); + assistantWidget->setToolTip(i18n("You need to add Assistants before this tool will work.")); connect(m_chkAssistant, SIGNAL(toggled(bool)), this, SLOT(setAssistant(bool))); addOptionWidgetOption(assistantWidget, m_chkAssistant); m_sliderMagnetism = new KisSliderSpinBox(optionsWidget); m_sliderMagnetism->setToolTip(i18n("Assistant Magnetism")); m_sliderMagnetism->setRange(0, MAXIMUM_MAGNETISM); m_sliderMagnetism->setValue(m_magnetism * MAXIMUM_MAGNETISM); connect(m_sliderMagnetism, SIGNAL(valueChanged(int)), SLOT(slotSetMagnetism(int))); QAction *toggleaction = KisActionRegistry::instance()->makeQAction("toggle_assistant", this); addAction("toggle_assistant", toggleaction); toggleaction->setShortcut(QKeySequence(Qt::ControlModifier + Qt::ShiftModifier + Qt::Key_L)); connect(toggleaction, SIGNAL(triggered(bool)), m_chkAssistant, SLOT(toggle())); QLabel* magnetismLabel = new QLabel(i18n("Magnetism:")); addOptionWidgetOption(m_sliderMagnetism, magnetismLabel); QLabel* snapSingleLabel = new QLabel(i18n("Snap Single:")); m_chkOnlyOneAssistant = new QCheckBox(optionsWidget); m_chkOnlyOneAssistant->setToolTip(i18nc("@info:tooltip","Make it only snap to a single assistant, prevents snapping mess while using the infinite assistants.")); m_chkOnlyOneAssistant->setCheckState(Qt::Checked);//turn on by default. connect(m_chkOnlyOneAssistant, SIGNAL(toggled(bool)), this, SLOT(setOnlyOneAssistantSnap(bool))); addOptionWidgetOption(m_chkOnlyOneAssistant, snapSingleLabel); // set the assistant snapping options to hidden by default and toggle their visibility based based off snapping checkbox m_sliderMagnetism->setVisible(false); m_chkOnlyOneAssistant->setVisible(false); snapSingleLabel->setVisible(false); magnetismLabel->setVisible(false); connect(m_chkAssistant, SIGNAL(toggled(bool)), m_sliderMagnetism, SLOT(setVisible(bool))); connect(m_chkAssistant, SIGNAL(toggled(bool)), m_chkOnlyOneAssistant, SLOT(setVisible(bool))); connect(m_chkAssistant, SIGNAL(toggled(bool)), snapSingleLabel, SLOT(setVisible(bool))); connect(m_chkAssistant, SIGNAL(toggled(bool)), magnetismLabel, SLOT(setVisible(bool))); KisConfig cfg; slotSetSmoothingType(cfg.lineSmoothingType()); return optionsWidget; } diff --git a/plugins/tools/basictools/kis_tool_colorpicker.cc b/plugins/tools/basictools/kis_tool_colorpicker.cc index 3ab9d688d6..d5d981ffc0 100644 --- a/plugins/tools/basictools/kis_tool_colorpicker.cc +++ b/plugins/tools/basictools/kis_tool_colorpicker.cc @@ -1,406 +1,350 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2002 Patrick Julien * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2018 Emmet & Eoin O'Neill * * 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_tool_colorpicker.h" #include #include #include "kis_cursor.h" #include "KisDocument.h" #include "kis_canvas2.h" #include "KisReferenceImagesLayer.h" #include "KoCanvasBase.h" #include "kis_random_accessor_ng.h" #include "KoResourceServerProvider.h" #include #include "kis_wrapped_rect.h" #include "kis_tool_utils.h" namespace { // GUI ComboBox index constants const int SAMPLE_MERGED = 0; } KisToolColorPicker::KisToolColorPicker(KoCanvasBase *canvas) : KisTool(canvas, KisCursor::pickerCursor()), m_config(new KisToolUtils::ColorPickerConfig) { setObjectName("tool_colorpicker"); m_isActivated = false; m_optionsWidget = 0; m_pickedColor = KoColor(); } KisToolColorPicker::~KisToolColorPicker() { if (m_isActivated) { m_config->save(m_toolActivationSource == KisTool::DefaultActivation); } } void KisToolColorPicker::paint(QPainter &gc, const KoViewConverter &converter) { Q_UNUSED(gc); Q_UNUSED(converter); } void KisToolColorPicker::activate(ToolActivation activation, const QSet &shapes) { m_isActivated = true; m_toolActivationSource = activation; m_config->load(m_toolActivationSource == KisTool::DefaultActivation); updateOptionWidget(); KisTool::activate(activation, shapes); } void KisToolColorPicker::deactivate() { m_config->save(m_toolActivationSource == KisTool::DefaultActivation); m_isActivated = false; KisTool::deactivate(); } void KisToolColorPicker::pickColor(const QPointF &pos) { + // Timer check. if (m_colorPickerDelayTimer.isActive()) { return; } else { m_colorPickerDelayTimer.setSingleShot(true); m_colorPickerDelayTimer.start(100); } QScopedPointer> imageLocker; m_pickedColor.setOpacity(0.0); + // Pick from reference images. if (m_optionsWidget->cmbSources->currentIndex() == SAMPLE_MERGED) { auto *kisCanvas = dynamic_cast(canvas()); KIS_SAFE_ASSERT_RECOVER_RETURN(kisCanvas); KisReferenceImagesLayer *referenceImageLayer = kisCanvas->imageView()->document()->referenceImagesLayer(); if (referenceImageLayer) { QColor color = referenceImageLayer->getPixel(pos); if (color.isValid()) { m_pickedColor.fromQColor(color); } } } if (m_pickedColor.opacityU8() == OPACITY_TRANSPARENT_U8) { KisPaintDeviceSP dev; if (m_optionsWidget->cmbSources->currentIndex() != SAMPLE_MERGED && currentNode() && currentNode()->colorPickSourceDevice()) { dev = currentNode()->colorPickSourceDevice(); } else { imageLocker.reset(new boost::lock_guard(*currentImage())); dev = currentImage()->projection(); } - // Color sampling radius. - if (m_config->radius == 1) { - QPoint realPos = pos.toPoint(); - if (currentImage()->wrapAroundModePermitted()) { - realPos = KisWrappedRect::ptToWrappedPt(realPos, currentImage()->bounds()); - } - - dev->pixel(realPos.x(), realPos.y(), &m_pickedColor); - } - else { - const KoColorSpace *cs = dev->colorSpace(); - int pixelSize = cs->pixelSize(); - - quint8 *dstColor = new quint8[pixelSize]; - QVector pixels; - - KisRandomConstAccessorSP accessor = dev->createRandomConstAccessorNG(0, 0); - - for (int y = -m_config->radius; y <= m_config->radius; y++) { - for (int x = -m_config->radius; x <= m_config->radius; x++) { - if (((x * x) + (y * y)) < m_config->radius * m_config->radius) { - - QPoint realPos(pos.x() + x, pos.y() + y); - - if (currentImage()->wrapAroundModePermitted()) { - realPos = KisWrappedRect::ptToWrappedPt(realPos, currentImage()->bounds()); - } - - accessor->moveTo(realPos.x(), realPos.y()); - pixels << accessor->oldRawData(); - } - } - } - - const quint8 **cpixels = const_cast(pixels.constData()); - cs->mixColorsOp()->mixColors(cpixels, pixels.size(), dstColor); - - m_pickedColor = KoColor(dstColor, cs); - - delete[] dstColor; - } - - // Color blending. - if(m_config->blend < 100){ - //Scale from 0..100% to 0..255 range for mixOp weights. - quint8 blendScaled = static_cast(m_config->blend * 2.55f); - - KoColor previousColor = canvas()->resourceManager()->foregroundColor(); - - const quint8 *colors[2]; - colors[0] = previousColor.data(); - colors[1] = m_pickedColor.data(); - qint16 weights[2]; - weights[0] = 255 - blendScaled; - weights[1] = blendScaled; - - const KoMixColorsOp *mixOp = dev->colorSpace()->mixColorsOp(); - mixOp->mixColors(colors, weights, 2, m_pickedColor.data()); - } + KoColor previousColor = canvas()->resourceManager()->foregroundColor(); - m_pickedColor.convertTo(dev->compositionSourceColorSpace()); + KisToolUtils::pickColor(m_pickedColor, dev, pos.toPoint(), &previousColor, m_config->radius, m_config->blend); /*!*/ if (m_config->updateColor && m_pickedColor.opacityU8() != OPACITY_TRANSPARENT_U8) { KoColor publicColor = m_pickedColor; publicColor.setOpacity(OPACITY_OPAQUE_U8); if (m_config->toForegroundColor) { canvas()->resourceManager()->setResource(KoCanvasResourceManager::ForegroundColor, publicColor); } else { canvas()->resourceManager()->setResource(KoCanvasResourceManager::BackgroundColor, publicColor); } } } } void KisToolColorPicker::beginPrimaryAction(KoPointerEvent *event) { bool sampleMerged = m_optionsWidget->cmbSources->currentIndex() == SAMPLE_MERGED; if (!sampleMerged) { if (!currentNode()) { QMessageBox::information(0, i18nc("@title:window", "Krita"), i18n("Cannot pick a color as no layer is active.")); event->ignore(); return; } if (!currentNode()->visible()) { QMessageBox::information(0, i18nc("@title:window", "Krita"), i18n("Cannot pick a color as the active layer is not visible.")); event->ignore(); return; } } QPoint pos = convertToImagePixelCoordFloored(event); // Color picking has to start in the visible part of the layer if (!currentImage()->bounds().contains(pos) && !currentImage()->wrapAroundModePermitted()) { event->ignore(); return; } setMode(KisTool::PAINT_MODE); pickColor(pos); displayPickedColor(); } void KisToolColorPicker::continuePrimaryAction(KoPointerEvent *event) { CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); QPoint pos = convertToImagePixelCoordFloored(event); pickColor(pos); displayPickedColor(); } void KisToolColorPicker::endPrimaryAction(KoPointerEvent *event) { Q_UNUSED(event); CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); if (m_config->addPalette) { KoColorSetEntry ent; ent.setColor(m_pickedColor); // We don't ask for a name, too intrusive here KoColorSet *palette = m_palettes.at(m_optionsWidget->cmbPalette->currentIndex()); palette->add(ent); if (!palette->save()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Cannot write to palette file %1. Maybe it is read-only.", palette->filename())); } } } struct PickedChannel { QString name; QString valueText; }; void KisToolColorPicker::displayPickedColor() { if (m_pickedColor.data() && m_optionsWidget) { QList channels = m_pickedColor.colorSpace()->channels(); m_optionsWidget->listViewChannels->clear(); QVector pickedChannels; for (int i = 0; i < channels.count(); ++i) { pickedChannels.append(PickedChannel()); } for (int i = 0; i < channels.count(); ++i) { PickedChannel pc; pc.name = channels[i]->name(); if (m_config->normaliseValues) { pc.valueText = m_pickedColor.colorSpace()->normalisedChannelValueText(m_pickedColor.data(), i); } else { pc.valueText = m_pickedColor.colorSpace()->channelValueText(m_pickedColor.data(), i); } pickedChannels[channels[i]->displayPosition()] = pc; } Q_FOREACH (const PickedChannel &pc, pickedChannels) { QTreeWidgetItem *item = new QTreeWidgetItem(m_optionsWidget->listViewChannels); item->setText(0, pc.name); item->setText(1, pc.valueText); } } } QWidget* KisToolColorPicker::createOptionWidget() { m_optionsWidget = new ColorPickerOptionsWidget(0); m_optionsWidget->setObjectName(toolId() + " option widget"); m_optionsWidget->listViewChannels->setSortingEnabled(false); // See https://bugs.kde.org/show_bug.cgi?id=316896 QWidget *specialSpacer = new QWidget(m_optionsWidget); specialSpacer->setObjectName("SpecialSpacer"); specialSpacer->setFixedSize(0, 0); m_optionsWidget->layout()->addWidget(specialSpacer); // Initialize blend KisSliderSpinBox m_optionsWidget->blend->setRange(0,100); m_optionsWidget->blend->setSuffix("%"); updateOptionWidget(); connect(m_optionsWidget->cbUpdateCurrentColor, SIGNAL(toggled(bool)), SLOT(slotSetUpdateColor(bool))); connect(m_optionsWidget->cbNormaliseValues, SIGNAL(toggled(bool)), SLOT(slotSetNormaliseValues(bool))); connect(m_optionsWidget->cbPalette, SIGNAL(toggled(bool)), SLOT(slotSetAddPalette(bool))); connect(m_optionsWidget->radius, SIGNAL(valueChanged(int)), SLOT(slotChangeRadius(int))); connect(m_optionsWidget->blend, SIGNAL(valueChanged(int)), SLOT(slotChangeBlend(int))); connect(m_optionsWidget->cmbSources, SIGNAL(currentIndexChanged(int)), SLOT(slotSetColorSource(int))); KoResourceServer *srv = KoResourceServerProvider::instance()->paletteServer(); if (!srv) { return m_optionsWidget; } QList palettes = srv->resources(); Q_FOREACH (KoColorSet *palette, palettes) { if (palette) { m_optionsWidget->cmbPalette->addSqueezedItem(palette->name()); m_palettes.append(palette); } } return m_optionsWidget; } void KisToolColorPicker::updateOptionWidget() { if (!m_optionsWidget) return; m_optionsWidget->cbNormaliseValues->setChecked(m_config->normaliseValues); m_optionsWidget->cbUpdateCurrentColor->setChecked(m_config->updateColor); m_optionsWidget->cmbSources->setCurrentIndex(SAMPLE_MERGED + !m_config->sampleMerged); m_optionsWidget->cbPalette->setChecked(m_config->addPalette); m_optionsWidget->radius->setValue(m_config->radius); m_optionsWidget->blend->setValue(m_config->blend); } void KisToolColorPicker::setToForeground(bool newValue) { m_config->toForegroundColor = newValue; emit toForegroundChanged(); } bool KisToolColorPicker::toForeground() const { return m_config->toForegroundColor; } void KisToolColorPicker::slotSetUpdateColor(bool state) { m_config->updateColor = state; } void KisToolColorPicker::slotSetNormaliseValues(bool state) { m_config->normaliseValues = state; displayPickedColor(); } void KisToolColorPicker::slotSetAddPalette(bool state) { m_config->addPalette = state; } void KisToolColorPicker::slotChangeRadius(int value) { m_config->radius = value; } void KisToolColorPicker::slotChangeBlend(int value) { m_config->blend = value; } void KisToolColorPicker::slotSetColorSource(int value) { m_config->sampleMerged = value == SAMPLE_MERGED; } void KisToolColorPicker::slotAddPalette(KoResource *resource) { KoColorSet *palette = dynamic_cast(resource); if (palette) { m_optionsWidget->cmbPalette->addSqueezedItem(palette->name()); m_palettes.append(palette); } } diff --git a/plugins/tools/basictools/kis_tool_fill.cc b/plugins/tools/basictools/kis_tool_fill.cc index 915598cc09..4f5a155b90 100644 --- a/plugins/tools/basictools/kis_tool_fill.cc +++ b/plugins/tools/basictools/kis_tool_fill.cc @@ -1,314 +1,314 @@ /* * kis_tool_fill.cc - part of Krayon * * Copyright (c) 2000 John Califf * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2004 Bart Coppens * * 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_tool_fill.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 "kis_resources_snapshot.h" #include #include KisToolFill::KisToolFill(KoCanvasBase * canvas) : KisToolPaint(canvas, KisCursor::load("tool_fill_cursor.png", 6, 6)) { setObjectName("tool_fill"); m_feather = 0; m_sizemod = 0; m_threshold = 80; m_usePattern = false; m_unmerged = false; m_fillOnlySelection = false; } KisToolFill::~KisToolFill() { } void KisToolFill::resetCursorStyle() { KisToolPaint::resetCursorStyle(); overrideCursorIfNotEditable(); } void KisToolFill::activate(ToolActivation toolActivation, const QSet &shapes) { KisToolPaint::activate(toolActivation, shapes); m_configGroup = KSharedConfig::openConfig()->group(toolId()); } void KisToolFill::beginPrimaryAction(KoPointerEvent *event) { // cannot use fill tool on non-painting layers. // this logic triggers with multiple layer types like vector layer, clone layer, file layer, group layer if ( currentNode().isNull() || currentNode()->inherits("KisShapeLayer") || nodePaintAbility()!=NodePaintAbility::PAINT ) { KisCanvas2 * kiscanvas = static_cast(canvas()); kiscanvas->viewManager()-> showFloatingMessage( i18n("You cannot use this tool with the selected layer type"), QIcon(), 2000, KisFloatingMessage::Medium, Qt::AlignCenter); event->ignore(); return; } if (!nodeEditable()) { event->ignore(); return; } setMode(KisTool::PAINT_MODE); m_startPos = convertToImagePixelCoordFloored(event); } void KisToolFill::endPrimaryAction(KoPointerEvent *event) { Q_UNUSED(event); CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); setMode(KisTool::HOVER_MODE); if (!currentNode() || (!image()->wrapAroundModePermitted() && !image()->bounds().contains(m_startPos))) { return; } // TODO: remove this block after recording refactoring if (image()) { KisNodeSP projectionNode; if(m_unmerged) { projectionNode = currentNode(); } else { projectionNode = image()->root(); } KisRecordedFillPaintAction paintAction(KisNodeQueryPath::absolutePath(currentNode()), m_startPos, KisNodeQueryPath::absolutePath(projectionNode)); setupPaintAction(&paintAction); paintAction.setPattern(currentPattern()); if(m_usePattern) { paintAction.setFillStyle(KisPainter::FillStylePattern); } image()->actionRecorder()->addAction(paintAction); } bool useFastMode = m_useFastMode->isChecked(); KisProcessingApplicator applicator(currentImage(), currentNode(), KisProcessingApplicator::SUPPORTS_WRAPAROUND_MODE, KisImageSignalVector() << ModifiedSignal, kundo2_i18n("Flood Fill")); KisResourcesSnapshotSP resources = new KisResourcesSnapshot(image(), currentNode(), this->canvas()->resourceManager()); KisProcessingVisitorSP visitor = new FillProcessingVisitor(m_startPos, resources->activeSelection(), resources, useFastMode, m_usePattern, m_fillOnlySelection, m_feather, m_sizemod, m_threshold, m_unmerged, false); applicator.applyVisitor(visitor, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); applicator.end(); } QWidget* KisToolFill::createOptionWidget() { QWidget *widget = KisToolPaint::createOptionWidget(); widget->setObjectName(toolId() + " option widget"); QLabel *lbl_fastMode = new QLabel(i18n("Fast mode: "), widget); m_useFastMode = new QCheckBox(QString(), widget); m_useFastMode->setToolTip( i18n("Fills area faster, but does not take composition " "mode into account. Selections and other extended " "features will also be disabled.")); QLabel *lbl_threshold = new QLabel(i18n("Threshold: "), widget); m_slThreshold = new KisSliderSpinBox(widget); m_slThreshold->setObjectName("int_widget"); m_slThreshold->setRange(1, 100); m_slThreshold->setPageStep(3); QLabel *lbl_sizemod = new QLabel(i18n("Grow selection: "), widget); m_sizemodWidget = new KisSliderSpinBox(widget); m_sizemodWidget->setObjectName("sizemod"); m_sizemodWidget->setRange(-40, 40); m_sizemodWidget->setSingleStep(1); m_sizemodWidget->setSuffix(i18n(" px")); QLabel *lbl_feather = new QLabel(i18n("Feathering radius: "), widget); m_featherWidget = new KisSliderSpinBox(widget); m_featherWidget->setObjectName("feather"); m_featherWidget->setRange(0, 40); - m_featherWidget->setSingleStep(1); + m_featherWidget->setSingleStep(1); m_featherWidget->setSuffix(i18n(" px")); QLabel *lbl_usePattern = new QLabel(i18n("Use pattern:"), widget); m_checkUsePattern = new QCheckBox(QString(), widget); - m_checkUsePattern->setToolTip(i18n("When checked do not use the foreground color, but the gradient selected to fill with")); + m_checkUsePattern->setToolTip(i18n("When checked do not use the foreground color, but the pattern selected to fill with")); QLabel *lbl_sampleMerged = new QLabel(i18n("Limit to current layer:"), widget); m_checkSampleMerged = new QCheckBox(QString(), widget); QLabel *lbl_fillSelection = new QLabel(i18n("Fill entire selection:"), widget); m_checkFillSelection = new QCheckBox(QString(), widget); m_checkFillSelection->setToolTip(i18n("When checked do not look at the current layer colors, but just fill all of the selected area")); connect (m_useFastMode , SIGNAL(toggled(bool)) , this, SLOT(slotSetUseFastMode(bool))); connect (m_slThreshold , SIGNAL(valueChanged(int)), this, SLOT(slotSetThreshold(int))); connect (m_sizemodWidget , SIGNAL(valueChanged(int)), this, SLOT(slotSetSizemod(int))); connect (m_featherWidget , SIGNAL(valueChanged(int)), this, SLOT(slotSetFeather(int))); connect (m_checkUsePattern , SIGNAL(toggled(bool)) , this, SLOT(slotSetUsePattern(bool))); connect (m_checkSampleMerged , SIGNAL(toggled(bool)) , this, SLOT(slotSetSampleMerged(bool))); connect (m_checkFillSelection, SIGNAL(toggled(bool)) , this, SLOT(slotSetFillSelection(bool))); addOptionWidgetOption(m_useFastMode, lbl_fastMode); addOptionWidgetOption(m_slThreshold, lbl_threshold); addOptionWidgetOption(m_sizemodWidget , lbl_sizemod); addOptionWidgetOption(m_featherWidget , lbl_feather); addOptionWidgetOption(m_checkFillSelection, lbl_fillSelection); addOptionWidgetOption(m_checkSampleMerged, lbl_sampleMerged); addOptionWidgetOption(m_checkUsePattern, lbl_usePattern); updateGUI(); widget->setFixedHeight(widget->sizeHint().height()); // load configuration options m_useFastMode->setChecked(m_configGroup.readEntry("useFastMode", false)); m_slThreshold->setValue(m_configGroup.readEntry("thresholdAmount", 80)); m_sizemodWidget->setValue(m_configGroup.readEntry("growSelection", 0)); m_featherWidget->setValue(m_configGroup.readEntry("featherAmount", 0)); m_checkUsePattern->setChecked(m_configGroup.readEntry("usePattern", false)); m_checkSampleMerged->setChecked(m_configGroup.readEntry("sampleMerged", false)); m_checkFillSelection->setChecked(m_configGroup.readEntry("fillSelection", false)); return widget; } void KisToolFill::updateGUI() { bool useAdvancedMode = !m_useFastMode->isChecked(); bool selectionOnly = m_checkFillSelection->isChecked(); m_useFastMode->setEnabled(!selectionOnly); m_slThreshold->setEnabled(!selectionOnly); m_sizemodWidget->setEnabled(!selectionOnly && useAdvancedMode); m_featherWidget->setEnabled(!selectionOnly && useAdvancedMode); m_checkSampleMerged->setEnabled(!selectionOnly && useAdvancedMode); m_checkUsePattern->setEnabled(useAdvancedMode); } void KisToolFill::slotSetUseFastMode(bool value) { updateGUI(); m_configGroup.writeEntry("useFastMode", value); } void KisToolFill::slotSetThreshold(int threshold) { m_threshold = threshold; m_configGroup.writeEntry("thresholdAmount", threshold); } void KisToolFill::slotSetUsePattern(bool state) { m_usePattern = state; m_configGroup.writeEntry("usePattern", state); } void KisToolFill::slotSetSampleMerged(bool state) { m_unmerged = state; m_configGroup.writeEntry("sampleMerged", state); } void KisToolFill::slotSetFillSelection(bool state) { m_fillOnlySelection = state; m_configGroup.writeEntry("fillSelection", state); updateGUI(); } void KisToolFill::slotSetSizemod(int sizemod) { m_sizemod = sizemod; m_configGroup.writeEntry("growSelection", sizemod); } void KisToolFill::slotSetFeather(int feather) { m_feather = feather; m_configGroup.writeEntry("featherAmount", feather); } diff --git a/plugins/tools/basictools/kis_tool_measure.cc b/plugins/tools/basictools/kis_tool_measure.cc index 72e8d26d89..2aa105c1b6 100644 --- a/plugins/tools/basictools/kis_tool_measure.cc +++ b/plugins/tools/basictools/kis_tool_measure.cc @@ -1,221 +1,220 @@ /* * * Copyright (c) 2007 Sven Langkamp * * 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_tool_measure.h" #include #include #include #include #include #include #include #include #include "kis_image.h" #include "kis_cursor.h" #include "KoPointerEvent.h" #include "KoCanvasBase.h" #include #define INNER_RADIUS 50 KisToolMeasureOptionsWidget::KisToolMeasureOptionsWidget(QWidget* parent, double resolution) : QWidget(parent), m_resolution(resolution), m_unit(KoUnit::Pixel) { m_distance = 0.0; QGridLayout* optionLayout = new QGridLayout(this); Q_CHECK_PTR(optionLayout); optionLayout->setMargin(0); optionLayout->addWidget(new QLabel(i18n("Distance:"), this), 0, 0); optionLayout->addWidget(new QLabel(i18n("Angle:"), this), 1, 0); m_distanceLabel = new QLabel(this); m_distanceLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); optionLayout->addWidget(m_distanceLabel, 0, 1); m_angleLabel = new QLabel(this); m_angleLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); optionLayout->addWidget(m_angleLabel, 1, 1); KComboBox* unitBox = new KComboBox(this); unitBox->addItems(KoUnit::listOfUnitNameForUi(KoUnit::ListAll)); connect(unitBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUnitChanged(int))); unitBox->setCurrentIndex(m_unit.indexInListForUi(KoUnit::ListAll)); optionLayout->addWidget(unitBox, 0, 2); - optionLayout->addWidget(new QLabel(i18n("Degree:"), this), 1, 2); optionLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Fixed, QSizePolicy::Expanding), 2, 0, 1, 2); } void KisToolMeasureOptionsWidget::slotSetDistance(double distance) { m_distance = distance / m_resolution; updateDistance(); } void KisToolMeasureOptionsWidget::slotSetAngle(double angle) { - m_angleLabel->setText(QString("%1").arg(angle, 5, 'f', 1)); + m_angleLabel->setText(QString(i18nc("angle value in degrees", "%1°")).arg(angle, 5, 'f', 1)); } void KisToolMeasureOptionsWidget::slotUnitChanged(int index) { m_unit = KoUnit::fromListForUi(index, KoUnit::ListAll, m_resolution); updateDistance(); } void KisToolMeasureOptionsWidget::updateDistance() { m_distanceLabel->setText(QString("%1").arg(m_unit.toUserValue(m_distance), 5, 'f', 1)); } KisToolMeasure::KisToolMeasure(KoCanvasBase * canvas) : KisTool(canvas, KisCursor::crossCursor()) { m_startPos = QPointF(0, 0); m_endPos = QPointF(0, 0); } KisToolMeasure::~KisToolMeasure() { } void KisToolMeasure::paint(QPainter& gc, const KoViewConverter &converter) { qreal sx, sy; converter.zoom(&sx, &sy); gc.scale(sx / currentImage()->xRes(), sy / currentImage()->yRes()); QPen old = gc.pen(); QPen pen(Qt::SolidLine); gc.setPen(pen); gc.drawLine(m_startPos, m_endPos); if (deltaX() >= 0) gc.drawLine(QPointF(m_startPos.x(), m_startPos.y()), QPointF(m_startPos.x() + INNER_RADIUS, m_startPos.y())); else gc.drawLine(QPointF(m_startPos.x(), m_startPos.y()), QPointF(m_startPos.x() - INNER_RADIUS, m_startPos.y())); if (distance() >= INNER_RADIUS) { QRectF rectangle(m_startPos.x() - INNER_RADIUS, m_startPos.y() - INNER_RADIUS, 2*INNER_RADIUS, 2*INNER_RADIUS); int startAngle = (deltaX() >= 0) ? 0 : 180 * 16; int spanAngle; if ((deltaY() >= 0 && deltaX() >= 0) || (deltaY() < 0 && deltaX() < 0)) spanAngle = static_cast(angle() * 16); else spanAngle = static_cast(-angle() * 16); gc.drawArc(rectangle, startAngle, spanAngle); } gc.setPen(old); } void KisToolMeasure::beginPrimaryAction(KoPointerEvent *event) { setMode(KisTool::PAINT_MODE); // Erase old temporary lines canvas()->updateCanvas(convertToPt(boundingRect())); m_startPos = convertToPixelCoord(event); m_endPos = m_startPos; emit sigDistanceChanged(0.0); emit sigAngleChanged(0.0); } void KisToolMeasure::continuePrimaryAction(KoPointerEvent *event) { CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); // Erase old temporary lines canvas()->updateCanvas(convertToPt(boundingRect())); QPointF pos = convertToPixelCoord(event); if (event->modifiers() == Qt::AltModifier) { QPointF trans = pos - m_endPos; m_startPos += trans; m_endPos += trans; } else { m_endPos = pos; } canvas()->updateCanvas(convertToPt(boundingRect())); emit sigDistanceChanged(distance()); emit sigAngleChanged(angle()); } void KisToolMeasure::endPrimaryAction(KoPointerEvent *event) { CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); Q_UNUSED(event); setMode(KisTool::HOVER_MODE); } QWidget* KisToolMeasure::createOptionWidget() { if (!currentImage()) return 0; m_optionsWidget = new KisToolMeasureOptionsWidget(0, currentImage()->xRes()); // See https://bugs.kde.org/show_bug.cgi?id=316896 QWidget *specialSpacer = new QWidget(m_optionsWidget); specialSpacer->setObjectName("SpecialSpacer"); specialSpacer->setFixedSize(0, 0); m_optionsWidget->layout()->addWidget(specialSpacer); m_optionsWidget->setObjectName(toolId() + " option widget"); connect(this, SIGNAL(sigDistanceChanged(double)), m_optionsWidget, SLOT(slotSetDistance(double))); connect(this, SIGNAL(sigAngleChanged(double)), m_optionsWidget, SLOT(slotSetAngle(double))); m_optionsWidget->setFixedHeight(m_optionsWidget->sizeHint().height()); return m_optionsWidget; } double KisToolMeasure::angle() { return atan(qAbs(deltaY()) / qAbs(deltaX())) / (2*M_PI)*360; } double KisToolMeasure::distance() { return sqrt(deltaX()*deltaX() + deltaY()*deltaY()); } QRectF KisToolMeasure::boundingRect() { QRectF bound; bound.setTopLeft(m_startPos); bound.setBottomRight(m_endPos); bound = bound.united(QRectF(m_startPos.x() - INNER_RADIUS, m_startPos.y() - INNER_RADIUS, 2 * INNER_RADIUS, 2 * INNER_RADIUS)); return bound.normalized(); } diff --git a/plugins/tools/defaulttool/CMakeLists.txt b/plugins/tools/defaulttool/CMakeLists.txt index 431d2f4d9e..d7be403f04 100644 --- a/plugins/tools/defaulttool/CMakeLists.txt +++ b/plugins/tools/defaulttool/CMakeLists.txt @@ -1,40 +1,41 @@ project( defaulttools ) set ( defaulttools_SRCS Plugin.cpp defaulttool/DefaultTool.cpp defaulttool/DefaultToolFactory.cpp defaulttool/DefaultToolTabbedWidget.cpp defaulttool/DefaultToolGeometryWidget.cpp defaulttool/ShapeMoveStrategy.cpp defaulttool/ShapeResizeStrategy.cpp defaulttool/ShapeRotateStrategy.cpp defaulttool/ShapeShearStrategy.cpp defaulttool/ShapeGradientEditStrategy.cpp defaulttool/SelectionDecorator.cpp defaulttool/KoShapeGradientHandles.cpp connectionTool/ConnectionTool.cpp connectionTool/ConnectionToolFactory.cpp connectionTool/AddConnectionPointCommand.cpp connectionTool/RemoveConnectionPointCommand.cpp connectionTool/ChangeConnectionPointCommand.cpp connectionTool/MoveConnectionPointStrategy.cpp connectionTool/ConnectionPointWidget.cpp referenceimagestool/ToolReferenceImages.cpp referenceimagestool/ToolReferenceImagesWidget.cpp + referenceimagestool/KisReferenceImageCollection.cpp ) ki18n_wrap_ui(defaulttools_SRCS defaulttool/DefaultToolGeometryWidget.ui connectionTool/ConnectionPointWidget.ui referenceimagestool/WdgToolOptions.ui ) qt5_add_resources(defaulttools_SRCS defaulttools.qrc) add_library(krita_flaketools MODULE ${defaulttools_SRCS}) target_link_libraries(krita_flaketools kritaflake kritawidgets kritaui) install(TARGETS krita_flaketools DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) diff --git a/plugins/tools/defaulttool/referenceimagestool/KisReferenceImageCollection.cpp b/plugins/tools/defaulttool/referenceimagestool/KisReferenceImageCollection.cpp new file mode 100644 index 0000000000..e8526477f1 --- /dev/null +++ b/plugins/tools/defaulttool/referenceimagestool/KisReferenceImageCollection.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2018 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 "KisReferenceImageCollection.h" + +#include + +#include +#include +#include + +const QString METADATA_FILE = "reference_images.xml"; + +KisReferenceImageCollection::KisReferenceImageCollection(const QVector &references) + : references(references) +{} + +const QVector &KisReferenceImageCollection::referenceImages() const +{ + return references; +} + +bool KisReferenceImageCollection::save(QIODevice *io) +{ + QScopedPointer store(KoStore::createStore(io, KoStore::Write, "application/x-krita-reference-images]", KoStore::Zip)); + if (store.isNull()) return false; + + QDomDocument doc; + QDomElement root = doc.createElement("referenceimages"); + doc.insertBefore(root, QDomNode()); + + int nextId = 0; + Q_FOREACH(KisReferenceImage *reference, references) { + reference->saveXml(doc, root, nextId++); + + if (reference->embed()) { + bool ok = reference->saveImage(store.data()); + if (!ok) return false; + } + } + + if (!store->open(METADATA_FILE)) { + return false; + } + + KoStoreDevice xmlDev(store.data()); + xmlDev.write(doc.toByteArray()); + xmlDev.close(); + store->close(); + + return true; +} + +bool KisReferenceImageCollection::load(QIODevice *io) +{ + QScopedPointer store(KoStore::createStore(io, KoStore::Read, "application/x-krita-reference-images", KoStore::Zip)); + if (!store || store->bad()) { + return false; + } + + if (!store->hasFile(METADATA_FILE) || !store->open(METADATA_FILE)) { + return false; + } + + QByteArray xml = store->device()->readAll(); + store->close(); + + QDomDocument doc; + doc.setContent(xml); + QDomElement root = doc.documentElement(); + + QDomElement element = root.firstChildElement("referenceimage"); + while (!element.isNull()) { + KisReferenceImage *reference = KisReferenceImage::fromXml(element); + reference->loadImage(store.data()); + references.append(reference); + + element = element.nextSiblingElement("referenceimage"); + } + + return true; +} diff --git a/libs/ui/dialogs/kis_about_application.h b/plugins/tools/defaulttool/referenceimagestool/KisReferenceImageCollection.h similarity index 57% copy from libs/ui/dialogs/kis_about_application.h copy to plugins/tools/defaulttool/referenceimagestool/KisReferenceImageCollection.h index 23a94d2dcb..45b13ab0f1 100644 --- a/libs/ui/dialogs/kis_about_application.h +++ b/plugins/tools/defaulttool/referenceimagestool/KisReferenceImageCollection.h @@ -1,35 +1,42 @@ /* - * Copyright (c) 2014 Boudewijn Rempt + * Copyright (c) 2018 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_ABOUT_APPLICATION_H -#define KIS_ABOUT_APPLICATION_H -#include +#ifndef KISREFERENCEIMAGECOLLECTION_H +#define KISREFERENCEIMAGECOLLECTION_H -class KisAboutApplication : public QDialog +#include + +class QIODevice; +class KisReferenceImage; + +class KisReferenceImageCollection { - Q_OBJECT public: - explicit KisAboutApplication(QWidget *parent = 0); + explicit KisReferenceImageCollection() = default; + explicit KisReferenceImageCollection(const QVector &references); -Q_SIGNALS: + const QVector &referenceImages() const; -public Q_SLOTS: + bool save(QIODevice *io); + bool load(QIODevice *io); +private: + QVector references; }; -#endif // KIS_ABOUT_APPLICATION_H +#endif diff --git a/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.cpp b/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.cpp index 2cd39ba9c4..a6627aa181 100644 --- a/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.cpp +++ b/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.cpp @@ -1,164 +1,229 @@ /* * Copyright (c) 2017 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ToolReferenceImages.h" #include +#include #include #include +#include +#include #include #include #include #include #include #include #include #include #include #include "ToolReferenceImagesWidget.h" +#include "KisReferenceImageCollection.h" ToolReferenceImages::ToolReferenceImages(KoCanvasBase * canvas) : DefaultTool(canvas) { setObjectName("ToolReferenceImages"); } ToolReferenceImages::~ToolReferenceImages() { } void ToolReferenceImages::activate(ToolActivation toolActivation, const QSet &shapes) { // Add code here to initialize your tool when it got activated DefaultTool::activate(toolActivation, shapes); KisReferenceImagesLayer *layer = getOrCreteReferenceImagesLayer(); connect(layer, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged())); } void ToolReferenceImages::deactivate() { DefaultTool::deactivate(); } void ToolReferenceImages::addReferenceImage() { auto kisCanvas = dynamic_cast(canvas()); KIS_ASSERT_RECOVER_RETURN(kisCanvas) KoFileDialog dialog(kisCanvas->viewManager()->mainWindow(), KoFileDialog::OpenFile, "OpenReferenceImage"); dialog.setCaption(i18n("Select a Reference Image")); QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation); if (!locations.isEmpty()) { dialog.setDefaultDir(locations.first()); } QString filename = dialog.filename(); if (filename.isEmpty()) return; if (!QFileInfo(filename).exists()) return; auto *reference = KisReferenceImage::fromFile(filename, *kisCanvas->coordinatesConverter()); KisReferenceImagesLayer *layer = getOrCreteReferenceImagesLayer(); kisCanvas->imageView()->document()->addCommand(layer->addReferenceImage(reference)); } void ToolReferenceImages::removeAllReferenceImages() { KisReferenceImagesLayer *layer = getOrCreteReferenceImagesLayer(); canvas()->addCommand(canvas()->shapeController()->removeShapes(layer->shapes())); } void ToolReferenceImages::loadReferenceImages() { + auto kisCanvas = dynamic_cast(canvas()); + KIS_ASSERT_RECOVER_RETURN(kisCanvas) + + KoFileDialog dialog(kisCanvas->viewManager()->mainWindow(), KoFileDialog::OpenFile, "OpenReferenceImageCollection"); + dialog.setMimeTypeFilters(QStringList() << "application/x-krita-reference-images"); + dialog.setCaption(i18n("Load Reference Images")); + + QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation); + if (!locations.isEmpty()) { + dialog.setDefaultDir(locations.first()); + } + + QString filename = dialog.filename(); + if (filename.isEmpty()) return; + if (!QFileInfo(filename).exists()) return; + + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + QMessageBox::critical(nullptr, i18nc("@title:window", "Krita"), i18n("Could not open '%1'.", filename)); + return; + } + + KisReferenceImageCollection collection; + if (collection.load(&file)) { + KisReferenceImagesLayer *layer = referenceImagesLayer(); + + Q_FOREACH(KisReferenceImage *reference, collection.referenceImages()) { + layer->addShape(reference); + } + } else { + QMessageBox::critical(nullptr, i18nc("@title:window", "Krita"), i18n("Could not load reference images from '%1'.", filename)); + } + file.close(); } void ToolReferenceImages::saveReferenceImages() { + auto kisCanvas = dynamic_cast(canvas()); + KIS_ASSERT_RECOVER_RETURN(kisCanvas) + + KoFileDialog dialog(kisCanvas->viewManager()->mainWindow(), KoFileDialog::SaveFile, "SaveReferenceImageCollection"); + dialog.setMimeTypeFilters(QStringList() << "application/x-krita-reference-images"); + dialog.setCaption(i18n("Save Reference Images")); + + QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation); + if (!locations.isEmpty()) { + dialog.setDefaultDir(locations.first()); + } + + QString filename = dialog.filename(); + if (filename.isEmpty()) return; + + QFile file(filename); + if (!file.open(QIODevice::WriteOnly)) { + QMessageBox::critical(nullptr, i18nc("@title:window", "Krita"), i18n("Could not open '%1' for saving.", filename)); + return; + } + + KisReferenceImageCollection collection(referenceImagesLayer()->referenceImages()); + bool ok = collection.save(&file); + file.close(); + + if (!ok) { + QMessageBox::critical(nullptr, i18nc("@title:window", "Krita"), i18n("Failed to save reference images.", filename)); + } } void ToolReferenceImages::slotSelectionChanged() { KisReferenceImagesLayer *layer = getOrCreteReferenceImagesLayer(); m_optionsWidget->selectionChanged(layer->shapeManager()->selection()); updateActions(); } QList> ToolReferenceImages::createOptionWidgets() { // Instead of inheriting DefaultTool's multi-tab implementation, inherit straight from KoToolBase return KoToolBase::createOptionWidgets(); } QWidget *ToolReferenceImages::createOptionWidget() { if (!m_optionsWidget) { m_optionsWidget = new ToolReferenceImagesWidget(this); // See https://bugs.kde.org/show_bug.cgi?id=316896 QWidget *specialSpacer = new QWidget(m_optionsWidget); specialSpacer->setObjectName("SpecialSpacer"); specialSpacer->setFixedSize(0, 0); m_optionsWidget->layout()->addWidget(specialSpacer); } return m_optionsWidget; } bool ToolReferenceImages::isValidForCurrentLayer() const { return true; } KoShapeManager *ToolReferenceImages::shapeManager() const { auto layer = referenceImagesLayer(); return layer ? referenceImagesLayer()->shapeManager() : nullptr; } KisReferenceImagesLayer *ToolReferenceImages::referenceImagesLayer() const { auto kisCanvas = dynamic_cast(canvas()); KisDocument *document = kisCanvas->imageView()->document(); return document->referenceImagesLayer(); } KisReferenceImagesLayer *ToolReferenceImages::getOrCreteReferenceImagesLayer() { auto kisCanvas = dynamic_cast(canvas()); KisDocument *document = kisCanvas->imageView()->document(); return document->createReferenceImagesLayer().data(); } KoSelection *ToolReferenceImages::koSelection() const { auto manager = shapeManager(); return manager ? manager->selection() : nullptr; } void ToolReferenceImages::updateDistinctiveActions(const QList &) { action("object_group")->setEnabled(false); action("object_unite")->setEnabled(false); action("object_intersect")->setEnabled(false); action("object_subtract")->setEnabled(false); action("object_split")->setEnabled(false); action("object_ungroup")->setEnabled(false); } diff --git a/plugins/tools/tool_lazybrush/kis_tool_lazy_brush_options_widget.cpp b/plugins/tools/tool_lazybrush/kis_tool_lazy_brush_options_widget.cpp index 3a8cc96faf..9321f28d58 100644 --- a/plugins/tools/tool_lazybrush/kis_tool_lazy_brush_options_widget.cpp +++ b/plugins/tools/tool_lazybrush/kis_tool_lazy_brush_options_widget.cpp @@ -1,377 +1,377 @@ /* * 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 "kis_tool_lazy_brush_options_widget.h" #include "ui_kis_tool_lazy_brush_options_widget.h" #include #include "KisPaletteModel.h" #include "kis_config.h" #include #include "kis_canvas_resource_provider.h" #include "kis_signal_auto_connection.h" #include "lazybrush/kis_colorize_mask.h" #include "kis_image.h" #include "kis_signals_blocker.h" #include "kis_signal_compressor.h" #include "kis_layer_properties_icons.h" struct KisToolLazyBrushOptionsWidget::Private { Private() : transparentColorIndex(-1), baseNodeChangedCompressor(500, KisSignalCompressor::FIRST_ACTIVE) { } Ui_KisToolLazyBrushOptionsWidget *ui; KisPaletteModel *colorModel; KisCanvasResourceProvider *provider; KisSignalAutoConnectionsStore providerSignals; KisSignalAutoConnectionsStore maskSignals; KisColorizeMaskSP activeMask; KoColorSet colorSet; int transparentColorIndex = -1; KisSignalCompressor baseNodeChangedCompressor; }; KisToolLazyBrushOptionsWidget::KisToolLazyBrushOptionsWidget(KisCanvasResourceProvider *provider, QWidget *parent) : QWidget(parent), m_d(new Private) { m_d->ui = new Ui_KisToolLazyBrushOptionsWidget(); m_d->ui->setupUi(this); m_d->colorModel = new KisPaletteModel(this); m_d->ui->colorView->setPaletteModel(m_d->colorModel); m_d->ui->colorView->setAllowModification(false); //people proly shouldn't be able to edit the colorentries themselves. m_d->ui->colorView->setCrossedKeyword("transparent"); connect(m_d->ui->chkUseEdgeDetection, SIGNAL(toggled(bool)), SLOT(slotUseEdgeDetectionChanged(bool))); connect(m_d->ui->intEdgeDetectionSize, SIGNAL(valueChanged(int)), SLOT(slotEdgeDetectionSizeChanged(int))); connect(m_d->ui->intRadius, SIGNAL(valueChanged(int)), SLOT(slotRadiusChanged(int))); connect(m_d->ui->intCleanUp, SIGNAL(valueChanged(int)), SLOT(slotCleanUpChanged(int))); connect(m_d->ui->chkLimitToDevice, SIGNAL(toggled(bool)), SLOT(slotLimitToDeviceChanged(bool))); m_d->ui->intEdgeDetectionSize->setRange(0, 100); m_d->ui->intEdgeDetectionSize->setExponentRatio(2.0); m_d->ui->intEdgeDetectionSize->setSuffix(i18n(" px")); - m_d->ui->intEdgeDetectionSize->setPrefix("Edge detection: "); + m_d->ui->intEdgeDetectionSize->setPrefix(i18n("Edge detection: ")); m_d->ui->intEdgeDetectionSize->setToolTip( i18nc("@info:tooltip", "Activate for images with vast solid areas. " "Set the value to the width of the thinnest " "lines on the image")); m_d->ui->intRadius->setRange(0, 1000); m_d->ui->intRadius->setExponentRatio(3.0); m_d->ui->intRadius->setSuffix(i18n(" px")); - m_d->ui->intRadius->setPrefix("Gap close hint: "); + m_d->ui->intRadius->setPrefix(i18n("Gap close hint: ")); m_d->ui->intRadius->setToolTip( i18nc("@info:tooltip", "The mask will try to close non-closed contours " "if the gap is smaller than \"Gap close hint\" value")); m_d->ui->intCleanUp->setRange(0, 100); m_d->ui->intCleanUp->setSuffix(i18n(" %")); - m_d->ui->intCleanUp->setPrefix("Clean up: "); + m_d->ui->intCleanUp->setPrefix(i18n("Clean up: ")); m_d->ui->intCleanUp->setToolTip( i18nc("@info:tooltip", "The mask will try to remove parts of the key strokes " "that are placed outside the closed contours. 0% - no effect, 100% - max effect")); connect(m_d->ui->colorView, SIGNAL(indexEntrySelected(QModelIndex)), this, SLOT(entrySelected(QModelIndex))); connect(m_d->ui->btnTransparent, SIGNAL(toggled(bool)), this, SLOT(slotMakeTransparent(bool))); connect(m_d->ui->btnRemove, SIGNAL(clicked()), this, SLOT(slotRemove())); connect(m_d->ui->chkAutoUpdates, SIGNAL(toggled(bool)), m_d->ui->btnUpdate, SLOT(setDisabled(bool))); connect(m_d->ui->btnUpdate, SIGNAL(clicked()), this, SLOT(slotUpdate())); connect(m_d->ui->chkAutoUpdates, SIGNAL(toggled(bool)), this, SLOT(slotSetAutoUpdates(bool))); connect(m_d->ui->chkShowKeyStrokes, SIGNAL(toggled(bool)), this, SLOT(slotSetShowKeyStrokes(bool))); connect(m_d->ui->chkShowOutput, SIGNAL(toggled(bool)), this, SLOT(slotSetShowOutput(bool))); connect(&m_d->baseNodeChangedCompressor, SIGNAL(timeout()), this, SLOT(slotUpdateNodeProperties())); m_d->provider = provider; const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); m_d->colorSet.add(KoColorSetEntry(KoColor(Qt::red, cs), "color1")); m_d->colorSet.add(KoColorSetEntry(KoColor(Qt::green, cs), "color2")); m_d->colorSet.add(KoColorSetEntry(KoColor(Qt::blue, cs), "color3")); m_d->colorModel->setColorSet(&m_d->colorSet); } KisToolLazyBrushOptionsWidget::~KisToolLazyBrushOptionsWidget() { } void KisToolLazyBrushOptionsWidget::showEvent(QShowEvent *event) { QWidget::showEvent(event); m_d->providerSignals.addConnection( m_d->provider, SIGNAL(sigNodeChanged(KisNodeSP)), this, SLOT(slotCurrentNodeChanged(KisNodeSP))); m_d->providerSignals.addConnection( m_d->provider, SIGNAL(sigFGColorChanged(const KoColor&)), this, SLOT(slotCurrentFgColorChanged(const KoColor&))); slotCurrentNodeChanged(m_d->provider->currentNode()); slotCurrentFgColorChanged(m_d->provider->fgColor()); } void KisToolLazyBrushOptionsWidget::hideEvent(QHideEvent *event) { QWidget::hideEvent(event); m_d->providerSignals.clear(); } void KisToolLazyBrushOptionsWidget::entrySelected(QModelIndex index) { if (!index.isValid()) return; const int i = m_d->colorModel->idFromIndex(index); if (i >= 0 && i < (int)m_d->colorSet.nColors()) { KoColorSetEntry entry = m_d->colorModel->colorSetEntryFromIndex(index); m_d->provider->setFGColor(entry.color()); } const bool transparentChecked = i >= 0 && i == m_d->transparentColorIndex; KisSignalsBlocker b(m_d->ui->btnTransparent); m_d->ui->btnTransparent->setChecked(transparentChecked); } void KisToolLazyBrushOptionsWidget::slotCurrentFgColorChanged(const KoColor &color) { int selectedIndex = -1; for (quint32 i = 0; i < m_d->colorSet.nColors(); i++) { KoColorSetEntry entry = m_d->colorSet.getColorGlobal(i); if (entry.color() == color) { selectedIndex = (int)i; break; } } m_d->ui->btnRemove->setEnabled(selectedIndex >= 0); m_d->ui->btnTransparent->setEnabled(selectedIndex >= 0); if (selectedIndex < 0) { KisSignalsBlocker b(m_d->ui->btnTransparent); m_d->ui->btnTransparent->setChecked(false); } QModelIndex newIndex = selectedIndex >= 0 ? m_d->colorModel->indexFromId(selectedIndex) : QModelIndex(); if (newIndex != m_d->ui->colorView->currentIndex()) { m_d->ui->colorView->setCurrentIndex(newIndex); } } void KisToolLazyBrushOptionsWidget::slotColorLabelsChanged() { m_d->colorSet.clear(); m_d->transparentColorIndex = -1; if (m_d->activeMask) { KisColorizeMask::KeyStrokeColors colors = m_d->activeMask->keyStrokesColors(); m_d->transparentColorIndex = colors.transparentIndex; for (int i = 0; i < colors.colors.size(); i++) { const QString name = i == m_d->transparentColorIndex ? "transparent" : ""; m_d->colorSet.add(KoColorSetEntry(colors.colors[i], name)); } } m_d->colorModel->setColorSet(&m_d->colorSet); slotCurrentFgColorChanged(m_d->provider->fgColor()); } void KisToolLazyBrushOptionsWidget::slotUpdateNodeProperties() { KisSignalsBlocker b1(m_d->ui->chkAutoUpdates, m_d->ui->btnUpdate, m_d->ui->chkShowKeyStrokes, m_d->ui->chkShowOutput); KisSignalsBlocker b2(m_d->ui->chkUseEdgeDetection, m_d->ui->intEdgeDetectionSize, m_d->ui->intRadius, m_d->ui->intCleanUp, m_d->ui->chkLimitToDevice); // not implemented yet! //m_d->ui->chkAutoUpdates->setEnabled(m_d->activeMask); m_d->ui->chkAutoUpdates->setEnabled(false); m_d->ui->chkAutoUpdates->setVisible(false); bool value = false; value = m_d->activeMask && KisLayerPropertiesIcons::nodeProperty(m_d->activeMask, KisLayerPropertiesIcons::colorizeNeedsUpdate, true).toBool(); m_d->ui->btnUpdate->setEnabled(m_d->activeMask && !m_d->ui->chkAutoUpdates->isChecked() && value); value = m_d->activeMask && KisLayerPropertiesIcons::nodeProperty(m_d->activeMask, KisLayerPropertiesIcons::colorizeEditKeyStrokes, true).toBool(); m_d->ui->chkShowKeyStrokes->setEnabled(m_d->activeMask); m_d->ui->chkShowKeyStrokes->setChecked(value); value = m_d->activeMask && KisLayerPropertiesIcons::nodeProperty(m_d->activeMask, KisLayerPropertiesIcons::colorizeShowColoring, true).toBool(); m_d->ui->chkShowOutput->setEnabled(m_d->activeMask); m_d->ui->chkShowOutput->setChecked(value); m_d->ui->chkUseEdgeDetection->setEnabled(m_d->activeMask); m_d->ui->chkUseEdgeDetection->setChecked(m_d->activeMask && m_d->activeMask->useEdgeDetection()); m_d->ui->intEdgeDetectionSize->setEnabled(m_d->activeMask && m_d->ui->chkUseEdgeDetection->isChecked()); m_d->ui->intEdgeDetectionSize->setValue(m_d->activeMask ? m_d->activeMask->edgeDetectionSize() : 4.0); m_d->ui->intRadius->setEnabled(m_d->activeMask); m_d->ui->intRadius->setValue(2 * (m_d->activeMask ? m_d->activeMask->fuzzyRadius() : 15)); m_d->ui->intCleanUp->setEnabled(m_d->activeMask); m_d->ui->intCleanUp->setValue(100 * (m_d->activeMask ? m_d->activeMask->cleanUpAmount() : 0.7)); m_d->ui->chkLimitToDevice->setEnabled(m_d->activeMask); m_d->ui->chkLimitToDevice->setChecked(m_d->activeMask && m_d->activeMask->limitToDeviceBounds()); } void KisToolLazyBrushOptionsWidget::slotCurrentNodeChanged(KisNodeSP node) { m_d->maskSignals.clear(); KisColorizeMask *mask = dynamic_cast(node.data()); m_d->activeMask = mask; if (m_d->activeMask) { m_d->maskSignals.addConnection( m_d->activeMask, SIGNAL(sigKeyStrokesListChanged()), this, SLOT(slotColorLabelsChanged())); m_d->maskSignals.addConnection( m_d->provider->currentImage(), SIGNAL(sigNodeChanged(KisNodeSP)), this, SLOT(slotUpdateNodeProperties())); } slotColorLabelsChanged(); slotUpdateNodeProperties(); m_d->ui->colorView->setEnabled(m_d->activeMask); } void KisToolLazyBrushOptionsWidget::slotMakeTransparent(bool value) { KIS_ASSERT_RECOVER_RETURN(m_d->activeMask); QModelIndex index = m_d->ui->colorView->currentIndex(); if (!index.isValid()) return; const int activeIndex = m_d->colorModel->idFromIndex(index); KIS_ASSERT_RECOVER_RETURN(activeIndex >= 0); KisColorizeMask::KeyStrokeColors colors; for (quint32 i = 0; i < m_d->colorSet.nColors(); i++) { colors.colors << m_d->colorSet.getColorGlobal(i).color(); } colors.transparentIndex = value ? activeIndex : -1; m_d->activeMask->setKeyStrokesColors(colors); } void KisToolLazyBrushOptionsWidget::slotRemove() { KIS_ASSERT_RECOVER_RETURN(m_d->activeMask); QModelIndex index = m_d->ui->colorView->currentIndex(); if (!index.isValid()) return; const int activeIndex = m_d->colorModel->idFromIndex(index); KIS_ASSERT_RECOVER_RETURN(activeIndex >= 0); const KoColor color = m_d->colorSet.getColorGlobal((quint32)activeIndex).color(); m_d->activeMask->removeKeyStroke(color); } void KisToolLazyBrushOptionsWidget::slotUpdate() { KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask); KisLayerPropertiesIcons::setNodeProperty(m_d->activeMask, KisLayerPropertiesIcons::colorizeNeedsUpdate, false, m_d->provider->currentImage()); } void KisToolLazyBrushOptionsWidget::slotSetAutoUpdates(bool value) { // not implemented yet! ENTER_FUNCTION() << ppVar(value); } void KisToolLazyBrushOptionsWidget::slotSetShowKeyStrokes(bool value) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask); KisLayerPropertiesIcons::setNodeProperty(m_d->activeMask, KisLayerPropertiesIcons::colorizeEditKeyStrokes, value, m_d->provider->currentImage()); } void KisToolLazyBrushOptionsWidget::slotSetShowOutput(bool value) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask); KisLayerPropertiesIcons::setNodeProperty(m_d->activeMask, KisLayerPropertiesIcons::colorizeShowColoring, value, m_d->provider->currentImage()); } void KisToolLazyBrushOptionsWidget::slotUseEdgeDetectionChanged(bool value) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask); m_d->activeMask->setUseEdgeDetection(value); m_d->ui->intEdgeDetectionSize->setEnabled(value); } void KisToolLazyBrushOptionsWidget::slotEdgeDetectionSizeChanged(int value) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask); m_d->activeMask->setEdgeDetectionSize(value); } void KisToolLazyBrushOptionsWidget::slotRadiusChanged(int value) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask); m_d->activeMask->setFuzzyRadius(0.5 * value); } void KisToolLazyBrushOptionsWidget::slotCleanUpChanged(int value) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask); m_d->activeMask->setCleanUpAmount(qreal(value) / 100.0); } void KisToolLazyBrushOptionsWidget::slotLimitToDeviceChanged(bool value) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask); m_d->activeMask->setLimitToDeviceBounds(value); }