diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e9bea3780..46cc9493bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,923 +1,931 @@ project(krita) message(STATUS "Using CMake version: ${CMAKE_VERSION}") cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR) set(MIN_QT_VERSION 5.9.0) set(MIN_FRAMEWORKS_VERSION 5.44.0) set( CMAKE_CXX_STANDARD 11 ) set( CMAKE_CXX_STANDARD_REQUIRED ON ) if (POLICY CMP0002) cmake_policy(SET CMP0002 OLD) endif() if (POLICY CMP0017) cmake_policy(SET CMP0017 NEW) endif () if (POLICY CMP0022) cmake_policy(SET CMP0022 OLD) endif () if (POLICY CMP0026) cmake_policy(SET CMP0026 OLD) endif() if (POLICY CMP0042) cmake_policy(SET CMP0042 NEW) endif() if (POLICY CMP0046) cmake_policy(SET CMP0046 OLD) endif () if (POLICY CMP0059) cmake_policy(SET CMP0059 OLD) endif() if (POLICY CMP0063) cmake_policy(SET CMP0063 OLD) endif() if (POLICY CMP0054) cmake_policy(SET CMP0054 OLD) endif() if (POLICY CMP0064) cmake_policy(SET CMP0064 OLD) endif() if (POLICY CMP0071) cmake_policy(SET CMP0071 OLD) endif() if (APPLE) set(APPLE_SUPPRESS_X11_WARNING TRUE) set(KDE_SKIP_RPATH_SETTINGS TRUE) set(CMAKE_MACOSX_RPATH 1) set(BUILD_WITH_INSTALL_RPATH 1) add_definitions(-mmacosx-version-min=10.12 -Wno-macro-redefined -Wno-deprecated-register) endif() if (CMAKE_COMPILER_IS_GNUCXX AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9 AND NOT WIN32) add_compile_options($<$:-Wno-suggest-override> -Wextra -Wno-class-memaccess) endif() ###################### ####################### ## Constants defines ## ####################### ###################### # define common versions of Krita applications, used to generate kritaversion.h # update these version for every release: set(KRITA_VERSION_STRING "4.3.1-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 3) # Bugfix release version, or 0 for before the first stable release set(KRITA_VERSION_RELEASE 1) # 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 if(NOT DEFINED KRITA_ALPHA AND NOT DEFINED KRITA_BETA AND NOT DEFINED KRITA_RC) set(KRITA_STABLE 1) # do not edit endif() message(STATUS "Krita version: ${KRITA_VERSION_STRING}") # Define the generic version of the Krita libraries here # This makes it easy to advance it when the next Krita release comes. # 14 was the last GENERIC_KRITA_LIB_VERSION_MAJOR of the previous Krita series # (2.x) so we're starting with 15 in 3.x series, 16 in 4.x series if(KRITA_STABLE_VERSION_MAJOR EQUAL 4) math(EXPR GENERIC_KRITA_LIB_VERSION_MAJOR "${KRITA_STABLE_VERSION_MINOR} + 16") else() # let's make sure we won't forget to update the "16" message(FATAL_ERROR "Reminder: please update offset == 16 used to compute GENERIC_KRITA_LIB_VERSION_MAJOR to something bigger") endif() set(GENERIC_KRITA_LIB_VERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}.0.0") set(GENERIC_KRITA_LIB_SOVERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}") LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/kde_macro") # fetch git revision for the current build set(KRITA_GIT_SHA1_STRING "") set(KRITA_GIT_BRANCH_STRING "") include(GetGitRevisionDescription) get_git_head_hash(GIT_SHA1) get_git_branch(GIT_BRANCH) if(GIT_SHA1) string(SUBSTRING ${GIT_SHA1} 0 7 GIT_SHA1) set(KRITA_GIT_SHA1_STRING ${GIT_SHA1}) if(GIT_BRANCH) set(KRITA_GIT_BRANCH_STRING ${GIT_BRANCH}) else() set(KRITA_GIT_BRANCH_STRING "(detached HEAD)") endif() endif() # create test make targets enable_testing() # collect list of broken tests, empty here to start fresh with each cmake run set(KRITA_BROKEN_TESTS "" CACHE INTERNAL "KRITA_BROKEN_TESTS") ############ ############# ## Options ## ############# ############ include(FeatureSummary) if (WIN32) option(USE_DRMINGW "Support the Dr. Mingw crash handler (only on windows)" ON) add_feature_info("Dr. Mingw" USE_DRMINGW "Enable the Dr. Mingw crash handler") if (MINGW) option(USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags (mingw-w64)" ON) add_feature_info("Linker Security Flags" USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags") if (USE_MINGW_HARDENING_LINKER) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") # Enable high-entropy ASLR for 64-bit # The image base has to be >4GB for HEASLR to be enabled. # The values used here are kind of arbitrary. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x140000000") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") endif ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") else (USE_MINGW_HARDENING_LINKER) message(WARNING "Linker Security Flags not enabled!") endif (USE_MINGW_HARDENING_LINKER) endif (MINGW) endif () option(HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal." ON) configure_file(config-hide-safe-asserts.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hide-safe-asserts.h) add_feature_info("Hide Safe Asserts" HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal.") option(USE_LOCK_FREE_HASH_TABLE "Use lock free hash table instead of blocking." ON) configure_file(config-hash-table-implementaion.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hash-table-implementaion.h) add_feature_info("Lock free hash table" USE_LOCK_FREE_HASH_TABLE "Use lock free hash table instead of blocking.") option(FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true." OFF) add_feature_info("Foundation Build" FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true.") option(KRITA_ENABLE_BROKEN_TESTS "Enable tests that are marked as broken" OFF) add_feature_info("Enable Broken Tests" KRITA_ENABLE_BROKEN_TESTS "Runs broken test when \"make test\" is invoked (use -DKRITA_ENABLE_BROKEN_TESTS=ON to enable).") option(LIMIT_LONG_TESTS "Run long running unittests in a limited quick mode" ON) configure_file(config-limit-long-tests.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-limit-long-tests.h) add_feature_info("Limit long tests" LIMIT_LONG_TESTS "Run long running unittests in a limited quick mode") option(ENABLE_PYTHON_2 "Enables the compiler to look for Python 2.7 instead of Python 3. Some packaged scripts are not compatible with Python 2 and this should only be used if you really have to use 2.7." OFF) option(BUILD_KRITA_QT_DESIGNER_PLUGINS "Build Qt Designer plugins for Krita widgets" OFF) add_feature_info("Build Qt Designer plugins" BUILD_KRITA_QT_DESIGNER_PLUGINS "Builds Qt Designer plugins for Krita widgets (use -DBUILD_KRITA_QT_DESIGNER_PLUGINS=ON to enable).") include(MacroJPEG) ######################################################### ## Look for Python3 It is also searched by KF5, ## ## so we should request the correct version in advance ## ######################################################### function(TestCompileLinkPythonLibs OUTPUT_VARNAME) include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_INCLUDES ${PYTHON_INCLUDE_PATH}) set(CMAKE_REQUIRED_LIBRARIES ${PYTHON_LIBRARIES}) if (MINGW) set(CMAKE_REQUIRED_DEFINITIONS -D_hypot=hypot) endif (MINGW) unset(${OUTPUT_VARNAME} CACHE) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { Py_InitializeEx(0); }" ${OUTPUT_VARNAME}) endfunction() if(MINGW) if(ENABLE_PYTHON_2) message(FATAL_ERROR "Python 2.7 is not supported on Windows at the moment.") else(ENABLE_PYTHON_2) find_package(PythonInterp 3.8 EXACT) find_package(PythonLibs 3.8 EXACT) endif(ENABLE_PYTHON_2) if (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) if(ENABLE_PYTHON_2) find_package(PythonLibrary 2.7) else(ENABLE_PYTHON_2) find_package(PythonLibrary 3.8) endif(ENABLE_PYTHON_2) TestCompileLinkPythonLibs(CAN_USE_PYTHON_LIBS) if (NOT CAN_USE_PYTHON_LIBS) message(FATAL_ERROR "Compiling with Python library failed, please check whether the architecture is correct. Python will be disabled.") endif (NOT CAN_USE_PYTHON_LIBS) endif (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) else(MINGW) if(ENABLE_PYTHON_2) find_package(PythonInterp 2.7) find_package(PythonLibrary 2.7) else(ENABLE_PYTHON_2) find_package(PythonInterp 3.0) find_package(PythonLibrary 3.0) endif(ENABLE_PYTHON_2) endif(MINGW) ######################## ######################### ## Look for KDE and Qt ## ######################### ######################## find_package(ECM 5.22 REQUIRED NOMODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(ECMOptionalAddSubdirectory) include(ECMAddAppIcon) include(ECMSetupVersion) include(ECMMarkNonGuiExecutable) include(ECMGenerateHeaders) include(GenerateExportHeader) include(ECMMarkAsTest) include(ECMInstallIcons) include(CMakePackageConfigHelpers) include(WriteBasicConfigVersionFile) include(CheckFunctionExists) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings) # do not reorder to be alphabetical: this is the order in which the frameworks # depend on each other. find_package(KF5 ${MIN_FRAMEWORKS_VERSION} REQUIRED COMPONENTS Config WidgetsAddons Completion CoreAddons GuiAddons I18n ItemModels ItemViews WindowSystem ) find_package(Qt5 ${MIN_QT_VERSION} REQUIRED COMPONENTS Core Gui Widgets Xml Network PrintSupport Svg Test Concurrent ) if (ANDROID) find_package(Qt5 ${MIN_QT_VERSION} REQUIRED COMPONENTS AndroidExtras ) endif() if (WIN32) set(CMAKE_REQUIRED_INCLUDES ${Qt5Core_INCLUDE_DIRS}) set(CMAKE_REQUIRED_LIBRARIES ${Qt5Core_LIBRARIES}) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_MSWindowsUseWinTabAPI); } " QT_HAS_WINTAB_SWITCH ) unset(CMAKE_REQUIRED_INCLUDES) unset(CMAKE_REQUIRED_LIBRARIES) option(USE_QT_TABLET_WINDOWS "Do not use Krita's forked Wintab and Windows Ink support on Windows, but leave everything to Qt." ON) add_feature_info("Use Qt's Windows Tablet Support" USE_QT_TABLET_WINDOWS "Do not use Krita's forked Wintab and Windows Ink support on Windows, but leave everything to Qt.") configure_file(config_use_qt_tablet_windows.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_use_qt_tablet_windows.h) endif () set(CMAKE_REQUIRED_INCLUDES ${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS}) set(CMAKE_REQUIRED_LIBRARIES ${Qt5Core_LIBRARIES} ${Qt5Gui_LIBRARIES}) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { QSurfaceFormat fmt; fmt.setColorSpace(QSurfaceFormat::scRGBColorSpace); fmt.setColorSpace(QSurfaceFormat::bt2020PQColorSpace); } " HAVE_HDR ) configure_file(config-hdr.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hdr.h) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round); QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor); QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); } " HAVE_HIGH_DPI_SCALE_FACTOR_ROUNDING_POLICY ) configure_file(config-high-dpi-scale-factor-rounding-policy.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-high-dpi-scale-factor-rounding-policy.h) if (WIN32) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { QWindowsWindowFunctions::setHasBorderInFullScreenDefault(true); } " HAVE_SET_HAS_BORDER_IN_FULL_SCREEN_DEFAULT ) configure_file(config-set-has-border-in-full-screen-default.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-set-has-border-in-full-screen-default.h) endif (WIN32) unset(CMAKE_REQUIRED_INCLUDES) unset(CMAKE_REQUIRED_LIBRARIES) include (MacroAddFileDependencies) include (MacroBoolTo01) include (MacroEnsureOutOfSourceBuild) macro_ensure_out_of_source_build("Compiling Krita inside the source directory is not possible. Please refer to the build instruction https://community.kde.org/Krita#Build_Instructions") # Note: OPTIONAL_COMPONENTS does not seem to be reliable # (as of ECM 5.15.0, CMake 3.2) find_package(Qt5Multimedia ${MIN_QT_VERSION}) set_package_properties(Qt5Multimedia PROPERTIES DESCRIPTION "Qt multimedia integration" URL "https://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 "https://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 "https://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used for the touch gui for Krita") endif() if (NOT WIN32 AND NOT APPLE AND NOT ANDROID) find_package(Qt5 ${MIN_QT_VERSION} REQUIRED X11Extras) find_package(Qt5DBus ${MIN_QT_VERSION}) set(HAVE_DBUS ${Qt5DBus_FOUND}) set_package_properties(Qt5DBus PROPERTIES DESCRIPTION "Qt DBUS integration" URL "https://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used to provide a dbus api on Linux") find_package(KF5Crash ${MIN_FRAMEWORKS_VERSION}) macro_bool_to_01(KF5Crash_FOUND HAVE_KCRASH) set_package_properties(KF5Crash PROPERTIES DESCRIPTION "KDE's Crash Handler" URL "https://api.kde.org/frameworks-api/frameworks5-apidocs/kcrash/html/index.html" TYPE OPTIONAL PURPOSE "Optionally used to provide crash reporting on Linux") find_package(X11 REQUIRED COMPONENTS Xinput) set(HAVE_X11 TRUE) add_definitions(-DHAVE_X11) else() set(HAVE_DBUS FALSE) set(HAVE_X11 FALSE) endif() add_definitions( -DQT_USE_QSTRINGBUILDER -DQT_STRICT_ITERATORS -DQT_NO_SIGNALS_SLOTS_KEYWORDS -DQT_NO_URL_CAST_FROM_STRING -DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS ) #if (${Qt5_VERSION} VERSION_GREATER "5.14.0" ) # add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50F00) #elseif (${Qt5_VERSION} VERSION_GREATER "5.13.0" ) # add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50E00) #elseif (${Qt5_VERSION} VERSION_GREATER "5.12.0" ) # add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50D00) #elseif (${Qt5_VERSION} VERSION_GREATER "5.11.0" ) # add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50C00) #if(${Qt5_VERSION} VERSION_GREATER "5.10.0" ) # add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50B00) #if(${Qt5_VERSION} VERSION_GREATER "5.9.0" ) # add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50A00) #else() add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50900) #endif() add_definitions(-DQT_DEPRECATED_WARNINGS) add_definitions(-DTRANSLATION_DOMAIN=\"krita\") # # The reason for this mode is that the Debug mode disable inlining # if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals") endif() option(KRITA_DEVS "For Krita developers. This modifies the DEBUG build type to use -O3 -g, while still enabling Q_ASSERT. This is necessary because the Qt5 cmake modules normally append QT_NO_DEBUG to any build type that is not labeled Debug") if (KRITA_DEVS) set(CMAKE_CXX_FLAGS_DEBUG "-O3 -g" CACHE STRING "" FORCE) endif() if(UNIX) set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES};m") endif() if(WIN32) if(MSVC) # C4522: 'class' : multiple assignment operators specified set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4522") endif() endif() # KDECompilerSettings adds the `--export-all-symbols` linker flag. # We don't really need it. if(MINGW) string(REPLACE "-Wl,--export-all-symbols" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") string(REPLACE "-Wl,--export-all-symbols" "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}") endif(MINGW) if(MINGW) # Hack CMake's variables to tell AR to create thin archives to reduce unnecessary writes. # Source of definition: https://github.com/Kitware/CMake/blob/v3.14.1/Modules/Platform/Windows-GNU.cmake#L128 # Thin archives: https://sourceware.org/binutils/docs/binutils/ar.html#index-thin-archives macro(mingw_use_thin_archive lang) foreach(rule CREATE_SHARED_MODULE CREATE_SHARED_LIBRARY LINK_EXECUTABLE) string(REGEX REPLACE "( [^ T]+) " "\\1T " CMAKE_${lang}_${rule} "${CMAKE_${lang}_${rule}}") endforeach() endmacro() mingw_use_thin_archive(CXX) endif(MINGW) # enable exceptions globally kde_enable_exceptions() set(KRITA_DEFAULT_TEST_DATA_DIR ${CMAKE_SOURCE_DIR}/sdk/tests/data/) macro(macro_add_unittest_definitions) add_definitions(-DFILES_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data/") add_definitions(-DFILES_OUTPUT_DIR="${CMAKE_CURRENT_BINARY_DIR}") add_definitions(-DFILES_DEFAULT_DATA_DIR="${KRITA_DEFAULT_TEST_DATA_DIR}") add_definitions(-DSYSTEM_RESOURCES_DATA_DIR="${CMAKE_SOURCE_DIR}/krita/data/") endmacro() # overcome some platform incompatibilities if(WIN32) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/winquirks) add_definitions(-D_USE_MATH_DEFINES) add_definitions(-DNOMINMAX) set(WIN32_PLATFORM_NET_LIBS ws2_32.lib netapi32.lib) endif() # set custom krita plugin installdir if (ANDROID) # use default ABI if (NOT ANDROID_ABI) set (ANDROID_ABI armeabi-v7a) endif() set (ANDROID_SDK_ROOT $ENV{ANDROID_SDK_ROOT}) set (KRITA_PLUGIN_INSTALL_DIR ${LIB_INSTALL_DIR}) # set (DATA_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/assets) else() set (KRITA_PLUGIN_INSTALL_DIR ${LIB_INSTALL_DIR}/kritaplugins) endif() ########################### ############################ ## Required dependencies ## ############################ ########################### # FIXME: Still hardcoded if (ANDROID) set (Boost_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/i/${ANDROID_ABI}/include/boost-1_69) set (Boost_LIBRARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/i/${ANDROID_ABI}/lib) set (KF5_LIBRARIES ${CMAKE_CURRENT_BINARY_DIR}/kf5/kde/install/lib) endif() find_package(PNG REQUIRED) list (APPEND ANDROID_EXTRA_LIBS ${PNG_LIBRARY}) if (APPLE) # this is not added correctly on OSX -- see https://forum.kde.org/viewtopic.php?f=139&t=101867&p=221242#p221242 include_directories(SYSTEM ${PNG_INCLUDE_DIR}) endif() add_definitions(-DBOOST_ALL_NO_LIB) find_package(Boost 1.55 REQUIRED COMPONENTS system) include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) ## ## Test for GNU Scientific Library ## find_package(GSL) set_package_properties(GSL PROPERTIES URL "https://www.gnu.org/software/gsl" TYPE RECOMMENDED PURPOSE "Required by Krita's Transform tool.") macro_bool_to_01(GSL_FOUND HAVE_GSL) configure_file(config-gsl.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-gsl.h ) if (GSL_FOUND) list (APPEND ANDROID_EXTRA_LIBS ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES}) endif() ########################### ############################ ## Optional dependencies ## ############################ ########################### find_package(ZLIB) set_package_properties(ZLIB PROPERTIES DESCRIPTION "Compression library" URL "https://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 "https://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_DIRS}) 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.libtiff.org" TYPE OPTIONAL PURPOSE "Required by the Krita TIFF filter") if (TIFF_FOUND) list (APPEND ANDROID_EXTRA_LIBS ${TIFF_LIBRARY}) endif() find_package(JPEG) set_package_properties(JPEG PROPERTIES DESCRIPTION "Free library for JPEG image compression. Note: libjpeg8 is NOT supported." URL "https://www.libjpeg-turbo.org" TYPE OPTIONAL PURPOSE "Required by the Krita JPEG filter") if (JPEG_FOUND) list (APPEND ANDROID_EXTRA_LIBS ${JPEG_LIBRARY}) endif() find_package(GIF) set_package_properties(GIF PROPERTIES DESCRIPTION "Library for loading and saving gif files." URL "http://giflib.sourceforge.net/" TYPE OPTIONAL PURPOSE "Required by the Krita GIF filter") if (GIF_FOUND) list (APPEND ANDROID_EXTRA_LIBS ${GIF_LIBRARY}) endif() find_package(HEIF "1.3.0") set_package_properties(HEIF PROPERTIES DESCRIPTION "Library for loading and saving heif files." URL "https://github.com/strukturag/libheif" TYPE OPTIONAL PURPOSE "Required by the Krita HEIF filter") find_package(OpenJPEG "2.3.0") set_package_properties(OpenJPEG PROPERTIES DESCRIPTION "Library for loading and saving jp2000 files." URL "https://www.openjpeg.org/" TYPE OPTIONAL PURPOSE "Required by the Krita JP2000 filter") set(LIBRAW_MIN_VERSION "0.16") find_package(LibRaw ${LIBRAW_MIN_VERSION}) set_package_properties(LibRaw PROPERTIES DESCRIPTION "Library to decode RAW images" URL "https://www.libraw.org/" TYPE OPTIONAL PURPOSE "Required to build the raw import plugin") find_package(FFTW3) set_package_properties(FFTW3 PROPERTIES DESCRIPTION "A fast, free C FFT library" URL "http://www.fftw.org/" TYPE OPTIONAL PURPOSE "Required by the Krita for fast convolution operators and some G'Mic features") macro_bool_to_01(FFTW3_FOUND HAVE_FFTW3) if (FFTW3_FOUND) list (APPEND ANDROID_EXTRA_LIBS ${FFTW3_LIBRARY}) endif() find_package(OCIO) set_package_properties(OCIO PROPERTIES DESCRIPTION "The OpenColorIO Library" URL "https://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 "https://www.python.org" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(PYTHONLIBS_FOUND HAVE_PYTHONLIBS) find_package(SIP "4.19.13") set_package_properties(SIP PROPERTIES DESCRIPTION "Support for generating SIP Python bindings" URL "https://www.riverbankcomputing.com/software/sip/download" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(SIP_FOUND HAVE_SIP) find_package(PyQt5 "5.6.0") set_package_properties(PyQt5 PROPERTIES DESCRIPTION "Python bindings for Qt5." URL "https://www.riverbankcomputing.com/software/pyqt/download5" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(PYQT5_FOUND HAVE_PYQT5) +find_package(LibMyPaint) +set_package_properties(LibMyPaint PROPERTIES + DESCRIPTION "MyPaint brush engine API for C/C++" + TYPE OPTIONAL + PURPOSE "Required for the mypaint brush engine") +if(LIBMYPAINT_VERSION VERSION_LESS "1.4.0") + message(WARNING "Please update libmypaint to version above 1.4.0 as lower versions are known to crash") +endif() ## ## Look for OpenGL ## # TODO: see if there is a better check for QtGui being built with opengl support (and thus the QOpenGL* classes) if(Qt5Gui_OPENGL_IMPLEMENTATION) message(STATUS "Found QtGui OpenGL support") else() message(FATAL_ERROR "Did NOT find QtGui OpenGL support. Check your Qt configuration. You cannot build Krita without Qt OpenGL support.") endif() ## ## Test for eigen3 ## find_package(Eigen3 3.0 REQUIRED) set_package_properties(Eigen3 PROPERTIES DESCRIPTION "C++ template library for linear algebra" URL "http://eigen.tuxfamily.org" TYPE REQUIRED) ## ## Test for exiv2 ## find_package(LibExiv2 0.16 REQUIRED) if (ANDROID) list (APPEND ANDROID_EXTRA_LIBS ${LibExiv2_LIBRARIES}) # because libexiv2 depends on libexpat and it is installed in the same folder get_filename_component (_base_dir ${LibExiv2_LIBRARIES} DIRECTORY) list (APPEND ANDROID_EXTRA_LIBS ${_base_dir}/libexpat.so) endif() ## ## Test for lcms ## find_package(LCMS2 2.4 REQUIRED) set_package_properties(LCMS2 PROPERTIES DESCRIPTION "LittleCMS Color management engine" URL "http://www.littlecms.com" TYPE REQUIRED PURPOSE "Will be used for color management and is necessary for Krita") if(LCMS2_FOUND) if(NOT ${LCMS2_VERSION} VERSION_LESS 2040 ) set(HAVE_LCMS24 TRUE) endif() set(HAVE_REQUIRED_LCMS_VERSION TRUE) set(HAVE_LCMS2 TRUE) endif() list (APPEND ANDROID_EXTRA_LIBS ${LCMS2_LIBRARIES}) ## ## Test for Vc ## set(OLD_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ) set(HAVE_VC FALSE) if (NOT ${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") if(NOT MSVC) find_package(Vc 1.1.0) set_package_properties(Vc PROPERTIES DESCRIPTION "Portable, zero-overhead SIMD library for C++" URL "https://github.com/VcDevel/Vc" TYPE OPTIONAL PURPOSE "Required by the Krita for vectorization") macro_bool_to_01(Vc_FOUND HAVE_VC) endif() endif() configure_file(config-vc.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-vc.h ) if(HAVE_VC) message(STATUS "Vc found!") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/vc") include (VcMacros) if(Vc_COMPILER_IS_CLANG) set(ADDITIONAL_VC_FLAGS "-ffp-contract=fast") if(NOT WIN32) set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC") endif() elseif (NOT MSVC) set(ADDITIONAL_VC_FLAGS "-fabi-version=0 -ffp-contract=fast") if(NOT WIN32) set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC") endif() endif() macro(ko_compile_for_all_implementations_no_scalar _objs _src) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) endmacro() macro(ko_compile_for_all_implementations _objs _src) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY Scalar SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) endmacro() endif() set(CMAKE_MODULE_PATH ${OLD_CMAKE_MODULE_PATH} ) add_definitions(${QT_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS}) ## ## Test endianness ## include (TestBigEndian) test_big_endian(CMAKE_WORDS_BIGENDIAN) ## ## Test for qt-poppler ## find_package(Poppler COMPONENTS Qt5) set_package_properties(Poppler PROPERTIES DESCRIPTION "A PDF rendering library" URL "https://poppler.freedesktop.org/" TYPE OPTIONAL PURPOSE "Required by the Krita PDF filter.") ## ## Test for quazip ## find_package(QuaZip 0.6) set_package_properties(QuaZip PROPERTIES DESCRIPTION "A library for reading and writing zip files" URL "https://stachenov.github.io/quazip/" TYPE REQUIRED PURPOSE "Needed for reading and writing KRA and ORA files" ) # FIXME: better way to do this? list (APPEND ANDROID_EXTRA_LIBS ${QUAZIP_LIBRARIES} ${EXPAT_LIBRARY} ${KF5_LIBRARIES}/libKF5Completion.so ${KF5_LIBRARIES}/libKF5WindowSystem.so ${KF5_LIBRARIES}/libKF5WidgetsAddons.so ${KF5_LIBRARIES}/libKF5ItemViews.so ${KF5_LIBRARIES}/libKF5ItemModels.so ${KF5_LIBRARIES}/libKF5GuiAddons.so ${KF5_LIBRARIES}/libKF5I18n.so ${KF5_LIBRARIES}/libKF5CoreAddons.so ${KF5_LIBRARIES}/libKF5ConfigGui.so ${KF5_LIBRARIES}/libKF5ConfigCore.so) ## ## Test for Atomics ## include(CheckAtomic) ############################ ############################# ## Add Krita helper macros ## ############################# ############################ include(MacroKritaAddBenchmark) #################### ##################### ## Define includes ## ##################### #################### # for config.h and includes (if any?) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/interfaces ) add_subdirectory(libs) add_subdirectory(plugins) if (BUILD_TESTING) add_subdirectory(benchmarks) endif() add_subdirectory(krita) configure_file(KoConfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/KoConfig.h ) configure_file(config_convolution.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_convolution.h) configure_file(config-ocio.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-ocio.h ) check_function_exists(powf HAVE_POWF) configure_file(config-powf.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-powf.h) if(WIN32) include(${CMAKE_CURRENT_LIST_DIR}/packaging/windows/installer/ConfigureInstallerNsis.cmake) endif() message("\nBroken tests:") foreach(tst ${KRITA_BROKEN_TESTS}) message(" * ${tst}") endforeach() feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/po OR EXISTS ${CMAKE_CURRENT_BINARY_DIR}/po ) find_package(KF5I18n CONFIG REQUIRED) ki18n_install(po) endif() if(DEFINED QTANDROID_EXPORTED_TARGET AND NOT TARGET "create-apk") set (_CMAKE_ANDROID_DIR "${ECM_DIR}/../toolchain") list(LENGTH QTANDROID_EXPORTED_TARGET targetsCount) include(${_CMAKE_ANDROID_DIR}/ECMAndroidDeployQt.cmake) math(EXPR last "${targetsCount}-1") foreach(idx RANGE 0 ${last}) list(GET QTANDROID_EXPORTED_TARGET ${idx} exportedTarget) list(GET ANDROID_APK_DIR ${idx} APK_DIR) if(APK_DIR AND NOT EXISTS "${ANDROID_APK_DIR}/AndroidManifest.xml" AND IS_ABSOLUTE ANDROID_APK_DIR) message(FATAL_ERROR "Cannot find ${APK_DIR}/AndroidManifest.xml according to ANDROID_APK_DIR. ${ANDROID_APK_DIR} ${exportedTarget}") elseif(NOT APK_DIR) get_filename_component(_qt5Core_install_prefix "${Qt5Core_DIR}/../../../" ABSOLUTE) set(APK_DIR "${_qt5Core_install_prefix}/src/android/templates/") endif() ecm_androiddeployqt("${exportedTarget}" "${ECM_ADDITIONAL_FIND_ROOT_PATH}") set_target_properties(create-apk-${exportedTarget} PROPERTIES ANDROID_APK_DIR "${APK_DIR}") endforeach() elseif(ANDROID) message(STATUS "You can export a target by specifying -DQTANDROID_EXPORTED_TARGET= and -DANDROID_APK_DIR=") endif() diff --git a/cmake/modules/FindLibMyPaint.cmake b/cmake/modules/FindLibMyPaint.cmake new file mode 100644 index 0000000000..cd2736dc4c --- /dev/null +++ b/cmake/modules/FindLibMyPaint.cmake @@ -0,0 +1,27 @@ +#For finding LibMyPaint library in the system + +include(LibFindMacros) +libfind_pkg_check_modules(MYPAINT_PKGCONF libmypaint) + +find_path(LIBMYPAINT_INCLUDE_DIR + NAMES mypaint-config.h + /usr/include + /usr/local/include + /sw/include + /opt/local/include + ${MYPAINT_PKGCONF_INCLUDE_DIRS} + ${MYPAINT_PKGCONF_INCLUDEDIR} + PATH_SUFFIXES libmypaint +) + +find_library(LIBMYPAINT_LIBRARY + NAMES libmypaint mypaint + HINTS ${MYPAINT_PKGCONF_LIBRARY_DIRS} ${MYPAINT_PKGCONF_LIBDIR} + DOC "Libraries to link against for mypaint brush engine Support" +) + +string(REGEX MATCH "(.*)/libmypaint.so" LIBMYPAINT_LIBRARIES ${LIBMYPAINT_LIBRARY}) + +set(LIBMYPAINT_LIBRARIES ${CMAKE_MATCH_1}) +set(LIBMYPAINT_FOUND ${MYPAINT_PKGCONF_FOUND}) +set(LIBMYPAINT_VERSION ${MYPAINT_PKGCONF_VERSION}) diff --git a/krita/pics/paintops/mypaintbrush.png b/krita/pics/paintops/mypaintbrush.png new file mode 100644 index 0000000000..af5f166d19 Binary files /dev/null and b/krita/pics/paintops/mypaintbrush.png differ diff --git a/krita/pics/paintops/paintops-icons.qrc b/krita/pics/paintops/paintops-icons.qrc index 535ed480f5..968c88f82b 100644 --- a/krita/pics/paintops/paintops-icons.qrc +++ b/krita/pics/paintops/paintops-icons.qrc @@ -1,37 +1,38 @@ dark_bristlebrush.svg dark_clonebrush.svg dark_colorsmudge.svg dark_curvebrush.svg dark_deformbrush.svg dark_dynabrush.svg dark_filterbrush.svg dark_gridbrush.svg dark_hatchingbrush.svg dark_particlebrush.svg dark_pixelbrush.svg dark_quickbrush.svg dark_shapebrush.svg dark_sketchbrush.svg dark_spraybrush.svg dark_tangentnormal.svg light_bristlebrush.svg light_clonebrush.svg light_colorsmudge.svg light_curvebrush.svg light_deformbrush.svg light_dynabrush.svg light_filterbrush.svg light_gridbrush.svg light_hatchingbrush.svg light_particlebrush.svg light_pixelbrush.svg light_quickbrush.svg light_shapebrush.svg light_sketchbrush.svg light_spraybrush.svg light_tangentnormal.svg + mypaintbrush.png diff --git a/libs/global/KisUsageLogger.cpp b/libs/global/KisUsageLogger.cpp index 4f6b6a5eea..d1303060e8 100644 --- a/libs/global/KisUsageLogger.cpp +++ b/libs/global/KisUsageLogger.cpp @@ -1,259 +1,259 @@ /* * Copyright (c) 2019 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 "KisUsageLogger.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Q_GLOBAL_STATIC(KisUsageLogger, s_instance) const QString KisUsageLogger::s_sectionHeader("================================================================================\n"); struct KisUsageLogger::Private { bool active {false}; QFile logFile; QFile sysInfoFile; }; KisUsageLogger::KisUsageLogger() : d(new Private) { d->logFile.setFileName(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/krita.log"); d->sysInfoFile.setFileName(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/krita-sysinfo.log"); rotateLog(); d->logFile.open(QFile::Append | QFile::Text); d->sysInfoFile.open(QFile::WriteOnly | QFile::Text); } KisUsageLogger::~KisUsageLogger() { if (d->active) { close(); } } void KisUsageLogger::initialize() { s_instance->d->active = true; QString systemInfo = basicSystemInfo(); s_instance->d->sysInfoFile.write(systemInfo.toUtf8()); } QString KisUsageLogger::basicSystemInfo() { QString systemInfo; // NOTE: This is intentionally not translated! // Krita version info systemInfo.append("Krita\n"); systemInfo.append("\n Version: ").append(KritaVersionWrapper::versionString(true)); systemInfo.append("\n Languages: ").append(KLocalizedString::languages().join(", ")); systemInfo.append("\n Hidpi: ").append(QCoreApplication::testAttribute(Qt::AA_EnableHighDpiScaling) ? "true" : "false"); systemInfo.append("\n\n"); systemInfo.append("Qt\n"); systemInfo.append("\n Version (compiled): ").append(QT_VERSION_STR); systemInfo.append("\n Version (loaded): ").append(qVersion()); systemInfo.append("\n\n"); // OS information systemInfo.append("OS Information\n"); systemInfo.append("\n Build ABI: ").append(QSysInfo::buildAbi()); systemInfo.append("\n Build CPU: ").append(QSysInfo::buildCpuArchitecture()); systemInfo.append("\n CPU: ").append(QSysInfo::currentCpuArchitecture()); systemInfo.append("\n Kernel Type: ").append(QSysInfo::kernelType()); systemInfo.append("\n Kernel Version: ").append(QSysInfo::kernelVersion()); systemInfo.append("\n Pretty Productname: ").append(QSysInfo::prettyProductName()); systemInfo.append("\n Product Type: ").append(QSysInfo::productType()); systemInfo.append("\n Product Version: ").append(QSysInfo::productVersion()); #ifdef Q_OS_LINUX systemInfo.append("\n Desktop: ").append(qgetenv("XDG_CURRENT_DESKTOP")); #endif systemInfo.append("\n\n"); return systemInfo; } void KisUsageLogger::close() { log("CLOSING SESSION"); s_instance->d->active = false; s_instance->d->logFile.flush(); s_instance->d->logFile.close(); s_instance->d->sysInfoFile.flush(); s_instance->d->sysInfoFile.close(); } void KisUsageLogger::log(const QString &message) { if (!s_instance->d->active) return; if (!s_instance->d->logFile.isOpen()) return; s_instance->d->logFile.write(QDateTime::currentDateTime().toString(Qt::RFC2822Date).toUtf8()); s_instance->d->logFile.write(": "); write(message); } void KisUsageLogger::write(const QString &message) { if (!s_instance->d->active) return; if (!s_instance->d->logFile.isOpen()) return; s_instance->d->logFile.write(message.toUtf8()); s_instance->d->logFile.write("\n"); s_instance->d->logFile.flush(); } void KisUsageLogger::writeSysInfo(const QString &message) { if (!s_instance->d->active) return; if (!s_instance->d->sysInfoFile.isOpen()) return; s_instance->d->sysInfoFile.write(message.toUtf8()); s_instance->d->sysInfoFile.write("\n"); s_instance->d->sysInfoFile.flush(); } void KisUsageLogger::writeHeader() { - Q_ASSERT(s_instance->d->sysInfoFile.isOpen()); +// Q_ASSERT(s_instance->d->sysInfoFile.isOpen()); s_instance->d->logFile.write(s_sectionHeader.toUtf8()); QString sessionHeader = QString("SESSION: %1. Executing %2\n\n") .arg(QDateTime::currentDateTime().toString(Qt::RFC2822Date)) .arg(qApp->arguments().join(' ')); s_instance->d->logFile.write(sessionHeader.toUtf8()); QString KritaAndQtVersion; KritaAndQtVersion.append("Krita Version: ").append(KritaVersionWrapper::versionString(true)) .append(", Qt version compiled: ").append(QT_VERSION_STR) .append(", loaded: ").append(qVersion()) .append(". Process ID: ") .append(QString::number(qApp->applicationPid())).append("\n"); KritaAndQtVersion.append("-- -- -- -- -- -- -- --\n"); s_instance->d->logFile.write(KritaAndQtVersion.toUtf8()); s_instance->d->logFile.flush(); log(QString("Style: %1. Available styles: %2") .arg(qApp->style()->objectName(), QStyleFactory::keys().join(", "))); } QString KisUsageLogger::screenInformation() { QList screens = qApp->screens(); QString info; info.append("Display Information"); info.append("\nNumber of screens: ").append(QString::number(screens.size())); for (int i = 0; i < screens.size(); ++i ) { QScreen *screen = screens[i]; info.append("\n\tScreen: ").append(QString::number(i)); info.append("\n\t\tName: ").append(screen->name()); info.append("\n\t\tDepth: ").append(QString::number(screen->depth())); info.append("\n\t\tScale: ").append(QString::number(screen->devicePixelRatio())); info.append("\n\t\tResolution in pixels: ").append(QString::number(screen->geometry().width())) .append("x") .append(QString::number(screen->geometry().height())); info.append("\n\t\tManufacturer: ").append(screen->manufacturer()); info.append("\n\t\tModel: ").append(screen->model()); info.append("\n\t\tRefresh Rate: ").append(QString::number(screen->refreshRate())); } info.append("\n"); return info; } void KisUsageLogger::rotateLog() { if (d->logFile.exists()) { { // Check for CLOSING SESSION d->logFile.open(QFile::ReadOnly); QString log = QString::fromUtf8(d->logFile.readAll()); if (!log.split(s_sectionHeader).last().contains("CLOSING SESSION")) { log.append("\nKRITA DID NOT CLOSE CORRECTLY\n"); QString crashLog = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QStringLiteral("/kritacrash.log"); if (QFileInfo(crashLog).exists()) { QFile f(crashLog); f.open(QFile::ReadOnly); QString crashes = QString::fromUtf8(f.readAll()); f.close(); QStringList crashlist = crashes.split("-------------------"); log.append(QString("\nThere were %1 crashes in total in the crash log.\n").arg(crashlist.size())); if (crashes.size() > 0) { log.append(crashlist.last()); } } d->logFile.close(); d->logFile.open(QFile::WriteOnly); d->logFile.write(log.toUtf8()); } d->logFile.flush(); d->logFile.close(); } { // Rotate d->logFile.open(QFile::ReadOnly); QString log = QString::fromUtf8(d->logFile.readAll()); int sectionCount = log.count(s_sectionHeader); int nextSectionIndex = log.indexOf(s_sectionHeader, s_sectionHeader.length()); while(sectionCount >= s_maxLogs) { log = log.remove(0, log.indexOf(s_sectionHeader, nextSectionIndex)); nextSectionIndex = log.indexOf(s_sectionHeader, s_sectionHeader.length()); sectionCount = log.count(s_sectionHeader); } d->logFile.close(); d->logFile.open(QFile::WriteOnly); d->logFile.write(log.toUtf8()); d->logFile.flush(); d->logFile.close(); } } } diff --git a/libs/image/CMakeLists.txt b/libs/image/CMakeLists.txt index 810dee2f16..510e1844a4 100644 --- a/libs/image/CMakeLists.txt +++ b/libs/image/CMakeLists.txt @@ -1,387 +1,396 @@ add_subdirectory( tests ) add_subdirectory( tiles3 ) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty ${CMAKE_CURRENT_SOURCE_DIR}/brushengine ${CMAKE_CURRENT_SOURCE_DIR}/commands ${CMAKE_CURRENT_SOURCE_DIR}/commands_new ${CMAKE_CURRENT_SOURCE_DIR}/filter ${CMAKE_CURRENT_SOURCE_DIR}/floodfill ${CMAKE_CURRENT_SOURCE_DIR}/generator ${CMAKE_CURRENT_SOURCE_DIR}/layerstyles ${CMAKE_CURRENT_SOURCE_DIR}/processing ${CMAKE_SOURCE_DIR}/sdk/tests ) include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR} ) if(FFTW3_FOUND) include_directories(${FFTW3_INCLUDE_DIR}) endif() +if(LIBMYPAINT_FOUND) + include_directories(${LIBMYPAINT_INCLUDE_DIR}) + link_directories(${LIBMYPAINT_LIBRARIES}) +endif() + if(HAVE_VC) include_directories(SYSTEM ${Vc_INCLUDE_DIR} ${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS}) ko_compile_for_all_implementations(__per_arch_circle_mask_generator_objs kis_brush_mask_applicator_factories.cpp) else() set(__per_arch_circle_mask_generator_objs kis_brush_mask_applicator_factories.cpp) endif() set(kritaimage_LIB_SRCS tiles3/kis_tile.cc tiles3/kis_tile_data.cc tiles3/kis_tile_data_store.cc tiles3/kis_tile_data_pooler.cc tiles3/kis_tiled_data_manager.cc tiles3/KisTiledExtentManager.cpp tiles3/kis_memento_manager.cc tiles3/kis_hline_iterator.cpp tiles3/kis_vline_iterator.cpp tiles3/kis_random_accessor.cc tiles3/swap/kis_abstract_compression.cpp tiles3/swap/kis_lzf_compression.cpp tiles3/swap/kis_abstract_tile_compressor.cpp tiles3/swap/kis_legacy_tile_compressor.cpp tiles3/swap/kis_tile_compressor_2.cpp tiles3/swap/kis_chunk_allocator.cpp tiles3/swap/kis_memory_window.cpp tiles3/swap/kis_swapped_data_store.cpp tiles3/swap/kis_tile_data_swapper.cpp kis_distance_information.cpp kis_painter.cc kis_painter_blt_multi_fixed.cpp kis_marker_painter.cpp KisPrecisePaintDeviceWrapper.cpp kis_progress_updater.cpp brushengine/kis_paint_information.cc brushengine/kis_random_source.cpp brushengine/KisPerStrokeRandomSource.cpp brushengine/kis_stroke_random_source.cpp brushengine/kis_paintop.cc brushengine/kis_paintop_factory.cpp brushengine/kis_paintop_preset.cpp brushengine/kis_paintop_registry.cc brushengine/kis_paintop_settings.cpp brushengine/kis_paintop_settings_update_proxy.cpp brushengine/kis_paintop_utils.cpp brushengine/kis_no_size_paintop_settings.cpp brushengine/kis_locked_properties.cc brushengine/kis_locked_properties_proxy.cpp brushengine/kis_locked_properties_server.cpp brushengine/kis_paintop_config_widget.cpp brushengine/kis_uniform_paintop_property.cpp brushengine/kis_combo_based_paintop_property.cpp brushengine/kis_slider_based_paintop_property.cpp brushengine/kis_standard_uniform_properties_factory.cpp brushengine/KisStrokeSpeedMeasurer.cpp brushengine/KisPaintopSettingsIds.cpp commands/kis_deselect_global_selection_command.cpp commands/KisDeselectActiveSelectionCommand.cpp commands/kis_image_change_layers_command.cpp commands/kis_image_change_visibility_command.cpp commands/kis_image_command.cpp commands/kis_image_layer_add_command.cpp commands/kis_image_layer_move_command.cpp commands/kis_image_layer_remove_command.cpp commands/kis_image_layer_remove_command_impl.cpp commands/kis_image_lock_command.cpp commands/kis_node_command.cpp commands/kis_node_compositeop_command.cpp commands/kis_node_opacity_command.cpp commands/kis_node_property_list_command.cpp commands/kis_reselect_global_selection_command.cpp commands/KisReselectActiveSelectionCommand.cpp commands/kis_set_global_selection_command.cpp commands/KisNodeRenameCommand.cpp commands_new/kis_saved_commands.cpp commands_new/kis_processing_command.cpp commands_new/kis_image_resize_command.cpp commands_new/kis_image_set_resolution_command.cpp commands_new/kis_node_move_command2.cpp commands_new/kis_set_layer_style_command.cpp commands_new/kis_selection_move_command2.cpp commands_new/kis_update_command.cpp commands_new/kis_switch_current_time_command.cpp commands_new/kis_change_projection_color_command.cpp commands_new/kis_activate_selection_mask_command.cpp commands_new/kis_transaction_based_command.cpp commands_new/KisHoldUIUpdatesCommand.cpp commands_new/KisChangeChannelFlagsCommand.cpp commands_new/KisChangeChannelLockFlagsCommand.cpp commands_new/KisMergeLabeledLayersCommand.cpp processing/kis_do_nothing_processing_visitor.cpp processing/kis_simple_processing_visitor.cpp processing/kis_convert_color_space_processing_visitor.cpp processing/kis_assign_profile_processing_visitor.cpp processing/kis_crop_processing_visitor.cpp processing/kis_crop_selections_processing_visitor.cpp processing/kis_transform_processing_visitor.cpp processing/kis_mirror_processing_visitor.cpp processing/KisSelectionBasedProcessingHelper.cpp filter/kis_filter.cc filter/kis_filter_category_ids.cpp filter/kis_filter_configuration.cc filter/kis_color_transformation_configuration.cc filter/kis_filter_registry.cc filter/kis_color_transformation_filter.cc generator/kis_generator.cpp generator/kis_generator_layer.cpp generator/kis_generator_registry.cpp floodfill/kis_fill_interval_map.cpp floodfill/kis_scanline_fill.cpp lazybrush/kis_min_cut_worker.cpp lazybrush/kis_lazy_fill_tools.cpp lazybrush/kis_multiway_cut.cpp lazybrush/KisWatershedWorker.cpp lazybrush/kis_colorize_mask.cpp lazybrush/kis_colorize_stroke_strategy.cpp KisDelayedUpdateNodeInterface.cpp KisCroppedOriginalLayerInterface.cpp KisDecoratedNodeInterface.cpp kis_adjustment_layer.cc kis_selection_based_layer.cpp kis_node_filter_interface.cpp kis_base_accessor.cpp kis_base_node.cpp kis_base_processor.cpp kis_bookmarked_configuration_manager.cc KisBusyWaitBroker.cpp KisSafeBlockingQueueConnectionProxy.cpp kis_node_uuid_info.cpp kis_clone_layer.cpp kis_config_widget.cpp kis_convolution_kernel.cc kis_convolution_painter.cc kis_gaussian_kernel.cpp kis_edge_detection_kernel.cpp kis_cubic_curve.cpp kis_default_bounds.cpp kis_default_bounds_node_wrapper.cpp kis_default_bounds_base.cpp kis_effect_mask.cc kis_fast_math.cpp kis_fill_painter.cc kis_filter_mask.cpp kis_filter_strategy.cc kis_transform_mask.cpp kis_transform_mask_params_interface.cpp kis_recalculate_transform_mask_job.cpp kis_recalculate_generator_layer_job.cpp kis_transform_mask_params_factory_registry.cpp kis_safe_transform.cpp kis_gradient_painter.cc kis_gradient_shape_strategy.cpp kis_cached_gradient_shape_strategy.cpp kis_polygonal_gradient_shape_strategy.cpp kis_iterator_ng.cpp kis_async_merger.cpp kis_merge_walker.cc kis_updater_context.cpp kis_update_job_item.cpp kis_stroke_strategy_undo_command_based.cpp kis_simple_stroke_strategy.cpp KisRunnableBasedStrokeStrategy.cpp KisRunnableStrokeJobDataBase.cpp KisRunnableStrokeJobData.cpp KisRunnableStrokeJobsInterface.cpp KisFakeRunnableStrokeJobsExecutor.cpp kis_stroke_job_strategy.cpp kis_stroke_strategy.cpp kis_stroke.cpp kis_strokes_queue.cpp KisStrokesQueueMutatedJobInterface.cpp kis_simple_update_queue.cpp kis_update_scheduler.cpp kis_queues_progress_updater.cpp kis_composite_progress_proxy.cpp kis_sync_lod_cache_stroke_strategy.cpp kis_lod_capable_layer_offset.cpp kis_update_time_monitor.cpp KisImageConfigNotifier.cpp kis_group_layer.cc kis_count_visitor.cpp kis_histogram.cc kis_image_interfaces.cpp kis_image_animation_interface.cpp kis_time_range.cpp kis_node_graph_listener.cpp kis_image.cc kis_image_signal_router.cpp KisImageSignals.cpp kis_image_config.cpp kis_projection_updates_filter.cpp kis_suspend_projection_updates_stroke_strategy.cpp kis_regenerate_frame_stroke_strategy.cpp kis_switch_time_stroke_strategy.cpp kis_crop_saved_extra_data.cpp kis_timed_signal_threshold.cpp kis_layer.cc kis_indirect_painting_support.cpp kis_abstract_projection_plane.cpp kis_layer_projection_plane.cpp kis_layer_utils.cpp kis_mask_projection_plane.cpp kis_projection_leaf.cpp KisSafeNodeProjectionStore.cpp kis_mask.cc kis_base_mask_generator.cpp kis_rect_mask_generator.cpp kis_circle_mask_generator.cpp kis_gauss_circle_mask_generator.cpp kis_gauss_rect_mask_generator.cpp ${__per_arch_circle_mask_generator_objs} kis_curve_circle_mask_generator.cpp kis_curve_rect_mask_generator.cpp kis_math_toolbox.cpp kis_memory_statistics_server.cpp kis_name_server.cpp kis_node.cpp kis_node_facade.cpp kis_node_progress_proxy.cpp kis_busy_progress_indicator.cpp kis_node_visitor.cpp kis_paint_device.cc kis_paint_device_debug_utils.cpp kis_fixed_paint_device.cpp KisOptimizedByteArray.cpp kis_paint_layer.cc kis_perspective_math.cpp kis_pixel_selection.cpp kis_processing_information.cpp kis_properties_configuration.cc kis_random_accessor_ng.cpp kis_random_generator.cc kis_random_sub_accessor.cpp kis_wrapped_random_accessor.cpp kis_selection.cc KisSelectionUpdateCompressor.cpp kis_selection_mask.cpp kis_update_outline_job.cpp kis_update_selection_job.cpp kis_serializable_configuration.cc kis_transaction_data.cpp kis_transform_worker.cc kis_perspectivetransform_worker.cpp bsplines/kis_bspline_1d.cpp bsplines/kis_bspline_2d.cpp bsplines/kis_nu_bspline_2d.cpp kis_warptransform_worker.cc kis_cage_transform_worker.cpp kis_liquify_transform_worker.cpp kis_green_coordinates_math.cpp kis_transparency_mask.cc kis_undo_adapter.cpp kis_macro_based_undo_store.cpp kis_surrogate_undo_adapter.cpp kis_legacy_undo_adapter.cpp kis_post_execution_undo_adapter.cpp kis_processing_visitor.cpp kis_processing_applicator.cpp krita_utils.cpp kis_outline_generator.cpp kis_layer_composition.cpp kis_selection_filters.cpp KisProofingConfiguration.h KisRecycleProjectionsJob.cpp kis_keyframe.cpp kis_keyframe_channel.cpp kis_keyframe_commands.cpp kis_scalar_keyframe_channel.cpp kis_raster_keyframe_channel.cpp kis_onion_skin_compositor.cpp kis_onion_skin_cache.cpp kis_idle_watcher.cpp kis_psd_layer_style.cpp kis_layer_properties_icons.cpp layerstyles/kis_multiple_projection.cpp layerstyles/kis_layer_style_filter.cpp layerstyles/kis_layer_style_filter_environment.cpp layerstyles/kis_layer_style_filter_projection_plane.cpp layerstyles/kis_layer_style_projection_plane.cpp layerstyles/kis_ls_drop_shadow_filter.cpp layerstyles/kis_ls_satin_filter.cpp layerstyles/kis_ls_stroke_filter.cpp layerstyles/kis_ls_bevel_emboss_filter.cpp layerstyles/kis_ls_overlay_filter.cpp layerstyles/kis_ls_utils.cpp layerstyles/gimp_bump_map.cpp layerstyles/KisLayerStyleKnockoutBlower.cpp KisProofingConfiguration.cpp kis_node_query_path.cc ) set(einspline_SRCS 3rdparty/einspline/bspline_create.cpp 3rdparty/einspline/bspline_data.cpp 3rdparty/einspline/multi_bspline_create.cpp 3rdparty/einspline/nubasis.cpp 3rdparty/einspline/nubspline_create.cpp 3rdparty/einspline/nugrid.cpp ) add_library(kritaimage SHARED ${kritaimage_LIB_SRCS} ${einspline_SRCS}) generate_export_header(kritaimage BASE_NAME kritaimage) target_link_libraries(kritaimage PUBLIC kritaversion kritawidgets kritaglobal kritapsd kritaodf kritapigment kritacommand kritawidgetutils kritametadata Qt5::Concurrent ) target_link_libraries(kritaimage PUBLIC ${Boost_SYSTEM_LIBRARY}) if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB) if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB) target_link_libraries(kritaimage PUBLIC atomic) endif() endif() if(OPENEXR_FOUND) target_link_libraries(kritaimage PUBLIC ${OPENEXR_LIBRARIES}) endif() if(FFTW3_FOUND) target_link_libraries(kritaimage PRIVATE ${FFTW3_LIBRARIES}) endif() +if(LIBMYPAINT_FOUND) + target_link_libraries(kritaimage PUBLIC mypaint) +endif() + if(HAVE_VC) target_link_libraries(kritaimage PUBLIC ${Vc_LIBRARIES}) endif() if (NOT GSL_FOUND) message (WARNING "KRITA WARNING! No GNU Scientific Library was found! Krita's Shaped Gradients might be non-normalized! Please install GSL library.") else () target_link_libraries(kritaimage PRIVATE ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES}) endif () target_include_directories(kritaimage PUBLIC $ $ $ $ $ ) set_target_properties(kritaimage PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritaimage ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/libs/image/brushengine/kis_paintop_preset.cpp b/libs/image/brushengine/kis_paintop_preset.cpp index 59d3f0aa2c..c78e645cc5 100644 --- a/libs/image/brushengine/kis_paintop_preset.cpp +++ b/libs/image/brushengine/kis_paintop_preset.cpp @@ -1,425 +1,452 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2008 * Copyright (C) Sven Langkamp , (C) 2009 * * 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 #include #include #include #include #include "kis_paintop_registry.h" #include "kis_painter.h" #include #include "kis_paint_device.h" #include "kis_image.h" #include "kis_paintop_settings_update_proxy.h" #include #include +#include struct Q_DECL_HIDDEN KisPaintOpPreset::Private { Private() : settings(0), dirtyPreset(false) { } KisPaintOpSettingsSP settings; bool dirtyPreset; QScopedPointer updateProxy; }; KisPaintOpPreset::KisPaintOpPreset() : KoResource(QString()) , m_d(new Private) { } KisPaintOpPreset::KisPaintOpPreset(const QString & fileName) : KoResource(fileName) , m_d(new Private) { } KisPaintOpPreset::~KisPaintOpPreset() { delete m_d; } KisPaintOpPresetSP KisPaintOpPreset::clone() const { KisPaintOpPresetSP preset(new KisPaintOpPreset()); if (settings()) { preset->setSettings(settings()); // the settings are cloned inside! } preset->setDirty(isDirty()); // only valid if we could clone the settings preset->setValid(settings()); preset->setPaintOp(paintOp()); preset->setName(name()); preset->setImage(image()); preset->settings()->setPreset(KisPaintOpPresetWSP(preset)); Q_ASSERT(preset->valid()); return preset; } void KisPaintOpPreset::setDirty(bool value) { m_d->dirtyPreset = value; } bool KisPaintOpPreset::isDirty() const { return m_d->dirtyPreset; } void KisPaintOpPreset::setPaintOp(const KoID & paintOp) { Q_ASSERT(m_d->settings); m_d->settings->setProperty("paintop", paintOp.id()); } KoID KisPaintOpPreset::paintOp() const { Q_ASSERT(m_d->settings); return KoID(m_d->settings->getString("paintop")); } void KisPaintOpPreset::setOptionsWidget(KisPaintOpConfigWidget* widget) { if (m_d->settings) { m_d->settings->setOptionsWidget(widget); if (widget) { widget->setConfigurationSafe(m_d->settings); } } } void KisPaintOpPreset::setSettings(KisPaintOpSettingsSP settings) { Q_ASSERT(settings); Q_ASSERT(!settings->getString("paintop", QString()).isEmpty()); DirtyStateSaver dirtyStateSaver(this); KisPaintOpConfigWidget *oldOptionsWidget = 0; if (m_d->settings) { oldOptionsWidget = m_d->settings->optionsWidget(); m_d->settings->setOptionsWidget(0); m_d->settings->setPreset(0); m_d->settings = 0; } if (settings) { m_d->settings = settings->clone(); m_d->settings->setPreset(KisPaintOpPresetWSP(this)); if (oldOptionsWidget) { oldOptionsWidget->setConfigurationSafe(m_d->settings); m_d->settings->setOptionsWidget(oldOptionsWidget); } } setValid(m_d->settings); if (m_d->updateProxy) { m_d->updateProxy->notifyUniformPropertiesChanged(); m_d->updateProxy->notifySettingsChanged(); } } KisPaintOpSettingsSP KisPaintOpPreset::settings() const { Q_ASSERT(m_d->settings); Q_ASSERT(!m_d->settings->getString("paintop", QString()).isEmpty()); return m_d->settings; } bool KisPaintOpPreset::load() { dbgImage << "Load preset " << filename(); setValid(false); if (filename().isEmpty()) { return false; } QIODevice *dev = 0; QByteArray ba; if (filename().startsWith("bundle://")) { QString bn = filename().mid(9); int pos = bn.lastIndexOf(":"); QString fn = bn.right(bn.size() - pos - 1); bn = bn.left(pos); QScopedPointer resourceStore(KoStore::createStore(bn, KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip)); if (!resourceStore || resourceStore->bad()) { warnKrita << "Could not open store on bundle" << bn; return false; } if (resourceStore->isOpen()) resourceStore->close(); if (!resourceStore->open(fn)) { warnKrita << "Could not open preset" << fn << "in bundle" << bn; return false; } ba = resourceStore->device()->readAll(); dev = new QBuffer(&ba); resourceStore->close(); } else { dev = new QFile(filename()); if (dev->size() == 0) { delete dev; return false; } if (!dev->open(QIODevice::ReadOnly)) { warnKrita << "Can't open file " << filename(); delete dev; return false; } } - bool res = loadFromDevice(dev); + bool res = false; + if(filename().endsWith(".myb")) { + // res = loadMYB(dev); + } + else { + res = loadFromDevice(dev); + } delete dev; setValid(res); setDirty(false); return res; } bool KisPaintOpPreset::loadFromDevice(QIODevice *dev) { QImageReader reader(dev, "PNG"); QString version = reader.text("version"); QString preset = reader.text("preset"); dbgImage << version; if (version != "2.2") { return false; } QImage img; if (!reader.read(&img)) { dbgImage << "Fail to decode PNG"; return false; } //Workaround for broken presets //Presets was saved with nested cdata section preset.replace(""); preset.replace("]]>", ""); QDomDocument doc; if (!doc.setContent(preset)) { return false; } fromXML(doc.documentElement()); if (!m_d->settings) { return false; } setValid(true); setImage(img); return true; } +bool KisPaintOpPreset::loadMYB(QIODevice *dev) { + + QString pngFilePath = filename(); + pngFilePath = pngFilePath.remove(filename().size()-4, 4); + pngFilePath = pngFilePath + "_prev.png"; + QIODevice *imgDev = new QFile(pngFilePath); + + QImageReader reader(imgDev, "PNG"); + + QImage img; + if (!reader.read(&img)) { + dbgImage << "Fail to decode PNG"; + return false; + } + + setValid(true); + setImage(img); + return true; +} + bool KisPaintOpPreset::save() { if (filename().isEmpty()) return false; QString paintopid = m_d->settings->getString("paintop", QString()); if (paintopid.isEmpty()) return false; QFile f(filename()); f.open(QFile::WriteOnly); return saveToDevice(&f); } void KisPaintOpPreset::toXML(QDomDocument& doc, QDomElement& elt) const { QString paintopid = m_d->settings->getString("paintop", QString()); elt.setAttribute("paintopid", paintopid); elt.setAttribute("name", name()); // sanitize the settings bool hasTexture = m_d->settings->getBool("Texture/Pattern/Enabled"); if (!hasTexture) { Q_FOREACH (const QString & key, m_d->settings->getProperties().keys()) { if (key.startsWith("Texture") && key != "Texture/Pattern/Enabled") { m_d->settings->removeProperty(key); } } } m_d->settings->toXML(doc, elt); } void KisPaintOpPreset::fromXML(const QDomElement& presetElt) { setName(presetElt.attribute("name")); QString paintopid = presetElt.attribute("paintopid"); if (paintopid.isEmpty()) { dbgImage << "No paintopid attribute"; setValid(false); return; } if (KisPaintOpRegistry::instance()->get(paintopid) == 0) { dbgImage << "No paintop " << paintopid; setValid(false); return; } KoID id(paintopid, QString()); KisPaintOpSettingsSP settings = KisPaintOpRegistry::instance()->settings(id); if (!settings) { setValid(false); warnKrita << "Could not load settings for preset" << paintopid; return; } settings->fromXML(presetElt); // sanitize the settings bool hasTexture = settings->getBool("Texture/Pattern/Enabled"); if (!hasTexture) { Q_FOREACH (const QString & key, settings->getProperties().keys()) { if (key.startsWith("Texture") && key != "Texture/Pattern/Enabled") { settings->removeProperty(key); } } } setSettings(settings); } bool KisPaintOpPreset::saveToDevice(QIODevice *dev) const { QImageWriter writer(dev, "PNG"); QDomDocument doc; QDomElement root = doc.createElement("Preset"); toXML(doc, root); doc.appendChild(root); writer.setText("version", "2.2"); writer.setText("preset", doc.toString()); QImage img; if (image().isNull()) { img = QImage(1, 1, QImage::Format_RGB32); } else { img = image(); } m_d->dirtyPreset = false; KoResource::saveToDevice(dev); return writer.write(img); } KisPaintopSettingsUpdateProxy* KisPaintOpPreset::updateProxy() const { if (!m_d->updateProxy) { m_d->updateProxy.reset(new KisPaintopSettingsUpdateProxy()); } return m_d->updateProxy.data(); } KisPaintopSettingsUpdateProxy* KisPaintOpPreset::updateProxyNoCreate() const { return m_d->updateProxy.data(); } QList KisPaintOpPreset::uniformProperties() { return m_d->settings->uniformProperties(m_d->settings); } bool KisPaintOpPreset::hasMaskingPreset() const { return m_d->settings && m_d->settings->hasMaskingSettings(); } KisPaintOpPresetSP KisPaintOpPreset::createMaskingPreset() const { KisPaintOpPresetSP result; if (m_d->settings && m_d->settings->hasMaskingSettings()) { result = new KisPaintOpPreset(); result->setSettings(m_d->settings->createMaskingSettings()); if (!result->valid()) { result.clear(); } } return result; } KisPaintOpPreset::UpdatedPostponer::UpdatedPostponer(KisPaintOpPreset *preset) : m_updateProxy(preset->updateProxyNoCreate()) { if (m_updateProxy) { m_updateProxy->postponeSettingsChanges(); } } KisPaintOpPreset::UpdatedPostponer::~UpdatedPostponer() { if (m_updateProxy) { m_updateProxy->unpostponeSettingsChanges(); } } diff --git a/libs/image/brushengine/kis_paintop_preset.h b/libs/image/brushengine/kis_paintop_preset.h index 3cfbd9c40a..b87abc7d1d 100644 --- a/libs/image/brushengine/kis_paintop_preset.h +++ b/libs/image/brushengine/kis_paintop_preset.h @@ -1,154 +1,156 @@ /* 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_PRESET_H #define KIS_PAINTOP_PRESET_H #include #include "KoID.h" #include "kis_types.h" #include "kis_shared.h" #include "kritaimage_export.h" #include class KisPaintopSettingsUpdateProxy; class KisPaintOpConfigWidget; /** * A KisPaintOpPreset contains a particular set of settings * associated with a paintop, like brush, paintopsettings. * A new property in this class is to make it dirty. That means the * user can now temporarily save any tweaks in the Preset throughout * the session. The Dirty Preset setting/unsetting is handled by KisPaintOpPresetSettings */ class KRITAIMAGE_EXPORT KisPaintOpPreset : public KoResource, public KisShared { public: KisPaintOpPreset(); KisPaintOpPreset(const QString& filename); ~KisPaintOpPreset() override; KisPaintOpPresetSP clone() const; /// set the id of the paintop plugin void setPaintOp(const KoID & paintOp); /// return the id of the paintop plugin KoID paintOp() const; /// replace the current settings object with the specified settings void setSettings(KisPaintOpSettingsSP settings); void setOriginalSettings(KisPaintOpSettingsSP originalSettings); /// return the settings that define this paintop preset KisPaintOpSettingsSP settings() const; KisPaintOpSettingsSP originalSettings() const; bool load() override; bool loadFromDevice(QIODevice *dev) override; bool save() override; bool saveToDevice(QIODevice* dev) const override; void toXML(QDomDocument& doc, QDomElement& elt) const; void fromXML(const QDomElement& elt); + bool loadMYB(QIODevice* dev); + bool removable() const { return true; } QString defaultFileExtension() const override { return ".kpp"; } /// Mark the preset as modified but not saved void setDirty(bool value); /// @return true if the preset has been modified, but not saved bool isDirty() const; /** * Never use manual save/restore calls to * isPresetDirty()/setPresetDirty()! They will lead to * hard-to-tack-down bugs when the dirty state will not be * restored on jumps like 'return', 'break' or exception. */ class KRITAIMAGE_EXPORT DirtyStateSaver { public: DirtyStateSaver(KisPaintOpPreset *preset) : m_preset(preset), m_isDirty(preset->isDirty()) { } ~DirtyStateSaver() { m_preset->setDirty(m_isDirty); } private: KisPaintOpPreset *m_preset; bool m_isDirty; }; /** * @brief The UpdatedPostponer class * @see KisPaintopSettingsUpdateProxy::postponeSettingsChanges() */ class KRITAIMAGE_EXPORT UpdatedPostponer{ public: UpdatedPostponer(KisPaintOpPreset *preset); ~UpdatedPostponer(); private: KisPaintopSettingsUpdateProxy *m_updateProxy; }; void setOptionsWidget(KisPaintOpConfigWidget *widget); KisPaintopSettingsUpdateProxy* updateProxy() const; KisPaintopSettingsUpdateProxy* updateProxyNoCreate() const; QList uniformProperties(); /** * @return true if this preset demands a secondary masked brush running * alongside it */ bool hasMaskingPreset() const; /** * @return a newly created preset of the masked brush that should be run * alongside the current brush */ KisPaintOpPresetSP createMaskingPreset() const; private: struct Private; Private * const m_d; }; Q_DECLARE_METATYPE(KisPaintOpPresetSP) #endif diff --git a/libs/ui/KisResourceServerProvider.cpp b/libs/ui/KisResourceServerProvider.cpp index 5360352ce8..602fe77054 100644 --- a/libs/ui/KisResourceServerProvider.cpp +++ b/libs/ui/KisResourceServerProvider.cpp @@ -1,120 +1,120 @@ /* * kis_resourceserver.cc - part of KImageShop * * Copyright (c) 1999 Matthias Elter * Copyright (c) 2003 Patrick Julien * Copyright (c) 2005 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 "KisResourceServerProvider.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Q_GLOBAL_STATIC(KisResourceServerProvider, s_instance) typedef KoResourceServerSimpleConstruction > KisPaintOpPresetResourceServer; typedef KoResourceServerAdapter > KisPaintOpPresetResourceServerAdapter; KisResourceServerProvider::KisResourceServerProvider() { KisBrushServer *brushServer = KisBrushServer::instance(); - m_paintOpPresetServer = new KisPaintOpPresetResourceServer("kis_paintoppresets", "*.kpp"); + m_paintOpPresetServer = new KisPaintOpPresetResourceServer("kis_paintoppresets", "*.kpp:*.myb"); m_paintOpPresetServer->loadResources(KoResourceServerProvider::blacklistFileNames(m_paintOpPresetServer->fileNames(), m_paintOpPresetServer->blackListedFiles())); m_workspaceServer = new KoResourceServerSimpleConstruction("kis_workspaces", "*.kws"); m_workspaceServer->loadResources(KoResourceServerProvider::blacklistFileNames(m_workspaceServer->fileNames(), m_workspaceServer->blackListedFiles())); m_windowLayoutServer = new KoResourceServerSimpleConstruction("kis_windowlayouts", "*.kwl"); m_windowLayoutServer->loadResources(KoResourceServerProvider::blacklistFileNames(m_windowLayoutServer->fileNames(), m_windowLayoutServer->blackListedFiles())); m_sessionServer = new KoResourceServerSimpleConstruction("kis_sessions", "*.ksn"); m_sessionServer->loadResources(KoResourceServerProvider::blacklistFileNames(m_sessionServer->fileNames(), m_sessionServer->blackListedFiles())); m_layerStyleCollectionServer = new KoResourceServerSimpleConstruction("psd_layer_style_collections", "*.asl"); m_layerStyleCollectionServer->loadResources(KoResourceServerProvider::blacklistFileNames(m_layerStyleCollectionServer->fileNames(), m_layerStyleCollectionServer->blackListedFiles())); connect(this, SIGNAL(notifyBrushBlacklistCleanup()), brushServer, SLOT(slotRemoveBlacklistedResources())); } KisResourceServerProvider::~KisResourceServerProvider() { delete m_paintOpPresetServer; delete m_workspaceServer; delete m_sessionServer; delete m_windowLayoutServer; delete m_layerStyleCollectionServer; } KisResourceServerProvider* KisResourceServerProvider::instance() { return s_instance; } KisPaintOpPresetResourceServer* KisResourceServerProvider::paintOpPresetServer() { return m_paintOpPresetServer; } KoResourceServer< KisWorkspaceResource >* KisResourceServerProvider::workspaceServer() { return m_workspaceServer; } KoResourceServer< KisWindowLayoutResource >* KisResourceServerProvider::windowLayoutServer() { return m_windowLayoutServer; } KoResourceServer< KisSessionResource >* KisResourceServerProvider::sessionServer() { return m_sessionServer; } KoResourceServer *KisResourceServerProvider::layerStyleCollectionServer() { return m_layerStyleCollectionServer; } void KisResourceServerProvider::brushBlacklistCleanup() { emit notifyBrushBlacklistCleanup(); } diff --git a/libs/widgetutils/KoResourcePaths.cpp b/libs/widgetutils/KoResourcePaths.cpp index 7cc3b4a3c5..ae27975a1b 100644 --- a/libs/widgetutils/KoResourcePaths.cpp +++ b/libs/widgetutils/KoResourcePaths.cpp @@ -1,602 +1,606 @@ /* * Copyright (c) 2015 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; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "KoResourcePaths.h" #include #include #include #include #include #include #include #include #include #include #include "kis_debug.h" Q_GLOBAL_STATIC(KoResourcePaths, s_instance) static QString cleanup(const QString &path) { return QDir::cleanPath(path); } static QStringList cleanup(const QStringList &pathList) { QStringList cleanedPathList; Q_FOREACH(const QString &path, pathList) { cleanedPathList << cleanup(path); } return cleanedPathList; } static QString cleanupDirs(const QString &path) { return QDir::cleanPath(path) + QDir::separator(); } static QStringList cleanupDirs(const QStringList &pathList) { QStringList cleanedPathList; Q_FOREACH(const QString &path, pathList) { cleanedPathList << cleanupDirs(path); } return cleanedPathList; } void appendResources(QStringList *dst, const QStringList &src, bool eliminateDuplicates) { Q_FOREACH (const QString &resource, src) { QString realPath = QDir::cleanPath(resource); if (!eliminateDuplicates || !dst->contains(realPath)) { *dst << realPath; } } } #ifdef Q_OS_WIN static const Qt::CaseSensitivity cs = Qt::CaseInsensitive; #else static const Qt::CaseSensitivity cs = Qt::CaseSensitive; #endif #ifdef Q_OS_MACOS #include #include #include #endif QString getInstallationPrefix() { #ifdef Q_OS_MACOS QString appPath = qApp->applicationDirPath(); dbgResources << "1" << appPath; appPath.chop(QString("MacOS/").length()); dbgResources << "2" << appPath; bool makeInstall = QDir(appPath + "/../../../share/kritaplugins").exists(); bool inBundle = QDir(appPath + "/Resources/kritaplugins").exists(); dbgResources << "3. After make install" << makeInstall; dbgResources << "4. In Bundle" << inBundle; QString bundlePath; if (inBundle) { bundlePath = appPath + "/"; } else if (makeInstall) { appPath.chop(QString("Contents/").length()); bundlePath = appPath + "/../../"; } else { qFatal("Cannot calculate the bundle path from the app path"); } dbgResources << ">>>>>>>>>>>" << bundlePath; return bundlePath; #else #ifdef Q_OS_QWIN QDir appdir(qApp->applicationDirPath()); // Corrects for mismatched case errors in path (qtdeclarative fails to load) wchar_t buffer[1024]; QString absolute = appdir.absolutePath(); DWORD rv = ::GetShortPathName((wchar_t*)absolute.utf16(), buffer, 1024); rv = ::GetLongPathName(buffer, buffer, 1024); QString correctedPath((QChar *)buffer); appdir.setPath(correctedPath); appdir.cdUp(); return appdir.canonicalPath(); #else #ifdef Q_OS_ANDROID // qApp->applicationDirPath() isn't writable and android system won't allow // any files other than libraries return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/"; #else return qApp->applicationDirPath() + "/../"; #endif #endif #endif } class Q_DECL_HIDDEN KoResourcePaths::Private { public: QMap absolutes; // For each resource type, the list of absolute paths, from most local (most priority) to most global QMap relatives; // Same with relative paths QMutex relativesMutex; QMutex absolutesMutex; QStringList aliases(const QString &type) { QStringList r; QStringList a; relativesMutex.lock(); if (relatives.contains(type)) { r += relatives[type]; } relativesMutex.unlock(); dbgResources << "\trelatives" << r; absolutesMutex.lock(); if (absolutes.contains(type)) { a += absolutes[type]; } dbgResources << "\tabsolutes" << a; absolutesMutex.unlock(); return r + a; } QStandardPaths::StandardLocation mapTypeToQStandardPaths(const QString &type) { if (type == "tmp") { return QStandardPaths::TempLocation; } else if (type == "appdata") { return QStandardPaths::AppDataLocation; } else if (type == "data") { return QStandardPaths::AppDataLocation; } else if (type == "cache") { return QStandardPaths::CacheLocation; } else if (type == "locale") { return QStandardPaths::AppDataLocation; } else { return QStandardPaths::AppDataLocation; } } }; KoResourcePaths::KoResourcePaths() : d(new Private) { } KoResourcePaths::~KoResourcePaths() { } QString KoResourcePaths::getApplicationRoot() { return getInstallationPrefix(); } void KoResourcePaths::addResourceType(const char *type, const char *basetype, const QString &relativeName, bool priority) { s_instance->addResourceTypeInternal(QString::fromLatin1(type), QString::fromLatin1(basetype), relativeName, priority); } void KoResourcePaths::addResourceDir(const char *type, const QString &dir, bool priority) { s_instance->addResourceDirInternal(QString::fromLatin1(type), dir, priority); } QString KoResourcePaths::findResource(const char *type, const QString &fileName) { return cleanup(s_instance->findResourceInternal(QString::fromLatin1(type), fileName)); } QStringList KoResourcePaths::findDirs(const char *type) { return cleanupDirs(s_instance->findDirsInternal(QString::fromLatin1(type))); } QStringList KoResourcePaths::findAllResources(const char *type, const QString &filter, SearchOptions options) { return cleanup(s_instance->findAllResourcesInternal(QString::fromLatin1(type), filter, options)); } QStringList KoResourcePaths::resourceDirs(const char *type) { return cleanupDirs(s_instance->resourceDirsInternal(QString::fromLatin1(type))); } QString KoResourcePaths::saveLocation(const char *type, const QString &suffix, bool create) { return cleanupDirs(s_instance->saveLocationInternal(QString::fromLatin1(type), suffix, create)); } QString KoResourcePaths::locate(const char *type, const QString &filename) { return cleanup(s_instance->locateInternal(QString::fromLatin1(type), filename)); } QString KoResourcePaths::locateLocal(const char *type, const QString &filename, bool createDir) { return cleanup(s_instance->locateLocalInternal(QString::fromLatin1(type), filename, createDir)); } void KoResourcePaths::addResourceTypeInternal(const QString &type, const QString &basetype, const QString &relativename, bool priority) { Q_UNUSED(basetype); if (relativename.isEmpty()) return; QString copy = relativename; Q_ASSERT(basetype == "data"); if (!copy.endsWith(QLatin1Char('/'))) { copy += QLatin1Char('/'); } d->relativesMutex.lock(); QStringList &rels = d->relatives[type]; // find or insert if (!rels.contains(copy, cs)) { if (priority) { rels.prepend(copy); } else { rels.append(copy); } } d->relativesMutex.unlock(); dbgResources << "addResourceType: type" << type << "basetype" << basetype << "relativename" << relativename << "priority" << priority << d->relatives[type]; } void KoResourcePaths::addResourceDirInternal(const QString &type, const QString &absdir, bool priority) { if (absdir.isEmpty() || type.isEmpty()) return; // find or insert entry in the map QString copy = absdir; if (copy.at(copy.length() - 1) != QLatin1Char('/')) { copy += QLatin1Char('/'); } d->absolutesMutex.lock(); QStringList &paths = d->absolutes[type]; if (!paths.contains(copy, cs)) { if (priority) { paths.prepend(copy); } else { paths.append(copy); } } d->absolutesMutex.unlock(); dbgResources << "addResourceDir: type" << type << "absdir" << absdir << "priority" << priority << d->absolutes[type]; } QString KoResourcePaths::findResourceInternal(const QString &type, const QString &fileName) { QStringList aliases = d->aliases(type); dbgResources<< "aliases" << aliases << getApplicationRoot(); QString resource = QStandardPaths::locate(QStandardPaths::AppDataLocation, fileName, QStandardPaths::LocateFile); if (resource.isEmpty()) { Q_FOREACH (const QString &alias, aliases) { resource = QStandardPaths::locate(d->mapTypeToQStandardPaths(type), alias + '/' + fileName, QStandardPaths::LocateFile); dbgResources << "\t1" << resource; if (QFile::exists(resource)) { continue; } } } if (resource.isEmpty() || !QFile::exists(resource)) { QString approot = getApplicationRoot(); Q_FOREACH (const QString &alias, aliases) { resource = approot + "/share/" + alias + '/' + fileName; dbgResources << "\t2" << resource; if (QFile::exists(resource)) { continue; } } } if (resource.isEmpty() || !QFile::exists(resource)) { QString approot = getApplicationRoot(); Q_FOREACH (const QString &alias, aliases) { resource = approot + "/share/krita/" + alias + '/' + fileName; dbgResources << "\t3" << resource; if (QFile::exists(resource)) { continue; } } } if (resource.isEmpty() || !QFile::exists(resource)) { QString extraResourceDirs = qgetenv("EXTRA_RESOURCE_DIRS"); if (!extraResourceDirs.isEmpty()) { Q_FOREACH(const QString &extraResourceDir, extraResourceDirs.split(':', QString::SkipEmptyParts)) { if (aliases.isEmpty()) { resource = extraResourceDir + '/' + fileName; dbgResources<< "\t4" << resource; if (QFile::exists(resource)) { continue; } } else { Q_FOREACH (const QString &alias, aliases) { resource = extraResourceDir + '/' + alias + '/' + fileName; dbgResources<< "\t4" << resource; if (QFile::exists(resource)) { continue; } } } } } } dbgResources<< "findResource: type" << type << "filename" << fileName << "resource" << resource; Q_ASSERT(!resource.isEmpty()); return resource; } QStringList filesInDir(const QString &startdir, const QString & filter, bool recursive) { dbgResources << "filesInDir: startdir" << startdir << "filter" << filter << "recursive" << recursive; QStringList result; // First the entries in this path QStringList nameFilters; nameFilters << filter; const QStringList fileNames = QDir(startdir).entryList(nameFilters, QDir::Files | QDir::CaseSensitive, QDir::Name); dbgResources << "\tFound:" << fileNames.size() << ":" << fileNames; Q_FOREACH (const QString &fileName, fileNames) { QString file = startdir + '/' + fileName; result << file; } // And then everything underneath, if recursive is specified if (recursive) { const QStringList entries = QDir(startdir).entryList(QDir::Dirs | QDir::NoDotAndDotDot); Q_FOREACH (const QString &subdir, entries) { dbgResources << "\tGoing to look in subdir" << subdir << "of" << startdir; result << filesInDir(startdir + '/' + subdir, filter, recursive); } } return result; } QStringList KoResourcePaths::findDirsInternal(const QString &type) { QStringList aliases = d->aliases(type); dbgResources << type << aliases << d->mapTypeToQStandardPaths(type); QStringList dirs; QStringList standardDirs = QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), "", QStandardPaths::LocateDirectory); appendResources(&dirs, standardDirs, true); Q_FOREACH (const QString &alias, aliases) { QStringList aliasDirs = QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias + '/', QStandardPaths::LocateDirectory); appendResources(&dirs, aliasDirs, true); #ifdef Q_OS_MACOS dbgResources << "MAC:" << getApplicationRoot(); QStringList bundlePaths; bundlePaths << getApplicationRoot() + "/share/krita/" + alias; bundlePaths << getApplicationRoot() + "/../share/krita/" + alias; dbgResources << "bundlePaths" << bundlePaths; appendResources(&dirs, bundlePaths, true); Q_ASSERT(!dirs.isEmpty()); #endif QStringList fallbackPaths; fallbackPaths << getApplicationRoot() + "/share/" + alias; fallbackPaths << getApplicationRoot() + "/share/krita/" + alias; appendResources(&dirs, fallbackPaths, true); } dbgResources << "findDirs: type" << type << "resource" << dirs; return dirs; } QStringList KoResourcePaths::findAllResourcesInternal(const QString &type, const QString &_filter, SearchOptions options) const { dbgResources << "====================================================="; dbgResources << type << _filter << QStandardPaths::standardLocations(d->mapTypeToQStandardPaths(type)); bool recursive = options & KoResourcePaths::Recursive; dbgResources << "findAllResources: type" << type << "filter" << _filter << "recursive" << recursive; QStringList aliases = d->aliases(type); QString filter = _filter; // In cases where the filter is like "color-schemes/*.colors" instead of "*.kpp", used with unregistered resource types if (filter.indexOf('*') > 0) { aliases << filter.split('*').first(); filter = '*' + filter.split('*')[1]; dbgResources << "Split up alias" << aliases << "filter" << filter; } QStringList resources; if (aliases.isEmpty()) { QStringList standardResources = QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), filter, QStandardPaths::LocateFile); dbgResources << "standardResources" << standardResources; appendResources(&resources, standardResources, true); dbgResources << "1" << resources; } QString extraResourceDirs = qgetenv("EXTRA_RESOURCE_DIRS"); dbgResources << "extraResourceDirs" << extraResourceDirs; if (!extraResourceDirs.isEmpty()) { Q_FOREACH(const QString &extraResourceDir, extraResourceDirs.split(':', QString::SkipEmptyParts)) { if (aliases.isEmpty()) { appendResources(&resources, filesInDir(extraResourceDir + '/' + type, filter, recursive), true); } else { Q_FOREACH (const QString &alias, aliases) { appendResources(&resources, filesInDir(extraResourceDir + '/' + alias + '/', filter, recursive), true); } } } } dbgResources << "\tresources from qstandardpaths:" << resources.size(); Q_FOREACH (const QString &alias, aliases) { dbgResources << "\t\talias:" << alias; QStringList dirs; QFileInfo dirInfo(alias); if (dirInfo.exists() && dirInfo.isDir() && dirInfo.isAbsolute()) { dirs << alias; } else { dirs << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory) << getInstallationPrefix() + "share/" + alias + "/" << getInstallationPrefix() + "share/krita/" + alias + "/"; } + if(_filter.endsWith("*.myb")) { + dirs << "/usr/share" << "/home"; + } + Q_FOREACH (const QString &dir, dirs) { appendResources(&resources, filesInDir(dir, filter, recursive), true); } } dbgResources << "\tresources also from aliases:" << resources.size(); // if the original filter is "input/*", we only want share/input/* and share/krita/input/* here, but not // share/*. therefore, use _filter here instead of filter which was split into alias and "*". QFileInfo fi(_filter); QStringList prefixResources; prefixResources << filesInDir(getInstallationPrefix() + "share/" + fi.path(), fi.fileName(), false); prefixResources << filesInDir(getInstallationPrefix() + "share/krita/" + fi.path(), fi.fileName(), false); appendResources(&resources, prefixResources, true); dbgResources << "\tresources from installation:" << resources.size(); dbgResources << "====================================================="; return resources; } QStringList KoResourcePaths::resourceDirsInternal(const QString &type) { QStringList resourceDirs; QStringList aliases = d->aliases(type); Q_FOREACH (const QString &alias, aliases) { QStringList aliasDirs; aliasDirs << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory); aliasDirs << getInstallationPrefix() + "share/" + alias + "/" << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory); aliasDirs << getInstallationPrefix() + "share/krita/" + alias + "/" << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory); appendResources(&resourceDirs, aliasDirs, true); } dbgResources << "resourceDirs: type" << type << resourceDirs; return resourceDirs; } QString KoResourcePaths::saveLocationInternal(const QString &type, const QString &suffix, bool create) { QStringList aliases = d->aliases(type); QString path; if (aliases.size() > 0) { path = QStandardPaths::writableLocation(d->mapTypeToQStandardPaths(type)) + '/' + aliases.first(); } else { path = QStandardPaths::writableLocation(d->mapTypeToQStandardPaths(type)); if (!path.endsWith("krita")) { path += "/krita"; } if (!suffix.isEmpty()) { path += "/" + suffix; } } QDir d(path); if (!d.exists() && create) { d.mkpath(path); } dbgResources << "saveLocation: type" << type << "suffix" << suffix << "create" << create << "path" << path; return path; } QString KoResourcePaths::locateInternal(const QString &type, const QString &filename) { QStringList aliases = d->aliases(type); QStringList locations; if (aliases.isEmpty()) { locations << QStandardPaths::locate(d->mapTypeToQStandardPaths(type), filename, QStandardPaths::LocateFile); } Q_FOREACH (const QString &alias, aliases) { locations << QStandardPaths::locate(d->mapTypeToQStandardPaths(type), (alias.endsWith('/') ? alias : alias + '/') + filename, QStandardPaths::LocateFile); } dbgResources << "locate: type" << type << "filename" << filename << "locations" << locations; if (locations.size() > 0) { return locations.first(); } else { return ""; } } QString KoResourcePaths::locateLocalInternal(const QString &type, const QString &filename, bool createDir) { QString path = saveLocationInternal(type, "", createDir); dbgResources << "locateLocal: type" << type << "filename" << filename << "CreateDir" << createDir << "path" << path; return path + '/' + filename; } diff --git a/plugins/paintops/CMakeLists.txt b/plugins/paintops/CMakeLists.txt index cf3377e38b..174718a820 100644 --- a/plugins/paintops/CMakeLists.txt +++ b/plugins/paintops/CMakeLists.txt @@ -1,18 +1,19 @@ include_directories(libpaintop) add_subdirectory( libpaintop ) add_subdirectory( defaultpresets ) add_subdirectory( defaultpaintops ) add_subdirectory( hairy ) add_subdirectory( deform ) add_subdirectory( curvebrush ) add_subdirectory( spray ) add_subdirectory( filterop ) add_subdirectory( experiment ) add_subdirectory( particle ) add_subdirectory( gridbrush ) add_subdirectory( hatching) add_subdirectory( sketch ) add_subdirectory( colorsmudge ) add_subdirectory( roundmarker ) add_subdirectory( tangentnormal ) +add_subdirectory( mypaint ) diff --git a/plugins/paintops/mypaint/CMakeLists.txt b/plugins/paintops/mypaint/CMakeLists.txt new file mode 100644 index 0000000000..b1d592ba0d --- /dev/null +++ b/plugins/paintops/mypaint/CMakeLists.txt @@ -0,0 +1,29 @@ +if(LIBMYPAINT_FOUND) + include_directories(${LIBMYPAINT_INCLUDE_DIR}) + link_directories(${LIBMYPAINT_LIBRARIES}) +endif() + +set(kritamypaintop_SOURCES + my_paintop_plugin.cpp + kis_my_paintop.cpp + #kis_myop_option.cpp + kis_my_paintop_settings.cpp + kis_my_paintop_settings_widget.cpp + kis_mypaint_surface.cpp + kis_mypaint_brush.cpp +) + +#ki18n_wrap_ui(kritasketchpaintop_SOURCES wdgsketchoptions.ui ) + +add_library(kritamypaintop MODULE ${kritamypaintop_SOURCES}) + +target_link_libraries(kritamypaintop kritalibpaintop mypaint) + +install(TARGETS kritamypaintop DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) + + +########### install files ############### + +install( FILES krita-mypaint.png DESTINATION ${DATA_INSTALL_DIR}/krita/images) + + diff --git a/plugins/paintops/mypaint/kis_my_paintop.cpp b/plugins/paintops/mypaint/kis_my_paintop.cpp new file mode 100644 index 0000000000..5980d531ed --- /dev/null +++ b/plugins/paintops/mypaint/kis_my_paintop.cpp @@ -0,0 +1,78 @@ +#include "kis_my_paintop.h" + +#include "kis_paintop.h" +#include "kis_spacing_information.h" +#include "kis_my_paintop_settings.h" +#include +#include +#include +#include +#include + +#include +#include +#include + + +KisMyPaintOp::KisMyPaintOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image) + : KisPaintOp (painter) { + + m_node = node; + m_brush = new KisMyPaintBrush(this->painter()); + m_surface = new KisMyPaintSurface(this->painter(), m_node); + + m_brush->apply(settings); + m_brush->setColor(this->painter()->paintColor()); + + //mypaint_brush_set_base_value(m_brush->brush(), MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC, 2.0); + + m_settings = settings; + + dtime = -1; + m_radius = exp(3.3); +} + +KisMyPaintOp::~KisMyPaintOp() { + + free(m_brush->brush()); + free(m_surface->surface()); +} + +KisSpacingInformation KisMyPaintOp::paintAt(const KisPaintInformation& info) { + + if(!painter()) { + return KisSpacingInformation(1.0); + } + + if(dtime < 0) { + mypaint_brush_stroke_to(m_brush->brush(), m_surface->surface(), info.pos().x(), info.pos().y(), info.pressure(), + info.xTilt(), info.yTilt(), 1.0f); + + dtime = 0.015; + } + else { + dtime = (info.currentTime() - dtime)*0.001; + } + + mypaint_brush_stroke_to(m_brush->brush(), m_surface->surface(), info.pos().x(), info.pos().y(), info.pressure(), + info.xTilt(), info.yTilt(), dtime); + + dtime = info.currentTime(); + + const qreal lodScale = KisLodTransform::lodToScale(painter()->device()); + m_radius = exp(3.3)*lodScale; + return computeSpacing(info, lodScale); +} + +KisSpacingInformation KisMyPaintOp::updateSpacingImpl(const KisPaintInformation &info) const +{ + KisSpacingInformation spacingInfo = computeSpacing(info, KisLodTransform::lodToScale(painter()->device())); + return spacingInfo; +} + +KisSpacingInformation KisMyPaintOp::computeSpacing(const KisPaintInformation &info, qreal lodScale) const { + + return KisPaintOpPluginUtils::effectiveSpacing(m_radius*2, m_radius*2, + false, 0.0, false, m_radius*2, + true, 1, lodScale, nullptr, nullptr, info); +} diff --git a/plugins/paintops/mypaint/kis_my_paintop.h b/plugins/paintops/mypaint/kis_my_paintop.h new file mode 100644 index 0000000000..50df62dad3 --- /dev/null +++ b/plugins/paintops/mypaint/kis_my_paintop.h @@ -0,0 +1,41 @@ +#ifndef KIS_MY_PAINTOP_H_ +#define KIS_MY_PAINTOP_H_ + +#include +#include +#include "kis_mypaint_surface.h" +#include "kis_mypaint_brush.h" + +#include + +class KisPainter; + + +class KisMyPaintOp : public KisPaintOp +{ + +public: + + KisMyPaintOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image); + ~KisMyPaintOp() 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: + KisMyPaintBrush *m_brush; + KisMyPaintSurface *m_surface; + KisPaintOpSettingsSP m_settings; + KisNodeSP m_node; + double dtime, m_radius; +}; + +#endif // KIS_MY_PAINTOP_H_ diff --git a/plugins/paintops/mypaint/kis_my_paintop_settings.cpp b/plugins/paintops/mypaint/kis_my_paintop_settings.cpp new file mode 100644 index 0000000000..dfa4e510e4 --- /dev/null +++ b/plugins/paintops/mypaint/kis_my_paintop_settings.cpp @@ -0,0 +1,78 @@ +#include + +#include +#include + +#include "kis_my_paintop_settings.h" +///#include "kis_sprayop_option.h" + +struct KisMyPaintOpSettings::Private +{ + QList uniformProperties; +}; + + +KisMyPaintOpSettings::KisMyPaintOpSettings() + : KisOutlineGenerationPolicy(KisCurrentOutlineFetcher::SIZE_OPTION | + KisCurrentOutlineFetcher::ROTATION_OPTION), + m_d(new Private) +{ +} + +KisMyPaintOpSettings::~KisMyPaintOpSettings() +{ +} + +void KisMyPaintOpSettings::setPaintOpSize(qreal value) +{ +} + +qreal KisMyPaintOpSettings::paintOpSize() const +{ + return 40; +} + +bool KisMyPaintOpSettings::paintIncremental() +{ + return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP; +} + + +QPainterPath KisMyPaintOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) +{ + QPainterPath path; + + if (mode.isVisible) { + qreal finalScale = 1.0; + + const qreal radius = 0.5 * 40; + + QPainterPath realOutline; + realOutline.addEllipse(QPointF(), radius, radius); + + path = outlineFetcher()->fetchOutline(info, this, realOutline, mode, alignForZoom, finalScale); + + if (mode.showTiltDecoration) { + QPainterPath tiltLine = makeTiltIndicator(info, + realOutline.boundingRect().center(), + realOutline.boundingRect().width() * 0.5, + 3.0); + path.addPath(outlineFetcher()->fetchOutline(info, this, tiltLine, mode, alignForZoom, finalScale, 0.0, true, realOutline.boundingRect().center().x(), realOutline.boundingRect().center().y())); + } + } + + return path; +} + +#include +#include "kis_paintop_preset.h" +#include "kis_paintop_settings_update_proxy.h" +#include "kis_standard_uniform_properties_factory.h" + + +QList KisMyPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings) +{ + QList props = + listWeakToStrong(m_d->uniformProperties); + return props; +} diff --git a/plugins/paintops/mypaint/kis_my_paintop_settings.h b/plugins/paintops/mypaint/kis_my_paintop_settings.h new file mode 100644 index 0000000000..b7ab6cff07 --- /dev/null +++ b/plugins/paintops/mypaint/kis_my_paintop_settings.h @@ -0,0 +1,44 @@ +#ifndef KIS_MY_PAINTOP_SETTINGS_H_ +#define KIS_MY_PAINTOP_SETTINGS_H_ + +#include + +#include +#include + +#include +#include "kis_my_paintop_settings_widget.h" + + +class KisMyPaintOpSettings : public KisOutlineGenerationPolicy +{ +public: + KisMyPaintOpSettings(); + ~KisMyPaintOpSettings() override; + + void setPaintOpSize(qreal value) override; + qreal paintOpSize() const override; + + QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) override; + + QString modelName() const override { + return "airbrush"; + } + + bool paintIncremental() override; + +protected: + + QList uniformProperties(KisPaintOpSettingsSP settings) override; + +private: + Q_DISABLE_COPY(KisMyPaintOpSettings) + + struct Private; + const QScopedPointer m_d; + +}; + +typedef KisSharedPtr KisMyPaintOpSettingsSP; + +#endif diff --git a/plugins/paintops/mypaint/kis_my_paintop_settings_widget.cpp b/plugins/paintops/mypaint/kis_my_paintop_settings_widget.cpp new file mode 100644 index 0000000000..eff36b52d1 --- /dev/null +++ b/plugins/paintops/mypaint/kis_my_paintop_settings_widget.cpp @@ -0,0 +1,33 @@ +#include "kis_my_paintop_settings_widget.h" + +#include "kis_my_paintop_settings.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +KisMyPaintOpSettingsWidget:: KisMyPaintOpSettingsWidget(QWidget* parent) + : KisPaintOpSettingsWidget(parent) +{ +} + +KisMyPaintOpSettingsWidget::~ KisMyPaintOpSettingsWidget() +{ +} + +KisPropertiesConfigurationSP KisMyPaintOpSettingsWidget::configuration() const +{ + KisMyPaintOpSettings* config = new KisMyPaintOpSettings(); + config->setOptionsWidget(const_cast(this)); + config->setProperty("paintop", "mypaintbrush"); // XXX: make this a const id string + writeConfiguration(config); + return config; +} diff --git a/plugins/paintops/mypaint/kis_my_paintop_settings_widget.h b/plugins/paintops/mypaint/kis_my_paintop_settings_widget.h new file mode 100644 index 0000000000..96bccbcb1d --- /dev/null +++ b/plugins/paintops/mypaint/kis_my_paintop_settings_widget.h @@ -0,0 +1,18 @@ +#ifndef KIS_MYPAINTOP_SETTINGS_WIDGET_H_ +#define KIS_MYPAINTOP_SETTINGS_WIDGET_H_ + +#include + +class KisMyPaintOpSettingsWidget : public KisPaintOpSettingsWidget +{ + Q_OBJECT + +public: + KisMyPaintOpSettingsWidget(QWidget* parent = 0); + ~KisMyPaintOpSettingsWidget() override; + + KisPropertiesConfigurationSP configuration() const override; + +}; + +#endif diff --git a/plugins/paintops/mypaint/kis_mypaint_brush.cpp b/plugins/paintops/mypaint/kis_mypaint_brush.cpp new file mode 100644 index 0000000000..2a4728450b --- /dev/null +++ b/plugins/paintops/mypaint/kis_mypaint_brush.cpp @@ -0,0 +1,90 @@ +#include "kis_mypaint_brush.h" + +#include +#include + +KisMyPaintBrush::KisMyPaintBrush(KisPainter *painter, QObject *parent) : QObject(parent) +{ + m_painter = painter; + m_brush = mypaint_brush_new(); + mypaint_brush_from_defaults(m_brush); +} + +void KisMyPaintBrush::setColor(const KoColor color) { + + float hue, saturation, value; + qreal r, g, b; + + color.toQColor().getRgbF(&r ,&g ,&b); + RGBToHSV(r , g, b, &hue, &saturation, &value); + + mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_COLOR_H, (hue)/360); + mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_COLOR_S, (saturation)); + mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_COLOR_V, (value)); +} + +void KisMyPaintBrush::apply(KisPaintOpSettingsSP settings) { + +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC, -1.00); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_OPAQUE, 0.6); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_HARDNESS, 0.64); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_ERASER, false); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_SMUDGE, 0.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_COLOR_H, 0.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_COLOR_S, 0.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_COLOR_V, 0.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_COLORIZE, 0.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_LOCK_ALPHA, 0.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_SPEED1_GAMMA, 4.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_SPEED2_GAMMA, 4.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_ANTI_ALIASING, 1.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_RESTORE_COLOR, 0.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_SLOW_TRACKING, 0.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_SMUDGE_LENGTH, 0.5); +// //mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_SNAP_TO_PIXEL, false); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_CHANGE_COLOR_H, 0.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_CHANGE_COLOR_L, 0.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_CHANGE_COLOR_V, 0.185); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_TRACKING_NOISE, 0.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_DABS_PER_SECOND, 0.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_OFFSET_BY_SPEED, 0.17); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_OPAQUE_MULTIPLY, 0.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_SPEED1_SLOWNESS, 0.09); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_SPEED2_SLOWNESS, 1.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_STROKE_HOLDTIME, 0.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_DIRECTION_FILTER, 2.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_OFFSET_BY_RANDOM, 2.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_OPAQUE_LINEARIZE, 0.9); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_RADIUS_BY_RANDOM, 0.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_STROKE_THRESHOLD, 0.0); +// //mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_PRESSURE_GAIN_LOG, false); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_SMUDGE_RADIUS_LOG, 0.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_CHANGE_COLOR_HSL_S, 0.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_CHANGE_COLOR_HSV_S, 0.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_ELLIPTICAL_DAB_ANGLE, 90.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_ELLIPTICAL_DAB_RATIO, 1.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_CUSTOM_INPUT_SLOWNESS, 0.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_DABS_PER_BASIC_RADIUS, 0.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_SLOW_TRACKING_PER_DAB, 0.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_DABS_PER_ACTUAL_RADIUS, 2.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_OFFSET_BY_SPEED_SLOWNESS, 1.0); +// mypaint_brush_set_base_value(m_brush, MYPAINT_BRUSH_SETTING_STROKE_DURATION_LOGARITHMIC, 4.0); + + QIODevice *dev = new QFile("/usr/share/mypaint-data/1.0/brushes/experimental/bubble.myb"); + + if (!dev->open(QIODevice::ReadOnly)) { + qDebug() << "Can't open file "; + delete dev; + } + else { + QByteArray ba = dev->readAll(); + mypaint_brush_from_string(m_brush, ba); + } + mypaint_brush_new_stroke(m_brush); + +} + +MyPaintBrush* KisMyPaintBrush::brush() { + + return m_brush; +} diff --git a/plugins/paintops/mypaint/kis_mypaint_brush.h b/plugins/paintops/mypaint/kis_mypaint_brush.h new file mode 100644 index 0000000000..923512775f --- /dev/null +++ b/plugins/paintops/mypaint/kis_mypaint_brush.h @@ -0,0 +1,24 @@ +#ifndef KIS_MYPAINT_BRUSH_H +#define KIS_MYPAINT_BRUSH_H + +#include +#include +#include +#include +#include + +class KisMyPaintBrush : public QObject +{ + Q_OBJECT +public: + explicit KisMyPaintBrush(KisPainter *painter, QObject *parent = nullptr); + void setColor(const KoColor color); + void apply(KisPaintOpSettingsSP settings); + MyPaintBrush* brush(); + +private: + MyPaintBrush *m_brush; + KisPainter *m_painter; +}; + +#endif // KIS_MYPAINT_BRUSH_H diff --git a/plugins/paintops/mypaint/kis_mypaint_surface.cpp b/plugins/paintops/mypaint/kis_mypaint_surface.cpp new file mode 100644 index 0000000000..0d1e9ad209 --- /dev/null +++ b/plugins/paintops/mypaint/kis_mypaint_surface.cpp @@ -0,0 +1,405 @@ +#include "kis_mypaint_surface.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +KisPainter* KisMyPaintSurface::m_painter=nullptr; +KisNodeSP KisMyPaintSurface::m_node=nullptr; + +using namespace std; + +KisMyPaintSurface::KisMyPaintSurface(KisPainter *painter, KisNodeSP node) +{ + m_painter = painter; + m_node = node; + + m_surface = new MyPaintSurface(); + mypaint_surface_init(m_surface); + + m_surface->draw_dab = this->draw_dab; + m_surface->get_color = this->get_color; +} + +/*GIMP's draw_dab and get_color code*/ +int KisMyPaintSurface::draw_dab(MyPaintSurface *self, float x, float y, float radius, float color_r, float color_g, + float color_b, float opaque, float hardness, float color_a, + float aspect_ratio, float angle, float lock_alpha, float colorize) { + + const float one_over_radius2 = 1.0f / (radius * radius); + const double angle_rad = angle / 360 * 2 * M_PI; + const float cs = cos(angle_rad); + const float sn = sin(angle_rad); + float normal_mode; + float segment1_slope; + float segment2_slope; + float r_aa_start; + + hardness = CLAMP (hardness, 0.0f, 1.0f); + segment1_slope = -(1.0f / hardness - 1.0f); + segment2_slope = -hardness / (1.0f - hardness); + aspect_ratio = max(1.0f, aspect_ratio); + + r_aa_start = radius - 1.0f; + r_aa_start = max(r_aa_start, 0.0f); + r_aa_start = (r_aa_start * r_aa_start) / aspect_ratio; + + normal_mode = opaque * (1.0f - colorize); + colorize = opaque * colorize; + + const KoColorSpace *colorSpace = painter()->device()->colorSpace(); + const QPoint pt = QPoint(x - radius, y - radius); + const QSize sz = QSize(2 * radius, 2 * radius); + + const QRect dabRectAligned = QRect(pt, sz); + const QPointF center = QPointF(x, y); + + QColor color = QColor(color_r*255, color_g*255, color_b*255, color_a*255); + KoColor currentColor(color, colorSpace); + KisAlgebra2D::OuterCircle outer(center, radius); + + KisSequentialIterator it(painter()->device(), dabRectAligned); + + while(it.nextPixel()) { + + QPoint pt(it.x(), it.y()); + + qreal value = outer.fadeSq(pt); + + if(value > 1.0) + continue; + + qreal rr, base_alpha, alpha, dst_alpha, r, g, b, a; + qreal opacity; + + if (radius < 3.0) { + rr = calculate_rr_antialiased (it.x(), it.y(), x, y, aspect_ratio, sn, cs, one_over_radius2, r_aa_start); + } + else { + rr = calculate_rr (it.x(), it.y(), x, y, aspect_ratio, sn, cs, one_over_radius2); + } + + KoColor dstPixel; + QColor dstQColor; + colorSpace->toQColor(it.rawData(), &dstQColor); + + base_alpha = calculate_alpha_for_rr (rr, hardness, segment1_slope, segment2_slope); + alpha = base_alpha * normal_mode; + + //dstPixel.toQColor().getRgbF(&r, &g, &b, &dst_alpha); + dstQColor.getRgbF(&r, &g, &b, &dst_alpha); + + a = alpha * (color_a - dst_alpha) + dst_alpha; + + if (a > 0.0f) { + + float src_term = (alpha * color_a) / a; + float dst_term = 1.0f - src_term; + r = color_r * src_term + r * dst_term; + g = color_g * src_term + g * dst_term; + b = color_b * src_term + b * dst_term; + } + + if (colorize > 0.0f && base_alpha > 0.0f) { + + alpha = base_alpha * colorize; + a = alpha + dst_alpha - alpha * dst_alpha; + + if (a > 0.0f) { + + float pixel_h, pixel_s, pixel_l, out_h, out_s, out_l; + float out_r = r, out_g = g, out_b = b; + + float src_term = alpha / a; + float dst_term = 1.0f - src_term; + + RGBToHSL(color_r, color_g, color_b, &pixel_h, &pixel_s, &pixel_l); + RGBToHSL(out_r, out_g, out_b, &out_h, &out_s, &out_l); + + out_h = pixel_h; + out_s = pixel_s; + + HSLToRGB(out_h, out_s, out_l, &out_r, &out_g, &out_b); + + r = (float)out_r * src_term + r * dst_term; + g = (float)out_g * src_term + g * dst_term; + b = (float)out_b * src_term + b * dst_term; + } + } + + QColor paintColor = QColor(r*255,g*255,b*255,a*255); + KoColor paintKolor(paintColor, colorSpace); + + memcpy(it.rawData(), paintKolor.data(), painter()->device()->pixelSize()); + } + + painter()->addDirtyRect(dabRectAligned); + return 1; +} + +void KisMyPaintSurface::get_color(MyPaintSurface *self, float x, float y, float radius, + float * color_r, float * color_g, float * color_b, float * color_a) { + + if (radius < 1.0f) + radius = 1.0f; + + *color_r = 0.0f; + *color_g = 0.0f; + *color_b = 0.0f; + *color_a = 0.0f; + +// KoColor canvasColor; +// KisCrossDeviceColorPicker colorPicker(m_nodeDevice, canvasColor); + const KoColorSpace *colorSpace = painter()->device()->colorSpace(); + const QPoint pt = QPoint(x - radius, y - radius); + const QSize sz = QSize(2 * radius, 2 * radius); + + const QRect dabRectAligned = QRect(pt, sz); + const QPointF center = QPointF(x, y); + KisAlgebra2D::OuterCircle outer(center, radius); + + const float one_over_radius2 = 1.0f / (radius * radius); + float sum_weight = 0.0f; + float sum_r = 0.0f; + float sum_g = 0.0f; + float sum_b = 0.0f; + float sum_a = 0.0f; + + KisSequentialIterator it(painter()->device(), dabRectAligned); + KisRandomAccessorSP im = m_node->paintDevice()->createRandomAccessorNG(x, y); + + while(it.nextPixel()) { + + QPointF pt(it.x(), it.y()); + + if(outer.fadeSq(pt) > 1.0) + continue; + + /* pixel_weight == a standard dab with hardness = 0.5, aspect_ratio = 1.0, and angle = 0.0 */ + float yy = (it.y() + 0.5f - y); + float xx = (it.x() + 0.5f - x); + + float rr = (yy * yy + xx * xx) * one_over_radius2; + float pixel_weight = 0.0f; + if (rr <= 1.0f) + pixel_weight = 1.0f - rr; + + QColor dstQColor; +// colorSpace->toQColor(it.rawData(), &dstQColor, colorSpace->profile()); + +// colorPicker.pickColor(it.x(), it.y(), canvasColor.data()); + + im->moveTo(it.x(), it.y()); + colorSpace->toQColor(im->rawData(), &dstQColor); + + qreal r, g, b, a; + dstQColor.getRgbF(&r, &g, &b, &a); + + sum_r += pixel_weight * r; + sum_g += pixel_weight * g; + sum_b += pixel_weight * b; + sum_a += pixel_weight * a; + sum_weight += pixel_weight; + } + + if (sum_a > 0.0f && sum_weight > 0.0f) { + + sum_r /= sum_weight; + sum_g /= sum_weight; + sum_b /= sum_weight; + sum_a /= sum_weight; + + sum_r /= sum_a; + sum_g /= sum_a; + sum_b /= sum_a; + + *color_r = CLAMP(sum_r, 0.0f, 1.0f); + *color_g = CLAMP(sum_g, 0.0f, 1.0f); + *color_b = CLAMP(sum_b, 0.0f, 1.0f); + *color_a = CLAMP(sum_a, 0.0f, 1.0f); + } +} + +KisPainter* KisMyPaintSurface::painter() { + return m_painter; +} + +MyPaintSurface* KisMyPaintSurface::surface() { + return m_surface; +} + +/*mypaint code*/ +qreal KisMyPaintSurface::calculateOpacity(float angle, float hardness, float opaque, float x, float y, + float xp, float yp, float aspect_ratio, float radius) { + + qreal cs = cos(angle/360*2*M_PI); + qreal sn = sin(angle/360*2*M_PI); + + qreal dx = xp - x; + qreal dy = yp - y; + qreal dyr = (dy*cs-dx*sn)*aspect_ratio; + qreal dxr = (dy*sn+dx*cs); + qreal dd = (dyr*dyr + dxr*dxr) / (radius*radius); + qreal opa; + + if (dd > 1) + opa = 0; + else if (dd < hardness) + opa = dd + 1-(dd/hardness); + else + opa = hardness/(1-hardness)*(1-dd); + + qreal pixel_opacity = opa * opaque; + return pixel_opacity; +} + +inline float KisMyPaintSurface::calculate_rr (int xp, + int yp, + float x, + float y, + float aspect_ratio, + float sn, + float cs, + float one_over_radius2) { + + const float yy = (yp + 0.5f - y); + const float xx = (xp + 0.5f - x); + const float yyr=(yy*cs-xx*sn)*aspect_ratio; + const float xxr=yy*sn+xx*cs; + const float rr = (yyr*yyr + xxr*xxr) * one_over_radius2; + /* rr is in range 0.0..1.0*sqrt(2) */ + return rr; +} + +static inline float +calculate_r_sample (float x, float y, float aspect_ratio, float sn, float cs) { + + const float yyr=(y*cs-x*sn)*aspect_ratio; + const float xxr=y*sn+x*cs; + const float r = (yyr*yyr + xxr*xxr); + return r; +} + +static inline float +sign_point_in_line (float px, float py, float vx, float vy) { + + return (px - vx) * (-vy) - (vx) * (py - vy); +} + +static inline void +closest_point_to_line (float lx, float ly, float px, float py, float *ox, float *oy) { + + const float l2 = lx*lx + ly*ly; + const float ltp_dot = px*lx + py*ly; + const float t = ltp_dot / l2; + *ox = lx * t; + *oy = ly * t; +} + + +/* This works by taking the visibility at the nearest point + * and dividing by 1.0 + delta. + * + * - nearest point: point where the dab has more influence + * - farthest point: point at a fixed distance away from + * the nearest point + * - delta: how much occluded is the farthest point relative + * to the nearest point + */ +inline float KisMyPaintSurface::calculate_rr_antialiased (int xp, int yp, float x, float y, + float aspect_ratio, float sn, float cs, float one_over_radius2, + float r_aa_start) { + + /* calculate pixel position and borders in a way + * that the dab's center is always at zero */ + float pixel_right = x - (float)xp; + float pixel_bottom = y - (float)yp; + float pixel_center_x = pixel_right - 0.5f; + float pixel_center_y = pixel_bottom - 0.5f; + float pixel_left = pixel_right - 1.0f; + float pixel_top = pixel_bottom - 1.0f; + + float nearest_x, nearest_y; /* nearest to origin, but still inside pixel */ + float farthest_x, farthest_y; /* farthest from origin, but still inside pixel */ + float r_near, r_far, rr_near, rr_far; + float center_sign, rad_area_1, visibilityNear, delta, delta2; + + /* Dab's center is inside pixel? */ + if( pixel_left<0 && pixel_right>0 && + pixel_top<0 && pixel_bottom>0 ) + { + nearest_x = 0; + nearest_y = 0; + r_near = rr_near = 0; + } + else + { + closest_point_to_line( cs, sn, pixel_center_x, pixel_center_y, &nearest_x, &nearest_y ); + nearest_x = CLAMP( nearest_x, pixel_left, pixel_right ); + nearest_y = CLAMP( nearest_y, pixel_top, pixel_bottom ); + /* XXX: precision of "nearest" values could be improved + * by intersecting the line that goes from nearest_x/Y to 0 + * with the pixel's borders here, however the improvements + * would probably not justify the perdormance cost. + */ + r_near = calculate_r_sample( nearest_x, nearest_y, aspect_ratio, sn, cs ); + rr_near = r_near * one_over_radius2; + } + + /* out of dab's reach? */ + if( rr_near > 1.0f ) + return rr_near; + + /* check on which side of the dab's line is the pixel center */ + center_sign = sign_point_in_line( pixel_center_x, pixel_center_y, cs, -sn ); + + /* radius of a circle with area=1 + * A = pi * r * r + * r = sqrt(1/pi) + */ + rad_area_1 = sqrtf( 1.0f / M_PI ); + + /* center is below dab */ + if( center_sign < 0 ) + { + farthest_x = nearest_x - sn*rad_area_1; + farthest_y = nearest_y + cs*rad_area_1; + } + /* above dab */ + else + { + farthest_x = nearest_x + sn*rad_area_1; + farthest_y = nearest_y - cs*rad_area_1; + } + + r_far = calculate_r_sample( farthest_x, farthest_y, aspect_ratio, sn, cs ); + rr_far = r_far * one_over_radius2; + + /* check if we can skip heavier AA */ + if( r_far < r_aa_start ) + return (rr_far+rr_near) * 0.5f; + + /* calculate AA approximate */ + visibilityNear = 1.0f - rr_near; + delta = rr_far - rr_near; + delta2 = 1.0f + delta; + visibilityNear /= delta2; + + return 1.0f - visibilityNear; +} +/* -- end mypaint code */ + +inline float KisMyPaintSurface::calculate_alpha_for_rr (float rr, float hardness, float slope1, float slope2) { + + if (rr > 1.0f) + return 0.0f; + else if (rr <= hardness) + return 1.0f + rr * slope1; + else + return rr * slope2 - slope2; +} diff --git a/plugins/paintops/mypaint/kis_mypaint_surface.h b/plugins/paintops/mypaint/kis_mypaint_surface.h new file mode 100644 index 0000000000..f80b6b97ba --- /dev/null +++ b/plugins/paintops/mypaint/kis_mypaint_surface.h @@ -0,0 +1,59 @@ +#ifndef KIS_MYPAINT_SURFACE_H +#define KIS_MYPAINT_SURFACE_H + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +class KisMyPaintSurface +{ +public: + KisMyPaintSurface(KisPainter* painter, KisNodeSP node=nullptr); + + /** + * mypaint_surface_draw_dab: + * + * Draw a dab onto the surface. + */ + static int draw_dab(MyPaintSurface *self, float x, float y, float radius, + float color_r, float color_g, float color_b, float opaque, float hardness, + float color_a, float aspect_ratio, float angle, float lock_alpha, + float colorize); + + static void get_color(MyPaintSurface *self, float x, float y, float radius, + float * color_r, float * color_g, float * color_b, float * color_a); + + static inline float + calculate_rr_antialiased (int xp, int yp, float x, float y, float aspect_ratio, + float sn, float cs, float one_over_radius2, float r_aa_start); + + static inline float + calculate_alpha_for_rr (float rr, float hardness, float slope1, float slope2); + + static inline float + calculate_rr (int xp, int yp, float x, float y, float aspect_ratio, + float sn, float cs, float one_over_radius2); + + + static KisPainter* painter(); + static void paint(KoColor *color, KoColor* bgColor); + static qreal calculateOpacity(float angle, float hardness, float opaque, float x, float y, + float xp, float yp, float aspect_ratio, float radius); + + MyPaintSurface* surface(); + +private: + static KisPainter *m_painter; + static KisNodeSP m_node; + MyPaintSurface *m_surface; +}; + +#endif // KIS_MYPAINT_SURFACE_H diff --git a/plugins/paintops/mypaint/krita-mypaint.png b/plugins/paintops/mypaint/krita-mypaint.png new file mode 100644 index 0000000000..8e0a1b77c7 Binary files /dev/null and b/plugins/paintops/mypaint/krita-mypaint.png differ diff --git a/plugins/paintops/mypaint/kritamypaintop.json b/plugins/paintops/mypaint/kritamypaintop.json new file mode 100644 index 0000000000..e5355b9358 --- /dev/null +++ b/plugins/paintops/mypaint/kritamypaintop.json @@ -0,0 +1,9 @@ +{ + "Id": "MyPaint Brush", + "Type": "Service", + "X-KDE-Library": "kritamypaintop", + "X-KDE-ServiceTypes": [ + "Krita/Paintop" + ], + "X-Krita-Version": "28" +} diff --git a/plugins/paintops/mypaint/my_paintop_plugin.cpp b/plugins/paintops/mypaint/my_paintop_plugin.cpp new file mode 100644 index 0000000000..1ab93c33dc --- /dev/null +++ b/plugins/paintops/mypaint/my_paintop_plugin.cpp @@ -0,0 +1,32 @@ +#include "my_paintop_plugin.h" + +#include + +#include +#include + +#include +#include +#include "kis_my_paintop.h" +#include "kis_my_paintop_settings.h" +#include "kis_my_paintop_settings_widget.h" +#include "kis_simple_paintop_factory.h" + +#include "kis_global.h" + +K_PLUGIN_FACTORY_WITH_JSON(MyPaintOpPluginFactory, "kritamypaintop.json", registerPlugin();) + + +MyPaintOpPlugin::MyPaintOpPlugin(QObject *parent, const QVariantList &) + : QObject(parent) +{ + KisPaintOpRegistry *r = KisPaintOpRegistry::instance(); + r->add(new KisSimplePaintOpFactory("mypaintbrush", i18n("MyPaint"), KisPaintOpFactory::categoryStable() , "krita-mypaint.png", QString(), QStringList(), 6)); + +} + +MyPaintOpPlugin::~MyPaintOpPlugin() +{ +} + +#include "my_paintop_plugin.moc" diff --git a/plugins/paintops/mypaint/my_paintop_plugin.h b/plugins/paintops/mypaint/my_paintop_plugin.h new file mode 100644 index 0000000000..2cef13a477 --- /dev/null +++ b/plugins/paintops/mypaint/my_paintop_plugin.h @@ -0,0 +1,18 @@ +#ifndef MY_PAINTOP_PLUGIN_H_ +#define MY_PAINTOP_PLUGIN_H_ + +#include +#include + +/** + * A plugin wrapper that adds the paintop factories to the paintop registry. + */ +class MyPaintOpPlugin : public QObject +{ + Q_OBJECT +public: + MyPaintOpPlugin(QObject *parent, const QVariantList &); + ~MyPaintOpPlugin() override; +}; + +#endif // MY_PAINTOP_PLUGIN_H_