diff --git a/CMakeLists.txt b/CMakeLists.txt index 77214ea5ba..15a243a6f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,951 +1,951 @@ project(krita) message(STATUS "Using CMake version: ${CMAKE_VERSION}") cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR) set(MIN_QT_VERSION 5.9.0) set(MIN_FRAMEWORKS_VERSION 5.44.0) set( CMAKE_CXX_STANDARD 11 ) set( CMAKE_CXX_STANDARD_REQUIRED ON ) if (POLICY CMP0002) cmake_policy(SET CMP0002 OLD) endif() if (POLICY CMP0017) cmake_policy(SET CMP0017 NEW) endif () if (POLICY CMP0022) cmake_policy(SET CMP0022 OLD) endif () if (POLICY CMP0026) cmake_policy(SET CMP0026 OLD) endif() if (POLICY CMP0042) cmake_policy(SET CMP0042 NEW) endif() if (POLICY CMP0046) cmake_policy(SET CMP0046 OLD) endif () if (POLICY CMP0059) cmake_policy(SET CMP0059 OLD) endif() if (POLICY CMP0063) cmake_policy(SET CMP0063 OLD) endif() if (POLICY CMP0054) cmake_policy(SET CMP0054 OLD) endif() if (POLICY CMP0064) cmake_policy(SET CMP0064 OLD) endif() if (POLICY CMP0071) cmake_policy(SET CMP0071 OLD) endif() if (APPLE) set(APPLE_SUPPRESS_X11_WARNING TRUE) set(KDE_SKIP_RPATH_SETTINGS TRUE) set(CMAKE_MACOSX_RPATH 1) set(BUILD_WITH_INSTALL_RPATH 1) add_definitions(-mmacosx-version-min=10.12 -Wno-macro-redefined -Wno-deprecated-register) endif() if (CMAKE_COMPILER_IS_GNUCXX AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9 AND NOT WIN32) add_compile_options($<$:-Wno-suggest-override> -Wextra -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.0-prealpha") # Major version: 3 for 3.x, 4 for 4.x, etc. set(KRITA_STABLE_VERSION_MAJOR 4) # Minor version: 0 for 4.0, 1 for 4.1, etc. set(KRITA_STABLE_VERSION_MINOR 3) # Bugfix release version, or 0 for before the first stable release set(KRITA_VERSION_RELEASE 0) # the 4th digit, really only used for the Windows installer: # - [Pre-]Alpha: Starts from 0, increment 1 per release # - Beta: Starts from 50, increment 1 per release # - Stable: Set to 100, bump to 101 if emergency update is needed set(KRITA_VERSION_REVISION 0) set(KRITA_ALPHA 1) # uncomment only for Alpha #set(KRITA_BETA 1) # uncomment only for Beta #set(KRITA_RC 1) # uncomment only for RC set(KRITA_YEAR 2018) # update every year if(NOT DEFINED KRITA_ALPHA AND NOT DEFINED KRITA_BETA AND NOT DEFINED KRITA_RC) set(KRITA_STABLE 1) # do not edit endif() message(STATUS "Krita version: ${KRITA_VERSION_STRING}") # Define the generic version of the Krita libraries here # This makes it easy to advance it when the next Krita release comes. # 14 was the last GENERIC_KRITA_LIB_VERSION_MAJOR of the previous Krita series # (2.x) so we're starting with 15 in 3.x series, 16 in 4.x series if(KRITA_STABLE_VERSION_MAJOR EQUAL 4) math(EXPR GENERIC_KRITA_LIB_VERSION_MAJOR "${KRITA_STABLE_VERSION_MINOR} + 16") else() # let's make sure we won't forget to update the "16" message(FATAL_ERROR "Reminder: please update offset == 16 used to compute GENERIC_KRITA_LIB_VERSION_MAJOR to something bigger") endif() set(GENERIC_KRITA_LIB_VERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}.0.0") set(GENERIC_KRITA_LIB_SOVERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}") LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/kde_macro") # fetch git revision for the current build set(KRITA_GIT_SHA1_STRING "") set(KRITA_GIT_BRANCH_STRING "") include(GetGitRevisionDescription) get_git_head_hash(GIT_SHA1) get_git_branch(GIT_BRANCH) if(GIT_SHA1) string(SUBSTRING ${GIT_SHA1} 0 7 GIT_SHA1) set(KRITA_GIT_SHA1_STRING ${GIT_SHA1}) if(GIT_BRANCH) set(KRITA_GIT_BRANCH_STRING ${GIT_BRANCH}) else() set(KRITA_GIT_BRANCH_STRING "(detached HEAD)") endif() endif() # create test make targets enable_testing() # collect list of broken tests, empty here to start fresh with each cmake run set(KRITA_BROKEN_TESTS "" CACHE INTERNAL "KRITA_BROKEN_TESTS") ############ ############# ## Options ## ############# ############ include(FeatureSummary) if (WIN32) option(USE_DRMINGW "Support the Dr. Mingw crash handler (only on windows)" ON) add_feature_info("Dr. Mingw" USE_DRMINGW "Enable the Dr. Mingw crash handler") if (MINGW) option(USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags (mingw-w64)" ON) add_feature_info("Linker Security Flags" USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags") if (USE_MINGW_HARDENING_LINKER) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") # Enable high-entropy ASLR for 64-bit # The image base has to be >4GB for HEASLR to be enabled. # The values used here are kind of arbitrary. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x140000000") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") endif ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") else (USE_MINGW_HARDENING_LINKER) message(WARNING "Linker Security Flags not enabled!") endif (USE_MINGW_HARDENING_LINKER) endif (MINGW) endif () option(HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal." ON) configure_file(config-hide-safe-asserts.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hide-safe-asserts.h) add_feature_info("Hide Safe Asserts" HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal.") option(USE_LOCK_FREE_HASH_TABLE "Use lock free hash table instead of blocking." ON) configure_file(config-hash-table-implementaion.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hash-table-implementaion.h) add_feature_info("Lock free hash table" USE_LOCK_FREE_HASH_TABLE "Use lock free hash table instead of blocking.") option(FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true." OFF) add_feature_info("Foundation Build" FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true.") option(KRITA_ENABLE_BROKEN_TESTS "Enable tests that are marked as broken" OFF) add_feature_info("Enable Broken Tests" KRITA_ENABLE_BROKEN_TESTS "Runs broken test when \"make test\" is invoked (use -DKRITA_ENABLE_BROKEN_TESTS=ON to enable).") option(LIMIT_LONG_TESTS "Run long running unittests in a limited quick mode" ON) configure_file(config-limit-long-tests.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-limit-long-tests.h) add_feature_info("Limit long tests" LIMIT_LONG_TESTS "Run long running unittests in a limited quick mode") option(ENABLE_PYTHON_2 "Enables the compiler to look for Python 2.7 instead of Python 3. Some packaged scripts are not compatible with Python 2 and this should only be used if you really have to use 2.7." OFF) option(BUILD_KRITA_QT_DESIGNER_PLUGINS "Build Qt Designer plugins for Krita widgets" OFF) add_feature_info("Build Qt Designer plugins" BUILD_KRITA_QT_DESIGNER_PLUGINS "Builds Qt Designer plugins for Krita widgets (use -DBUILD_KRITA_QT_DESIGNER_PLUGINS=ON to enable).") include(MacroJPEG) ######################################################### ## Look for Python3 It is also searched by KF5, ## ## so we should request the correct version in advance ## ######################################################### function(TestCompileLinkPythonLibs OUTPUT_VARNAME) include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_INCLUDES ${PYTHON_INCLUDE_PATH}) set(CMAKE_REQUIRED_LIBRARIES ${PYTHON_LIBRARIES}) if (MINGW) set(CMAKE_REQUIRED_DEFINITIONS -D_hypot=hypot) endif (MINGW) unset(${OUTPUT_VARNAME} CACHE) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { Py_InitializeEx(0); }" ${OUTPUT_VARNAME}) endfunction() if(MINGW) if(ENABLE_PYTHON_2) message(FATAL_ERROR "Python 2.7 is not supported on Windows at the moment.") else(ENABLE_PYTHON_2) find_package(PythonInterp 3.6 EXACT) find_package(PythonLibs 3.6 EXACT) endif(ENABLE_PYTHON_2) if (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) if(ENABLE_PYTHON_2) find_package(PythonLibrary 2.7) else(ENABLE_PYTHON_2) find_package(PythonLibrary 3.6) endif(ENABLE_PYTHON_2) TestCompileLinkPythonLibs(CAN_USE_PYTHON_LIBS) if (NOT CAN_USE_PYTHON_LIBS) message(FATAL_ERROR "Compiling with Python library failed, please check whether the architecture is correct. Python will be disabled.") endif (NOT CAN_USE_PYTHON_LIBS) endif (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) else(MINGW) if(ENABLE_PYTHON_2) find_package(PythonInterp 2.7) find_package(PythonLibrary 2.7) else(ENABLE_PYTHON_2) find_package(PythonInterp 3.0) find_package(PythonLibrary 3.0) endif(ENABLE_PYTHON_2) endif(MINGW) ######################## ######################### ## Look for KDE and Qt ## ######################### ######################## find_package(ECM 5.22 REQUIRED NOMODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(ECMOptionalAddSubdirectory) include(ECMAddAppIcon) include(ECMSetupVersion) include(ECMMarkNonGuiExecutable) include(ECMGenerateHeaders) include(GenerateExportHeader) include(ECMMarkAsTest) include(ECMInstallIcons) include(CMakePackageConfigHelpers) include(WriteBasicConfigVersionFile) include(CheckFunctionExists) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings) # do not reorder to be alphabetical: this is the order in which the frameworks # depend on each other. find_package(KF5 ${MIN_FRAMEWORKS_VERSION} REQUIRED COMPONENTS Config WidgetsAddons Completion CoreAddons GuiAddons I18n ItemModels ItemViews WindowSystem Archive ) find_package(Qt5 ${MIN_QT_VERSION} REQUIRED COMPONENTS Core Gui Widgets Xml Network PrintSupport Svg Test Concurrent ) if (ANDROID) find_package(Qt5 ${MIN_QT_VERSION} REQUIRED COMPONENTS AndroidExtras ) endif() if (WIN32) set(CMAKE_REQUIRED_INCLUDES ${Qt5Core_INCLUDE_DIRS}) set(CMAKE_REQUIRED_LIBRARIES ${Qt5Core_LIBRARIES}) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_MSWindowsUseWinTabAPI); } " QT_HAS_WINTAB_SWITCH ) unset(CMAKE_REQUIRED_INCLUDES) unset(CMAKE_REQUIRED_LIBRARIES) option(USE_QT_TABLET_WINDOWS "Do not use Krita's forked Wintab and Windows Ink support on Windows, but leave everything to Qt." ON) add_feature_info("Use Qt's Windows Tablet Support" USE_QT_TABLET_WINDOWS "Do not use Krita's forked Wintab and Windows Ink support on Windows, but leave everything to Qt.") configure_file(config_use_qt_tablet_windows.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_use_qt_tablet_windows.h) endif () set(CMAKE_REQUIRED_INCLUDES ${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS}) set(CMAKE_REQUIRED_LIBRARIES ${Qt5Core_LIBRARIES} ${Qt5Gui_LIBRARIES}) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { QSurfaceFormat fmt; fmt.setColorSpace(QSurfaceFormat::scRGBColorSpace); fmt.setColorSpace(QSurfaceFormat::bt2020PQColorSpace); } " HAVE_HDR ) configure_file(config-hdr.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hdr.h) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round); QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor); QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); } " HAVE_HIGH_DPI_SCALE_FACTOR_ROUNDING_POLICY ) configure_file(config-high-dpi-scale-factor-rounding-policy.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-high-dpi-scale-factor-rounding-policy.h) if (WIN32) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { QWindowsWindowFunctions::setHasBorderInFullScreenDefault(true); } " HAVE_SET_HAS_BORDER_IN_FULL_SCREEN_DEFAULT ) configure_file(config-set-has-border-in-full-screen-default.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-set-has-border-in-full-screen-default.h) endif (WIN32) unset(CMAKE_REQUIRED_INCLUDES) unset(CMAKE_REQUIRED_LIBRARIES) include (MacroAddFileDependencies) include (MacroBoolTo01) include (MacroEnsureOutOfSourceBuild) macro_ensure_out_of_source_build("Compiling Krita inside the source directory is not possible. Please refer to the build instruction https://community.kde.org/Krita#Build_Instructions") # Note: OPTIONAL_COMPONENTS does not seem to be reliable # (as of ECM 5.15.0, CMake 3.2) find_package(Qt5Multimedia ${MIN_QT_VERSION}) set_package_properties(Qt5Multimedia PROPERTIES DESCRIPTION "Qt multimedia integration" - URL "http://www.qt.io/" + 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 "http://www.qt.io/" + 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 "http://www.qt.io/" + 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 "http://www.qt.io/" + 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 "http://api.kde.org/frameworks-api/frameworks5-apidocs/kcrash/html/index.html" + 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 http://forum.kde.org/viewtopic.php?f=139&t=101867&p=221242#p221242 + # 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 "http://www.gnu.org/software/gsl" + 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 "http://www.zlib.net/" + 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 "http://www.openexr.com" + 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_DIR}) set(LINK_OPENEXR_LIB ${OPENEXR_LIBRARIES}) add_definitions(${OPENEXR_DEFINITIONS}) endif() find_package(TIFF) set_package_properties(TIFF PROPERTIES DESCRIPTION "TIFF Library and Utilities" - URL "http://www.remotesensing.org/libtiff" + 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 "http://www.libjpeg-turbo.org" + 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}) macro_bool_to_01(JPEG_FOUND HAVE_JPEG) endif() find_package(GIF) set_package_properties(GIF PROPERTIES DESCRIPTION "Library for loading and saving gif files." URL "http://giflib.sourceforge.net/" TYPE OPTIONAL PURPOSE "Required by the Krita GIF filter") if (GIF_FOUND) list (APPEND ANDROID_EXTRA_LIBS ${GIF_LIBRARY}) endif() find_package(HEIF "1.3.0") set_package_properties(HEIF PROPERTIES DESCRIPTION "Library for loading and saving heif files." URL "https://github.com/strukturag/libheif" TYPE OPTIONAL PURPOSE "Required by the Krita HEIF filter") find_package(OpenJPEG "2.3.0") set_package_properties(OpenJPEG PROPERTIES DESCRIPTION "Library for loading and saving jp2000 files." - URL "http://www.openjpeg.org/" + 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 "http://www.libraw.org" + 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 "http://www.opencolorio.org" + 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 "http://www.python.org" + 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(GSTREAMER "1.5.2") set_package_properties(GSTREAMER PROPERTIES DESCRIPTION "gstreamer video encoder." URL "https://gstreamer.freedesktop.org/" TYPE OPTIONAL PURPOSE "Required by the Krita recorder plugin") macro_bool_to_01(GSTREAMER_FOUND HAVE_GSTREAMER) find_package(GLIB "2.0") set_package_properties(GLIB PROPERTIES DESCRIPTION "glib." URL "https://www.gtk.org/" TYPE OPTIONAL PURPOSE "Required by the Krita recorder plugin") macro_bool_to_01(GLIB_FOUND HAVE_GLIB) find_package(GOBJECT "2.0") set_package_properties(GOBJECT PROPERTIES DESCRIPTION "gobject." URL "https://www.gtk.org/" TYPE OPTIONAL PURPOSE "Required by the Krita recorder plugin") macro_bool_to_01(GOBJECT_FOUND HAVE_GOBJECT) ## ## Look for OpenGL ## # TODO: see if there is a better check for QtGui being built with opengl support (and thus the QOpenGL* classes) if(Qt5Gui_OPENGL_IMPLEMENTATION) message(STATUS "Found QtGui OpenGL support") else() message(FATAL_ERROR "Did NOT find QtGui OpenGL support. Check your Qt configuration. You cannot build Krita without Qt OpenGL support.") endif() ## ## Test for eigen3 ## find_package(Eigen3 3.0 REQUIRED) set_package_properties(Eigen3 PROPERTIES DESCRIPTION "C++ template library for linear algebra" URL "http://eigen.tuxfamily.org" TYPE REQUIRED) ## ## Test for exiv2 ## find_package(LibExiv2 0.16 REQUIRED) if (ANDROID) list (APPEND ANDROID_EXTRA_LIBS ${LibExiv2_LIBRARIES}) # because libexiv2 depends on libexpat and it is installed in the same folder get_filename_component (_base_dir ${LibExiv2_LIBRARIES} DIRECTORY) list (APPEND ANDROID_EXTRA_LIBS ${_base_dir}/libexpat.so) endif() ## ## Test for lcms ## find_package(LCMS2 2.4 REQUIRED) set_package_properties(LCMS2 PROPERTIES DESCRIPTION "LittleCMS Color management engine" URL "http://www.littlecms.com" TYPE REQUIRED PURPOSE "Will be used for color management and is necessary for Krita") if(LCMS2_FOUND) if(NOT ${LCMS2_VERSION} VERSION_LESS 2040 ) set(HAVE_LCMS24 TRUE) endif() set(HAVE_REQUIRED_LCMS_VERSION TRUE) set(HAVE_LCMS2 TRUE) endif() list (APPEND ANDROID_EXTRA_LIBS ${LCMS2_LIBRARIES}) ## ## Test for Vc ## set(OLD_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ) set(HAVE_VC FALSE) if (NOT ${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") if(NOT MSVC) find_package(Vc 1.1.0) set_package_properties(Vc PROPERTIES DESCRIPTION "Portable, zero-overhead SIMD library for C++" URL "https://github.com/VcDevel/Vc" TYPE OPTIONAL PURPOSE "Required by the Krita for vectorization") macro_bool_to_01(Vc_FOUND HAVE_VC) endif() endif() configure_file(config-vc.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-vc.h ) if(HAVE_VC) message(STATUS "Vc found!") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/vc") include (VcMacros) if(Vc_COMPILER_IS_CLANG) set(ADDITIONAL_VC_FLAGS "-ffp-contract=fast") if(NOT WIN32) set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC") endif() elseif (NOT MSVC) set(ADDITIONAL_VC_FLAGS "-fabi-version=0 -ffp-contract=fast") if(NOT WIN32) set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC") endif() endif() macro(ko_compile_for_all_implementations_no_scalar _objs _src) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) endmacro() macro(ko_compile_for_all_implementations _objs _src) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY Scalar SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) endmacro() endif() set(CMAKE_MODULE_PATH ${OLD_CMAKE_MODULE_PATH} ) add_definitions(${QT_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS}) ## ## Test endianness ## include (TestBigEndian) test_big_endian(CMAKE_WORDS_BIGENDIAN) ## ## Test for qt-poppler ## find_package(Poppler COMPONENTS Qt5) set_package_properties(Poppler PROPERTIES DESCRIPTION "A PDF rendering library" - URL "http://poppler.freedesktop.org" + 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 ${KF5_LIBRARIES}/libKF5Archive.so) ## ## Test for Atomics ## include(CheckAtomic) ############################ ############################# ## Add Krita helper macros ## ############################# ############################ include(MacroKritaAddBenchmark) #################### ##################### ## Define includes ## ##################### #################### # for config.h and includes (if any?) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/interfaces ) add_subdirectory(libs) add_subdirectory(plugins) if (BUILD_TESTING) add_subdirectory(benchmarks) endif() add_subdirectory(krita) configure_file(KoConfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/KoConfig.h ) configure_file(config_convolution.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_convolution.h) configure_file(config-ocio.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-ocio.h ) check_function_exists(powf HAVE_POWF) configure_file(config-powf.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-powf.h) if(WIN32) include(${CMAKE_CURRENT_LIST_DIR}/packaging/windows/installer/ConfigureInstallerNsis.cmake) endif() message("\nBroken tests:") foreach(tst ${KRITA_BROKEN_TESTS}) message(" * ${tst}") endforeach() feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/po OR EXISTS ${CMAKE_CURRENT_BINARY_DIR}/po ) find_package(KF5I18n CONFIG REQUIRED) ki18n_install(po) endif() if(DEFINED QTANDROID_EXPORTED_TARGET AND NOT TARGET "create-apk") set (_CMAKE_ANDROID_DIR "${ECM_DIR}/../toolchain") list(LENGTH QTANDROID_EXPORTED_TARGET targetsCount) include(${_CMAKE_ANDROID_DIR}/ECMAndroidDeployQt.cmake) math(EXPR last "${targetsCount}-1") foreach(idx RANGE 0 ${last}) list(GET QTANDROID_EXPORTED_TARGET ${idx} exportedTarget) list(GET ANDROID_APK_DIR ${idx} APK_DIR) if(APK_DIR AND NOT EXISTS "${ANDROID_APK_DIR}/AndroidManifest.xml" AND IS_ABSOLUTE ANDROID_APK_DIR) message(FATAL_ERROR "Cannot find ${APK_DIR}/AndroidManifest.xml according to ANDROID_APK_DIR. ${ANDROID_APK_DIR} ${exportedTarget}") elseif(NOT APK_DIR) get_filename_component(_qt5Core_install_prefix "${Qt5Core_DIR}/../../../" ABSOLUTE) set(APK_DIR "${_qt5Core_install_prefix}/src/android/templates/") endif() ecm_androiddeployqt("${exportedTarget}" "${ECM_ADDITIONAL_FIND_ROOT_PATH}") set_target_properties(create-apk-${exportedTarget} PROPERTIES ANDROID_APK_DIR "${APK_DIR}") endforeach() elseif(ANDROID) message(STATUS "You can export a target by specifying -DQTANDROID_EXPORTED_TARGET= and -DANDROID_APK_DIR=") endif() diff --git a/README.md b/README.md index 4c87ca2901..bf67829595 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,51 @@ ![Picture](https://krita.org/wp-content/uploads/2019/04/krita-logo-2019.png) | Jenkins CI Name | Master | Stable | | --------------- | ------ | ------ | | OpenSuse Qt 5.12 | [![Build Status](https://build.kde.org/job/Extragear/job/krita/job/kf5-qt5%20SUSEQt5.12/badge/icon)](https://build.kde.org/job/Extragear/job/krita/job/kf5-qt5%20SUSEQt5.12/) |[![Build Status](https://build.kde.org/buildStatus/icon?job=Extragear%2Fkrita%2Fstable-kf5-qt5+SUSEQt5.12)](https://build.kde.org/job/Extragear/job/krita/job/stable-kf5-qt5%20SUSEQt5.12/)| | FreeBSD Qt 5.13 | [![Build Status](https://build.kde.org/job/Extragear/job/krita/job/kf5-qt5%20FreeBSDQt5.13/badge/icon)](https://build.kde.org/job/Extragear/job/krita/job/kf5-qt5%20FreeBSDQt5.13/) |[![Build Status](https://build.kde.org/job/Extragear/job/krita/job/stable-kf5-qt5%20FreeBSDQt5.13/badge/icon)](https://build.kde.org/job/Extragear/job/krita/job/stable-kf5-qt5%20FreeBSDQt5.13/)| Krita is a free and open source digital painting application. It is for artists who want to create professional work from start to end. Krita is used by comic book artists, illustrators, concept artists, matte and texture painters and in the digital VFX industry. If you are reading this on Github, be aware that this is just a mirror. Our real code repository is provided by KDE: https://invent.kde.org/kde/krita ![Picture](https://krita.org/wp-content/uploads/2016/04/krita-30-screenshot.jpg) ### User Manual https://docs.krita.org/en/user_manual.html ### Development Notes and Build Instructions If you're building on Windows or OSX you'll need to build some third-party dependencies first. You should look at the README in the 3rdparty folder for directions. -If you're building on Linux, please follow David Revoy's Cat Guide: http://www.davidrevoy.com/article193/guide-building-krita-on-linux-for-cats +If you're building on Linux, please follow [the online documentation](https://docs.krita.org/en/untranslatable_pages/building_krita.html). Other developer guides, notes and wiki: https://docs.krita.org/en/untranslatable_pages.html Apidox: https://api.kde.org/extragear-api/graphics-apidocs/krita/html/index.html ### Bugs and Wishes https://bugs.kde.org/buglist.cgi?bug_status=UNCONFIRMED&bug_status=CONFIRMED&bug_status=ASSIGNED&bug_status=REOPENED&list_id=1315444&product=krita&query_format=advanced ### Discussion Forum http://forum.kde.org/viewforum.php?f=136 ### IRC channel Most of the developers hang out here. If you are interested in helping with the project this is a great place to start. Many of the developers based in Europe so they may be offline depending on when you join. irc.freenode.net, #krita ### Project Website http://www.krita.org ### License Krita as a whole is licensed under the GNU Public License, Version 3. Individual files may have a different, but compatible license. diff --git a/krita/data/profiles/elles-icc-profiles/make-elles-profiles.c b/krita/data/profiles/elles-icc-profiles/make-elles-profiles.c index 9e71e8d65e..f7212ee31c 100644 --- a/krita/data/profiles/elles-icc-profiles/make-elles-profiles.c +++ b/krita/data/profiles/elles-icc-profiles/make-elles-profiles.c @@ -1,1653 +1,1653 @@ /* License: * * Code for making well-behaved ICC profiles * Copyright © 2013, 2014, 2015 Elle Stone * * 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. * * Contact information: * ellestone@ninedegreesbelow.com - * http://ninedegreesbelow.com + * https://ninedegreesbelow.com * * */ /* About the ICC profile header "Platform" tag: * * When creating a profile, LCMS checks to see if the platform is * Windows ('MSFT'). If your platform isn't Windows, LCMS defaults * to using the Apple ('APPL') platform tag for the profile header. * * There is an unofficial Platform * cmsPlatformSignature cmsSigUnices 0x2A6E6978 '*nix'. There is, * however, no LCMS2 API for changing the platform when making a profile. * * So on my own computer, to replace 'APPL' with '*nix' in the header, * I modified the LCMS source file 'cmsio0.c' and recompiled LCMS: * #ifdef CMS_IS_WINDOWS_ * Header.platform= (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMicrosoft); * #else * Header.platform= (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigUnices); * #endif * * */ /* Sample command line to compile this code: * * gcc -g -O0 -Wall -o make-elles-profiles make-elles-profiles.c -llcms2 * * You'll see a lot of harmless warnings about unused variables. * That's because I included variables for profiles that the code * doesn't actually make, in case you might want to make * these profiles for your own use. * * */ #include int main () { /* prints D50 XYZ values from lcms2.h, * mostly to let you know the code did something! * */ printf("D50X, D50Y, D50Z = %1.8f %1.8f %1.8f\n", cmsD50X, cmsD50Y, cmsD50Z); /* ************************** TONE CURVES *************************** */ /* sRGB, Rec709, and labl Tone Reproduction Curves ("TRCs") */ /* About these TRCs: * This code makes V2 and V4 ICC profiles. * For the V4 profiles, which are made using parametric curves, * these TRCs can work in unbounded mode. * For the V2 profiles, the resulting TRC is a 4096-point curve and * cannot work in unbounded mode. - * See http://ninedegreesbelow.com/photography/lcms2-unbounded-mode.html + * See https://ninedegreesbelow.com/photography/lcms2-unbounded-mode.html * for an explanation of unbounded mode ICC profile conversions. * * Also, during ICC profile conversions, * LCMS quantizes images with ICC profiles that have point TRCs. * So use the V4 profile variants for editing images with software * that uses LCMS2 as the Color Management System. * */ /* sRGB TRC */ cmsFloat64Number srgb_parameters[5] = { 2.4, 1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 0.04045 }; cmsToneCurve *srgb_parametic_curve = cmsBuildParametricToneCurve(NULL, 4, srgb_parameters); cmsToneCurve *srgb_parametric[3] = {srgb_parametic_curve,srgb_parametic_curve,srgb_parametic_curve}; /* LAB "L" (perceptually uniform) TRC */ cmsFloat64Number labl_parameters[5] = { 3.0, 0.862076, 0.137924, 0.110703, 0.080002 }; cmsToneCurve *labl_parametic_curve = cmsBuildParametricToneCurve(NULL, 4, labl_parameters); cmsToneCurve *labl_parametric[3] = {labl_parametic_curve,labl_parametic_curve,labl_parametic_curve}; /* Rec 709 TRC */ cmsFloat64Number rec709_parameters[5] = { 1.0 / 0.45, 1.0 / 1.099, 0.099 / 1.099, 1.0 / 4.5, 0.018 }; cmsToneCurve *rec709_parametic_curve = cmsBuildParametricToneCurve(NULL, 4, rec709_parameters); cmsToneCurve *rec709_parametric[3] = {rec709_parametic_curve,rec709_parametic_curve,rec709_parametic_curve}; /* The following true gamma TRCs work in unbounded mode * for both V2 and V4 profiles, and quantization during ICC profile * conversions is not an issue with either the V2 or V4 variants: */ /* gamma=1.00, linear gamma, "linear light", etc tone response curve */ cmsToneCurve* gamma100[3]; gamma100[0] = gamma100[1] = gamma100[2] = cmsBuildGamma (NULL, 1.00); cmsToneCurve* gamma200[3]; gamma200[0] = gamma200[1] = gamma200[2] = cmsBuildGamma (NULL, 2.00); /* Because of hexadecimal rounding during the profile making process, * the following two gamma values for the profile's TRC are not precisely * preserved when a V2 profile is made. So I used the V2 value for * both V2 and V4 versions of the profiles that use these TRCs. * Otherwise V2 and V4 versions of nominally gamma=1.80 and gamma=2.20 * profiles would have slightly different gamma curves. * */ /* gamma=1.80078125 tone response curve */ /* http://www.color.org/chardata/rgb/ROMMRGB.pdf indicates that * the official tone response curve for ROMM isn't a simple gamma curve * but rather has a linear portion in shadows, just like sRGB. * Most ProPhotoRGB profiles use a gamma curve equal to 1.80078125. * This odd value is because of hexadecimal rounding. * */ cmsToneCurve* gamma180[3]; gamma180[0] = gamma180[1] = gamma180[2] = cmsBuildGamma (NULL, 1.80078125); /* gamma=2.19921875 tone response curve */ -/* per http://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf; +/* per https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf; * ClayRGB uses this value. Based on an old proprietary profile, * it also appears to be the correct gamma for WideGamutRGB. * It also is what you get when you * try to create a V2 profile with a gamma=2.20 gamma curve. * So perhaps the AdobeRGB1998 specifications * were simply bowing to the inevitable hexadecimal rounding. * Almost all (all?) V2 ICC profiles with nominally gamma=2.20 * really have a gamma of 2.19921875, not 2.20. * */ cmsToneCurve* gamma220[3]; gamma220[0] = gamma220[1] = gamma220[2] = cmsBuildGamma (NULL, 2.19921875); /* ************************** WHITE POINTS ************************** */ /* D50 WHITE POINTS */ cmsCIExyY d50_romm_spec= {0.3457, 0.3585, 1.0}; -/* http://photo-lovers.org/pdf/color/romm.pdf */ +/* https://photo-lovers.org/pdf/color/romm.pdf */ cmsCIExyY d50_illuminant_specs = {0.345702915, 0.358538597, 1.0}; /* calculated from D50 illuminant XYZ values in ICC specs */ /* D65 WHITE POINTS */ cmsCIExyY d65_srgb_adobe_specs = {0.3127, 0.3290, 1.0}; /* White point from the sRGB.icm and AdobeRGB1998 profile specs: - * http://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf + * https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf * 4.2.1 Reference Display White Point * The chromaticity coordinates of white displayed on * the reference color monitor shall be x=0.3127, y=0.3290. * . . . [which] correspond to CIE Standard Illuminant D65. * * Wikipedia gives this same white point for SMPTE-C. * This white point is also given in the sRGB color space specs. * It's probably correct for most or all of the standard D65 profiles. * * The D65 white point values used in the LCMS virtual sRGB profile * is slightly different than the D65 white point values given in the * sRGB color space specs, so the LCMS virtual sRGB profile * doesn't match sRGB profiles made using the values given in the * sRGB color space specs. * * */ /* Various C and E WHITE POINTS */ cmsCIExyY c_astm = {0.310060511, 0.316149551, 1.0}; /* see http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html */ cmsCIExyY e_astm = {0.333333333, 0.333333333, 1.0}; /* see http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html */ cmsCIExyY c_cie= {0.310, 0.316}; /* https://en.wikipedia.org/wiki/NTSC#Colorimetry */ cmsCIExyY e_cie= {0.333, 0.333}; cmsCIExyY c_6774_robertson= {0.308548930, 0.324928102, 1.0}; -/* see http://en.wikipedia.org/wiki/Standard_illuminant#White_points_of_standard_illuminants +/* see https://en.wikipedia.org/wiki/Standard_illuminant#White_points_of_standard_illuminants * also see http://www.brucelindbloom.com/index.html?Eqn_T_to_xy.html for the equations */ cmsCIExyY e_5454_robertson= {0.333608970, 0.348572909, 1.0}; -/* see http://en.wikipedia.org/wiki/Standard_illuminant#White_points_of_standard_illuminants +/* see https://en.wikipedia.org/wiki/Standard_illuminant#White_points_of_standard_illuminants * also see http://www.brucelindbloom.com/index.html?Eqn_T_to_xy.html for the equations */ /* ACES white point, taken from * Specification S-2014-004 * ACEScg – A Working Space for CGI Render and Compositing */ cmsCIExyY d60_aces= {0.32168, 0.33767, 1.0}; /* *****************Set up profile variables and values *************** */ cmsHPROFILE profile; cmsToneCurve* tone_curve[3]; cmsCIExyY whitepoint; cmsCIExyYTRIPLE primaries; const char* filename; cmsMLU *copyright = cmsMLUalloc(NULL, 1); /* I put a Creative Commons Attribution-ShareAlike 3.0 Unported License * on the profiles that I distribute (see * https://creativecommons.org/licenses/by-sa/3.0/legalcode) * The CC V3 Attribution-ShareAlike unported licence is accepted by both * Debian and Fedora as a free licence. * * Note that the CC V3 BY-SA licence that I put on the ICC profiles * is not the same licence that's applied to my profile-making code, * which is LGPL2.1+. * * Of course you can modify my profile-making code to put some other * *profile* copyright on the actual profiles that the code makes, * for example public domain (Creative Commons CC0) * or one of the GPL copyrights. * * The ICC suggested wording for profile copyrights is here * (see the last section, entitled "Licensing"): * http://www.color.org/registry/profileregistration.xalter * * The ICC copyright sounds like it's probably a free licence, * but as of June 2015 it hasn't been accepted by Fedora or Debian as a * free license. * * Here are the Fedora and Debian lists of free licences: * * https://fedoraproject.org/wiki/Licensing:Main?rd=Licensing#Content_Licenses * https://wiki.debian.org/DFSGLicenses * * */ cmsMLUsetASCII(copyright, "en", "US", "Copyright 2015, Elle Stone (website: http://ninedegreesbelow.com/; email: ellestone@ninedegreesbelow.com). This ICC profile is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License (https://creativecommons.org/licenses/by-sa/3.0/legalcode)."); cmsMLU *manufacturer = cmsMLUalloc(NULL, 1); cmsMLU *description; /* ********************** MAKE THE PROFILES ************************* */ /* ACES PROFILES */ /* ***** Make profile: ACEScg, D60, gamma=1.00 */ /* ACEScg chromaticities taken from * Specification S-2014-004 * ACEScg – A Working Space for CGI Render and Compositing */ cmsCIExyYTRIPLE aces_cg_primaries = { {0.713, 0.293, 1.0}, {0.165, 0.830, 1.0}, {0.128, 0.044, 1.0} }; whitepoint = d60_aces; primaries = aces_cg_primaries; /* linear gamma */ tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma100[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ACEScg-elle-V4-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ACEScg-elle-V4-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ACEScg-elle-V2-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ACEScg-elle-V2-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* gamma=2.2 */ tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma220[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ACEScg-elle-V4-g22.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ACEScg-elle-V4-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ACEScg-elle-V2-g22.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ACEScg-elle-V2-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* sRGB TRC */ tone_curve[0] = tone_curve[1] = tone_curve[2] = srgb_parametric[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ACEScg-elle-V4-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ACEScg-elle-V4-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ACEScg-elle-V2-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ACEScg-elle-V2-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* labl TRC */ tone_curve[0] = tone_curve[1] = tone_curve[2] = labl_parametric[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ACEScg-elle-V4-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ACEScg-elle-V4-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ACEScg-elle-V2-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ACEScg-elle-V2-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* ***** Make profile: ACES, D60, gamma=1.00 */ /* ACES chromaticities taken from * Specification * */ cmsCIExyYTRIPLE aces_primaries = { {0.73470, 0.26530, 1.0}, {0.00000, 1.00000, 1.0}, {0.00010, -0.07700, 1.0} }; cmsCIExyYTRIPLE aces_primaries_prequantized = { {0.734704192222, 0.265298276252, 1.0}, {-0.000004945077, 0.999992850272, 1.0}, {0.000099889199, -0.077007518685, 1.0} }; whitepoint = d60_aces; primaries = aces_primaries_prequantized; /* linear gamma */ tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma100[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ACES-elle-V4-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ACES-elle-V4-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ACES-elle-V2-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ACES-elle-V2-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* gamma=2.2 */ tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma220[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ACES-elle-V4-g122.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ACES-elle-V4-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ACES-elle-V2-g22.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ACES-elle-V2-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* sRGB TRC */ tone_curve[0] = tone_curve[1] = tone_curve[2] = srgb_parametric[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ACES-elle-V4-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ACES-elle-V4-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ACES-elle-V2-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ACES-elle-V2-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* labl TRC */ tone_curve[0] = tone_curve[1] = tone_curve[2] = labl_parametric[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ACES-elle-V4-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ACES-elle-V4-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ACES-elle-V2-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ACES-elle-V2-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* D50 PROFILES */ /* ***** Make profile: AllColorsRGB, D50, gamma=1.00 */ /* AllColors.icc has a slightly larger color gamut than the ACES color space. * It has a D50 white point and a linear gamma TRC. * It holds all visible colors. * Just like the ACES color space, * AllColors also holds a high percentage of imaginary colors. - * See http://ninedegreesbelow.com/photography/xyz-rgb.html#xyY + * See https://ninedegreesbelow.com/photography/xyz-rgb.html#xyY * for more information about imaginary colors. * AllColors primaries for red and blue from - * http://www.ledtuning.nl/en/cie-convertor + * https://www.ledtuning.nl/en/cie-convertor * blue 375nm red 780nm, plus Y intercepts: * Color Wavelength (): 375 nm. * Spectral Locus coordinates: X:0.17451 Y:0.005182 * Color Wavelength (): 780 nm. * Spectral Locus coordinates: X:0.734690265 Y:0.265309735 * X1:0.17451 Y1:0.005182 * X2:0.734690265 Y2:0.265309735 * X3:0.00Y3:? Solve for Y3: * (0.265309735-0.005182)/(0.734690265-0.17451)=0.46436433279205221554=m * y=mx+b let x=0; y=b * Y1=0.005182=(0.46436433279205221554*0.17451)+b * b=0.005182-(0.46436433279205221554*0.17451)=-.07585421971554103213 * */ cmsCIExyYTRIPLE allcolors_primaries = { {0.734690265, 0.265309735, 1.0}, {0.000000000, 1.000000000, 1.0}, {0.000000000, -.0758542197, 1.0} }; whitepoint = d50_illuminant_specs; primaries = allcolors_primaries; /* linear gamma */ tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma100[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "AllColorsRGB-elle-V4-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "AllColorsRGB-elle-V4-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "AllColorsRGB-elle-V2-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "AllColorsRGB-elle-V2-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* gamma=2.2 */ tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma220[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "AllColorsRGB-elle-V4-g22.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "AllColorsRGB-elle-V4-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "AllColorsRGB-elle-V2-g22.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "AllColorsRGB-elle-V2-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* sRGB TRC */ tone_curve[0] = tone_curve[1] = tone_curve[2] = srgb_parametric[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "AllColorsRGB-elle-V4-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "AllColorsRGB-elle-V4-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "AllColorsRGB-elle-V2-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "AllColorsRGB-elle-V2-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* labl TRC */ tone_curve[0] = tone_curve[1] = tone_curve[2] = labl_parametric[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "AllColorsRGB-elle-V4-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "AllColorsRGB-elle-V4-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "AllColorsRGB-elle-V2-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "AllColorsRGB-elle-V2-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* ***** Make profile: Identity, D50, gamma=1.00. */ /* These primaries also hold all possible visible colors, * but less efficiently than the AllColors profile.*/ cmsCIExyYTRIPLE identity_primaries = {/* */ {1.0, 0.0, 1.0}, {0.0, 1.0, 1.0}, {0.0, 0.0, 1.0} }; whitepoint = d50_illuminant_specs; primaries = identity_primaries; /* linear gamma */ tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma100[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "IdentityRGB-elle-V4-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "IdentityRGB-elle-V4-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "IdentityRGB-elle-V2-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "IdentityRGB-elle-V2-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* gamma=2.2 */ tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma220[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "IdentityRGB-elle-V4-g22.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "IdentityRGB-elle-V4-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "IdentityRGB-elle-V2-g22.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "IdentityRGB-elle-V2-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* sRGB TRC */ tone_curve[0] = tone_curve[1] = tone_curve[2] = srgb_parametric[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "IdentityRGB-elle-V4-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "IdentityRGB-elle-V4-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "IdentityRGB-elle-V2-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "IdentityRGB-elle-V2-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* labl TRC */ tone_curve[0] = tone_curve[1] = tone_curve[2] = labl_parametric[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "IdentityRGB-elle-V4-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "IdentityRGB-elle-V4-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "IdentityRGB-elle-V2-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "IdentityRGB-elle-V2-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* ***** Make profile: Romm/Prophoto, D50, gamma=1.80 */ /* Reference Input/Output Medium Metric RGB Color Encodings (RIMM/ROMM RGB) * Kevin E. Spaulding, Geoffrey J. Woolfe and Edward J. Giorgianni * Eastman Kodak Company, Rochester, New York, U.S.A. - * Above document is available at http://photo-lovers.org/pdf/color/romm.pdf + * Above document is available at https://photo-lovers.org/pdf/color/romm.pdf * Kodak designed the Romm (ProPhoto) color gamut to include all printable * and most real world colors. It includes some imaginary colors and excludes * some of the real world blues and violet blues that can be captured by * digital cameras. For high bit depth image editing only. */ cmsCIExyYTRIPLE romm_primaries = { {0.7347, 0.2653, 1.0}, {0.1596, 0.8404, 1.0}, {0.0366, 0.0001, 1.0} }; primaries = romm_primaries; whitepoint = d50_romm_spec; /* gamma 1.80 */ tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma180[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "LargeRGB-elle-V4-g18.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "LargeRGB-elle-V4-g18.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "LargeRGB-elle-V2-g18.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "LargeRGB-elle-V2-g18.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* linear gamma */ tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma100[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "LargeRGB-elle-V4-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "LargeRGB-elle-V4-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "LargeRGB-elle-V2-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "LargeRGB-elle-V2-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* gamma=2.2 */ tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma220[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "LargeRGB-elle-V4-g22.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "LargeRGB-elle-V4-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "LargeRGB-elle-V2-g22.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "LargeRGB-elle-V2-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* sRGB TRC */ tone_curve[0] = tone_curve[1] = tone_curve[2] = srgb_parametric[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "LargeRGB-elle-V4-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "LargeRGB-elle-V4-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "LargeRGB-elle-V2-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "LargeRGB-elle-V2-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* labl TRC */ tone_curve[0] = tone_curve[1] = tone_curve[2] = labl_parametric[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "LargeRGB-elle-V4-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "LargeRGB-elle-V4-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "LargeRGB-elle-V2-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "LargeRGB-elle-V2-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* ***** Make profile: WidegamutRGB, D50, gamma=2.19921875 */ /* Pascale's primary values produce a profile that matches * old V2 Widegamut profiles from Adobe and Canon. * Danny Pascale: A review of RGB color spaces * http://www.babelcolor.com/download/A%20review%20of%20RGB%20color%20spaces.pdf * WideGamutRGB was designed by Adobe to be a wide gamut color space that uses * spectral colors as its primaries. For high bit depth image editing only. */ cmsCIExyYTRIPLE widegamut_pascale_primaries = { {0.7347, 0.2653, 1.0}, {0.1152, 0.8264, 1.0}, {0.1566, 0.0177, 1.0} }; primaries = widegamut_pascale_primaries; whitepoint = d50_romm_spec; /* gamma 2.20 */ tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma220[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "WideRGB-elle-V4-g22.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "WideRGB-elle-V4-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "WideRGB-elle-V2-g22.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "WideRGB-elle-V2-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* linear gamma */ tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma100[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "WideRGB-elle-V4-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "WideRGB-elle-V4-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "WideRGB-elle-V2-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "WideRGB-elle-V2-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* sRGB TRC */ tone_curve[0] = tone_curve[1] = tone_curve[2] = srgb_parametric[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "WideRGB-elle-V4-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "WideRGB-elle-V4-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "WideRGB-elle-V2-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "WideRGB-elle-V2-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* labl TRC */ tone_curve[0] = tone_curve[1] = tone_curve[2] = labl_parametric[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "WideRGB-elle-V4-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "WideRGB-elle-V4-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "WideRGB-elle-V2-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "WideRGB-elle-V2-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* D65 PROFILES */ /* ***** Make profile: ClayRGB (AdobeRGB), D65, gamma=2.19921875 */ /* The Adobe RGB 1998 color gamut covers a higher percentage of * real-world greens than sRGB, but still doesn't include all printable * greens, yellows, and cyans. * When made using the gamma=2.19921875 tone response curve, * this profile can be used for 8-bit image editing * if used with appropriate caution to avoid posterization. * When made with the gamma=2.19921875 tone response curve * this profile can be applied to DCF R98 camera-generated jpegs. * */ cmsCIExyYTRIPLE adobe_primaries = { {0.6400, 0.3300, 1.0}, {0.2100, 0.7100, 1.0}, {0.1500, 0.0600, 1.0} }; cmsCIExyYTRIPLE adobe_primaries_prequantized = { {0.639996511, 0.329996864, 1.0}, {0.210005295, 0.710004866, 1.0}, {0.149997606, 0.060003644, 1.0} }; primaries = adobe_primaries_prequantized; whitepoint = d65_srgb_adobe_specs; /* gamma 2.20 */ tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma220[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ClayRGB-elle-V4-g22.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ClayRGB-elle-V4-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ClayRGB-elle-V2-g22.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ClayRGB-elle-V2-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* linear gamma */ tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma100[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ClayRGB-elle-V4-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ClayRGB-elle-V4-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ClayRGB-elle-V2-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ClayRGB-elle-V2-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* sRGB TRC */ tone_curve[0] = tone_curve[1] = tone_curve[2] = srgb_parametric[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ClayRGB-elle-V4-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ClayRGB-elle-V4-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ClayRGB-elle-V2-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ClayRGB-elle-V2-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* labl TRC */ tone_curve[0] = tone_curve[1] = tone_curve[2] = labl_parametric[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ClayRGB-elle-V4-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ClayRGB-elle-V4-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "ClayRGB-elle-V2-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "ClayRGB-elle-V2-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* ***** Make profile: Rec.2020, D65, Rec709 TRC */ /* * */ cmsCIExyYTRIPLE rec2020_primaries = { {0.7079, 0.2920, 1.0}, {0.1702, 0.7965, 1.0}, {0.1314, 0.0459, 1.0} }; cmsCIExyYTRIPLE rec2020_primaries_prequantized = { {0.708012540607, 0.291993664388, 1.0}, {0.169991652439, 0.797007778423, 1.0}, {0.130997824007, 0.045996550894, 1.0} }; primaries = rec2020_primaries_prequantized; whitepoint = d65_srgb_adobe_specs; /* rec.709 */ tone_curve[0] = tone_curve[1] = tone_curve[2] = rec709_parametric[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Rec2020-elle-V4-rec709.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Rec2020-elle-V4-rec709.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Rec2020-elle-V2-rec709.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Rec2020-elle-V2-rec709.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* linear gamma */ tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma100[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Rec2020-elle-V4-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Rec2020-elle-V4-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Rec2020-elle-V2-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Rec2020-elle-V2-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* gamma=2.2 */ tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma220[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Rec2020-elle-V4-g22.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Rec2020-elle-V4-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Rec2020-elle-V2-g22.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Rec2020-elle-V2-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* sRGB TRC */ tone_curve[0] = tone_curve[1] = tone_curve[2] = srgb_parametric[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Rec2020-elle-V4-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Rec2020-elle-V4-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Rec2020-elle-V2-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Rec2020-elle-V2-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* labl TRC */ tone_curve[0] = tone_curve[1] = tone_curve[2] = labl_parametric[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Rec2020-elle-V4-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Rec2020-elle-V4-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Rec2020-elle-V2-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Rec2020-elle-V2-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* ***** Make profile: sRGB, D65, sRGB TRC */ -/* http://en.wikipedia.org/wiki/Srgb */ +/* https://en.wikipedia.org/wiki/Srgb */ /* Hewlett-Packard and Microsoft designed sRGB to match * the color gamut of consumer-grade CRTs from the 1990s * and to be the standard color space for the world wide web. * When made using the standard sRGB TRC, this sRGB profile * can be applied to DCF R03 camera-generated jpegs and * is an excellent color space for editing 8-bit images. * When made using the linear gamma TRC, the resulting profile * should only be used for high bit depth image editing. * */ cmsCIExyYTRIPLE srgb_primaries = { {0.6400, 0.3300, 1.0}, {0.3000, 0.6000, 1.0}, {0.1500, 0.0600, 1.0} }; cmsCIExyYTRIPLE srgb_primaries_pre_quantized = { {0.639998686, 0.330010138, 1.0}, {0.300003784, 0.600003357, 1.0}, {0.150002046, 0.059997204, 1.0} }; primaries = srgb_primaries_pre_quantized; whitepoint = d65_srgb_adobe_specs; /* sRGB TRC */ tone_curve[0] = tone_curve[1] = tone_curve[2] = srgb_parametric[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "sRGB-elle-V4-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "sRGB-elle-V4-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "sRGB-elle-V2-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "sRGB-elle-V2-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* linear gamma */ tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma100[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "sRGB-elle-V4-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "sRGB-elle-V4-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "sRGB-elle-V2-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "sRGB-elle-V2-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* gamma=2.2 */ tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma220[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "sRGB-elle-V4-g22.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "sRGB-elle-V4-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "sRGB-elle-V2-g22.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "sRGB-elle-V2-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* labl TRC */ tone_curve[0] = tone_curve[1] = tone_curve[2] = labl_parametric[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "sRGB-elle-V4-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "sRGB-elle-V4-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "sRGB-elle-V2-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "sRGB-elle-V2-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* Rec.709 TRC */ tone_curve[0] = tone_curve[1] = tone_curve[2] = rec709_parametric[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "sRGB-elle-V4-rec709.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "sRGB-elle-V4-rec709.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "sRGB-elle-V2-rec709.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "sRGB-elle-V2-rec709.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* ***** Make profile: CIE-RGB profile, E white point*/ /* The ASTM E white point is probably the right white point * to use when making the CIE-RGB color space profile. * It's not clear to me what the correct CIE-RGB primaries really are. * Lindbloom gives one set. The LCMS version 1 tutorial gives a different set. * I asked a friend to ask an expert, who said the real primaries should * be calculated from the spectral wavelengths. * Two sets of primaries are given below: * */ /* This page explains what the CIE color space is: * https://en.wikipedia.org/wiki/CIE_1931 * These pages give the wavelengths: * http://hackipedia.org/Color%20space/pdf/CIE%20Color%20Space.pdf * http://infocom.ing.uniroma1.it/~gaetano/texware/Full-How%20the%20CIE%201931%20Color-Matching%20Functions%20Were%20Derived%20from%20Wright-Guild%20Data.pdf * This page has resources for calculating xy values given a spectral color wavelength: * http://www.cvrl.org/cmfs.htm * This page does the calculations for you: - * http://www.ledtuning.nl/cie.php + * https://www.ledtuning.nl/en/cie-convertor * Plugging the wavelengths into the ledtuning website * gives the following CIE RGB xy primaries: 700.0 nm has Spectral Locus coordinates: x:0.734690023 y:0.265309977 546.1 nm has Spectral Locus coordinates: x:0.2736747378 y:0.7174284409 435.8 nm has Spectral Locus coordinates: x:0.1665361196 y:0.0088826412 * */ cmsCIExyYTRIPLE cie_primaries_ledtuning = { {0.7346900230, 0.2653099770, 1.0}, {0.2736747378, 0.7174284409, 1.0}, {0.1665361196, 0.0088826412, 1.0} }; /* Assuming you want to use the ASTM values for the E white point, * here are the prequantized ledtuning primaries */ cmsCIExyYTRIPLE cie_primaries_ledtuning_prequantized = { {0.734689082, 0.265296653, 1.0}, {0.273673341, 0.717437354, 1.0}, {0.166531028, 0.008882428, 1.0} }; primaries = cie_primaries_ledtuning_prequantized; whitepoint = e_astm; tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma220[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "CIERGB-elle-V4-g22.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "CIERGB-elle-V4-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "CIERGB-elle-V2-g22.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "CIERGB-elle-V2-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* A linear gamma version of this profile makes more sense * than a gamma=2.2 version */ tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma100[0]; whitepoint = e_astm; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "CIERGB-elle-V4-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "CIERGB-elle-V4-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "CIERGB-elle-V2-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "CIERGB-elle-V2-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* sRGB TRC*/ tone_curve[0] = tone_curve[1] = tone_curve[2] = srgb_parametric[0]; whitepoint = e_astm; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "CIERGB-elle-V4-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "CIERGB-elle-V4-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "CIERGB-elle-V2-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "CIERGB-elle-V2-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* labl TRC */ tone_curve[0] = tone_curve[1] = tone_curve[2] = labl_parametric[0]; profile = cmsCreateRGBProfile ( &whitepoint, &primaries, tone_curve ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "CIERGB-elle-V4-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "CIERGB-elle-V4-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "CIERGB-elle-V2-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "CIERGB-elle-V2-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* * Here's the second set of primaries * http://www.cis.rit.edu/research/mcsl2/research/broadbent/CIE1931_RGB.pdf * https://groups.google.com/forum/#!topic/sci.engr.color/fBI3k1llm-g * Lindbloom gives these values on his Working Space Information page: */ cmsCIExyYTRIPLE cie_primaries_lindbloom = { {0.7350, 0.2650, 1.0}, {0.2740, 0.7170, 1.0}, {0.1670, 0.0090, 1.0} }; /* Assuming you want to use the ASTM values for the E white point, * here are the prequantized Lindbloom primaries */ cmsCIExyYTRIPLE cie_primaries_lindbloom_prequantized = { {0.714504840, 0.297234644, 1.0}, {0.520085568, 0.452364535, 1.0}, {0.090957433, 0.051485032, 1.0} }; /* ***** Make profile: Gray ICC profiles - D50 white point ********* */ whitepoint = d50_illuminant_specs; const cmsToneCurve* grayTRC; /* Gray profile with gamma=1.00, linear gamma, "linear light", etc */ grayTRC = cmsBuildGamma (NULL, 1.00); profile = cmsCreateGrayProfile ( &whitepoint, grayTRC ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Gray-D50-elle-V4-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Gray-D50-elle-V4-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Gray-D50-elle-V2-g10.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Gray-D50-elle-V2-g10.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* Gray profile with gamma=1.80, * actually 1.80078125, * in order to create the same gamma curve in V2 and V4 profiles */ grayTRC = cmsBuildGamma (NULL, 1.80078125); profile = cmsCreateGrayProfile ( &whitepoint, grayTRC ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Gray-D50-elle-V4-g18.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Gray-D50-elle-V4-g18.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Gray-D50-elle-V2-g18.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Gray-D50-elle-V2-g18.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* Gray profile with gamma=2.20 * actually gamma=2.19921875, * in order to create the same gamma curve in V2 and V4 profiles */ grayTRC = cmsBuildGamma (NULL, 2.19921875); profile = cmsCreateGrayProfile ( &whitepoint, grayTRC ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Gray-D50-elle-V4-g22.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Gray-D50-elle-V4-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Gray-D50-elle-V2-g22.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Gray-D50-elle-V2-g22.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* Gray profile with srgb-trc */ grayTRC = cmsBuildParametricToneCurve(NULL, 4, srgb_parameters); profile = cmsCreateGrayProfile ( &whitepoint, grayTRC ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Gray-D50-elle-V4-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Gray-D50-elle-V4-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Gray-D50-elle-V2-srgbtrc.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Gray-D50-elle-V2-srgbtrc.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* Gray profile with labl TRC */ grayTRC = cmsBuildParametricToneCurve(NULL, 4, labl_parameters); profile = cmsCreateGrayProfile ( &whitepoint, grayTRC ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Gray-D50-elle-V4-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Gray-D50-elle-V4-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Gray-D50-elle-V2-labl.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Gray-D50-elle-V2-labl.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* Gray profile with Rec709 TRC */ grayTRC = cmsBuildParametricToneCurve(NULL, 4, rec709_parameters); profile = cmsCreateGrayProfile ( &whitepoint, grayTRC ); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); /* V4 */ description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Gray-D50-elle-V4-rec709.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Gray-D50-elle-V4-rec709.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* V2 */ cmsSetProfileVersion(profile, 2.1); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Gray-D50-elle-V2-rec709.icc"); cmsWriteTag(profile, cmsSigProfileDescriptionTag, description); filename = "Gray-D50-elle-V2-rec709.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* ***** Make profile: LCMS built-in LAB and XYZ profiles *********** */ /* Based on transicc output, the V4 profiles * can be used in unbounded mode, but the V2 versions cannot. */ profile = cmsCreateLab2Profile(&d50_illuminant_specs); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Lab-D50-Identity-elle-V2.icc"); filename = "Lab-D50-Identity-elle-V2.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); profile = cmsCreateLab4Profile(&d50_illuminant_specs); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "Lab-D50-Identity-elle-V4.icc"); filename = "Lab-D50-Identity-elle-V4.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); profile = cmsCreateXYZProfile(); cmsWriteTag(profile, cmsSigCopyrightTag, copyright); description = cmsMLUalloc(NULL, 1); cmsMLUsetASCII(description, "en", "US", "XYZ-D50-Identity-elle-V4.icc"); filename = "XYZ-D50-Identity-elle-V4.icc"; cmsSaveProfileToFile(profile, filename); cmsMLUfree(description); /* For the following profiles, information is provided, but not the actual * profile making code. * */ /* old monitor-based editing profiles */ /* ColorMatchRGB, D50, gamma=1.80 */ -/* http://www.dpreview.com/forums/post/3902882 - * http://lists.apple.com/archives/colorsync-users/2001/Apr/msg00073.html +/* https://www.dpreview.com/forums/post/3902882 + * https://lists.apple.com/archives/colorsync-users/2001/Apr/msg00073.html * ColorMatch was designed to fit Radius PressView CRT monitors, * similar to sRGB fitting consumer-grade CRT monitors, * "fit" meaning "could be calibrated to match". * Adobe does still distribute a ColorMatchRGB profile. * Making this profile using the D50_romm_doc white point that is used * in other old V2 profiles (ProPhoto, WideGamut) * doesn't make a well behaved profile, * but the resulting profile is very close to the Adobe-supplied version. * Using the prequantized primaries makes a profile that's just as close * to the Adobe-supplied version and in addition is well behaved. * Unless you have untagged images created on a PressView CRT, * there is no reason to make or use this profile. * */ cmsCIExyYTRIPLE colormatch_primaries = { {0.6300, 0.3400, 1.0}, {0.2950, 0.6050, 1.0}, {0.1500, 0.0750, 1.0} }; cmsCIExyYTRIPLE colormatch_primaries_prequantized = { {0.629992636, 0.339999723, 1.0}, {0.295006332, 0.604997745, 1.0}, {0.149992036, 0.075005244, 1.0} }; primaries = colormatch_primaries_prequantized; whitepoint = d50_romm_spec; tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma180[0]; /* AppleRGB, D65, gamma=1.80 */ /* AppleRGB was created to fit the old Apple CRT displays * just as sRGB fit consumer-grade CRT monitors * and ColorMatch fit PressView CRT monitors. * */ cmsCIExyYTRIPLE apple_primaries = { {0.6250, 0.3400, 1.0}, {0.2800, 0.5950, 1.0}, {0.1550, 0.0700, 1.0} }; cmsCIExyYTRIPLE apple_primaries_prequantized = { {0.625012368, 0.340000081, 1.0}, {0.279996113, 0.595006943, 1.0}, {0.155001212, 0.070001183, 1.0} }; primaries = apple_primaries_prequantized; whitepoint = d65_srgb_adobe_specs; tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma180[0]; /* video profiles */ /* PAL-SECAM, D65 gamma=2.20 */ -/* http://en.wikipedia.org/wiki/PAL */ +/* https://en.wikipedia.org/wiki/PAL */ cmsCIExyYTRIPLE pal_primaries = { {0.6400, 0.3300, 1.0}, {0.2900, 0.6000, 1.0}, {0.1500, 0.0600, 1.0} }; /* PAL is one of many video and television-related color spaces. * If you need the original profile with all its tags, * I recommend that you use the Argyllcms version of this profile * (EBU3213_PAL.icm, located in the "ref" folder), * rather than making your own. * But if you do want to make your own PAL profile using LCMS2, * these prequantized primaries and white point make a profile with * the same primaries and white point as the Argyllcms profile. * The Argyllcms profile has a point curve TRC. * */ cmsCIExyYTRIPLE pal_primaries_prequantized = { {0.640007798, 0.330006592, 1.0}, {0.290000327, 0.600000840, 1.0}, {0.149998025, 0.059996098, 1.0} }; primaries = pal_primaries_prequantized; whitepoint = d65_srgb_adobe_specs; tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma220[0]; /* SMPTE-C, D65, gamma=2.20 */ -/* http://en.wikipedia.org/wiki/NTSC#SMPTE_C +/* https://en.wikipedia.org/wiki/NTSC#SMPTE_C * SMPTE-C is one of many video and television-related color spaces * and is an update of the original NTSC. */ cmsCIExyYTRIPLE smpte_c_primaries = { {0.6300, 0.3400, 1.0}, {0.3100, 0.5950, 1.0}, {0.1550, 0.0700, 1.0} }; cmsCIExyYTRIPLE smpte_c_primaries_prequantized = { {0.629996495, 0.339990597, 1.0}, {0.309997880, 0.594995808, 1.0}, {0.149999952, 0.069999431, 1.0} }; primaries = smpte_c_primaries_prequantized; whitepoint = d65_srgb_adobe_specs; tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma220[0]; /* NTSC, C, gamma=2.20 */ -/* http://en.wikipedia.org/wiki/NTSC#Colorimetry*/ +/* https://en.wikipedia.org/wiki/NTSC#Colorimetry*/ /* According to Wikipedia, these "original 1953 color NTSC specifications" * were used by early television receivers. */ cmsCIExyYTRIPLE ntcs_primaries = { {0.6700, 0.3300, 1.0}, {0.2100, 0.7100, 1.0}, {0.1400, 0.0800, 1.0} }; cmsCIExyYTRIPLE ntcs_primaries_prequantized = { {0.670010373, 0.330001186, 1.0}, {0.209999261, 0.710001124, 1.0}, {0.139996061, 0.080002934, 1.0} }; primaries = ntcs_primaries_prequantized; whitepoint = c_astm; tone_curve[0] = tone_curve[1] = tone_curve[2] = gamma220[0]; /* *********************** wrap up and close out ****************** */ /* free copyright */ cmsMLUfree(copyright); /* make gcc happy by returning an integer from main() */ return 0; } diff --git a/krita/data/templates/animation/.directory b/krita/data/templates/animation/.directory index 6cbbe623b2..d8cd91b610 100644 --- a/krita/data/templates/animation/.directory +++ b/krita/data/templates/animation/.directory @@ -1,29 +1,30 @@ [Desktop Entry] Name=Animation Templates Name[ar]=قوالب الحركات Name[ca]=Plantilles d'animació Name[ca@valencia]=Plantilles d'animació Name[cs]=Šablony animací: Name[de]=Animations-Vorlagen Name[el]=Πρότυπα εφέ κίνησης Name[en_GB]=Animation Templates Name[es]=Plantillas de animación +Name[et]=Animatsioonimallid Name[eu]=Animazio-txantiloiak Name[fi]=Animaatiopohjat Name[fr]=Modèles pour animation Name[gl]=Modelos de animación Name[is]=Sniðmát fyrir hreyfimyndir Name[it]=Modelli di animazioni Name[ko]=애니메이션 템플릿 Name[nl]=Animatiesjablonen Name[nn]=Animasjonsmalar Name[pl]=Szablony animacji Name[pt]=Modelos de Animações Name[pt_BR]=Modelos de animação Name[sv]=Animeringsmallar Name[tr]=Canlandırma Şablonları Name[uk]=Шаблони анімацій Name[x-test]=xxAnimation Templatesxx Name[zh_CN]=动画模板 Name[zh_TW]=動畫範本 X-KDE-DefaultTab=true diff --git a/krita/main.cc b/krita/main.cc index d2d84e3d89..1523923528 100644 --- a/krita/main.cc +++ b/krita/main.cc @@ -1,598 +1,598 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2002 Patrick Julien * Copyright (c) 2015 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "data/splash/splash_screen.xpm" #include "data/splash/splash_holidays.xpm" #include "data/splash/splash_screen_x2.xpm" #include "data/splash/splash_holidays_x2.xpm" #include "KisDocument.h" #include "kis_splash_screen.h" #include "KisPart.h" #include "KisApplicationArguments.h" #include #include "input/KisQtWidgetsTweaker.h" #include #include #ifdef Q_OS_ANDROID #include #endif #if defined Q_OS_WIN #include "config_use_qt_tablet_windows.h" #include #ifndef USE_QT_TABLET_WINDOWS #include #include #else #include #endif #include "config-high-dpi-scale-factor-rounding-policy.h" #include "config-set-has-border-in-full-screen-default.h" #ifdef HAVE_SET_HAS_BORDER_IN_FULL_SCREEN_DEFAULT #include #endif #include #endif #if defined HAVE_KCRASH #include #elif defined USE_DRMINGW namespace { void tryInitDrMingw() { wchar_t path[MAX_PATH]; QString pathStr = QCoreApplication::applicationDirPath().replace(L'/', L'\\') + QStringLiteral("\\exchndl.dll"); if (pathStr.size() > MAX_PATH - 1) { return; } int pathLen = pathStr.toWCharArray(path); path[pathLen] = L'\0'; // toWCharArray doesn't add NULL terminator HMODULE hMod = LoadLibraryW(path); if (!hMod) { return; } // No need to call ExcHndlInit since the crash handler is installed on DllMain auto myExcHndlSetLogFileNameA = reinterpret_cast(GetProcAddress(hMod, "ExcHndlSetLogFileNameA")); if (!myExcHndlSetLogFileNameA) { return; } // Set the log file path to %LocalAppData%\kritacrash.log QString logFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation).replace(L'/', L'\\') + QStringLiteral("\\kritacrash.log"); myExcHndlSetLogFileNameA(logFile.toLocal8Bit()); } } // namespace #endif #ifdef Q_OS_WIN namespace { typedef enum ORIENTATION_PREFERENCE { ORIENTATION_PREFERENCE_NONE = 0x0, ORIENTATION_PREFERENCE_LANDSCAPE = 0x1, ORIENTATION_PREFERENCE_PORTRAIT = 0x2, ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED = 0x4, ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED = 0x8 } ORIENTATION_PREFERENCE; typedef BOOL WINAPI (*pSetDisplayAutoRotationPreferences_t)( ORIENTATION_PREFERENCE orientation ); void resetRotation() { QLibrary user32Lib("user32"); if (!user32Lib.load()) { qWarning() << "Failed to load user32.dll! This really should not happen."; return; } pSetDisplayAutoRotationPreferences_t pSetDisplayAutoRotationPreferences = reinterpret_cast(user32Lib.resolve("SetDisplayAutoRotationPreferences")); if (!pSetDisplayAutoRotationPreferences) { dbgKrita << "Failed to load function SetDisplayAutoRotationPreferences"; return; } bool result = pSetDisplayAutoRotationPreferences(ORIENTATION_PREFERENCE_NONE); dbgKrita << "SetDisplayAutoRotationPreferences(ORIENTATION_PREFERENCE_NONE) returned" << result; } } // namespace #endif #ifdef Q_OS_ANDROID extern "C" JNIEXPORT void JNICALL Java_org_krita_android_JNIWrappers_saveState(JNIEnv* /*env*/, jobject /*obj*/, jint /*n*/) { if (!KisPart::exists()) return; KisPart *kisPart = KisPart::instance(); QList> list = kisPart->documents(); for (QPointer &doc: list) { doc->autoSaveOnPause(); } const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); kritarc.setValue("canvasState", "OPENGL_SUCCESS"); } extern "C" JNIEXPORT void JNICALL Java_org_krita_android_JNIWrappers_exitFullScreen(JNIEnv* /*env*/, jobject /*obj*/, jint /*n*/) { if (!KisPart::exists()) return; KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow(); mainWindow->viewFullscreen(false); } __attribute__ ((visibility ("default"))) #endif extern "C" int main(int argc, char **argv) { // The global initialization of the random generator qsrand(time(0)); bool runningInKDE = !qgetenv("KDE_FULL_SESSION").isEmpty(); #if defined HAVE_X11 qputenv("QT_QPA_PLATFORM", "xcb"); #endif // Workaround a bug in QNetworkManager qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1)); // A per-user unique string, without /, because QLocalServer cannot use names with a / in it QString key = "Krita4" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation).replace("/", "_"); key = key.replace(":", "_").replace("\\","_"); QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true); QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); QCoreApplication::setAttribute(Qt::AA_DisableShaderDiskCache, true); #ifdef HAVE_HIGH_DPI_SCALE_FACTOR_ROUNDING_POLICY // This rounding policy depends on a series of patches to Qt related to // https://bugreports.qt.io/browse/QTBUG-53022. These patches are applied // in ext_qt for WIndows (patches 0031-0036). // // The rounding policy can be set externally by setting the environment // variable `QT_SCALE_FACTOR_ROUNDING_POLICY` to one of the following: // Round: Round up for .5 and above. // Ceil: Always round up. // Floor: Always round down. // RoundPreferFloor: Round up for .75 and above. // PassThrough: Don't round. // // The default is set to RoundPreferFloor for better behaviour than before, // but can be overridden by the above environment variable. QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor); #endif #ifdef Q_OS_ANDROID const QString write_permission = "android.permission.WRITE_EXTERNAL_STORAGE"; const QStringList permissions = { write_permission }; const QtAndroid::PermissionResultMap resultHash = QtAndroid::requestPermissionsSync(QStringList(permissions)); if (resultHash[write_permission] == QtAndroid::PermissionResult::Denied) { // TODO: show a dialog and graciously exit dbgKrita << "Permission denied by the user"; } else { dbgKrita << "Permission granted"; } #endif const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); bool singleApplication = true; bool enableOpenGLDebug = false; bool openGLDebugSynchronous = false; bool logUsage = true; { singleApplication = kritarc.value("EnableSingleApplication", true).toBool(); if (kritarc.value("EnableHiDPI", true).toBool()) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); } if (!qgetenv("KRITA_HIDPI").isEmpty()) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); } #ifdef HAVE_HIGH_DPI_SCALE_FACTOR_ROUNDING_POLICY if (kritarc.value("EnableHiDPIFractionalScaling", true).toBool()) { QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); } #endif if (!qgetenv("KRITA_OPENGL_DEBUG").isEmpty()) { enableOpenGLDebug = true; } else { enableOpenGLDebug = kritarc.value("EnableOpenGLDebug", false).toBool(); } if (enableOpenGLDebug && (qgetenv("KRITA_OPENGL_DEBUG") == "sync" || kritarc.value("OpenGLDebugSynchronous", false).toBool())) { openGLDebugSynchronous = true; } KisConfig::RootSurfaceFormat rootSurfaceFormat = KisConfig::rootSurfaceFormat(&kritarc); KisOpenGL::OpenGLRenderer preferredRenderer = KisOpenGL::RendererAuto; logUsage = kritarc.value("LogUsage", true).toBool(); #ifdef Q_OS_WIN const QString preferredRendererString = kritarc.value("OpenGLRenderer", "angle").toString(); #else const QString preferredRendererString = kritarc.value("OpenGLRenderer", "auto").toString(); #endif preferredRenderer = KisOpenGL::convertConfigToOpenGLRenderer(preferredRendererString); const KisOpenGL::RendererConfig config = KisOpenGL::selectSurfaceConfig(preferredRenderer, rootSurfaceFormat, enableOpenGLDebug); KisOpenGL::setDefaultSurfaceConfig(config); KisOpenGL::setDebugSynchronous(openGLDebugSynchronous); #ifdef Q_OS_WIN // HACK: https://bugs.kde.org/show_bug.cgi?id=390651 resetRotation(); #endif } if (logUsage) { KisUsageLogger::initialize(); } QString root; QString language; { // Create a temporary application to get the root QCoreApplication app(argc, argv); Q_UNUSED(app); root = KoResourcePaths::getApplicationRoot(); QSettings languageoverride(configPath + QStringLiteral("/klanguageoverridesrc"), QSettings::IniFormat); languageoverride.beginGroup(QStringLiteral("Language")); language = languageoverride.value(qAppName(), "").toString(); } #ifdef Q_OS_LINUX { QByteArray originalXdgDataDirs = qgetenv("XDG_DATA_DIRS"); if (originalXdgDataDirs.isEmpty()) { // We don't want to completely override the default originalXdgDataDirs = "/usr/local/share/:/usr/share/"; } qputenv("XDG_DATA_DIRS", QFile::encodeName(root + "share") + ":" + originalXdgDataDirs); } #else qputenv("XDG_DATA_DIRS", QFile::encodeName(root + "share")); #endif dbgKrita << "Setting XDG_DATA_DIRS" << qgetenv("XDG_DATA_DIRS"); // Now that the paths are set, set the language. First check the override from the language // selection dialog. dbgKrita << "Override language:" << language; bool rightToLeft = false; if (!language.isEmpty()) { KLocalizedString::setLanguages(language.split(":")); // And override Qt's locale, too qputenv("LANG", language.split(":").first().toLocal8Bit()); QLocale locale(language.split(":").first()); QLocale::setDefault(locale); const QStringList rtlLanguages = QStringList() << "ar" << "dv" << "he" << "ha" << "ku" << "fa" << "ps" << "ur" << "yi"; if (rtlLanguages.contains(language.split(':').first())) { rightToLeft = true; } } else { dbgKrita << "Qt UI languages:" << QLocale::system().uiLanguages() << qgetenv("LANG"); // And if there isn't one, check the one set by the system. QLocale locale = QLocale::system(); if (locale.name() != QStringLiteral("en")) { QStringList uiLanguages = locale.uiLanguages(); for (QString &uiLanguage : uiLanguages) { // This list of language codes that can have a specifier should // be extended whenever we have translations that need it; right // now, only en, pt, zh are in this situation. if (uiLanguage.startsWith("en") || uiLanguage.startsWith("pt")) { uiLanguage.replace(QChar('-'), QChar('_')); } else if (uiLanguage.startsWith("zh-Hant") || uiLanguage.startsWith("zh-TW")) { uiLanguage = "zh_TW"; } else if (uiLanguage.startsWith("zh-Hans") || uiLanguage.startsWith("zh-CN")) { uiLanguage = "zh_CN"; } } for (int i = 0; i < uiLanguages.size(); i++) { QString uiLanguage = uiLanguages[i]; // Strip the country code int idx = uiLanguage.indexOf(QChar('-')); if (idx != -1) { uiLanguage = uiLanguage.left(idx); uiLanguages.replace(i, uiLanguage); } } dbgKrita << "Converted ui languages:" << uiLanguages; qputenv("LANG", uiLanguages.first().toLocal8Bit()); #ifdef Q_OS_MAC // See https://bugs.kde.org/show_bug.cgi?id=396370 KLocalizedString::setLanguages(QStringList() << uiLanguages.first()); #else KLocalizedString::setLanguages(QStringList() << uiLanguages); #endif } } #if defined Q_OS_WIN && defined USE_QT_TABLET_WINDOWS && defined QT_HAS_WINTAB_SWITCH const bool forceWinTab = !KisConfig::useWin8PointerInputNoApp(&kritarc); QCoreApplication::setAttribute(Qt::AA_MSWindowsUseWinTabAPI, forceWinTab); if (qEnvironmentVariableIsEmpty("QT_WINTAB_DESKTOP_RECT") && qEnvironmentVariableIsEmpty("QT_IGNORE_WINTAB_MAPPING")) { QRect customTabletRect; KisDlgCustomTabletResolution::Mode tabletMode = KisDlgCustomTabletResolution::getTabletMode(&customTabletRect); KisDlgCustomTabletResolution::applyConfiguration(tabletMode, customTabletRect); } #endif // first create the application so we can create a pixmap KisApplication app(key, argc, argv); KisUsageLogger::writeHeader(); KisOpenGL::initialize(); #ifdef HAVE_SET_HAS_BORDER_IN_FULL_SCREEN_DEFAULT if (QCoreApplication::testAttribute(Qt::AA_UseDesktopOpenGL)) { QWindowsWindowFunctions::setHasBorderInFullScreenDefault(true); } #endif if (!language.isEmpty()) { if (rightToLeft) { app.setLayoutDirection(Qt::RightToLeft); } else { app.setLayoutDirection(Qt::LeftToRight); } } KLocalizedString::setApplicationDomain("krita"); dbgKrita << "Available translations" << KLocalizedString::availableApplicationTranslations(); dbgKrita << "Available domain translations" << KLocalizedString::availableDomainTranslations("krita"); #ifdef Q_OS_WIN QDir appdir(KoResourcePaths::getApplicationRoot()); QString path = qgetenv("PATH"); qputenv("PATH", QFile::encodeName(appdir.absolutePath() + "/bin" + ";" + appdir.absolutePath() + "/lib" + ";" + appdir.absolutePath() + "/Frameworks" + ";" + appdir.absolutePath() + ";" + path)); dbgKrita << "PATH" << qgetenv("PATH"); #endif if (qApp->applicationDirPath().contains(KRITA_BUILD_DIR)) { qFatal("FATAL: You're trying to run krita from the build location. You can only run Krita from the installation location."); } #if defined HAVE_KCRASH KCrash::initialize(); #elif defined USE_DRMINGW tryInitDrMingw(); #endif // If we should clear the config, it has to be done as soon as possible after // KisApplication has been created. Otherwise the config file may have been read // and stored in a KConfig object we have no control over. app.askClearConfig(); KisApplicationArguments args(app); if (singleApplication && app.isRunning()) { // only pass arguments to main instance if they are not for batch processing // any batch processing would be done in this separate instance const bool batchRun = args.exportAs() || args.exportSequence(); if (!batchRun) { QByteArray ba = args.serialize(); if (app.sendMessage(ba)) { return 0; } } } if (!runningInKDE) { // Icons in menus are ugly and distracting app.setAttribute(Qt::AA_DontShowIconsInMenus); } #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) app.setAttribute(Qt::AA_DisableWindowContextHelpButton); #endif app.installEventFilter(KisQtWidgetsTweaker::instance()); if (!args.noSplash()) { // then create the pixmap from an xpm: we cannot get the // location of our datadir before we've started our components, // so use an xpm. QDate currentDate = QDate::currentDate(); QWidget *splash = 0; if (currentDate > QDate(currentDate.year(), 12, 4) || currentDate < QDate(currentDate.year(), 1, 9)) { splash = new KisSplashScreen(app.applicationVersion(), QPixmap(splash_holidays_xpm), QPixmap(splash_holidays_x2_xpm)); } else { splash = new KisSplashScreen(app.applicationVersion(), QPixmap(splash_screen_xpm), QPixmap(splash_screen_x2_xpm)); } app.setSplashScreen(splash); } #if defined Q_OS_WIN KisConfig cfg(false); bool supportedWindowsVersion = true; QOperatingSystemVersion osVersion = QOperatingSystemVersion::current(); if (osVersion.type() == QOperatingSystemVersion::Windows) { if (osVersion.majorVersion() >= QOperatingSystemVersion::Windows7.majorVersion()) { supportedWindowsVersion = true; } else { supportedWindowsVersion = false; if (cfg.readEntry("WarnedAboutUnsupportedWindows", false)) { QMessageBox::information(0, i18nc("@title:window", "Krita: Warning"), i18n("You are running an unsupported version of Windows: %1.\n" "This is not recommended. Do not report any bugs.\n" "Please update to a supported version of Windows: Windows 7, 8, 8.1 or 10.", osVersion.name())); cfg.writeEntry("WarnedAboutUnsupportedWindows", true); } } } #ifndef USE_QT_TABLET_WINDOWS { if (cfg.useWin8PointerInput() && !KisTabletSupportWin8::isAvailable()) { cfg.setUseWin8PointerInput(false); } if (!cfg.useWin8PointerInput()) { bool hasWinTab = KisTabletSupportWin::init(); if (!hasWinTab && supportedWindowsVersion) { if (KisTabletSupportWin8::isPenDeviceAvailable()) { // Use WinInk automatically cfg.setUseWin8PointerInput(true); } else if (!cfg.readEntry("WarnedAboutMissingWinTab", false)) { if (KisTabletSupportWin8::isAvailable()) { QMessageBox::information(nullptr, i18n("Krita Tablet Support"), i18n("Cannot load WinTab driver and no Windows Ink pen devices are found. If you have a drawing tablet, please make sure the tablet driver is properly installed."), QMessageBox::Ok, QMessageBox::Ok); } else { QMessageBox::information(nullptr, i18n("Krita Tablet Support"), i18n("Cannot load WinTab driver. If you have a drawing tablet, please make sure the tablet driver is properly installed."), QMessageBox::Ok, QMessageBox::Ok); } cfg.writeEntry("WarnedAboutMissingWinTab", true); } } } if (cfg.useWin8PointerInput()) { KisTabletSupportWin8 *penFilter = new KisTabletSupportWin8(); if (penFilter->init()) { // penFilter.registerPointerDeviceNotifications(); app.installNativeEventFilter(penFilter); dbgKrita << "Using Win8 Pointer Input for tablet support"; } else { dbgKrita << "No Win8 Pointer Input available"; delete penFilter; } } } #elif defined QT_HAS_WINTAB_SWITCH // Check if WinTab/WinInk has actually activated const bool useWinTabAPI = app.testAttribute(Qt::AA_MSWindowsUseWinTabAPI); if (useWinTabAPI != !cfg.useWin8PointerInput()) { cfg.setUseWin8PointerInput(useWinTabAPI); } #endif #endif app.setAttribute(Qt::AA_CompressHighFrequencyEvents, false); // Set up remote arguments. QObject::connect(&app, SIGNAL(messageReceived(QByteArray,QObject*)), &app, SLOT(remoteArguments(QByteArray,QObject*))); QObject::connect(&app, SIGNAL(fileOpenRequest(QString)), &app, SLOT(fileOpenRequested(QString))); // Hardware information - KisUsageLogger::write("\nHardware Information\n"); - KisUsageLogger::write(QString(" GPU Acceleration: %1").arg(kritarc.value("OpenGLRenderer", "auto").toString())); - KisUsageLogger::write(QString(" Memory: %1 Mb").arg(KisImageConfig(true).totalRAM())); - KisUsageLogger::write(QString(" Number of Cores: %1").arg(QThread::idealThreadCount())); - KisUsageLogger::write(QString(" Swap Location: %1\n").arg(KisImageConfig(true).swapDir())); + KisUsageLogger::writeSysInfo("\nHardware Information\n"); + KisUsageLogger::writeSysInfo(QString(" GPU Acceleration: %1").arg(kritarc.value("OpenGLRenderer", "auto").toString())); + KisUsageLogger::writeSysInfo(QString(" Memory: %1 Mb").arg(KisImageConfig(true).totalRAM())); + KisUsageLogger::writeSysInfo(QString(" Number of Cores: %1").arg(QThread::idealThreadCount())); + KisUsageLogger::writeSysInfo(QString(" Swap Location: %1\n").arg(KisImageConfig(true).swapDir())); KisConfig(true).logImportantSettings(); if (!app.start(args)) { KisUsageLogger::log("Could not start Krita Application"); return 1; } int state = app.exec(); { QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); kritarc.setValue("canvasState", "OPENGL_SUCCESS"); } if (logUsage) { KisUsageLogger::close(); } return state; } diff --git a/krita/org.kde.krita.appdata.xml b/krita/org.kde.krita.appdata.xml index c549db0adc..fb0c5677c0 100644 --- a/krita/org.kde.krita.appdata.xml +++ b/krita/org.kde.krita.appdata.xml @@ -1,381 +1,386 @@ org.kde.krita org.kde.krita.desktop CC0-1.0 GPL-3.0-only Krita Foundation Fundació Krita Fundació Krita Krita Foundation Krita Foundation Krita Foundation Fundación Krita + Krita sihtasutus Krita Fundazioa Krita Foundation La Fondation Krita Fundación Krita Asas Krita Fondazione Krita Krita Foundation Krita Foundation Krita Foundation Fundacja Krity Fundação do Krita Krita Foundation Nadácia Krita Krita-stiftelsen Krita Vakfı Фундація Krita xxKrita Foundationxx Krita 基金会 Krita 基金會 foundation@krita.org Krita كريتا Krita Krita Krita Krita Krita Krita Krita + Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita xxKritaxx Krita Krita Digital Painting, Creative Freedom رسم رقميّ، حريّة إبداعيّة Digitalno crtanje, kreativna sloboda Dibuix digital, Llibertat creativa Dibuix digital, Llibertat creativa Digitální malování, svoboda tvorby Digital tegning, kunstnerisk frihed Digitales Malen, kreative Freiheit Ψηφιακή ζωγραφική, δημιουργική ελευθερία Digital Painting, Creative Freedom Pintura digital, libertad creativa Digitaalne joonistamine, loominguline vabadus Margolan digitala, sormen askatasuna Digitaalimaalaus, luova vapaus Peinture numérique, liberté créatrice Debuxo dixital, liberdade creativa Pictura digital, Libertate creative Pelukisan Digital, Kebebasan Berkreatif Pittura digitale, libertà creativa 디지털 페인팅, 자유로운 창의성 Digital Painting, Creative Freedom Digital teikning – kreativ fridom Cyfrowe malowanie, Wolność Twórcza Pintura Digital, Liberdade Criativa Pintura digital, liberdade criativa Цифровое рисование. Творческая свобода Digitálne maľovanie, kreatívna sloboda Digital målning, kreativ frihet Sayısal Boyama, Yaratıcı Özgürlük Цифрове малювання, творча свобода xxDigital Painting, Creative Freedomxx 自由挥洒数字绘画的无限创意 數位繪畫,創作自由

Krita is the full-featured digital art studio.

Krita je potpuni digitalni umjetnički studio.

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

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

Krita ist ein digitales Designstudio mit umfangreichen Funktionen.

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

Krita is the full-featured digital art studio.

Krita es un estudio de arte digital completo

Krita on rohkete võimalustega digitaalkunstistuudio.

Krita arte lantegi digital osoa da.

Krita on täyspiirteinen digitaiteen ateljee.

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

Krita é un estudio completo de arte dixital.

Krita es le studio de arte digital complete.

Krita adalah studio seni digital yang penuh dengan fitur.

Krita è uno studio d'arte digitale completo.

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

Krita는 디지털 예술 스튜디오입니다.

Krita is de digitale kunststudio vol mogelijkheden.

Krita er ei funksjonsrik digital teiknestove.

Krita jest pełnowymiarowym, cyfrowym studiem artystycznym

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

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

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

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

Krita är den fullfjädrade digitala konststudion.

Krita, tam özellikli dijital sanat stüdyosudur.

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

xxKrita is the full-featured digital art studio.xx

Krita 是一款功能齐全的数字绘画工作室软件。

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

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

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

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

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

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

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

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

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

Zirriborratzeko eta margotzeko ezin hobea da, eta margolan digitalen fitxategiak hutsetik sortzeko muturretik-muturrera konponbide bat aurkezten du, maisuentzako mailakoa.

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

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

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

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

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

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

스케치, 페인팅을 위한 완벽한 도구이며, 생각에서부터 디지털 페인팅 파일을 만들어 낼 수 있는 종합적인 도구를 제공합니다.

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

Passar perfekt for både teikning og måling, og dekkjer alle ledd i prosessen med å laga digitale måleri frå grunnen av.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Krita aukera bikaina da kontzeptuzko artea, komikiak, errendatzeko ehundurak eta «matte» margolanak sortzeko. Kritak kolore-espazio ugari onartzen ditu hala nola GBU eta CMYK, 8 eta 16 biteko osoko kanaletan, baita 16 eta 32 biteko koma-higikorreko kanaletan.

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

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

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

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

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

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

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

Krita는 컨셉 아트, 만화, 렌더링용 텍스처, 풍경화 등을 그릴 때 사용할 수 있는 완벽한 도구입니다. RGB, CMYK와 같은 여러 색 공간 및 8비트/16비트 정수 채널, 16비트/32비트 부동 소수점 채널을 지원합니다.

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

Krita er det ideelle valet dersom du vil laga konseptskisser, teikneseriar, teksturar for 3D-rendering eller «matte paintings». Programmet støttar fleire fargerom, både RGB- og CMYK-baserte, med 8- og 16-bits heiltals- eller flyttalskanalar.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Marrazten ondo pasa ezazu, isipu motor aurreratuekin, iragazki txundigarriekin eta eginbide praktiko ugariekin, zeintzuek Krita ikaragarri emankorra egiten duten.

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

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

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

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

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

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

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

Krita의 고급 브러시 엔진, 다양한 필터, 여러 도움이 되는 기능으로 생산성을 즐겁게 향상시킬 수 있습니다.

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

Leik deg med avanserte penselmotorar og fantastiske biletfilter – og mange andre nyttige funksjonar som gjer deg produktiv med Krita.

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

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

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

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

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

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

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

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

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

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

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

https://www.krita.org/ https://docs.krita.org/KritaFAQ.html https://krita.org/support-us/donations/ https://docs.krita.org/ https://docs.krita.org/en/untranslatable_pages/reporting_bugs.html Krita is a full-featured digital painting studio. Krita és un estudi de pintura digital ple de funcionalitats. Krita és un estudi de pintura digital ple de funcionalitats. Krita ist ein digitales Zeichenstudio mit umfangreichen Funktionen. Krita is a full-featured digital painting studio. Krita es un completo estudio de dibujo digital. + Krita on rohkete võimalustega digitaalkunstistuudio. Krita é un estudio completo de debuxo dixital. Krita adalah studio pelukisan digital dengan fitur yang lengkap. Krita è uno studio d'arte digitale completo. Krita는 다기능 디지털 예술 스튜디오입니다. Krita is een digitale schilderstudio vol mogelijkheden. Krita er ei funksjonsrik digital teiknestove. Krita jest pełnowymiarowym, cyfrowym studiem artystycznym. O Krita é um estúdio de arte digital completo. O Krita é um estúdio de pintura digital completo. Krita je plnohodnotné digitálne maliarske štúdio. Krita är en fullfjädrad digital konststudio. Krita — повноцінний комплекс для цифрового малювання. xxKrita is a full-featured digital painting studio.xx Krita 是一款功能齐全的数字绘画工作室软件。 Krita 是全功能的數位繪圖工作室。 https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_001.png The startup window now also gives you the latest news about Krita. La finestra d'inici ara proporciona les darreres notícies quant al Krita. La finestra d'inici ara proporciona les darreres noticies quant al Krita. The startup window now also gives you the latest news about Krita. La ventana de bienvenida también le proporciona ahora las últimas noticias sobre Krita. + Käivitusaken jagab nüüd ka Krita värskemaid uudiseid. Agora a xanela de inicio tamén fornece as últimas novas sobre Krita. Window pemulaian kini juga memberikan kamu kabar terkini tentang Krita. La finestra di avvio ora fornisce anche le ultime novità su Krita. 시작 창에서 Krita의 최신 소식을 볼 수 있습니다. Het opstartvenster geeft u nu ook you het laatste nieuws over Krita. Oppstartsvindauget viser no siste nytt om Krita. Okno początkowe teraz wyświetla wieści o Kricie. A janela inicial agora também lhe dá as últimas notícias sobre o Krita. A janela de inicialização agora também mostra as últimas notícias sobre o Krita. V úvodnom okne sa tiež nachádzajú najnovšie správy o Krita. Startfönstret ger nu också senaste nytt om Krita. У початковому вікні програми ви можете бачити найсвіжіші новини щодо Krita. xxThe startup window now also gives you the latest news about Krita.xx 启动画面现在可以为你呈现与 Krita 有关的最新资讯。 開始視窗也提供給您關於 Krita 的最新消息。 https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_002.png There are over ten immensely powerful brush engines. Hi ha uns deu motors de pinzell immensament potents. Hi ha uns deu motors de pinzell immensament potents. There are over ten immensely powerful brush engines. Existen unos diez inmensamente potentes motores de pinceles. + Üle kümne ääretult võimeka pintslimootori. Hai máis de dez motores de pinceis inmensamente potentes. Ada lebih dari sepuluh mesin kuas yang sangat manjur. Ci sono oltre dieci motori di pennelli incredibilmente potenti. 10가지 종류의 강력한 브러시 엔진을 사용할 수 있습니다. Er zijn meer dan tien immens krachtige penseelengines. Det finst meir enn ti enormt kraftige penselmotorar. Istnieje ponad dziesięć zaawansowanych silników pędzli. Existem mais de dez motores de pincéis extremamente poderosos. Mais de dez engines de pincéis incrivelmente poderosos disponíveis. Existuje viac ako desať nesmierne výkonných štetcových enginov. Det finns mer än tio enormt kraftfulla penselgränssnitt. У програмі передбачено понад десяток надзвичайно потужних рушіїв пензлів. xxThere are over ten immensely powerful brush engines.xx 它具备超过十种相当强大的笔刷引擎。 https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_003.png Create and use gamut masks to give your images a coherent feel. Creeu i useu màscares de gamma per donar a les imatges una aparença coherent. Creeu i useu màscares de gamma per donar a les imatges una aparença coherent. Create and use gamut masks to give your images a coherent feel. Cree y use gamas para proporcionar a sus imágenes un aspecto coherente. Crea e usa máscaras de gama para dar ás túas imaxes un aspecto coherente. Ciptakan dan gunakan masker gamut untuk memberikan gambarmu sebuah suasana koheren. Crea e utilizza maschere gamut per dare alle tue immagini un aspetto coerente. 색역 마스크를 만들고 사용할 수 있습니다. Maak en gebruik gamut-maskers om uw afbeeldingen een coherent gevoel te geven. Bruk fargeområde-masker for å gje bileta eit heilsleg uttrykk. Stwórz i używaj masek gamut, aby nadać swoim obrazom spójny wygląd. Crie e use máscaras de gamute para dar às suas imagens uma aparência coerente. Crie e use máscaras de gama para dar um senso de coerência às suas imagens. Vytvorte a používajte gamutové masky, aby vašim obrázkom poskytli ucelený pocit. Att skapa och använda färgomfångsmasker ger bilder en sammanhängande känsla. Створюйте маски палітри і користуйтеся ними для надання вашим малюнкам однорідного вигляду. xxCreate and use gamut masks to give your images a coherent feel.xx 创建并使用色域蒙版可以为你的图像带来更加一致的观感。 https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_004.png Into animation? Krita provides everything you need for traditional, hand-drawn animation. Esteu amb animacions? El Krita proporciona tot el que cal per a l'animació a mà tradicional. Esteu amb animacions? El Krita proporciona tol el que cal per a l'animació a mà tradicional. Into animation? Krita provides everything you need for traditional, hand-drawn animation. ¿Trabaja con animación? Krita proporciona todo lo necesario para la animación manual tradicional. Gusta das animacións? Krita fornece todo o necesario para animacións tradicionais debuxadas a man. Soal animasi? krita menyediakan apa pun yang kamu perlukan untuk animasi gambar-tangan, tradisional. Per le animazioni? Krita fornisce tutto ciò che ti server per l'animazione tradizionale, disegnata a mano. 애니메이션을 만들 계획이 있으신가요? Krita를 통해서 수작업 애니메이션을 작업할 수 있습니다. Naar animatie? Krita biedt alles wat u nodig hebt voor traditionele, met de hand getekende animatie. Interessert i animasjon? Krita har alt du treng for tradisjonelle, handteikna animasjonar. Zajmujesz się animacjami? Krita zapewnia wszystko czego potrzebujesz do tworzenia tradycyjnych, ręcznie rysowanych animacji. Gosta de animação? O Krita oferece tudo o que precisa para o desenho animado tradicional e desenhado à mão. Curte animação? O Krita fornece tudo necessário para você poder trabalhar com animação tradicional ou feita à mão. Ste do animácie? Krita poskytuje všetko, čo potrebujete pre tradičné ručne kreslené animácie. Gillar du animering? Krita tillhandahåller allt som behövs för traditionella, handritade animeringar. Працюєте із анімацією? У Krita ви знайдете усе, що потрібно для створення традиційної, намальованої вручну анімації. xxInto animation? Krita provides everything you need for traditional, hand-drawn animation.xx 喜欢制作动画吗?Krita 提供了制作传统手绘动画的全套工具。 想做動畫?Krita 提供您在傳統、手繪動畫所需的任何東西。 https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_005.png If you're new to digital painting, or want to know more about Krita's possibilities, there's an extensive, up-to-date manual. Si sou nou a la pintura digital, o voleu conèixer més quant a les possibilitats del Krita, hi ha un ampli manual actualitzat. Si sou nou a la pintura digital, o voleu conèixer més quant a les possibilitats del Krita, hi ha un ampli manual actualitzat. If you're new to digital painting, or want to know more about Krita's possibilities, there's an extensive, up-to-date manual. Si está empezando con el dibujo digital o si quiere saber más sobre la posibilidades de Krita, dispone de un extenso y actualizado manual. Se está a empezar co debuxo dixital, ou quere saber máis sobre as posibilidades de Krita, existe un manual exhaustivo e actualizado. Jika kamu baru dalam pelukisan digital, atau ingin mengetahui selebihnya tentang Krita, di situ ada manual yang update dan luas. Se sei nuovo del disegno digitale, o vuoi saperne di più sulle possibilità di Krita, è disponibile un manuale completo e aggiornato. 디지털 페인팅을 처음 시작하시거나, Krita의 기능을 더 알아 보려면 사용 설명서를 참조하십시오. Als u nieuw bent in digitaal schilderen of u wilt meer weten over de mogelijkheden van Krita, dan is er een uitgebreide, bijgewerkte handleiding. Viss du er nybegynnar innan digital teikning, eller ønskjer å veta meir om kva som er mogleg med Krita, finst det ei omfattande og oppdatert brukarhandbok. Jeśli cyfrowe malowanie to dla ciebie nowość, lub jeśli chcesz dowiedzieć się więcej o możliwościach Krity, to dostępny jest wyczerpująca i aktualna instrukcja obsługi. Se é novo na pintura digital, ou deseja saber mais sobre as possibilidades do Krita, existe um manual extenso e actualizado. Se você for iniciante em pintura digital ou gostaria de saber mais sobre as possibilidades que o Krita oferece, há um extenso e atualizado manual para isso. Ak ste v oblasti digitálnej maľby nováčikom alebo sa chcete dozvedieť viac o možnostiach programu Krita, existuje o tom rozsiahla a aktuálna príručka. Om digital målning är nytt för dig, eller om du vill veta mer om Kritas möjligheter, finns en omfattande, aktuell handbok. Якщо ви не маєте достатнього досвіду у цифровому малюванні або хочете дізнатися більше про можливості Krita, скористайтеся нашим докладним і актуальним підручником. xxIf you're new to digital painting, or want to know more about Krita's possibilities, there's an extensive, up-to-date manual.xx 不管你是数字绘画的新手,还是想发现 Krita 更多的用法,你都可以在我们详尽并持续更新的使用手册中找到答案。 https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_006.png Graphics 2DGraphics RasterGraphics KDE krita org.kde.krita.desktop https://www.microsoft.com/store/apps/9n6x57zgrw96
diff --git a/libs/command/kis_undo_store.h b/libs/command/kis_undo_store.h index dc57a06516..2a56a77686 100644 --- a/libs/command/kis_undo_store.h +++ b/libs/command/kis_undo_store.h @@ -1,81 +1,81 @@ /* * Copyright (c) 2003 Patrick Julien * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_UNDO_STORE_H_ #define KIS_UNDO_STORE_H_ #include #include #include class KUndo2Command; class KUndo2MagicString; /** - * See also: http://community.kde.org/Krita/Undo_adapter_vs_Undo_store + * See also: https://community.kde.org/Krita/Undo_adapter_vs_Undo_store * * Split the functionality of KisUndoAdapter into two classes: * KisUndoStore and KisUndoAdapter. The former one works as an * interface to an external storage of the undo information: * undo stack, KisDocument, /dev/null. The latter one defines the * behavior of the system when someone wants to add a command. There * are three variants: * 1) KisSurrogateUndoAdapter -- saves commands directly to the * internal stack. Used for wrapping around legacy code into * a single command. * 2) KisLegacyUndoAdapter -- blocks the strokes and updates queue, * and then adds the command to a store * 3) KisPostExecutionUndoAdapter -- used by the strokes. It doesn't * call redo() when you add a command. It is assumed, that you have * already executed the command yourself and now just notify * the system about it. Warning: it doesn't inherit KisUndoAdapter * because it doesn't fit the contract of this class. And, more * important, KisTransaction should work differently with this class. * * The ownership on the KisUndoStore (that substituted KisUndoAdapter * in the document's code) now belongs to the image. It means that * KisDocument::createUndoStore() is just a factory method, the document * doesn't store the undo store itself. */ class KRITACOMMAND_EXPORT KisUndoStore { public: KisUndoStore(); virtual ~KisUndoStore(); public: /** * WARNING: All these methods are not considered as thread-safe */ virtual const KUndo2Command* presentCommand() = 0; virtual void undoLastCommand() = 0; virtual void addCommand(KUndo2Command *cmd) = 0; virtual void beginMacro(const KUndo2MagicString& macroName) = 0; virtual void endMacro() = 0; virtual void purgeRedoState() = 0; private: Q_DISABLE_COPY(KisUndoStore) }; #endif // KIS_UNDO_STORE_H_ diff --git a/libs/command/kundo2magicstring.h b/libs/command/kundo2magicstring.h index a8a6ebe0b0..0d5d486e8b 100644 --- a/libs/command/kundo2magicstring.h +++ b/libs/command/kundo2magicstring.h @@ -1,318 +1,318 @@ /* * Copyright (c) 2014 Dmitry Kazakov * Copyright (c) 2014 Alexander Potashev * * 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 KUNDO2MAGICSTRING_H #define KUNDO2MAGICSTRING_H #include #include #include #include "kritacommand_export.h" /** * \class KUndo2MagicString is a special wrapper for a string that is * going to passed to a KUndo2Command and be later shown in the undo * history and undo action in menu. The strings like that must have * (qtundo-format) context to let translators know that they are * allowed to use magic split in them. * * Magic split is used in some languages to split the message in the * undo history docker (which is either verb or noun in + * href="https://en.wikipedia.org/wiki/Nominative_case">noun in * nominative) and the message in undo/redo actions (which is - * usually a noun + * usually a noun * in accusative). When the translator needs it he, splits two * translations with '\n' symbol and the magic string will recognize * it. * * \note KUndo2MagicString will never support concatenation operators, * because in many languages you cannot combine words without * knowing the proper case. */ class KRITACOMMAND_EXPORT KUndo2MagicString { public: /** * Construct an empty string. Note that you cannot create a * non-empy string without special functions, all the calls to which * are processed by xgettext. */ KUndo2MagicString(); /** * Fetch the main translated string. That is the one that goes to * undo history and resembles the action name in verb/nominative */ QString toString() const; /** * Fetch the secondary string which will go to the undo/redo * action. This is usually a noun in accusative. If the * translator didn't provide a secondary string, toString() and * toSecondaryString() return the same values. */ QString toSecondaryString() const; /** * \return true if the contained string is empty */ bool isEmpty() const; bool operator==(const KUndo2MagicString &rhs) const; bool operator!=(const KUndo2MagicString &rhs) const; private: /** * Construction of a magic string is allowed only with the means * of special macros which resemble their kde-wide counterparts */ explicit KUndo2MagicString(const QString &text); friend KUndo2MagicString kundo2_noi18n(const QString &text); template friend KUndo2MagicString kundo2_noi18n(const char *text, const A1 &a1); template friend KUndo2MagicString kundo2_noi18n(const char *text, const A1 &a1, const A2 &a2); template friend KUndo2MagicString kundo2_noi18n(const char *text, const A1 &a1, const A2 &a2, const A3 &a3); template friend KUndo2MagicString kundo2_noi18n(const char *text, const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4); friend KUndo2MagicString kundo2_i18n(const char *text); template friend KUndo2MagicString kundo2_i18n(const char *text, const A1 &a1); template friend KUndo2MagicString kundo2_i18n(const char *text, const A1 &a1, const A2 &a2); template friend KUndo2MagicString kundo2_i18n(const char *text, const A1 &a1, const A2 &a2, const A3 &a3); template friend KUndo2MagicString kundo2_i18n(const char *text, const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4); friend KUndo2MagicString kundo2_i18nc(const char *ctxt, const char *text); template friend KUndo2MagicString kundo2_i18nc(const char *ctxt, const char *text, const A1 &a1); template friend KUndo2MagicString kundo2_i18nc(const char *ctxt, const char *text, const A1 &a1, const A2 &a2); template friend KUndo2MagicString kundo2_i18nc(const char *ctxt, const char *text, const A1 &a1, const A2 &a2, const A3 &a3); template friend KUndo2MagicString kundo2_i18np(const char *sing, const char *plur, const A1 &a1); template friend KUndo2MagicString kundo2_i18np(const char *sing, const char *plur, const A1 &a1, const A2 &a2); template friend KUndo2MagicString kundo2_i18np(const char *sing, const char *plur, const A1 &a1, const A2 &a2, const A3 &a3); template friend KUndo2MagicString kundo2_i18ncp(const char *ctxt, const char *sing, const char *plur, const A1 &a1); template friend KUndo2MagicString kundo2_i18ncp(const char *ctxt, const char *sing, const char *plur, const A1 &a1, const A2 &a2); template friend KUndo2MagicString kundo2_i18ncp(const char *ctxt, const char *sing, const char *plur, const A1 &a1, const A2 &a2, const A3 &a3); private: QString m_text; }; inline QDebug operator<<(QDebug dbg, const KUndo2MagicString &v) { if (v.toString() != v.toSecondaryString()) { dbg.nospace() << v.toString() << "(" << v.toSecondaryString() << ")"; } else { dbg.nospace() << v.toString(); } return dbg.space(); } /** * This is a special wrapper to a string which tells explicitly * that we don't need a translation for a given string. It is used * either in testing or internal commands, which don't go to the * stack directly. */ inline KUndo2MagicString kundo2_noi18n(const QString &text) { return KUndo2MagicString(text); } template inline KUndo2MagicString kundo2_noi18n(const char *text, const A1 &a1) { return KUndo2MagicString(QString(text).arg(a1)); } template inline KUndo2MagicString kundo2_noi18n(const char *text, const A1 &a1, const A2 &a2) { return KUndo2MagicString(QString(text).arg(a1).arg(a2)); } template inline KUndo2MagicString kundo2_noi18n(const char *text, const A1 &a1, const A2 &a2, const A3 &a3) { return KUndo2MagicString(QString(text).arg(a1).arg(a2).arg(a3)); } template inline KUndo2MagicString kundo2_noi18n(const char *text, const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4) { return KUndo2MagicString(QString(text).arg(a1).arg(a2).arg(a3).arg(a4)); } /** * Same as ki18n, but is supposed to work with strings going to * undo stack */ inline KUndo2MagicString kundo2_i18n(const char *text) { return KUndo2MagicString(i18nc("(qtundo-format)", text)); } template inline KUndo2MagicString kundo2_i18n(const char *text, const A1 &a1) { return KUndo2MagicString(i18nc("(qtundo-format)", text, a1)); } template inline KUndo2MagicString kundo2_i18n(const char *text, const A1 &a1, const A2 &a2) { return KUndo2MagicString(i18nc("(qtundo-format)", text, a1, a2)); } template inline KUndo2MagicString kundo2_i18n(const char *text, const A1 &a1, const A2 &a2, const A3 &a3) { return KUndo2MagicString(i18nc("(qtundo-format)", text, a1, a2, a3)); } template inline KUndo2MagicString kundo2_i18n(const char *text, const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4) { return KUndo2MagicString(i18nc("(qtundo-format)", text, a1, a2, a3, a4)); } inline QString prependContext(const char *ctxt) { return QString("(qtundo-format) %1").arg(ctxt); } /** * Same as ki18nc, but is supposed to work with strings going to * undo stack */ inline KUndo2MagicString kundo2_i18nc(const char *ctxt, const char *text) { return KUndo2MagicString(i18nc(prependContext(ctxt).toLatin1().data(), text)); } template inline KUndo2MagicString kundo2_i18nc(const char *ctxt, const char *text, const A1 &a1) { return KUndo2MagicString(i18nc(prependContext(ctxt).toLatin1().data(), text, a1)); } template inline KUndo2MagicString kundo2_i18nc(const char *ctxt, const char *text, const A1 &a1, const A2 &a2) { return KUndo2MagicString(i18nc(prependContext(ctxt).toLatin1().data(), text, a1, a2)); } template inline KUndo2MagicString kundo2_i18nc(const char *ctxt, const char *text, const A1 &a1, const A2 &a2, const A3 &a3) { return KUndo2MagicString(i18nc(prependContext(ctxt).toLatin1().data(), text, a1, a2, a3)); } template inline KUndo2MagicString kundo2_i18nc(const char *ctxt, const char *text, const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4) { return KUndo2MagicString(i18nc(prependContext(ctxt).toLatin1().data(), text, a1, a2, a3, a4)); } /** * Same as ki18np, but is supposed to work with strings going to * undo stack */ template inline KUndo2MagicString kundo2_i18np(const char *sing, const char *plur, const A1 &a1) { return KUndo2MagicString(i18ncp("(qtundo-format)", sing, plur, a1)); } template inline KUndo2MagicString kundo2_i18np(const char *sing, const char *plur, const A1 &a1, const A2 &a2) { return i18ncp("(qtundo-format)", sing, plur, a1, a2); } template inline KUndo2MagicString kundo2_i18np(const char *sing, const char *plur, const A1 &a1, const A2 &a2, const A3 &a3) { return i18ncp("(qtundo-format)", sing, plur, a1, a2, a3); } template inline KUndo2MagicString kundo2_i18np(const char *sing, const char *plur, const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4) { return i18ncp("(qtundo-format)", sing, plur, a1, a2, a3, a4); } /** * Same as ki18ncp, but is supposed to work with strings going to * undo stack */ template inline KUndo2MagicString kundo2_i18ncp(const char *ctxt, const char *sing, const char *plur, const A1 &a1) { return KUndo2MagicString(i18ncp(prependContext(ctxt).toLatin1().data(), sing, plur, a1)); } template inline KUndo2MagicString kundo2_i18ncp(const char *ctxt, const char *sing, const char *plur, const A1 &a1, const A2 &a2) { return i18ncp(prependContext(ctxt).toLatin1().data(), sing, plur, a1, a2); } template inline KUndo2MagicString kundo2_i18ncp(const char *ctxt, const char *sing, const char *plur, const A1 &a1, const A2 &a2, const A3 &a3) { return i18ncp(prependContext(ctxt).toLatin1().data(), sing, plur, a1, a2, a3); } template inline KUndo2MagicString kundo2_i18ncp(const char *ctxt, const char *sing, const char *plur, const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4) { return i18ncp(prependContext(ctxt).toLatin1().data(), sing, plur, a1, a2, a3, a4); } #endif /* KUNDO2MAGICSTRING_H */ diff --git a/libs/flake/KoCurveFit.h b/libs/flake/KoCurveFit.h index 35ab8b29a3..0f1ae931d8 100644 --- a/libs/flake/KoCurveFit.h +++ b/libs/flake/KoCurveFit.h @@ -1,49 +1,49 @@ /* This file is part of the KDE project Copyright (C) 2001-2003 Rob Buis Copyright (C) 2007 Jan Hambrecht 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 KOCURVEFIT_H #define KOCURVEFIT_H #include #include #include "kritaflake_export.h" class KoPathShape; /* * Fits bezier curve to given list of points. * * An Algorithm for Automatically Fitting Digitized Curves * by Philip J. Schneider * from "Graphics Gems", Academic Press, 1990 * - * http://www.acm.org/pubs/tog/GraphicsGems/gems/FitCurves.c - * http://www.acm.org/pubs/tog/GraphicsGems/gems/README + * http://web.archive.org/web/20061118130015/http://www.acm.org/pubs/tog/GraphicsGems/gems/FitCurves.c + * http://web.archive.org/web/20040519052901/http://www.acm.org/pubs/tog/GraphicsGems/gems/README * * @param points the list of points to fit curve to * @param error the max. fitting error * @return a path shape representing the fitted curve */ KRITAFLAKE_EXPORT KoPathShape * bezierFit(const QList &points, float error); #endif diff --git a/libs/flake/KoOdfWorkaround.h b/libs/flake/KoOdfWorkaround.h index 038f4cf8c9..9bf9e91d95 100644 --- a/libs/flake/KoOdfWorkaround.h +++ b/libs/flake/KoOdfWorkaround.h @@ -1,162 +1,162 @@ /* This file is part of the KDE project Copyright (C) 2009 Thorsten Zachmann Copyright (C) 2011 Jan Hambrecht Copyright (C) 2011 Lukáš Tvrdý 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 KOODFWORKAROUND_H #define KOODFWORKAROUND_H #include "kritaflake_export.h" #include "KoTextShapeDataBase.h" #include #include #include class KoShape; class KoShapeLoadingContext; class QPen; class QColor; class QString; class KoColorBackground; /** * This class should contain all workarounds to correct problems with different ODF * implementations. If you need to access application specific things please create a * new namespace in the application you need it in * All calls to methods of this class should be wrapped into ifndefs like e.g. * * @code * #ifndef NWORKAROUND_ODF_BUGS * KoOdfWorkaround::fixPenWidth(pen, context); * #endif * @endcode */ namespace KoOdfWorkaround { /** * OpenOffice handles a line with the width of 0 as a cosmetic line but in svg it makes the line invisible. * To show it in calligra use a very small line width. However this is not a cosmetic line. */ KRITAFLAKE_EXPORT void fixPenWidth(QPen &pen, KoShapeLoadingContext &context); /** * OpenOffice < 3.0 does not store the draw:enhanced-path for draw:type="ellipse" * Add the path needed for the ellipse */ KRITAFLAKE_EXPORT void fixEnhancedPath(QString &path, const KoXmlElement &element, KoShapeLoadingContext &context); /** * OpenOffice interchanges the position coordinates for polar handles. * According to the specification the first coordinate is the angle, the * second coordinates is the radius. OpenOffice does it the other way around. */ KRITAFLAKE_EXPORT void fixEnhancedPathPolarHandlePosition(QString &position, const KoXmlElement &element, KoShapeLoadingContext &context); KRITAFLAKE_EXPORT bool fixMissingStroke(QPen &pen, const KoXmlElement &element, KoShapeLoadingContext &context, const KoShape *shape = 0); KRITAFLAKE_EXPORT QColor fixMissingFillColor(const KoXmlElement &element, KoShapeLoadingContext &context); KRITAFLAKE_EXPORT bool fixMissingStyle_DisplayLabel(const KoXmlElement &element, KoShapeLoadingContext &context); KRITAFLAKE_EXPORT QSharedPointer fixBackgroundColor(const KoShape *shape, KoShapeLoadingContext &context); /** * Old versions of ooimpress does not set the placeholder for shapes that should have it set - * See open office issue http://www.openoffice.org/issues/show_bug.cgi?id=96406 + * See open office issue https://bz.apache.org/ooo/show_bug.cgi?id=96406 * And kde bug https://bugs.kde.org/show_bug.cgi?id=185354 */ KRITAFLAKE_EXPORT void setFixPresentationPlaceholder(bool fix, KoShapeLoadingContext &context); KRITAFLAKE_EXPORT bool fixPresentationPlaceholder(); KRITAFLAKE_EXPORT void fixPresentationPlaceholder(KoShape *shape); /** * OpenOffice and LibreOffice save gluepoint positions wrong when no align is specified. * According to the specification for the above situation, the position should be saved * as percent values relative to the shapes center point. OpenOffice seems to write * these percent values converted to length units, where the millimeter value corresponds * to the correct percent value (i.e. -5cm = -50mm = -50%). */ KRITAFLAKE_EXPORT void fixGluePointPosition(QString &positionString, KoShapeLoadingContext &context); /** * OpenOffice and LibreOffice does not conform to the specification about default value * of the svg:fill-rule. If this attribute is missing, according the spec, the initial * value is nonzero, but OOo uses evenodd. Because we are conform to the spec, we need * to set what OOo display. * See http://www.w3.org/TR/SVG/painting.html#FillRuleProperty */ KRITAFLAKE_EXPORT void fixMissingFillRule(Qt::FillRule &fillRule, KoShapeLoadingContext &context); /** * OpenOffice resizes text shapes with autogrow in both directions. If the text box is saved to * small the text will not fit and it needs to be adjusted during the first layout. * This methods returns true if we need to adjust the layout. The adjusting is handled at a different place. */ KRITAFLAKE_EXPORT bool fixAutoGrow(KoTextShapeDataBase::ResizeMethod method, KoShapeLoadingContext &context); /** * OpenOffice and LibreOffice do not set the svg:width, svg:height, svg:x and svg:y correctly when saving * parts of draw:ellipses or draw:circle * This method returns true when the width, height, x and y is given for the full circle */ KRITAFLAKE_EXPORT bool fixEllipse(const QString &kind, KoShapeLoadingContext &context); /** * Calligra did use the bad strings "Formula.hidden" and "protected Formula.hidden" as values * for style:cell-protect, instead of "formula-hidden" and "protected formula-hidden". * This method fixes the bad strings to the correct ones. */ KRITAFLAKE_EXPORT void fixBadFormulaHiddenForStyleCellProtect(QString &value); /** * Calligra used to store text:time-value with a "0-00-00T" prefix * This method removes that prefix. */ KRITAFLAKE_EXPORT void fixBadDateForTextTime(QString &value); /** * OpenOffice.org used to write the "rect(...)" value for fo:clip without * separating the 4 offset values by commas. * This method changes the string with the offset values to have commas as separators. */ KRITAFLAKE_EXPORT void fixClipRectOffsetValuesString(QString &offsetValuesString); /** * LibreOffice used to write text:style-name attribute for table:table-template element, * which is not a valid attribute for the element. */ KRITAFLAKE_EXPORT QString fixTableTemplateName(const KoXmlElement &e); /** * LibreOffice used to write text:style-name attribute for * table:first-row, table:last-row, table:first-column, * table:last-column, table:odd-rows, table:odd-columns, * table:body elements, which is not a valid attribute for the element. */ KRITAFLAKE_EXPORT QString fixTableTemplateCellStyleName(const KoXmlElement &e); /** * LibreOffice used to have a bug with handling of z command in svg path. * This resulted in broken marker path used (and copied also to Calligra). * This methods substitutes known old marker paths with the latest (fixed) * path variant. */ KRITAFLAKE_EXPORT void fixMarkerPath(QString &path); } #endif /* KOODFWORKAROUND_H */ diff --git a/libs/flake/KoShapeAnchor.cpp b/libs/flake/KoShapeAnchor.cpp index 30288c0641..0728a2462d 100644 --- a/libs/flake/KoShapeAnchor.cpp +++ b/libs/flake/KoShapeAnchor.cpp @@ -1,528 +1,528 @@ /* This file is part of the KDE project * Copyright (C) 2007, 2009-2010 Thomas Zander * Copyright (C) 2010 Ko Gmbh * Copyright (C) 2011 Matus Hanzes * Copyright (C) 2013 C. Boemann * * 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 "KoShapeAnchor.h" #include "KoStyleStack.h" #include "KoOdfLoadingContext.h" #include #include #include #include #include #include #include #include #include #include class Q_DECL_HIDDEN KoShapeAnchor::Private { public: Private(KoShape *s) : shape(s) , verticalPos(KoShapeAnchor::VTop) , verticalRel(KoShapeAnchor::VLine) , horizontalPos(KoShapeAnchor::HLeft) , horizontalRel(KoShapeAnchor::HChar) , flowWithText(true) , anchorType(KoShapeAnchor::AnchorToCharacter) , placementStrategy(0) , pageNumber(-1) , textLocation(0) { } QDebug printDebug(QDebug dbg) const { #ifndef NDEBUG dbg.space() << "KoShapeAnchor" << this; dbg.space() << "offset:" << offset; dbg.space() << "shape:" << shape->name(); #endif return dbg.space(); } KoShape * const shape; QPointF offset; KoShapeAnchor::VerticalPos verticalPos; KoShapeAnchor::VerticalRel verticalRel; KoShapeAnchor::HorizontalPos horizontalPos; KoShapeAnchor::HorizontalRel horizontalRel; QString wrapInfluenceOnPosition; bool flowWithText; KoShapeAnchor::AnchorType anchorType; KoShapeAnchor::PlacementStrategy *placementStrategy; int pageNumber; KoShapeAnchor::TextLocation *textLocation; }; KoShapeAnchor::KoShapeAnchor(KoShape *shape) : d(new Private(shape)) { } KoShapeAnchor::~KoShapeAnchor() { if (d->placementStrategy != 0) { delete d->placementStrategy; } delete d; } KoShape *KoShapeAnchor::shape() const { return d->shape; } KoShapeAnchor::AnchorType KoShapeAnchor::anchorType() const { return d->anchorType; } void KoShapeAnchor::setHorizontalPos(HorizontalPos hp) { d->horizontalPos = hp; } KoShapeAnchor::HorizontalPos KoShapeAnchor::horizontalPos() const { return d->horizontalPos; } void KoShapeAnchor::setHorizontalRel(HorizontalRel hr) { d->horizontalRel = hr; } KoShapeAnchor::HorizontalRel KoShapeAnchor::horizontalRel() const { return d->horizontalRel; } void KoShapeAnchor::setVerticalPos(VerticalPos vp) { d->verticalPos = vp; } KoShapeAnchor::VerticalPos KoShapeAnchor::verticalPos() const { return d->verticalPos; } void KoShapeAnchor::setVerticalRel(VerticalRel vr) { d->verticalRel = vr; } KoShapeAnchor::VerticalRel KoShapeAnchor::verticalRel() const { return d->verticalRel; } QString KoShapeAnchor::wrapInfluenceOnPosition() const { return d->wrapInfluenceOnPosition; } bool KoShapeAnchor::flowWithText() const { return d->flowWithText; } int KoShapeAnchor::pageNumber() const { return d->pageNumber; } const QPointF &KoShapeAnchor::offset() const { return d->offset; } void KoShapeAnchor::setOffset(const QPointF &offset) { d->offset = offset; } void KoShapeAnchor::saveOdf(KoShapeSavingContext &context) const { // anchor-type switch (d->anchorType) { case AnchorToCharacter: shape()->setAdditionalAttribute("text:anchor-type", "char"); break; case AnchorAsCharacter: shape()->setAdditionalAttribute("text:anchor-type", "as-char"); break; case AnchorParagraph: shape()->setAdditionalAttribute("text:anchor-type", "paragraph"); break; case AnchorPage: shape()->setAdditionalAttribute("text:anchor-type", "page"); break; default: break; } // vertical-pos switch (d->verticalPos) { case VBelow: shape()->setAdditionalStyleAttribute("style:vertical-pos", "below"); break; case VBottom: shape()->setAdditionalStyleAttribute("style:vertical-pos", "bottom"); break; case VFromTop: shape()->setAdditionalStyleAttribute("style:vertical-pos", "from-top"); break; case VMiddle: shape()->setAdditionalStyleAttribute("style:vertical-pos", "middle"); break; case VTop: shape()->setAdditionalStyleAttribute("style:vertical-pos", "top"); break; default: break; } // vertical-rel switch (d->verticalRel) { case VBaseline: shape()->setAdditionalStyleAttribute("style:vertical-rel", "baseline"); break; case VChar: shape()->setAdditionalStyleAttribute("style:vertical-rel", "char"); break; case VFrame: shape()->setAdditionalStyleAttribute("style:vertical-rel", "frame"); break; case VFrameContent: shape()->setAdditionalStyleAttribute("style:vertical-rel", "frame-content"); break; case VLine: shape()->setAdditionalStyleAttribute("style:vertical-rel", "line"); break; case VPage: shape()->setAdditionalStyleAttribute("style:vertical-rel", "page"); break; case VPageContent: shape()->setAdditionalStyleAttribute("style:vertical-rel", "page-content"); break; case VParagraph: shape()->setAdditionalStyleAttribute("style:vertical-rel", "paragraph"); break; case VParagraphContent: shape()->setAdditionalStyleAttribute("style:vertical-rel", "paragraph-content"); break; case VText: shape()->setAdditionalStyleAttribute("style:vertical-rel", "text"); break; default: break; } // horizontal-pos switch (d->horizontalPos) { case HCenter: shape()->setAdditionalStyleAttribute("style:horizontal-pos", "center"); break; case HFromInside: shape()->setAdditionalStyleAttribute("style:horizontal-pos", "from-inside"); break; case HFromLeft: shape()->setAdditionalStyleAttribute("style:horizontal-pos", "from-left"); break; case HInside: shape()->setAdditionalStyleAttribute("style:horizontal-posl", "inside"); break; case HLeft: shape()->setAdditionalStyleAttribute("style:horizontal-pos", "left"); break; case HOutside: shape()->setAdditionalStyleAttribute("style:horizontal-pos", "outside"); break; case HRight: shape()->setAdditionalStyleAttribute("style:horizontal-pos", "right"); break; default: break; } // horizontal-rel switch (d->horizontalRel) { case HChar: shape()->setAdditionalStyleAttribute("style:horizontal-rel", "char"); break; case HPage: shape()->setAdditionalStyleAttribute("style:horizontal-rel", "page"); break; case HPageContent: shape()->setAdditionalStyleAttribute("style:horizontal-rel", "page-content"); break; case HPageStartMargin: shape()->setAdditionalStyleAttribute("style:horizontal-rel", "page-start-margin"); break; case HPageEndMargin: shape()->setAdditionalStyleAttribute("style:horizontal-rel", "page-end-margin"); break; case HFrame: shape()->setAdditionalStyleAttribute("style:horizontal-rel", "frame"); break; case HFrameContent: shape()->setAdditionalStyleAttribute("style:horizontal-rel", "frame-content"); break; case HFrameEndMargin: shape()->setAdditionalStyleAttribute("style:horizontal-rel", "frame-end-margin"); break; case HFrameStartMargin: shape()->setAdditionalStyleAttribute("style:horizontal-rel", "frame-start-margin"); break; case HParagraph: shape()->setAdditionalStyleAttribute("style:horizontal-rel", "paragraph"); break; case HParagraphContent: shape()->setAdditionalStyleAttribute("style:horizontal-rel", "paragraph-content"); break; case HParagraphEndMargin: shape()->setAdditionalStyleAttribute("style:horizontal-rel", "paragraph-end-margin"); break; case HParagraphStartMargin: shape()->setAdditionalStyleAttribute("style:horizontal-rel", "paragraph-start-margin"); break; default: break; } if (!d->wrapInfluenceOnPosition.isEmpty()) { shape()->setAdditionalStyleAttribute("draw:wrap-influence-on-position", d->wrapInfluenceOnPosition); } if (d->flowWithText) { shape()->setAdditionalStyleAttribute("style:flow-with-text", "true"); } else { shape()->setAdditionalStyleAttribute("style:flow-with-text", "false"); } if (shape()->parent()) {// an anchor may not yet have been layout-ed QTransform parentMatrix = shape()->parent()->absoluteTransformation(0).inverted(); QTransform shapeMatrix = shape()->absoluteTransformation(0); qreal dx = d->offset.x() - shapeMatrix.dx()*parentMatrix.m11() - shapeMatrix.dy()*parentMatrix.m21(); qreal dy = d->offset.y() - shapeMatrix.dx()*parentMatrix.m12() - shapeMatrix.dy()*parentMatrix.m22(); context.addShapeOffset(shape(), QTransform(parentMatrix.m11(),parentMatrix.m12(), parentMatrix.m21(),parentMatrix.m22(), dx,dy)); } shape()->saveOdf(context); context.removeShapeOffset(shape()); } bool KoShapeAnchor::loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context) { d->offset = shape()->position(); QString anchorType = shape()->additionalAttribute("text:anchor-type"); if (anchorType == "char") { d->anchorType = AnchorToCharacter; } else if (anchorType == "as-char") { d->anchorType = AnchorAsCharacter; d->horizontalRel = HChar; d->horizontalPos = HLeft; } else if (anchorType == "paragraph") { d->anchorType = AnchorParagraph; } else { d->anchorType = AnchorPage; // it has different defaults at least LO thinks so - ODF doesn't define defaults for this d->horizontalPos = HFromLeft; d->verticalPos = VFromTop; d->horizontalRel = HPage; d->verticalRel = VPage; } if (anchorType == "page" && shape()->hasAdditionalAttribute("text:anchor-page-number")) { d->pageNumber = shape()->additionalAttribute("text:anchor-page-number").toInt(); if (d->pageNumber <= 0) { // invalid if the page-number is invalid (OO.org does the same) - // see http://bugs.kde.org/show_bug.cgi?id=281869 + // see https://bugs.kde.org/show_bug.cgi?id=281869 d->pageNumber = -1; } } else { d->pageNumber = -1; } // always make it invisible or it will create empty rects on the first page // during initial layout. This is because only when we layout it's final page is // the shape moved away from page 1 // in KWRootAreaProvider of textlayout it's set back to visible shape()->setVisible(false); // load settings from graphic style KoStyleStack &styleStack = context.odfLoadingContext().styleStack(); styleStack.save(); if (element.hasAttributeNS(KoXmlNS::draw, "style-name")) { context.odfLoadingContext().fillStyleStack(element, KoXmlNS::draw, "style-name", "graphic"); styleStack.setTypeProperties("graphic"); } QString verticalPos = styleStack.property(KoXmlNS::style, "vertical-pos"); QString verticalRel = styleStack.property(KoXmlNS::style, "vertical-rel"); QString horizontalPos = styleStack.property(KoXmlNS::style, "horizontal-pos"); QString horizontalRel = styleStack.property(KoXmlNS::style, "horizontal-rel"); d->wrapInfluenceOnPosition = styleStack.property(KoXmlNS::draw, "wrap-influence-on-position"); QString flowWithText = styleStack.property(KoXmlNS::style, "flow-with-text"); d->flowWithText = flowWithText.isEmpty() ? false : flowWithText == "true"; styleStack.restore(); // vertical-pos if (verticalPos == "below") {//svg:y attribute is ignored d->verticalPos = VBelow; d->offset.setY(0); } else if (verticalPos == "bottom") {//svg:y attribute is ignored d->verticalPos = VBottom; d->offset.setY(-shape()->size().height()); } else if (verticalPos == "from-top") { d->verticalPos = VFromTop; } else if (verticalPos == "middle") {//svg:y attribute is ignored d->verticalPos = VMiddle; d->offset.setY(-(shape()->size().height()/2)); } else if (verticalPos == "top") {//svg:y attribute is ignored d->verticalPos = VTop; d->offset.setY(0); } // vertical-rel if (verticalRel == "baseline") d->verticalRel = VBaseline; else if (verticalRel == "char") d->verticalRel = VChar; else if (verticalRel == "frame") d->verticalRel = VFrame; else if (verticalRel == "frame-content") d->verticalRel = VFrameContent; else if (verticalRel == "line") d->verticalRel = VLine; else if (verticalRel == "page") d->verticalRel = VPage; else if (verticalRel == "page-content") d->verticalRel = VPageContent; else if (verticalRel == "paragraph") d->verticalRel = VParagraph; else if (verticalRel == "paragraph-content") d->verticalRel = VParagraphContent; else if (verticalRel == "text") d->verticalRel = VText; // horizontal-pos if (horizontalPos == "center") {//svg:x attribute is ignored d->horizontalPos = HCenter; d->offset.setX(-(shape()->size().width()/2)); } else if (horizontalPos == "from-inside") { d->horizontalPos = HFromInside; } else if (horizontalPos == "from-left") { d->horizontalPos = HFromLeft; } else if (horizontalPos == "inside") {//svg:x attribute is ignored d->horizontalPos = HInside; d->offset.setX(0); } else if (horizontalPos == "left") {//svg:x attribute is ignored d->horizontalPos = HLeft; d->offset.setX(0); }else if (horizontalPos == "outside") {//svg:x attribute is ignored d->horizontalPos = HOutside; d->offset.setX(-shape()->size().width()); }else if (horizontalPos == "right") {//svg:x attribute is ignored d->horizontalPos = HRight; d->offset.setX(-shape()->size().width()); } // horizontal-rel if (horizontalRel == "char") d->horizontalRel = HChar; else if (horizontalRel == "page") d->horizontalRel = HPage; else if (horizontalRel == "page-content") d->horizontalRel = HPageContent; else if (horizontalRel == "page-start-margin") d->horizontalRel = HPageStartMargin; else if (horizontalRel == "page-end-margin") d->horizontalRel = HPageEndMargin; else if (horizontalRel == "frame") d->horizontalRel = HFrame; else if (horizontalRel == "frame-content") d->horizontalRel = HFrameContent; else if (horizontalRel == "frame-end-margin") d->horizontalRel = HFrameEndMargin; else if (horizontalRel == "frame-start-margin") d->horizontalRel = HFrameStartMargin; else if (horizontalRel == "paragraph") d->horizontalRel = HParagraph; else if (horizontalRel == "paragraph-content") d->horizontalRel = HParagraphContent; else if (horizontalRel == "paragraph-end-margin") d->horizontalRel = HParagraphEndMargin; else if (horizontalRel == "paragraph-start-margin") d->horizontalRel = HParagraphStartMargin; // if svg:x or svg:y should be ignored set new position shape()->setPosition(d->offset); return true; } void KoShapeAnchor::setAnchorType(KoShapeAnchor::AnchorType type) { d->anchorType = type; if (type == AnchorAsCharacter) { d->horizontalRel = HChar; d->horizontalPos = HLeft; } } KoShapeAnchor::TextLocation *KoShapeAnchor::textLocation() const { return d->textLocation; } void KoShapeAnchor::setTextLocation(TextLocation *textLocation) { d->textLocation = textLocation; } KoShapeAnchor::PlacementStrategy *KoShapeAnchor::placementStrategy() const { return d->placementStrategy; } void KoShapeAnchor::setPlacementStrategy(PlacementStrategy *placementStrategy) { if (placementStrategy != d->placementStrategy) { delete d->placementStrategy; d->placementStrategy = placementStrategy; } } diff --git a/libs/flake/KoShapeShadow.cpp b/libs/flake/KoShapeShadow.cpp index 299759097a..2d35c2eda5 100644 --- a/libs/flake/KoShapeShadow.cpp +++ b/libs/flake/KoShapeShadow.cpp @@ -1,357 +1,357 @@ /* This file is part of the KDE project * Copyright (C) 2008-2009 Jan Hambrecht * Copyright (C) 2010 Thomas Zander * Copyright (C) 2010 Ariya Hidayat * Copyright (C) 2010-2011 Yue Liu * * 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 "KoShapeShadow.h" #include "KoShapeGroup.h" #include "KoSelection.h" #include "KoShapeSavingContext.h" #include "KoShapeStrokeModel.h" #include "KoShape.h" #include "KoInsets.h" #include "KoPathShape.h" #include #include #include #include #include #include #include class Q_DECL_HIDDEN KoShapeShadow::Private { public: Private() : offset(2, 2), color(Qt::black), blur(8), visible(true), refCount(0) { } QPointF offset; QColor color; qreal blur; bool visible; QAtomicInt refCount; /** * Paints the shadow of the shape group to the buffer image. * @param group the shape group to paint around * @param painter the painter to paint on the image * @param converter to convert between internal and view coordinates. */ void paintGroupShadow(KoShapeGroup *group, QPainter &painter, const KoViewConverter &converter); /** * Paints the shadow of the shape to the buffer image. * @param shape the shape to paint around * @param painter the painter to paint on the image * @param converter to convert between internal and view coordinates. */ void paintShadow(KoShape *shape, QPainter &painter, const KoViewConverter &converter); void blurShadow(QImage &image, int radius, const QColor& shadowColor); }; void KoShapeShadow::Private::paintGroupShadow(KoShapeGroup *group, QPainter &painter, const KoViewConverter &converter) { QList shapes = group->shapes(); Q_FOREACH (KoShape *child, shapes) { // we paint recursively here, so we do not have to check recursively for visibility if (!child->isVisible(false)) continue; painter.save(); //apply group child's transformation painter.setTransform(child->absoluteTransformation(&converter), true); paintShadow(child, painter, converter); painter.restore(); } } void KoShapeShadow::Private::paintShadow(KoShape *shape, QPainter &painter, const KoViewConverter &converter) { QPainterPath path(shape->shadowOutline()); if (!path.isEmpty()) { painter.save(); KoShape::applyConversion(painter, converter); painter.setBrush(QBrush(color)); // Make sure the shadow has the same fill rule as the shape. KoPathShape * pathShape = dynamic_cast(shape); if (pathShape) path.setFillRule(pathShape->fillRule()); painter.drawPath(path); painter.restore(); } if (shape->stroke()) { shape->stroke()->paint(shape, painter, converter); } } /* You can also find a BSD version to this method from - * http://gitorious.org/ofi-labs/x2/blobs/master/graphics/shadowblur/ + * https://github.com/ariya/X2/tree/master/graphics/shadowblur */ void KoShapeShadow::Private::blurShadow(QImage &image, int radius, const QColor& shadowColor) { static const int BlurSumShift = 15; // Check http://www.w3.org/TR/SVG/filters.html# // As noted in the SVG filter specification, ru // approximates a real gaussian blur nicely. - // See comments in http://webkit.org/b/40793, it seems sensible + // See comments in https://bugs.webkit.org/show_bug.cgi?id=40793, it seems sensible // to follow Skia's limit of 128 pixels for the blur radius. if (radius > 128) radius = 128; int channels[4] = { 3, 0, 1, 3 }; int dmax = radius >> 1; int dmin = dmax - 1 + (radius & 1); if (dmin < 0) dmin = 0; // Two stages: horizontal and vertical for (int k = 0; k < 2; ++k) { unsigned char* pixels = image.bits(); int stride = (k == 0) ? 4 : image.bytesPerLine(); int delta = (k == 0) ? image.bytesPerLine() : 4; int jfinal = (k == 0) ? image.height() : image.width(); int dim = (k == 0) ? image.width() : image.height(); for (int j = 0; j < jfinal; ++j, pixels += delta) { // For each step, we blur the alpha in a channel and store the result // in another channel for the subsequent step. // We use sliding window algorithm to accumulate the alpha values. // This is much more efficient than computing the sum of each pixels // covered by the box kernel size for each x. for (int step = 0; step < 3; ++step) { int side1 = (step == 0) ? dmin : dmax; int side2 = (step == 1) ? dmin : dmax; int pixelCount = side1 + 1 + side2; int invCount = ((1 << BlurSumShift) + pixelCount - 1) / pixelCount; int ofs = 1 + side2; int alpha1 = pixels[channels[step]]; int alpha2 = pixels[(dim - 1) * stride + channels[step]]; unsigned char* ptr = pixels + channels[step + 1]; unsigned char* prev = pixels + stride + channels[step]; unsigned char* next = pixels + ofs * stride + channels[step]; int i; int sum = side1 * alpha1 + alpha1; int limit = (dim < side2 + 1) ? dim : side2 + 1; for (i = 1; i < limit; ++i, prev += stride) sum += *prev; if (limit <= side2) sum += (side2 - limit + 1) * alpha2; limit = (side1 < dim) ? side1 : dim; for (i = 0; i < limit; ptr += stride, next += stride, ++i, ++ofs) { *ptr = (sum * invCount) >> BlurSumShift; sum += ((ofs < dim) ? *next : alpha2) - alpha1; } prev = pixels + channels[step]; for (; ofs < dim; ptr += stride, prev += stride, next += stride, ++i, ++ofs) { *ptr = (sum * invCount) >> BlurSumShift; sum += (*next) - (*prev); } for (; i < dim; ptr += stride, prev += stride, ++i) { *ptr = (sum * invCount) >> BlurSumShift; sum += alpha2 - (*prev); } } } } // "Colorize" with the right shadow color. QPainter p(&image); p.setCompositionMode(QPainter::CompositionMode_SourceIn); p.fillRect(image.rect(), shadowColor); p.end(); } // ---------------------------------------------------------------- // KoShapeShadow KoShapeShadow::KoShapeShadow() : d(new Private()) { } KoShapeShadow::~KoShapeShadow() { delete d; } KoShapeShadow::KoShapeShadow(const KoShapeShadow &rhs) : d(new Private(*rhs.d)) { d->refCount = 0; } KoShapeShadow& KoShapeShadow::operator=(const KoShapeShadow &rhs) { *d = *rhs.d; d->refCount = 0; return *this; } void KoShapeShadow::fillStyle(KoGenStyle &style, KoShapeSavingContext &context) { Q_UNUSED(context); style.addProperty("draw:shadow", d->visible ? "visible" : "hidden", KoGenStyle::GraphicType); style.addProperty("draw:shadow-color", d->color.name(), KoGenStyle::GraphicType); if (d->color.alphaF() != 1.0) style.addProperty("draw:shadow-opacity", QString("%1%").arg(d->color.alphaF() * 100.0), KoGenStyle::GraphicType); style.addProperty("draw:shadow-offset-x", QString("%1pt").arg(d->offset.x()), KoGenStyle::GraphicType); style.addProperty("draw:shadow-offset-y", QString("%1pt").arg(d->offset.y()), KoGenStyle::GraphicType); if (d->blur != 0) style.addProperty("calligra:shadow-blur-radius", QString("%1pt").arg(d->blur), KoGenStyle::GraphicType); } void KoShapeShadow::paint(KoShape *shape, QPainter &painter, const KoViewConverter &converter) { if (! d->visible) return; // So the approach we are taking here is to draw into a buffer image the size of boundingRect // We offset by the shadow offset at the time we draw into the buffer // Then we filter the image and draw it at the position of the bounding rect on canvas //the boundingRect of the shape or the KoSelection boundingRect of the group QRectF shadowRect = shape->boundingRect(); QRectF zoomedClipRegion = converter.documentToView(shadowRect); // Init the buffer image QImage sourceGraphic(zoomedClipRegion.size().toSize(), QImage::Format_ARGB32_Premultiplied); sourceGraphic.fill(qRgba(0,0,0,0)); // Init the buffer painter QPainter imagePainter(&sourceGraphic); imagePainter.setPen(Qt::NoPen); imagePainter.setBrush(Qt::NoBrush); imagePainter.setRenderHint(QPainter::Antialiasing, painter.testRenderHint(QPainter::Antialiasing)); // Since our imagebuffer and the canvas don't align we need to offset our drawings imagePainter.translate(-1.0f*converter.documentToView(shadowRect.topLeft())); // Handle the shadow offset imagePainter.translate(converter.documentToView(offset())); KoShapeGroup *group = dynamic_cast(shape); if (group) { d->paintGroupShadow(group, imagePainter, converter); } else { //apply shape's transformation imagePainter.setTransform(shape->absoluteTransformation(&converter), true); d->paintShadow(shape, imagePainter, converter); } imagePainter.end(); // Blur the shadow (well the entire buffer) d->blurShadow(sourceGraphic, converter.documentToViewX(d->blur), d->color); // Paint the result painter.save(); // The painter is initialized for us with canvas transform 'plus' shape transform // we are only interested in the canvas transform so 'subtract' the shape transform part painter.setTransform(shape->absoluteTransformation(&converter).inverted() * painter.transform()); painter.drawImage(zoomedClipRegion.topLeft(), sourceGraphic); painter.restore(); } void KoShapeShadow::setOffset(const QPointF & offset) { d->offset = offset; } QPointF KoShapeShadow::offset() const { return d->offset; } void KoShapeShadow::setColor(const QColor &color) { d->color = color; } QColor KoShapeShadow::color() const { return d->color; } void KoShapeShadow::setBlur(qreal blur) { // force positive blur radius d->blur = qAbs(blur); } qreal KoShapeShadow::blur() const { return d->blur; } void KoShapeShadow::setVisible(bool visible) { d->visible = visible; } bool KoShapeShadow::isVisible() const { return d->visible; } void KoShapeShadow::insets(KoInsets &insets) const { if (!d->visible) { insets.top = 0; insets.bottom = 0; insets.left = 0; insets.right = 0; return; } qreal expand = d->blur; insets.left = (d->offset.x() < 0.0) ? qAbs(d->offset.x()) : 0.0; insets.top = (d->offset.y() < 0.0) ? qAbs(d->offset.y()) : 0.0; insets.right = (d->offset.x() > 0.0) ? d->offset.x() : 0.0; insets.bottom = (d->offset.y() > 0.0) ? d->offset.y() : 0.0; insets.left += expand; insets.top += expand; insets.right += expand; insets.bottom += expand; } bool KoShapeShadow::ref() { return d->refCount.ref(); } bool KoShapeShadow::deref() { return d->refCount.deref(); } int KoShapeShadow::useCount() const { return d->refCount; } diff --git a/libs/flake/svg/SvgUtil.cpp b/libs/flake/svg/SvgUtil.cpp index 892b39db88..c59700f8f9 100644 --- a/libs/flake/svg/SvgUtil.cpp +++ b/libs/flake/svg/SvgUtil.cpp @@ -1,527 +1,527 @@ /* This file is part of the KDE project * Copyright (C) 2009 Jan Hambrecht * * 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 "SvgUtil.h" #include "SvgGraphicContext.h" #include #include #include #include #include #include #include #include #include "kis_debug.h" #include "kis_global.h" #include #include "kis_dom_utils.h" #define DPI 72.0 #define DEG2RAD(degree) degree/180.0*M_PI double SvgUtil::fromUserSpace(double value) { return value; } double SvgUtil::toUserSpace(double value) { return value; } double SvgUtil::ptToPx(SvgGraphicsContext *gc, double value) { return value * gc->pixelsPerInch / DPI; } QPointF SvgUtil::toUserSpace(const QPointF &point) { return QPointF(toUserSpace(point.x()), toUserSpace(point.y())); } QRectF SvgUtil::toUserSpace(const QRectF &rect) { return QRectF(toUserSpace(rect.topLeft()), toUserSpace(rect.size())); } QSizeF SvgUtil::toUserSpace(const QSizeF &size) { return QSizeF(toUserSpace(size.width()), toUserSpace(size.height())); } QString SvgUtil::toPercentage(qreal value) { return KisDomUtils::toString(value * 100.0) + "%"; } double SvgUtil::fromPercentage(QString s) { if (s.endsWith('%')) return KisDomUtils::toDouble(s.remove('%')) / 100.0; else return KisDomUtils::toDouble(s); } QPointF SvgUtil::objectToUserSpace(const QPointF &position, const QRectF &objectBound) { qreal x = objectBound.left() + position.x() * objectBound.width(); qreal y = objectBound.top() + position.y() * objectBound.height(); return QPointF(x, y); } QSizeF SvgUtil::objectToUserSpace(const QSizeF &size, const QRectF &objectBound) { qreal w = size.width() * objectBound.width(); qreal h = size.height() * objectBound.height(); return QSizeF(w, h); } QPointF SvgUtil::userSpaceToObject(const QPointF &position, const QRectF &objectBound) { qreal x = 0.0; if (objectBound.width() != 0) x = (position.x() - objectBound.x()) / objectBound.width(); qreal y = 0.0; if (objectBound.height() != 0) y = (position.y() - objectBound.y()) / objectBound.height(); return QPointF(x, y); } QSizeF SvgUtil::userSpaceToObject(const QSizeF &size, const QRectF &objectBound) { qreal w = objectBound.width() != 0 ? size.width() / objectBound.width() : 0.0; qreal h = objectBound.height() != 0 ? size.height() / objectBound.height() : 0.0; return QSizeF(w, h); } QString SvgUtil::transformToString(const QTransform &transform) { if (transform.isIdentity()) return QString(); if (transform.type() == QTransform::TxTranslate) { return QString("translate(%1, %2)") .arg(KisDomUtils::toString(toUserSpace(transform.dx()))) .arg(KisDomUtils::toString(toUserSpace(transform.dy()))); } else { return QString("matrix(%1 %2 %3 %4 %5 %6)") .arg(KisDomUtils::toString(transform.m11())) .arg(KisDomUtils::toString(transform.m12())) .arg(KisDomUtils::toString(transform.m21())) .arg(KisDomUtils::toString(transform.m22())) .arg(KisDomUtils::toString(toUserSpace(transform.dx()))) .arg(KisDomUtils::toString(toUserSpace(transform.dy()))); } } void SvgUtil::writeTransformAttributeLazy(const QString &name, const QTransform &transform, KoXmlWriter &shapeWriter) { const QString value = transformToString(transform); if (!value.isEmpty()) { shapeWriter.addAttribute(name.toLatin1().data(), value); } } bool SvgUtil::parseViewBox(const KoXmlElement &e, const QRectF &elementBounds, QRectF *_viewRect, QTransform *_viewTransform) { KIS_ASSERT(_viewRect); KIS_ASSERT(_viewTransform); QString viewBoxStr = e.attribute("viewBox"); if (viewBoxStr.isEmpty()) return false; bool result = false; QRectF viewBoxRect; // this is a workaround for bug 260429 for a file generated by blender // who has px in the viewbox which is wrong. - // reported as bug http://projects.blender.org/tracker/?group_id=9&atid=498&func=detail&aid=30971 + // reported as bug https://developer.blender.org/T30971 viewBoxStr.remove("px"); QStringList points = viewBoxStr.replace(',', ' ').simplified().split(' '); if (points.count() == 4) { viewBoxRect.setX(SvgUtil::fromUserSpace(points[0].toFloat())); viewBoxRect.setY(SvgUtil::fromUserSpace(points[1].toFloat())); viewBoxRect.setWidth(SvgUtil::fromUserSpace(points[2].toFloat())); viewBoxRect.setHeight(SvgUtil::fromUserSpace(points[3].toFloat())); result = true; } else { // TODO: WARNING! } if (!result) return false; QTransform viewBoxTransform = QTransform::fromTranslate(-viewBoxRect.x(), -viewBoxRect.y()) * QTransform::fromScale(elementBounds.width() / viewBoxRect.width(), elementBounds.height() / viewBoxRect.height()) * QTransform::fromTranslate(elementBounds.x(), elementBounds.y()); const QString aspectString = e.attribute("preserveAspectRatio"); // give initial value if value not defined PreserveAspectRatioParser p( (!aspectString.isEmpty())? aspectString : QString("xMidYMid meet")); parseAspectRatio(p, elementBounds, viewBoxRect, &viewBoxTransform); *_viewRect = viewBoxRect; *_viewTransform = viewBoxTransform; return result; } void SvgUtil::parseAspectRatio(const PreserveAspectRatioParser &p, const QRectF &elementBounds, const QRectF &viewBoxRect, QTransform *_viewTransform) { if (p.mode != Qt::IgnoreAspectRatio) { QTransform viewBoxTransform = *_viewTransform; const qreal tan1 = viewBoxRect.height() / viewBoxRect.width(); const qreal tan2 = elementBounds.height() / elementBounds.width(); const qreal uniformScale = (p.mode == Qt::KeepAspectRatioByExpanding) ^ (tan1 > tan2) ? elementBounds.height() / viewBoxRect.height() : elementBounds.width() / viewBoxRect.width(); viewBoxTransform = QTransform::fromTranslate(-viewBoxRect.x(), -viewBoxRect.y()) * QTransform::fromScale(uniformScale, uniformScale) * QTransform::fromTranslate(elementBounds.x(), elementBounds.y()); const QPointF viewBoxAnchor = viewBoxTransform.map(p.rectAnchorPoint(viewBoxRect)); const QPointF elementAnchor = p.rectAnchorPoint(elementBounds); const QPointF offset = elementAnchor - viewBoxAnchor; viewBoxTransform = viewBoxTransform * QTransform::fromTranslate(offset.x(), offset.y()); *_viewTransform = viewBoxTransform; } } qreal SvgUtil::parseUnit(SvgGraphicsContext *gc, const QString &unit, bool horiz, bool vert, const QRectF &bbox) { if (unit.isEmpty()) return 0.0; QByteArray unitLatin1 = unit.toLatin1(); // TODO : percentage? const char *start = unitLatin1.data(); if (!start) { return 0.0; } qreal value = 0.0; const char *end = parseNumber(start, value); if (int(end - start) < unit.length()) { if (unit.right(2) == "px") value = SvgUtil::fromUserSpace(value); else if (unit.right(2) == "pt") value = ptToPx(gc, value); else if (unit.right(2) == "cm") value = ptToPx(gc, CM_TO_POINT(value)); else if (unit.right(2) == "pc") value = ptToPx(gc, PI_TO_POINT(value)); else if (unit.right(2) == "mm") value = ptToPx(gc, MM_TO_POINT(value)); else if (unit.right(2) == "in") value = ptToPx(gc, INCH_TO_POINT(value)); else if (unit.right(2) == "em") // NOTE: all the fonts should be created with 'pt' size, not px! value = ptToPx(gc, value * gc->font.pointSize()); else if (unit.right(2) == "ex") { QFontMetrics metrics(gc->font); value = ptToPx(gc, value * metrics.xHeight()); } else if (unit.right(1) == "%") { if (horiz && vert) value = (value / 100.0) * (sqrt(pow(bbox.width(), 2) + pow(bbox.height(), 2)) / sqrt(2.0)); else if (horiz) value = (value / 100.0) * bbox.width(); else if (vert) value = (value / 100.0) * bbox.height(); } } else { value = SvgUtil::fromUserSpace(value); } /*else { if( m_gc.top() ) { if( horiz && vert ) value *= sqrt( pow( m_gc.top()->matrix.m11(), 2 ) + pow( m_gc.top()->matrix.m22(), 2 ) ) / sqrt( 2.0 ); else if( horiz ) value /= m_gc.top()->matrix.m11(); else if( vert ) value /= m_gc.top()->matrix.m22(); } }*/ //value *= 90.0 / DPI; return value; } qreal SvgUtil::parseUnitX(SvgGraphicsContext *gc, const QString &unit) { if (gc->forcePercentage) { return SvgUtil::fromPercentage(unit) * gc->currentBoundingBox.width(); } else { return SvgUtil::parseUnit(gc, unit, true, false, gc->currentBoundingBox); } } qreal SvgUtil::parseUnitY(SvgGraphicsContext *gc, const QString &unit) { if (gc->forcePercentage) { return SvgUtil::fromPercentage(unit) * gc->currentBoundingBox.height(); } else { return SvgUtil::parseUnit(gc, unit, false, true, gc->currentBoundingBox); } } qreal SvgUtil::parseUnitXY(SvgGraphicsContext *gc, const QString &unit) { if (gc->forcePercentage) { const qreal value = SvgUtil::fromPercentage(unit); return value * sqrt(pow(gc->currentBoundingBox.width(), 2) + pow(gc->currentBoundingBox.height(), 2)) / sqrt(2.0); } else { return SvgUtil::parseUnit(gc, unit, true, true, gc->currentBoundingBox); } } qreal SvgUtil::parseUnitAngular(SvgGraphicsContext *gc, const QString &unit) { Q_UNUSED(gc); qreal value = 0.0; if (unit.isEmpty()) return value; QByteArray unitLatin1 = unit.toLower().toLatin1(); const char *start = unitLatin1.data(); if (!start) return value; const char *end = parseNumber(start, value); if (int(end - start) < unit.length()) { if (unit.right(3) == "deg") { value = kisDegreesToRadians(value); } else if (unit.right(4) == "grad") { value *= M_PI / 200; } else if (unit.right(3) == "rad") { // noop! } else { value = kisDegreesToRadians(value); } } else { value = kisDegreesToRadians(value); } return value; } qreal SvgUtil::parseNumber(const QString &string) { qreal value = 0.0; if (string.isEmpty()) return value; QByteArray unitLatin1 = string.toLatin1(); const char *start = unitLatin1.data(); if (!start) return value; const char *end = parseNumber(start, value); KIS_SAFE_ASSERT_RECOVER_NOOP(int(end - start) == string.length()); return value; } const char * SvgUtil::parseNumber(const char *ptr, qreal &number) { int integer, exponent; qreal decimal, frac; int sign, expsign; exponent = 0; integer = 0; frac = 1.0; decimal = 0; sign = 1; expsign = 1; // read the sign if (*ptr == '+') { ptr++; } else if (*ptr == '-') { ptr++; sign = -1; } // read the integer part while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9') integer = (integer * 10) + *(ptr++) - '0'; if (*ptr == '.') { // read the decimals ptr++; while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9') decimal += (*(ptr++) - '0') * (frac *= 0.1); } if (*ptr == 'e' || *ptr == 'E') { // read the exponent part ptr++; // read the sign of the exponent if (*ptr == '+') { ptr++; } else if (*ptr == '-') { ptr++; expsign = -1; } exponent = 0; while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9') { exponent *= 10; exponent += *ptr - '0'; ptr++; } } number = integer + decimal; number *= sign * pow((double)10, double(expsign * exponent)); return ptr; } QString SvgUtil::mapExtendedShapeTag(const QString &tagName, const KoXmlElement &element) { QString result = tagName; if (tagName == "path") { QString kritaType = element.attribute("krita:type", ""); QString sodipodiType = element.attribute("sodipodi:type", ""); if (kritaType == "arc") { result = "krita:arc"; } else if (sodipodiType == "arc") { result = "sodipodi:arc"; } } return result; } QStringList SvgUtil::simplifyList(const QString &str) { QString attribute = str; attribute.replace(',', ' '); attribute.remove('\r'); attribute.remove('\n'); return attribute.simplified().split(' ', QString::SkipEmptyParts); } SvgUtil::PreserveAspectRatioParser::PreserveAspectRatioParser(const QString &str) { QRegExp rexp("(defer)?\\s*(none|(x(Min|Max|Mid)Y(Min|Max|Mid)))\\s*(meet|slice)?", Qt::CaseInsensitive); int index = rexp.indexIn(str.toLower()); if (index >= 0) { if (rexp.cap(1) == "defer") { defer = true; } if (rexp.cap(2) != "none") { xAlignment = alignmentFromString(rexp.cap(4)); yAlignment = alignmentFromString(rexp.cap(5)); mode = rexp.cap(6) == "slice" ? Qt::KeepAspectRatioByExpanding : Qt::KeepAspectRatio; } } } QPointF SvgUtil::PreserveAspectRatioParser::rectAnchorPoint(const QRectF &rc) const { return QPointF(alignedValue(rc.x(), rc.x() + rc.width(), xAlignment), alignedValue(rc.y(), rc.y() + rc.height(), yAlignment)); } QString SvgUtil::PreserveAspectRatioParser::toString() const { QString result; if (!defer && xAlignment == Middle && yAlignment == Middle && mode == Qt::KeepAspectRatio) { return result; } if (defer) { result += "defer "; } if (mode == Qt::IgnoreAspectRatio) { result += "none"; } else { result += QString("x%1Y%2") .arg(alignmentToString(xAlignment)) .arg(alignmentToString(yAlignment)); if (mode == Qt::KeepAspectRatioByExpanding) { result += " slice"; } } return result; } SvgUtil::PreserveAspectRatioParser::Alignment SvgUtil::PreserveAspectRatioParser::alignmentFromString(const QString &str) const { return str == "max" ? Max : str == "mid" ? Middle : Min; } QString SvgUtil::PreserveAspectRatioParser::alignmentToString(SvgUtil::PreserveAspectRatioParser::Alignment alignment) const { return alignment == Max ? "Max" : alignment == Min ? "Min" : "Mid"; } qreal SvgUtil::PreserveAspectRatioParser::alignedValue(qreal min, qreal max, SvgUtil::PreserveAspectRatioParser::Alignment alignment) { qreal result = min; switch (alignment) { case Min: result = min; break; case Middle: result = 0.5 * (min + max); break; case Max: result = max; break; } return result; } diff --git a/libs/flake/svg/SvgWriter.cpp b/libs/flake/svg/SvgWriter.cpp index 2e139c5bfd..eccbe1134a 100644 --- a/libs/flake/svg/SvgWriter.cpp +++ b/libs/flake/svg/SvgWriter.cpp @@ -1,321 +1,321 @@ /* This file is part of the KDE project Copyright (C) 2002 Lars Siebold Copyright (C) 2002-2003,2005 Rob Buis Copyright (C) 2002,2005-2006 David Faure Copyright (C) 2002 Werner Trobin Copyright (C) 2002 Lennart Kudling Copyright (C) 2004 Nicolas Goutte Copyright (C) 2005 Boudewijn Rempt Copyright (C) 2005 Raphael Langerhorst Copyright (C) 2005 Thomas Zander Copyright (C) 2005,2007-2008 Jan Hambrecht Copyright (C) 2006 Inge Wallin Copyright (C) 2006 Martin Pfeiffer Copyright (C) 2006 Gábor Lehel Copyright (C) 2006 Laurent Montel Copyright (C) 2006 Christian Mueller Copyright (C) 2006 Ariya Hidayat Copyright (C) 2010 Thorsten Zachmann 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 "SvgWriter.h" #include "SvgUtil.h" #include "SvgSavingContext.h" #include "SvgShape.h" #include "SvgStyleWriter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include SvgWriter::SvgWriter(const QList &layers) : m_writeInlineImages(true) { Q_FOREACH (KoShapeLayer *layer, layers) m_toplevelShapes.append(layer); } SvgWriter::SvgWriter(const QList &toplevelShapes) : m_toplevelShapes(toplevelShapes) , m_writeInlineImages(true) { } SvgWriter::~SvgWriter() { } bool SvgWriter::save(const QString &filename, const QSizeF &pageSize, bool writeInlineImages) { QFile fileOut(filename); if (!fileOut.open(QIODevice::WriteOnly)) return false; m_writeInlineImages = writeInlineImages; const bool success = save(fileOut, pageSize); m_writeInlineImages = true; fileOut.close(); return success; } bool SvgWriter::save(QIODevice &outputDevice, const QSizeF &pageSize) { if (m_toplevelShapes.isEmpty()) { return false; } QTextStream svgStream(&outputDevice); svgStream.setCodec("UTF-8"); // standard header: svgStream << "" << endl; svgStream << "" << endl; // add some PR. one line is more than enough. - svgStream << "" << endl; + svgStream << "" << endl; svgStream << "" << endl; if (!m_documentTitle.isNull() && !m_documentTitle.isEmpty()) { svgStream << "" << m_documentTitle << "" << endl; } if (!m_documentDescription.isNull() && !m_documentDescription.isEmpty()) { svgStream << "" << m_documentDescription << "" << endl; } { SvgSavingContext savingContext(outputDevice, m_writeInlineImages); saveShapes(m_toplevelShapes, savingContext); } // end tag: svgStream << endl << "" << endl; return true; } bool SvgWriter::saveDetached(QIODevice &outputDevice) { if (m_toplevelShapes.isEmpty()) return false; SvgSavingContext savingContext(outputDevice, m_writeInlineImages); saveShapes(m_toplevelShapes, savingContext); return true; } bool SvgWriter::saveDetached(SvgSavingContext &savingContext) { if (m_toplevelShapes.isEmpty()) return false; saveShapes(m_toplevelShapes, savingContext); return true; } void SvgWriter::saveShapes(const QList shapes, SvgSavingContext &savingContext) { // top level shapes Q_FOREACH (KoShape *shape, shapes) { KoShapeLayer *layer = dynamic_cast(shape); if(layer) { saveLayer(layer, savingContext); } else { KoShapeGroup *group = dynamic_cast(shape); if (group) saveGroup(group, savingContext); else saveShape(shape, savingContext); } } } void SvgWriter::saveLayer(KoShapeLayer *layer, SvgSavingContext &context) { context.shapeWriter().startElement("g"); context.shapeWriter().addAttribute("id", context.getID(layer)); QList sortedShapes = layer->shapes(); std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex); Q_FOREACH (KoShape * shape, sortedShapes) { KoShapeGroup * group = dynamic_cast(shape); if (group) saveGroup(group, context); else saveShape(shape, context); } context.shapeWriter().endElement(); } void SvgWriter::saveGroup(KoShapeGroup * group, SvgSavingContext &context) { context.shapeWriter().startElement("g"); context.shapeWriter().addAttribute("id", context.getID(group)); SvgUtil::writeTransformAttributeLazy("transform", group->transformation(), context.shapeWriter()); SvgStyleWriter::saveSvgStyle(group, context); QList sortedShapes = group->shapes(); std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex); Q_FOREACH (KoShape * shape, sortedShapes) { KoShapeGroup * childGroup = dynamic_cast(shape); if (childGroup) saveGroup(childGroup, context); else saveShape(shape, context); } context.shapeWriter().endElement(); } void SvgWriter::saveShape(KoShape *shape, SvgSavingContext &context) { SvgShape *svgShape = dynamic_cast(shape); if (svgShape && svgShape->saveSvg(context)) return; KoPathShape * path = dynamic_cast(shape); if (path) { savePath(path, context); } else { // generic saving of shape via a switch element saveGeneric(shape, context); } } void SvgWriter::savePath(KoPathShape *path, SvgSavingContext &context) { context.shapeWriter().startElement("path"); context.shapeWriter().addAttribute("id", context.getID(path)); SvgUtil::writeTransformAttributeLazy("transform", path->transformation(), context.shapeWriter()); SvgStyleWriter::saveSvgStyle(path, context); context.shapeWriter().addAttribute("d", path->toString(context.userSpaceTransform())); context.shapeWriter().endElement(); } void SvgWriter::saveGeneric(KoShape *shape, SvgSavingContext &context) { KIS_SAFE_ASSERT_RECOVER_RETURN(shape); const QRectF bbox = shape->boundingRect(); // paint shape to the image KoShapePainter painter; painter.setShapes(QList()<< shape); // generate svg from shape QBuffer svgBuffer; QSvgGenerator svgGenerator; svgGenerator.setOutputDevice(&svgBuffer); /** * HACK ALERT: Qt (and Krita 3.x) has a weird bug, it assumes that all font sizes are * defined in 96 ppi resolution, even though your the resolution in QSvgGenerator * is manually set to 72 ppi. So here we do a tricky thing: we set a fake resolution * to (72 * 72 / 96) = 54 ppi, which guarantees that the text, when painted in 96 ppi, * will be actually painted in 72 ppi. * * BUG: 389802 */ if (shape->shapeId() == "TextShapeID") { svgGenerator.setResolution(54); } QPainter svgPainter; svgPainter.begin(&svgGenerator); painter.paint(svgPainter, SvgUtil::toUserSpace(bbox).toRect(), bbox); svgPainter.end(); // remove anything before the start of the svg element from the buffer int startOfContent = svgBuffer.buffer().indexOf("0) { svgBuffer.buffer().remove(0, startOfContent); } // check if painting to svg produced any output if (svgBuffer.buffer().isEmpty()) { // prepare a transparent image, make it twice as big as the original size QImage image(2*bbox.size().toSize(), QImage::Format_ARGB32); image.fill(0); painter.paint(image); context.shapeWriter().startElement("image"); context.shapeWriter().addAttribute("id", context.getID(shape)); context.shapeWriter().addAttribute("x", bbox.x()); context.shapeWriter().addAttribute("y", bbox.y()); context.shapeWriter().addAttribute("width", bbox.width()); context.shapeWriter().addAttribute("height", bbox.height()); context.shapeWriter().addAttribute("xlink:href", context.saveImage(image)); context.shapeWriter().endElement(); // image } else { context.shapeWriter().addCompleteElement(&svgBuffer); } // TODO: once we support saving single (flat) odf files // we can embed these here to have full support for generic shapes } void SvgWriter::setDocumentTitle(QString title) { m_documentTitle = title; } void SvgWriter::setDocumentDescription(QString description) { m_documentDescription = description; } diff --git a/libs/flake/text/KoSvgTextShapeMarkupConverter.h b/libs/flake/text/KoSvgTextShapeMarkupConverter.h index 9b58ce2468..c57c21fa5b 100644 --- a/libs/flake/text/KoSvgTextShapeMarkupConverter.h +++ b/libs/flake/text/KoSvgTextShapeMarkupConverter.h @@ -1,144 +1,144 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KOSVGTEXTSHAPEMARKUPCONVERTER_H #define KOSVGTEXTSHAPEMARKUPCONVERTER_H #include "kritaflake_export.h" #include #include #include #include class QRectF; class KoSvgTextShape; /** * KoSvgTextShapeMarkupConverter is a utility class for converting a * KoSvgTextShape to/from user-editable markup/svg representation. * * Please note that the converted SVG is **not** the same as when saved into * .kra! Some attributes are dropped to make the editing is easier for the * user. */ class KRITAFLAKE_EXPORT KoSvgTextShapeMarkupConverter { public: KoSvgTextShapeMarkupConverter(KoSvgTextShape *shape); ~KoSvgTextShapeMarkupConverter(); /** * Convert the text shape into two strings: text and styles. Styles string * is non-empty only when the text has some gradient/pattern attached. It is * intended to be places into a separate tab in the GUI. * * @return true on success */ bool convertToSvg(QString *svgText, QString *stylesText); /** * @brief upload the svg representation of text into the shape * @param svgText \ part of SVG * @param stylesText \ part of SVG (used only for gradients and patterns) * @param boundsInPixels bounds of the entire image in pixel. Used for parsing percentage units. * @param pixelsPerInch resolution of the image where we load the shape to * * @return true if the text was parsed successfully. Check `errors()` and `warnings()` for details. */ bool convertFromSvg(const QString &svgText, const QString &stylesText, const QRectF &boundsInPixels, qreal pixelsPerInch); /** * @brief convertToHtml convert the text in the text shape to html * @param htmlText will be filled with correct html representing the text in the shape * @return @c true on success */ bool convertToHtml(QString *htmlText); /** - * @brief convertFromHtml converted Qt rich text html (and no other: http://doc.qt.io/qt-5/richtext-html-subset.html) to SVG + * @brief convertFromHtml converted Qt rich text html (and no other: https://doc.qt.io/qt-5/richtext-html-subset.html) to SVG * @param htmlText the input html * @param svgText the converted svg text element * @param styles * @return @c true if the conversion was successful */ bool convertFromHtml(const QString &htmlText, QString *svgText, QString *styles); /** * @brief convertDocumentToSvg * @param doc the QTextDocument to convert. * @param svgText the converted svg text element * @return @c true if the conversion was successful */ bool convertDocumentToSvg(const QTextDocument *doc, QString *svgText); /** * @brief convertSvgToDocument * @param svgText the \ element and it's children as a string. * @param doc the QTextDocument that the conversion is written to. * @return @c true if the conversion was successful */ bool convertSvgToDocument(const QString &svgText, QTextDocument *doc); /** * A list of errors happened during loading the user's text */ QStringList errors() const; /** * A list of warnings produced during loading the user's text */ QStringList warnings() const; /** * @brief style * creates a style string based on the blockformat and the format. * @param format the textCharFormat of the current text. * @param blockFormat the block format of the current text. * @param mostCommon the most common format to compare the format to. * @return a string that can be written into a style element. */ QString style(QTextCharFormat format, QTextBlockFormat blockFormat, QTextCharFormat mostCommon = QTextCharFormat()); /** * @brief stylesFromString * returns a qvector with two textformats: * at 0 is the QTextCharFormat * at 1 is the QTextBlockFormat * @param styles a style string split at ";" * @param currentCharFormat the current charformat to compare against. * @param currentBlockFormat the current blockformat to compare against. * @return A QVector with at 0 a QTextCharFormat and at 1 a QBlockCharFormat. */ static QVector stylesFromString(QStringList styles, QTextCharFormat currentCharFormat, QTextBlockFormat currentBlockFormat); /** * @brief formatDifference * A class to get the difference between two text-char formats. * @param test the format to test * @param reference the format to test against. * @return the difference between the two. */ QTextFormat formatDifference(QTextFormat test, QTextFormat reference); private: struct Private; const QScopedPointer d; }; #endif // KOSVGTEXTSHAPEMARKUPCONVERTER_H diff --git a/libs/global/KisUsageLogger.cpp b/libs/global/KisUsageLogger.cpp index fdce7c3901..9ae72e5064 100644 --- a/libs/global/KisUsageLogger.cpp +++ b/libs/global/KisUsageLogger.cpp @@ -1,206 +1,209 @@ /* * 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 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; + + // 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()); + systemInfo.append("\n\n"); + + s_instance->d->sysInfoFile.write(systemInfo.toUtf8()); + } 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::writeSectionHeader() +void KisUsageLogger::writeSysInfo(const QString &message) { - s_instance->d->logFile.write(s_sectionHeader.toUtf8()); + 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->logFile.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(' ')); - QString disclaimer = i18n("WARNING: This file contains information about your system and the\n" - "images you have been working with.\n" - "\n" - "If you have problems with Krita, the Krita developers might ask\n" - "you to share this file with them. The information in this file is\n" - "not shared automatically with the Krita developers in any way. You\n" - "can disable logging to this file in Krita's Configure Krita Dialog.\n" - "\n" - "Please review the contents of this file before sharing this file with\n" - "anyone.\n\n"); - - 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()); - systemInfo.append("\n\n"); - - writeSectionHeader(); s_instance->d->logFile.write(sessionHeader.toUtf8()); - s_instance->d->logFile.write(disclaimer.toUtf8()); - s_instance->d->logFile.write(systemInfo.toUtf8()); - - + s_instance->d->logFile.flush(); } 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("\n").last().contains("CLOSING SESSION")) { + 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/global/KisUsageLogger.h b/libs/global/KisUsageLogger.h index 34dd84755f..9f51eb4ea3 100644 --- a/libs/global/KisUsageLogger.h +++ b/libs/global/KisUsageLogger.h @@ -1,64 +1,64 @@ /* * 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. */ #ifndef KISUSAGELOGGER_H #define KISUSAGELOGGER_H #include #include #include "kritaglobal_export.h" /** * @brief The KisUsageLogger class logs messages to a logfile */ class KRITAGLOBAL_EXPORT KisUsageLogger { public: KisUsageLogger(); ~KisUsageLogger(); static void initialize(); static void close(); /// Logs with date/time static void log(const QString &message); /// Writes without date/time static void write(const QString &message); - static void writeSectionHeader(); + static void writeSysInfo(const QString &message); static void writeHeader(); private: void rotateLog(); Q_DISABLE_COPY(KisUsageLogger) struct Private; const QScopedPointer d; static const QString s_sectionHeader; static const int s_maxLogs {10}; }; #endif // KISUSAGELOGGER_H diff --git a/libs/global/kis_shared_ptr.h b/libs/global/kis_shared_ptr.h index b9f1c93602..833d2128ca 100644 --- a/libs/global/kis_shared_ptr.h +++ b/libs/global/kis_shared_ptr.h @@ -1,526 +1,526 @@ /* * Copyright (c) 2005 Frerich Raabe * Copyright (c) 2006,2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_SHAREDPTR_H #define KIS_SHAREDPTR_H #include #include #include "kis_memory_leak_tracker.h" template class KisWeakSharedPtr; /** * KisSharedPtr is a shared pointer similar to KSharedPtr and * boost::shared_ptr. The difference with KSharedPtr is that our * constructor is not explicit. * * A shared pointer is a wrapper around a real pointer. The shared * pointer keeps a reference count, and when the reference count drops * to 0 the contained pointer is deleted. You can use the shared * pointer just as you would use a real pointer. * * See also also item 28 and 29 of More Effective C++ and - * http://bugs.kde.org/show_bug.cgi?id=52261 as well as - * http://www.boost.org/libs/smart_ptr/shared_ptr.htm. + * https://bugs.kde.org/show_bug.cgi?id=52261 as well as + * https://www.boost.org/libs/smart_ptr/shared_ptr.htm. * * Advantage of KisSharedPtr over boost pointer or QSharedPointer? * * The difference with boost share pointer is that in * boost::shared_ptr, the counter is kept inside the smart pointer, * meaning that you should never never remove the pointer from its * smart pointer object, because if you do that, and somewhere in the * code, the pointer is put back in a smart pointer, then you have two * counters, and when one reach zero, then the object gets deleted * while some other code thinks the pointer is still valid. * * Disadvantage of KisSharedPtr compared to boost pointer? * * KisSharedPtr requires the class to inherits KisShared. * * Difference with QSharedPointer * * QSharedPointer and KisSharedPtr are very similar, but * QSharedPointer has an explicit constructor which makes it more * painful to use in some constructions. And QSharedPointer * doesn't offer a weak pointer. */ template class KisSharedPtr { friend class KisWeakSharedPtr; public: /** * Creates a null pointer. */ inline KisSharedPtr() : d(0) { } /** * Creates a new pointer. * @param p the pointer */ inline KisSharedPtr(T* p) : d(p) { ref(); } inline KisSharedPtr(const KisWeakSharedPtr& o); // Free the pointer and set it to new value void attach(T* p); // Free the pointer void clear(); /** * Copies a pointer. * @param o the pointer to copy */ inline KisSharedPtr(const KisSharedPtr& o) : d(o.d) { ref(); } /** * Dereferences the object that this pointer points to. If it was * the last reference, the object will be deleted. */ inline ~KisSharedPtr() { deref(); } inline KisSharedPtr& operator= (const KisSharedPtr& o) { attach(o.d); return *this; } inline bool operator== (const T* p) const { return (d == p); } inline bool operator!= (const T* p) const { return (d != p); } inline bool operator== (const KisSharedPtr& o) const { return (d == o.d); } inline bool operator!= (const KisSharedPtr& o) const { return (d != o.d); } inline KisSharedPtr& operator= (T* p) { attach(p); return *this; } inline operator const T*() const { return d; } template< class T2> inline operator KisSharedPtr() const { return KisSharedPtr(d); } /** * @return the contained pointer. If you delete the contained * pointer, you will make KisSharedPtr very unhappy. It is * perfectly save to put the contained pointer in another * KisSharedPtr, though. */ inline T* data() { return d; } /** * @return the pointer */ inline const T* data() const { return d; } /** * @return a const pointer to the shared object. */ inline const T* constData() const { return d; } inline const T& operator*() const { Q_ASSERT(d); return *d; } inline T& operator*() { Q_ASSERT(d); return *d; } inline const T* operator->() const { Q_ASSERT(d); return d; } inline T* operator->() { Q_ASSERT(d); return d; } /** * @return true if the pointer is null */ inline bool isNull() const { return (d == 0); } inline static void ref(const KisSharedPtr* sp, T* t) { #ifndef HAVE_MEMORY_LEAK_TRACKER Q_UNUSED(sp); #else KisMemoryLeakTracker::instance()->reference(t, sp); #endif if (t) { t->ref(); } } inline static bool deref(const KisSharedPtr* sp, T* t) { #ifndef HAVE_MEMORY_LEAK_TRACKER Q_UNUSED(sp); #else KisMemoryLeakTracker::instance()->dereference(t, sp); #endif if (t && !t->deref()) { delete t; return false; } return true; } private: inline void ref() const { ref(this, d); } inline void deref() const { bool v = deref(this, d); #ifndef NDEBUG if (!v) { d = 0; } #else Q_UNUSED(v); #endif } private: mutable T* d; }; /** * A weak shared ptr is an ordinary shared ptr, with two differences: * it doesn't delete the contained pointer if the refcount drops to * zero and it doesn't prevent the contained pointer from being * deleted if the last strong shared pointer goes out of scope. */ template class KisWeakSharedPtr { friend class KisSharedPtr; public: /** * Creates a null pointer. */ inline KisWeakSharedPtr() : d(0), weakReference(0) { } /** * Creates a new pointer. * @param p the pointer */ inline KisWeakSharedPtr(T* p) { load(p); } inline KisWeakSharedPtr(const KisSharedPtr& o) { load(o.d); } /** * Copies a pointer. * @param o the pointer to copy */ inline KisWeakSharedPtr(const KisWeakSharedPtr& o) { if (o.isConsistent()) { load(o.d); } else { d = 0; weakReference = 0; } } inline ~KisWeakSharedPtr() { detach(); } inline KisWeakSharedPtr& operator= (const KisWeakSharedPtr& o) { attach(o); return *this; } inline bool operator== (const T* p) const { return (d == p); } inline bool operator!= (const T* p) const { return (d != p); } inline bool operator== (const KisWeakSharedPtr& o) const { return (d == o.d); } inline bool operator!= (const KisWeakSharedPtr& o) const { return (d != o.d); } inline KisWeakSharedPtr& operator= (T* p) { attach(p); return *this; } template< class T2> inline operator KisWeakSharedPtr() const { return KisWeakSharedPtr(d); } /** * Note that if you use this function, the pointer might be destroyed * if KisSharedPtr pointing to this pointer are deleted, resulting in * a segmentation fault. Use with care. * @return a const pointer to the shared object. */ inline T* data() { if (!isConsistent()) { warnKrita << kisBacktrace(); Q_ASSERT_X(0, "KisWeakSharedPtr", "Weak pointer is not valid!"); } return d; } /** * @see data() */ inline const T* data() const { if (!isConsistent()) { warnKrita << kisBacktrace(); Q_ASSERT_X(0, "KisWeakSharedPtr", "Weak pointer is not valid!"); } return d; } /** * @see data() */ inline const T* constData() const { if (!isConsistent()) { warnKrita << kisBacktrace(); Q_ASSERT_X(0, "KisWeakSharedPtr", "Weak pointer is not valid!"); } return d; } /** * @see data() */ inline operator const T*() const { /** * This operator is used in boolean expressions where we check * for pointer consistency, so return 0 instead of asserting. */ return isConsistent() ? d : 0; } inline const T& operator*() const { if (!isValid()) { warnKrita << kisBacktrace(); Q_ASSERT_X(0, "KisWeakSharedPtr", "Weak pointer is not valid!"); } return *d; } inline T& operator*() { if (!isValid()) { warnKrita << kisBacktrace(); Q_ASSERT_X(0, "KisWeakSharedPtr", "Weak pointer is not valid!"); } return *d; } inline const T* operator->() const { if (!isValid()) { warnKrita << kisBacktrace(); Q_ASSERT_X(0, "KisWeakSharedPtr", "Weak pointer is not valid!"); } return d; } inline T* operator->() { if (!isValid()) { warnKrita << kisBacktrace(); Q_ASSERT_X(0, "KisWeakSharedPtr", "Weak pointer is not valid!"); } return d; } /** * @return true if the pointer is null */ inline bool isNull() const { return (d == 0); } /** * @return true if the weak pointer points to a valid pointer * and false if the data has been deleted or is null */ inline bool isValid() const { Q_ASSERT(!d || weakReference); return d && weakReference && isOdd((int)*weakReference); } /** * @brief toStrongRef returns a KisSharedPtr which may be dereferenced. * * Weak pointers should only be used to track ownership but never be used as pointers. * This has historically not been the case, but in new API this function should be used * instead of directly using a weak pointer as pointer. * @return a KisSharedPtr, which may be null */ inline KisSharedPtr toStrongRef() const { return KisSharedPtr(*this); } private: static const qint32 WEAK_REF = 2; static inline bool isOdd(const qint32 &x) { return x & 0x01; } inline bool isConsistent() const { Q_ASSERT(!d || weakReference); return !d || (weakReference && isOdd((int)*weakReference)); } void load(T* newValue) { d = newValue; if (d) { weakReference = d->sharedWeakReference(); weakReference->fetchAndAddOrdered(WEAK_REF); } else { weakReference = 0; } } // see note in kis_shared.cc inline void attach(T* newValue) { detach(); load(newValue); } inline void attach(const KisWeakSharedPtr& o) { detach(); if (o.isConsistent()) { load(o.d); } else { d = 0; weakReference = 0; } } // see note in kis_shared.cc void detach() { d = 0; if (weakReference && weakReference->fetchAndAddOrdered(-WEAK_REF) <= WEAK_REF) { // sanity check: Q_ASSERT((int)*weakReference == 0); delete weakReference; weakReference = 0; } } mutable T* d; QAtomicInt *weakReference; }; template Q_INLINE_TEMPLATE KisSharedPtr::KisSharedPtr(const KisWeakSharedPtr& o) : d(o.d) { if (o.isValid()) { ref(); /** * Thread safety: * Is the object we have just referenced still valid? */ Q_ASSERT(o.isConsistent()); } else { d = 0; } } template Q_INLINE_TEMPLATE void KisSharedPtr::attach(T* p) { if (d != p) { ref(this, p); T* old = d; d = p; deref(this, old); } } template Q_INLINE_TEMPLATE void KisSharedPtr::clear() { attach((T*)0); } #endif diff --git a/libs/image/brushengine/kis_paintop.cc b/libs/image/brushengine/kis_paintop.cc index ac28205c37..c896dbb605 100644 --- a/libs/image/brushengine/kis_paintop.cc +++ b/libs/image/brushengine/kis_paintop.cc @@ -1,211 +1,211 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2004 Adrian Page * Copyright (c) 2004,2007,2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_paintop.h" -#include +#include #include #include #include #include "kis_painter.h" #include "kis_layer.h" #include "kis_image.h" #include "kis_paint_device.h" #include "kis_global.h" #include "kis_datamanager.h" #include #include #include "kis_vec.h" #include "kis_perspective_math.h" #include "kis_fixed_paint_device.h" #include "kis_paintop_utils.h" #define BEZIER_FLATNESS_THRESHOLD 0.5 #include #include struct Q_DECL_HIDDEN KisPaintOp::Private { Private(KisPaintOp *_q) : q(_q), dab(0), fanCornersEnabled(false), fanCornersStep(1.0) {} KisPaintOp *q; KisFixedPaintDeviceSP dab; KisPainter* painter; bool fanCornersEnabled; qreal fanCornersStep; }; KisPaintOp::KisPaintOp(KisPainter * painter) : d(new Private(this)) { d->painter = painter; } KisPaintOp::~KisPaintOp() { d->dab.clear(); delete d; } KisFixedPaintDeviceSP KisPaintOp::cachedDab() { return cachedDab(d->painter->device()->colorSpace()); } KisFixedPaintDeviceSP KisPaintOp::cachedDab(const KoColorSpace *cs) { if (!d->dab || *d->dab->colorSpace() != *cs) { d->dab = new KisFixedPaintDevice(cs); } return d->dab; } void KisPaintOp::setFanCornersInfo(bool fanCornersEnabled, qreal fanCornersStep) { d->fanCornersEnabled = fanCornersEnabled; d->fanCornersStep = fanCornersStep; } void KisPaintOp::splitCoordinate(qreal coordinate, qint32 *whole, qreal *fraction) { - const qint32 i = std::floor(coordinate); + const qint32 i = qFloor(coordinate); const qreal f = coordinate - i; *whole = i; *fraction = f; } std::pair KisPaintOp::doAsyncronousUpdate(QVector &jobs) { Q_UNUSED(jobs); return std::make_pair(40, false); } static void paintBezierCurve(KisPaintOp *paintOp, const KisPaintInformation &pi1, const KisVector2D &control1, const KisVector2D &control2, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) { LineEquation line = LineEquation::Through(toKisVector2D(pi1.pos()), toKisVector2D(pi2.pos())); qreal d1 = line.absDistance(control1); qreal d2 = line.absDistance(control2); if ((d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) || qIsNaN(d1) || qIsNaN(d2)) { paintOp->paintLine(pi1, pi2, currentDistance); } else { // Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508 KisVector2D l2 = (toKisVector2D(pi1.pos()) + control1) / 2; KisVector2D h = (control1 + control2) / 2; KisVector2D l3 = (l2 + h) / 2; KisVector2D r3 = (control2 + toKisVector2D(pi2.pos())) / 2; KisVector2D r2 = (h + r3) / 2; KisVector2D l4 = (l3 + r2) / 2; KisPaintInformation middlePI = KisPaintInformation::mix(toQPointF(l4), 0.5, pi1, pi2); paintBezierCurve(paintOp, pi1, l2, l3, middlePI, currentDistance); paintBezierCurve(paintOp, middlePI, r2, r3, pi2, currentDistance); } } void KisPaintOp::paintBezierCurve(const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) { return ::paintBezierCurve(this, pi1, toKisVector2D(control1), toKisVector2D(control2), pi2, currentDistance); } void KisPaintOp::paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) { KisPaintOpUtils::paintLine(*this, pi1, pi2, currentDistance, d->fanCornersEnabled, d->fanCornersStep); } void KisPaintOp::paintAt(const KisPaintInformation& info, KisDistanceInformation *currentDistance) { Q_ASSERT(currentDistance); KisPaintInformation pi(info); pi.paintAt(*this, currentDistance); } void KisPaintOp::updateSpacing(const KisPaintInformation &info, KisDistanceInformation ¤tDistance) const { KisPaintInformation pi(info); KisSpacingInformation spacingInfo; { KisPaintInformation::DistanceInformationRegistrar r = pi.registerDistanceInformation(¤tDistance); spacingInfo = updateSpacingImpl(pi); } currentDistance.updateSpacing(spacingInfo); } void KisPaintOp::updateTiming(const KisPaintInformation &info, KisDistanceInformation ¤tDistance) const { KisPaintInformation pi(info); KisTimingInformation timingInfo; { KisPaintInformation::DistanceInformationRegistrar r = pi.registerDistanceInformation(¤tDistance); timingInfo = updateTimingImpl(pi); } currentDistance.updateTiming(timingInfo); } KisTimingInformation KisPaintOp::updateTimingImpl(const KisPaintInformation &info) const { Q_UNUSED(info); return KisTimingInformation(); } KisPainter* KisPaintOp::painter() const { return d->painter; } KisPaintDeviceSP KisPaintOp::source() const { return d->painter->device(); } diff --git a/libs/image/commands_new/kis_change_projection_color_command.cpp b/libs/image/commands_new/kis_change_projection_color_command.cpp index 5d4c0d8bbf..d32934af51 100644 --- a/libs/image/commands_new/kis_change_projection_color_command.cpp +++ b/libs/image/commands_new/kis_change_projection_color_command.cpp @@ -1,78 +1,78 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_change_projection_color_command.h" #include "kis_image.h" #include "kis_image_animation_interface.h" KisChangeProjectionColorCommand::KisChangeProjectionColorCommand(KisImageSP image, const KoColor &newColor, KUndo2Command *parent) : KUndo2Command(kundo2_noi18n("CHANGE_PROJECTION_COLOR_COMMAND"), parent), m_image(image), m_oldColor(image->defaultProjectionColor()), m_newColor(newColor) { } KisChangeProjectionColorCommand::~KisChangeProjectionColorCommand() { } int KisChangeProjectionColorCommand::id() const { // we don't have a common commands id source in Krita yet, so // just use a random one ;) - // http://www.scientificamerican.com/article/most-popular-numbers-grapes-of-math/ + // https://www.scientificamerican.com/article/most-popular-numbers-grapes-of-math/ return 142857; } bool KisChangeProjectionColorCommand::mergeWith(const KUndo2Command* command) { const KisChangeProjectionColorCommand *other = dynamic_cast(command); if (!other || other->id() != id()) { return false; } m_newColor = other->m_newColor; return true; } void KisChangeProjectionColorCommand::redo() { KisImageSP image = m_image.toStrongRef(); if (!image) { return; } image->setDefaultProjectionColor(m_newColor); image->animationInterface()->setDefaultProjectionColor(m_newColor); } void KisChangeProjectionColorCommand::undo() { KisImageSP image = m_image.toStrongRef(); if (!image) { return; } image->setDefaultProjectionColor(m_oldColor); image->animationInterface()->setDefaultProjectionColor(m_oldColor); } diff --git a/libs/image/kis_async_merger.cpp b/libs/image/kis_async_merger.cpp index c0790e8986..b20080f160 100644 --- a/libs/image/kis_async_merger.cpp +++ b/libs/image/kis_async_merger.cpp @@ -1,380 +1,384 @@ /* Copyright (c) Dmitry Kazakov , 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 "kis_async_merger.h" #include #include #include #include #include "kis_node_visitor.h" #include "kis_painter.h" #include "kis_layer.h" #include "kis_group_layer.h" #include "kis_adjustment_layer.h" #include "generator/kis_generator_layer.h" #include "kis_external_layer_iface.h" #include "kis_paint_layer.h" #include "filter/kis_filter.h" #include "filter/kis_filter_configuration.h" #include "filter/kis_filter_registry.h" #include "kis_selection.h" #include "kis_clone_layer.h" #include "kis_processing_information.h" #include "kis_busy_progress_indicator.h" #include "kis_merge_walker.h" #include "kis_refresh_subtree_walker.h" #include "kis_abstract_projection_plane.h" //#define DEBUG_MERGER #ifdef DEBUG_MERGER #define DEBUG_NODE_ACTION(message, type, leaf, rect) \ qDebug() << message << type << ":" << leaf->node()->name() << rect #else #define DEBUG_NODE_ACTION(message, type, leaf, rect) #endif class KisUpdateOriginalVisitor : public KisNodeVisitor { public: KisUpdateOriginalVisitor(const QRect &updateRect, KisPaintDeviceSP projection, const QRect &cropRect) : m_updateRect(updateRect), m_cropRect(cropRect), m_projection(projection) { } ~KisUpdateOriginalVisitor() override { } public: using KisNodeVisitor::visit; bool visit(KisAdjustmentLayer* layer) override { if (!layer->visible()) return true; if (!m_projection) { warnImage << "ObligeChild mechanism has been activated for " "an adjustment layer! Do nothing..."; layer->original()->clear(); return true; } const QRect originalUpdateRect = layer->projectionPlane()->needRectForOriginal(m_updateRect); KisPaintDeviceSP originalDevice = layer->original(); originalDevice->clear(originalUpdateRect); const QRect applyRect = originalUpdateRect & m_projection->extent(); // If the intersection of the updaterect and the projection extent is // null, we are finish here. if(applyRect.isNull()) return true; KisFilterConfigurationSP filterConfig = layer->filter(); if (!filterConfig) { /** * When an adjustment layer is just created, it may have no * filter inside. Then the layer has work as a pass-through * node. Just copy the merged data to the layer's original. */ KisPainter::copyAreaOptimized(applyRect.topLeft(), m_projection, originalDevice, applyRect); return true; } KisSelectionSP selection = layer->fetchComposedInternalSelection(applyRect); const QRect filterRect = selection ? applyRect & selection->selectedRect() : applyRect; KisFilterSP filter = KisFilterRegistry::instance()->value(filterConfig->name()); if (!filter) return false; KisPaintDeviceSP dstDevice = originalDevice; if (selection) { dstDevice = new KisPaintDevice(originalDevice->colorSpace()); } if (!filterRect.isEmpty()) { KIS_ASSERT_RECOVER_NOOP(layer->busyProgressIndicator()); layer->busyProgressIndicator()->update(); // We do not create a transaction here, as srcDevice != dstDevice filter->process(m_projection, dstDevice, 0, filterRect, filterConfig.data(), 0); } if (selection) { KisPainter::copyAreaOptimized(applyRect.topLeft(), m_projection, originalDevice, applyRect); KisPainter::copyAreaOptimized(filterRect.topLeft(), dstDevice, originalDevice, filterRect, selection); } return true; } bool visit(KisExternalLayer*) override { return true; } bool visit(KisGeneratorLayer*) override { return true; } bool visit(KisPaintLayer*) override { return true; } bool visit(KisGroupLayer*) override { return true; } bool visit(KisCloneLayer *layer) override { QRect emptyRect; KisRefreshSubtreeWalker walker(emptyRect); KisAsyncMerger merger; KisLayerSP srcLayer = layer->copyFrom(); QRect srcRect = m_updateRect.translated(-layer->x(), -layer->y()); QRegion prepareRegion(srcRect); prepareRegion -= m_cropRect; /** * If a clone has complicated masks, we should prepare additional * source area to ensure the rect is prepared. */ QRect needRectOnSource = layer->needRectOnSourceForMasks(srcRect); if (!needRectOnSource.isEmpty()) { prepareRegion += needRectOnSource; } + if (srcLayer.isNull()) { + return true; + } + Q_FOREACH (const QRect &rect, prepareRegion.rects()) { walker.collectRects(srcLayer, rect); merger.startMerge(walker, false); } return true; } bool visit(KisNode*) override { return true; } bool visit(KisFilterMask*) override { return true; } bool visit(KisTransformMask*) override { return true; } bool visit(KisTransparencyMask*) override { return true; } bool visit(KisSelectionMask*) override { return true; } bool visit(KisColorizeMask*) override { return true; } private: QRect m_updateRect; QRect m_cropRect; KisPaintDeviceSP m_projection; }; /*********************************************************************/ /* KisAsyncMerger */ /*********************************************************************/ void KisAsyncMerger::startMerge(KisBaseRectsWalker &walker, bool notifyClones) { KisMergeWalker::LeafStack &leafStack = walker.leafStack(); const bool useTempProjections = walker.needRectVaries(); while(!leafStack.isEmpty()) { KisMergeWalker::JobItem item = leafStack.pop(); KisProjectionLeafSP currentLeaf = item.m_leaf; /** * In some unidentified cases teh nodes might be removed * while the updates are still running. We have no proof * of it yet, so just add a safety assert here. */ KIS_SAFE_ASSERT_RECOVER_RETURN(currentLeaf); KIS_SAFE_ASSERT_RECOVER_RETURN(currentLeaf->node()); // All the masks should be filtered by the walkers KIS_SAFE_ASSERT_RECOVER_RETURN(currentLeaf->isLayer()); QRect applyRect = item.m_applyRect; if (currentLeaf->isRoot()) { currentLeaf->projectionPlane()->recalculate(applyRect, walker.startNode()); continue; } if(item.m_position & KisMergeWalker::N_EXTRA) { // The type of layers that will not go to projection. DEBUG_NODE_ACTION("Updating", "N_EXTRA", currentLeaf, applyRect); KisUpdateOriginalVisitor originalVisitor(applyRect, m_currentProjection, walker.cropRect()); currentLeaf->accept(originalVisitor); currentLeaf->projectionPlane()->recalculate(applyRect, currentLeaf->node()); continue; } if (!m_currentProjection) { setupProjection(currentLeaf, applyRect, useTempProjections); } KisUpdateOriginalVisitor originalVisitor(applyRect, m_currentProjection, walker.cropRect()); if(item.m_position & KisMergeWalker::N_FILTHY) { DEBUG_NODE_ACTION("Updating", "N_FILTHY", currentLeaf, applyRect); if (currentLeaf->visible() || currentLeaf->hasClones()) { currentLeaf->accept(originalVisitor); currentLeaf->projectionPlane()->recalculate(applyRect, walker.startNode()); } } else if(item.m_position & KisMergeWalker::N_ABOVE_FILTHY) { DEBUG_NODE_ACTION("Updating", "N_ABOVE_FILTHY", currentLeaf, applyRect); if(currentLeaf->dependsOnLowerNodes()) { if (currentLeaf->visible() || currentLeaf->hasClones()) { currentLeaf->accept(originalVisitor); currentLeaf->projectionPlane()->recalculate(applyRect, currentLeaf->node()); } } } else if(item.m_position & KisMergeWalker::N_FILTHY_PROJECTION) { DEBUG_NODE_ACTION("Updating", "N_FILTHY_PROJECTION", currentLeaf, applyRect); if (currentLeaf->visible() || currentLeaf->hasClones()) { currentLeaf->projectionPlane()->recalculate(applyRect, walker.startNode()); } } else /*if(item.m_position & KisMergeWalker::N_BELOW_FILTHY)*/ { DEBUG_NODE_ACTION("Updating", "N_BELOW_FILTHY", currentLeaf, applyRect); /* nothing to do */ } compositeWithProjection(currentLeaf, applyRect); if(item.m_position & KisMergeWalker::N_TOPMOST) { writeProjection(currentLeaf, useTempProjections, applyRect); resetProjection(); } // FIXME: remove it from the inner loop and/or change to a warning! Q_ASSERT(currentLeaf->projection()->defaultBounds()->currentLevelOfDetail() == walker.levelOfDetail()); } if(notifyClones) { doNotifyClones(walker); } if(m_currentProjection) { warnImage << "BUG: The walker hasn't reached the root layer!"; warnImage << " Start node:" << walker.startNode() << "Requested rect:" << walker.requestedRect(); warnImage << " An inconsistency in the walkers occurred!"; warnImage << " Please report a bug describing how you got this message."; // reset projection to avoid artifacts in next merges and allow people to work further resetProjection(); } } void KisAsyncMerger::resetProjection() { m_currentProjection = 0; m_finalProjection = 0; } void KisAsyncMerger::setupProjection(KisProjectionLeafSP currentLeaf, const QRect& rect, bool useTempProjection) { KisPaintDeviceSP parentOriginal = currentLeaf->parent()->original(); if (parentOriginal != currentLeaf->projection()) { if (useTempProjection) { if(!m_cachedPaintDevice) m_cachedPaintDevice = new KisPaintDevice(parentOriginal->colorSpace()); m_currentProjection = m_cachedPaintDevice; m_currentProjection->prepareClone(parentOriginal); m_finalProjection = parentOriginal; } else { parentOriginal->clear(rect); m_finalProjection = m_currentProjection = parentOriginal; } } else { /** * It happened so that our parent uses our own projection as * its original. It means obligeChild mechanism works. * We won't initialise m_currentProjection. This will cause * writeProjection() and compositeWithProjection() do nothing * when called. */ /* NOP */ } } void KisAsyncMerger::writeProjection(KisProjectionLeafSP topmostLeaf, bool useTempProjection, const QRect &rect) { Q_UNUSED(useTempProjection); Q_UNUSED(topmostLeaf); if (!m_currentProjection) return; if(m_currentProjection != m_finalProjection) { KisPainter::copyAreaOptimized(rect.topLeft(), m_currentProjection, m_finalProjection, rect); } DEBUG_NODE_ACTION("Writing projection", "", topmostLeaf->parent(), rect); } bool KisAsyncMerger::compositeWithProjection(KisProjectionLeafSP leaf, const QRect &rect) { if (!m_currentProjection) return true; if (!leaf->visible()) return true; KisPainter gc(m_currentProjection); leaf->projectionPlane()->apply(&gc, rect); DEBUG_NODE_ACTION("Compositing projection", "", leaf, rect); return true; } void KisAsyncMerger::doNotifyClones(KisBaseRectsWalker &walker) { KisBaseRectsWalker::CloneNotificationsVector &vector = walker.cloneNotifications(); KisBaseRectsWalker::CloneNotificationsVector::iterator it; for(it = vector.begin(); it != vector.end(); ++it) { (*it).notify(); } } diff --git a/libs/image/kis_edge_detection_kernel.cpp b/libs/image/kis_edge_detection_kernel.cpp index 839c88d07d..2596140852 100644 --- a/libs/image/kis_edge_detection_kernel.cpp +++ b/libs/image/kis_edge_detection_kernel.cpp @@ -1,428 +1,428 @@ /* * Copyright (c) 2017 Wolthera van Hövell tot Westerflier * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_edge_detection_kernel.h" #include "kis_global.h" #include "kis_convolution_kernel.h" #include #include #include #include #include #include KisEdgeDetectionKernel::KisEdgeDetectionKernel() { } /* * This code is very similar to the gaussian kernel code, except unlike the gaussian code, * edge-detection kernels DO use the diagonals. - * Except for the simple mode. We implement the simple mode because it is an analogue to + * Except for the simple mode. We implement the simple mode because it is an analog to * the old sobel filter. */ Eigen::Matrix KisEdgeDetectionKernel::createHorizontalMatrix(qreal radius, FilterType type, bool reverse) { int kernelSize = kernelSizeFromRadius(radius); Eigen::Matrix matrix(kernelSize, kernelSize); KIS_ASSERT_RECOVER_NOOP(kernelSize & 0x1); const int center = kernelSize / 2; if (type==Prewit) { for (int x = 0; x < kernelSize; x++) { for (int y=0; y KisEdgeDetectionKernel::createVerticalMatrix(qreal radius, FilterType type, bool reverse) { int kernelSize = kernelSizeFromRadius(radius); Eigen::Matrix matrix(kernelSize, kernelSize); KIS_ASSERT_RECOVER_NOOP(kernelSize & 0x1); const int center = kernelSize / 2; if (type==Prewit) { for (int y = 0; y < kernelSize; y++) { for (int x=0; x matrix = createHorizontalMatrix(radius, type, reverse); if (denormalize) { return KisConvolutionKernel::fromMatrix(matrix, 0.5, 1); } else { return KisConvolutionKernel::fromMatrix(matrix, 0, matrix.sum()); } } KisConvolutionKernelSP KisEdgeDetectionKernel::createVerticalKernel(qreal radius, KisEdgeDetectionKernel::FilterType type, bool denormalize, bool reverse) { Eigen::Matrix matrix = createVerticalMatrix(radius, type, reverse); if (denormalize) { return KisConvolutionKernel::fromMatrix(matrix, 0.5, 1); } else { return KisConvolutionKernel::fromMatrix(matrix, 0, matrix.sum()); } } int KisEdgeDetectionKernel::kernelSizeFromRadius(qreal radius) { return qMax((int)(2 * ceil(sigmaFromRadius(radius)) + 1), 3); } qreal KisEdgeDetectionKernel::sigmaFromRadius(qreal radius) { return 0.3 * radius + 0.3; } void KisEdgeDetectionKernel::applyEdgeDetection(KisPaintDeviceSP device, const QRect &rect, qreal xRadius, qreal yRadius, KisEdgeDetectionKernel::FilterType type, const QBitArray &channelFlags, KoUpdater *progressUpdater, FilterOutput output, bool writeToAlpha) { QPoint srcTopLeft = rect.topLeft(); KisPainter finalPainter(device); finalPainter.setChannelFlags(channelFlags); finalPainter.setProgress(progressUpdater); if (output == pythagorean || output == radian) { KisPaintDeviceSP x_denormalised = new KisPaintDevice(device->colorSpace()); KisPaintDeviceSP y_denormalised = new KisPaintDevice(device->colorSpace()); x_denormalised->prepareClone(device); y_denormalised->prepareClone(device); KisConvolutionKernelSP kernelHorizLeftRight = KisEdgeDetectionKernel::createHorizontalKernel(xRadius, type); KisConvolutionKernelSP kernelVerticalTopBottom = KisEdgeDetectionKernel::createVerticalKernel(yRadius, type); qreal horizontalCenter = qreal(kernelHorizLeftRight->width()) / 2.0; qreal verticalCenter = qreal(kernelVerticalTopBottom->height()) / 2.0; KisConvolutionPainter horizPainterLR(x_denormalised); horizPainterLR.setChannelFlags(channelFlags); horizPainterLR.setProgress(progressUpdater); horizPainterLR.applyMatrix(kernelHorizLeftRight, device, srcTopLeft - QPoint(0, ceil(horizontalCenter)), srcTopLeft - QPoint(0, ceil(horizontalCenter)), rect.size() + QSize(0, 2 * ceil(horizontalCenter)), BORDER_REPEAT); KisConvolutionPainter verticalPainterTB(y_denormalised); verticalPainterTB.setChannelFlags(channelFlags); verticalPainterTB.setProgress(progressUpdater); verticalPainterTB.applyMatrix(kernelVerticalTopBottom, device, srcTopLeft - QPoint(0, ceil(verticalCenter)), srcTopLeft - QPoint(0, ceil(verticalCenter)), rect.size() + QSize(0, 2 * ceil(verticalCenter)), BORDER_REPEAT); KisSequentialIterator yItterator(y_denormalised, rect); KisSequentialIterator xItterator(x_denormalised, rect); KisSequentialIterator finalIt(device, rect); const int pixelSize = device->colorSpace()->pixelSize(); const int channels = device->colorSpace()->channelCount(); const int alphaPos = device->colorSpace()->alphaPos(); KIS_SAFE_ASSERT_RECOVER_RETURN(alphaPos >= 0); QVector yNormalised(channels); QVector xNormalised(channels); QVector finalNorm(channels); while(yItterator.nextPixel() && xItterator.nextPixel() && finalIt.nextPixel()) { device->colorSpace()->normalisedChannelsValue(yItterator.rawData(), yNormalised); device->colorSpace()->normalisedChannelsValue(xItterator.rawData(), xNormalised); device->colorSpace()->normalisedChannelsValue(finalIt.rawData(), finalNorm); if (output == pythagorean) { for (int c = 0; ccolorSpace()); qreal alpha = 0; for (int c = 0; c<(channels-1); c++) { alpha = alpha+finalNorm[c]; } alpha = qMin(alpha/(channels-1), col.opacityF()); col.setOpacity(alpha); memcpy(finalIt.rawData(), col.data(), pixelSize); } else { quint8* f = finalIt.rawData(); finalNorm[alphaPos] = 1.0; device->colorSpace()->fromNormalisedChannelsValue(f, finalNorm); memcpy(finalIt.rawData(), f, pixelSize); } } } else { KisConvolutionKernelSP kernel; qreal center = 0; bool denormalize = !writeToAlpha; if (output == xGrowth) { kernel = KisEdgeDetectionKernel::createHorizontalKernel(xRadius, type, denormalize); center = qreal(kernel->width()) / 2.0; } else if (output == xFall) { kernel = KisEdgeDetectionKernel::createHorizontalKernel(xRadius, type, denormalize, true); center = qreal(kernel->width()) / 2.0; } else if (output == yGrowth) { kernel = KisEdgeDetectionKernel::createVerticalKernel(yRadius, type, denormalize); center = qreal(kernel->height()) / 2.0; } else { //yFall kernel = KisEdgeDetectionKernel::createVerticalKernel(yRadius, type, denormalize, true); center = qreal(kernel->height()) / 2.0; } if (writeToAlpha) { KisPaintDeviceSP denormalised = new KisPaintDevice(device->colorSpace()); denormalised->prepareClone(device); KisConvolutionPainter kernelP(denormalised); kernelP.setChannelFlags(channelFlags); kernelP.setProgress(progressUpdater); kernelP.applyMatrix(kernel, device, srcTopLeft - QPoint(0, ceil(center)), srcTopLeft - QPoint(0, ceil(center)), rect.size() + QSize(0, 2 * ceil(center)), BORDER_REPEAT); KisSequentialIterator iterator(denormalised, rect); KisSequentialIterator finalIt(device, rect); const int pixelSize = device->colorSpace()->pixelSize(); const int channels = device->colorSpace()->colorChannelCount(); QVector normalised(channels); while (iterator.nextPixel() && finalIt.nextPixel()) { device->colorSpace()->normalisedChannelsValue(iterator.rawData(), normalised); KoColor col(finalIt.rawData(), device->colorSpace()); qreal alpha = 0; for (int c = 0; ccolorSpace()->setOpacity(finalIt.rawData(), 1.0, numConseqPixels); } } } } void KisEdgeDetectionKernel::convertToNormalMap(KisPaintDeviceSP device, const QRect &rect, qreal xRadius, qreal yRadius, KisEdgeDetectionKernel::FilterType type, int channelToConvert, QVector channelOrder, QVector channelFlip, const QBitArray &channelFlags, KoUpdater *progressUpdater) { QPoint srcTopLeft = rect.topLeft(); KisPainter finalPainter(device); finalPainter.setChannelFlags(channelFlags); finalPainter.setProgress(progressUpdater); KisPaintDeviceSP x_denormalised = new KisPaintDevice(device->colorSpace()); KisPaintDeviceSP y_denormalised = new KisPaintDevice(device->colorSpace()); x_denormalised->prepareClone(device); y_denormalised->prepareClone(device); KisConvolutionKernelSP kernelHorizLeftRight = KisEdgeDetectionKernel::createHorizontalKernel(yRadius, type, true, !channelFlip[1]); KisConvolutionKernelSP kernelVerticalTopBottom = KisEdgeDetectionKernel::createVerticalKernel(xRadius, type, true, !channelFlip[0]); qreal horizontalCenter = qreal(kernelHorizLeftRight->width()) / 2.0; qreal verticalCenter = qreal(kernelVerticalTopBottom->height()) / 2.0; KisConvolutionPainter horizPainterLR(y_denormalised); horizPainterLR.setChannelFlags(channelFlags); horizPainterLR.setProgress(progressUpdater); horizPainterLR.applyMatrix(kernelHorizLeftRight, device, srcTopLeft - QPoint(ceil(horizontalCenter), 0), srcTopLeft - QPoint(ceil(horizontalCenter), 0), rect.size() + QSize(2 * ceil(horizontalCenter), 0), BORDER_REPEAT); KisConvolutionPainter verticalPainterTB(x_denormalised); verticalPainterTB.setChannelFlags(channelFlags); verticalPainterTB.setProgress(progressUpdater); verticalPainterTB.applyMatrix(kernelVerticalTopBottom, device, srcTopLeft - QPoint(0, ceil(verticalCenter)), srcTopLeft - QPoint(0, ceil(verticalCenter)), rect.size() + QSize(0, 2 * ceil(verticalCenter)), BORDER_REPEAT); KisSequentialIterator yItterator(y_denormalised, rect); KisSequentialIterator xItterator(x_denormalised, rect); KisSequentialIterator finalIt(device, rect); const int pixelSize = device->colorSpace()->pixelSize(); const int channels = device->colorSpace()->channelCount(); const int alphaPos = device->colorSpace()->alphaPos(); KIS_SAFE_ASSERT_RECOVER_RETURN(alphaPos >= 0); QVector yNormalised(channels); QVector xNormalised(channels); QVector finalNorm(channels); while(yItterator.nextPixel() && xItterator.nextPixel() && finalIt.nextPixel()) { device->colorSpace()->normalisedChannelsValue(yItterator.rawData(), yNormalised); device->colorSpace()->normalisedChannelsValue(xItterator.rawData(), xNormalised); qreal z = 1.0; if (channelFlip[2]==true){ z=-1.0; } QVector3D normal = QVector3D((xNormalised[channelToConvert]-0.5)*2, (yNormalised[channelToConvert]-0.5)*2, z); normal.normalize(); finalNorm.fill(1.0); for (int c = 0; c<3; c++) { finalNorm[device->colorSpace()->channels().at(channelOrder[c])->displayPosition()] = (normal[channelOrder[c]]/2)+0.5; } finalNorm[alphaPos]= 1.0; quint8* pixel = finalIt.rawData(); device->colorSpace()->fromNormalisedChannelsValue(pixel, finalNorm); memcpy(finalIt.rawData(), pixel, pixelSize); } } diff --git a/libs/image/kis_fast_math.cpp b/libs/image/kis_fast_math.cpp index 4b6fe8df74..980f14cbce 100644 --- a/libs/image/kis_fast_math.cpp +++ b/libs/image/kis_fast_math.cpp @@ -1,132 +1,132 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * adopted from here http://www.snippetcenter.org/en/a-fast-atan2-function-s1868.aspx + * adopted from here http://web.archive.org/web/20090728150504/http://www.snippetcenter.org/en/a-fast-atan2-function-s1868.aspx */ #include "kis_fast_math.h" #include #include #include #include #include -// Algorithm from http://www.snippetcenter.org/en/a-fast-atan2-function-s1868.aspx +// Algorithm from http://web.archive.org/web/20090728150504/http://www.snippetcenter.org/en/a-fast-atan2-function-s1868.aspx const qreal MAX_SECOND_DERIV_IN_RANGE = 0.6495; /// precision const qreal MAX_ERROR = 0.0001; struct KisATanTable { KisATanTable() { qreal nf = ::sqrt(MAX_SECOND_DERIV_IN_RANGE / (8 * MAX_ERROR)); NUM_ATAN_ENTRIES = int(nf) + 1; // Build table qreal y = 10.0; qreal x; ATanTable = new qreal[NUM_ATAN_ENTRIES + 1]; ATanTable[0] = 0.0; for (quint32 i = 1; i <= NUM_ATAN_ENTRIES; i++) { x = (y / i) * NUM_ATAN_ENTRIES; ATanTable[i] = (qreal)::atan2(y, x); } } ~KisATanTable() { delete [] ATanTable; } quint32 NUM_ATAN_ENTRIES; qreal* ATanTable; }; Q_GLOBAL_STATIC(KisATanTable, kisATanTable) /// private functions inline qreal interp(qreal r, qreal a, qreal b) { return r*(b - a) + a; } inline qreal calcAngle(qreal x, qreal y) { static qreal af = kisATanTable->NUM_ATAN_ENTRIES; static int ai = kisATanTable->NUM_ATAN_ENTRIES; static qreal* ATanTable = kisATanTable->ATanTable; qreal di = (y / x) * af; int i = (int)(di); if (i >= ai) return ::atan2(y, x); return interp(di - i, ATanTable[i], ATanTable[i+1]); } qreal KisFastMath::atan2(qreal y, qreal x) { if (y == 0.0) { // the line is horizontal if (x >= 0.0) { // towards the right return(0.0);// the angle is 0 } // toward the left return qreal(M_PI); } // we now know that y is not 0 check x if (x == 0.0) { // the line is vertical if (y > 0.0) { return M_PI_2; } return -M_PI_2; } // from here on we know that niether x nor y is 0 if (x > 0.0) { // we are in quadrant 1 or 4 if (y > 0.0) { // we are in quadrant 1 // now figure out which side of the 45 degree line if (x > y) { return(calcAngle(x, y)); } return(M_PI_2 - calcAngle(y, x)); } // we are in quadrant 4 y = -y; // now figure out which side of the 45 degree line if (x > y) { return(-calcAngle(x, y)); } return(-M_PI_2 + calcAngle(y, x)); } // we are in quadrant 2 or 3 x = -x; // flip x so we can use it as a positive if (y > 0.0) { // we are in quadrant 2 // now figure out which side of the 45 degree line if (x > y) { return(M_PI - calcAngle(x, y)); } return(M_PI_2 + calcAngle(y, x)); } // we are in quadrant 3 y = -y; // flip y so we can use it as a positive // now figure out which side of the 45 degree line if (x > y) { return(-M_PI + calcAngle(x, y)); } return(-M_PI_2 - calcAngle(y, x)); } diff --git a/libs/image/kis_fast_math.h b/libs/image/kis_fast_math.h index a5f88008d3..fbf626a381 100644 --- a/libs/image/kis_fast_math.h +++ b/libs/image/kis_fast_math.h @@ -1,38 +1,38 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * adopted from here http://www.snippetcenter.org/en/a-fast-atan2-function-s1868.aspx + * adopted from here http://web.archive.org/web/20090728150504/http://www.snippetcenter.org/en/a-fast-atan2-function-s1868.aspx */ #ifndef _KIS_IMAGE_FAST_ #define _KIS_IMAGE_FAST_ #include #include "kritaimage_export.h" /** * This namespace contains fast but inaccurate version of mathematical function. */ namespace KisFastMath { /// atan2 replacement KRITAIMAGE_EXPORT qreal atan2(qreal y, qreal x); } #endif diff --git a/libs/image/kis_onion_skin_compositor.cpp b/libs/image/kis_onion_skin_compositor.cpp index 9e408005e7..b96609819c 100644 --- a/libs/image/kis_onion_skin_compositor.cpp +++ b/libs/image/kis_onion_skin_compositor.cpp @@ -1,227 +1,237 @@ /* * Copyright (c) 2015 Jouni Pentikäinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_onion_skin_compositor.h" #include "kis_paint_device.h" #include "kis_painter.h" #include "KoColor.h" #include "KoColorSpace.h" #include "KoCompositeOpRegistry.h" #include "KoColorSpaceConstants.h" #include "kis_image_config.h" #include "kis_raster_keyframe_channel.h" Q_GLOBAL_STATIC(KisOnionSkinCompositor, s_instance) struct KisOnionSkinCompositor::Private { int numberOfSkins = 0; int tintFactor = 0; QColor backwardTintColor; QColor forwardTintColor; QVector backwardOpacities; QVector forwardOpacities; int configSeqNo = 0; QList colorLabelFilter; int skinOpacity(int offset) { const QVector &bo = backwardOpacities; const QVector &fo = forwardOpacities; return offset > 0 ? fo[qAbs(offset) - 1] : bo[qAbs(offset) - 1]; } KisPaintDeviceSP setUpTintDevice(const QColor &tintColor, const KoColorSpace *colorSpace) { KisPaintDeviceSP tintDevice = new KisPaintDevice(colorSpace); KoColor color = KoColor(tintColor, colorSpace); tintDevice->setDefaultPixel(color); return tintDevice; } KisKeyframeSP getNextFrameToComposite(KisKeyframeChannel *channel, KisKeyframeSP keyframe, bool backwards) { while (!keyframe.isNull()) { keyframe = backwards ? channel->previousKeyframe(keyframe) : channel->nextKeyframe(keyframe); if (colorLabelFilter.isEmpty()) { return keyframe; } else if (!keyframe.isNull()) { if (colorLabelFilter.contains(keyframe->colorLabel())) { return keyframe; } } } return keyframe; } void tryCompositeFrame(KisRasterKeyframeChannel *keyframes, KisKeyframeSP keyframe, KisPainter &gcFrame, KisPainter &gcDest, KisPaintDeviceSP tintSource, int opacity, const QRect &rect) { if (keyframe.isNull() || opacity == OPACITY_TRANSPARENT_U8) return; keyframes->fetchFrame(keyframe, gcFrame.device()); gcFrame.bitBlt(rect.topLeft(), tintSource, rect); gcDest.setOpacity(opacity); gcDest.bitBlt(rect.topLeft(), gcFrame.device(), rect); } void refreshConfig() { KisImageConfig config(true); numberOfSkins = config.numberOfOnionSkins(); tintFactor = config.onionSkinTintFactor(); backwardTintColor = config.onionSkinTintColorBackward(); forwardTintColor = config.onionSkinTintColorForward(); backwardOpacities.resize(numberOfSkins); forwardOpacities.resize(numberOfSkins); const int mainState = (int) config.onionSkinState(0); const qreal scaleFactor = mainState * config.onionSkinOpacity(0) / 255.0; for (int i = 0; i < numberOfSkins; i++) { int backwardState = (int) config.onionSkinState(-(i + 1)); int forwardState = (int) config.onionSkinState(i + 1); backwardOpacities[i] = scaleFactor * backwardState * config.onionSkinOpacity(-(i + 1)); forwardOpacities[i] = scaleFactor * forwardState * config.onionSkinOpacity(i + 1); } configSeqNo++; } }; KisOnionSkinCompositor *KisOnionSkinCompositor::instance() { return s_instance; } KisOnionSkinCompositor::KisOnionSkinCompositor() : m_d(new Private) { m_d->refreshConfig(); } KisOnionSkinCompositor::~KisOnionSkinCompositor() {} int KisOnionSkinCompositor::configSeqNo() const { return m_d->configSeqNo; } void KisOnionSkinCompositor::setColorLabelFilter(QList colors) { m_d->colorLabelFilter = colors; } void KisOnionSkinCompositor::composite(const KisPaintDeviceSP sourceDevice, KisPaintDeviceSP targetDevice, const QRect& rect) { KisRasterKeyframeChannel *keyframes = sourceDevice->keyframeChannel(); KisPaintDeviceSP frameDevice = new KisPaintDevice(sourceDevice->colorSpace()); KisPainter gcFrame(frameDevice); QBitArray channelFlags = targetDevice->colorSpace()->channelFlags(true, false); gcFrame.setChannelFlags(channelFlags); gcFrame.setOpacity(m_d->tintFactor); KisPaintDeviceSP backwardTintDevice = m_d->setUpTintDevice(m_d->backwardTintColor, sourceDevice->colorSpace()); KisPaintDeviceSP forwardTintDevice = m_d->setUpTintDevice(m_d->forwardTintColor, sourceDevice->colorSpace()); KisPainter gcDest(targetDevice); gcDest.setCompositeOp(sourceDevice->colorSpace()->compositeOp(COMPOSITE_BEHIND)); KisKeyframeSP keyframeBck; KisKeyframeSP keyframeFwd; int time = sourceDevice->defaultBounds()->currentTime(); + + if (!keyframes) { // it happens when you try to show onion skins on non-animated layer with opacity keyframes + return; + } + keyframeBck = keyframeFwd = keyframes->activeKeyframeAt(time); for (int offset = 1; offset <= m_d->numberOfSkins; offset++) { keyframeBck = m_d->getNextFrameToComposite(keyframes, keyframeBck, true); keyframeFwd = m_d->getNextFrameToComposite(keyframes, keyframeFwd, false); if (!keyframeBck.isNull()) { m_d->tryCompositeFrame(keyframes, keyframeBck, gcFrame, gcDest, backwardTintDevice, m_d->skinOpacity(-offset), rect); } if (!keyframeFwd.isNull()) { m_d->tryCompositeFrame(keyframes, keyframeFwd, gcFrame, gcDest, forwardTintDevice, m_d->skinOpacity(offset), rect); } } } QRect KisOnionSkinCompositor::calculateFullExtent(const KisPaintDeviceSP device) { QRect rect; KisRasterKeyframeChannel *channel = device->keyframeChannel(); if (!channel) return rect; KisKeyframeSP keyframe = channel->firstKeyframe(); while (keyframe) { rect |= channel->frameExtents(keyframe); keyframe = channel->nextKeyframe(keyframe); } return rect; } QRect KisOnionSkinCompositor::calculateExtent(const KisPaintDeviceSP device) { QRect rect; KisKeyframeSP keyframeBck; KisKeyframeSP keyframeFwd; KisRasterKeyframeChannel *channel = device->keyframeChannel(); + + if (!channel) { // it happens when you try to show onion skins on non-animated layer with opacity keyframes + return rect; + } + keyframeBck = keyframeFwd = channel->activeKeyframeAt(device->defaultBounds()->currentTime()); for (int offset = 1; offset <= m_d->numberOfSkins; offset++) { if (!keyframeBck.isNull()) { keyframeBck = channel->previousKeyframe(keyframeBck); if (!keyframeBck.isNull()) { rect |= channel->frameExtents(keyframeBck); } } if (!keyframeFwd.isNull()) { keyframeFwd = channel->nextKeyframe(keyframeFwd); if (!keyframeFwd.isNull()) { rect |= channel->frameExtents(keyframeFwd); } } } return rect; } void KisOnionSkinCompositor::configChanged() { m_d->refreshConfig(); emit sigOnionSkinChanged(); } diff --git a/libs/image/kis_painter.cc b/libs/image/kis_painter.cc index 7e8cc21aef..473a69fade 100644 --- a/libs/image/kis_painter.cc +++ b/libs/image/kis_painter.cc @@ -1,3014 +1,3022 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2004 Adrian Page * Copyright (c) 2004 Cyrille Berger * Copyright (c) 2008-2010 Lukáš Tvrdý * Copyright (c) 2010 José Luis Vergara Toloza * Copyright (c) 2011 Silvio Heinrich * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_painter.h" #include #include #include #include #include #ifndef Q_OS_WIN #include #endif #include #include #include #include #include #include #include #include "kis_image.h" #include "filter/kis_filter.h" #include "kis_layer.h" #include "kis_paint_device.h" #include "kis_fixed_paint_device.h" #include "kis_transaction.h" #include "kis_vec.h" #include "kis_iterator_ng.h" #include "kis_random_accessor_ng.h" #include "filter/kis_filter_configuration.h" #include "kis_pixel_selection.h" #include #include "kis_paintop_registry.h" #include "kis_perspective_math.h" #include "tiles3/kis_random_accessor.h" #include #include #include "kis_lod_transform.h" #include "kis_algebra_2d.h" #include "krita_utils.h" // Maximum distance from a Bezier control point to the line through the start // and end points for the curve to be considered flat. #define BEZIER_FLATNESS_THRESHOLD 0.5 #include "kis_painter_p.h" KisPainter::KisPainter() : d(new Private(this)) { init(); } KisPainter::KisPainter(KisPaintDeviceSP device) : d(new Private(this, device->colorSpace())) { init(); Q_ASSERT(device); begin(device); } KisPainter::KisPainter(KisPaintDeviceSP device, KisSelectionSP selection) : d(new Private(this, device->colorSpace())) { init(); Q_ASSERT(device); begin(device); d->selection = selection; } void KisPainter::init() { d->selection = 0 ; d->transaction = 0; d->paintOp = 0; d->pattern = 0; d->sourceLayer = 0; d->fillStyle = FillStyleNone; d->strokeStyle = StrokeStyleBrush; d->antiAliasPolygonFill = true; d->progressUpdater = 0; d->gradient = 0; d->maskPainter = 0; d->fillPainter = 0; d->maskImageWidth = 255; d->maskImageHeight = 255; d->mirrorHorizontally = false; d->mirrorVertically = false; d->isOpacityUnit = true; d->paramInfo = KoCompositeOp::ParameterInfo(); d->renderingIntent = KoColorConversionTransformation::internalRenderingIntent(); d->conversionFlags = KoColorConversionTransformation::internalConversionFlags(); } KisPainter::~KisPainter() { // TODO: Maybe, don't be that strict? // deleteTransaction(); end(); delete d->paintOp; delete d->maskPainter; delete d->fillPainter; delete d; } template void copyAreaOptimizedImpl(const QPoint &dstPt, KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect &srcRect) { const QRect dstRect(dstPt, srcRect.size()); const QRect srcExtent = src->extent(); const QRect dstExtent = dst->extent(); const QRect srcSampleRect = srcExtent & srcRect; const QRect dstSampleRect = dstExtent & dstRect; const bool srcEmpty = srcSampleRect.isEmpty(); const bool dstEmpty = dstSampleRect.isEmpty(); if (!srcEmpty || !dstEmpty) { if (srcEmpty) { dst->clear(dstRect); } else { QRect srcCopyRect = srcRect; QRect dstCopyRect = dstRect; if (!srcExtent.contains(srcRect)) { if (src->defaultPixel() == dst->defaultPixel()) { const QRect dstSampleInSrcCoords = dstSampleRect.translated(srcRect.topLeft() - dstPt); if (dstSampleInSrcCoords.isEmpty() || srcSampleRect.contains(dstSampleInSrcCoords)) { srcCopyRect = srcSampleRect; } else { srcCopyRect = srcSampleRect | dstSampleInSrcCoords; } dstCopyRect = QRect(dstPt + srcCopyRect.topLeft() - srcRect.topLeft(), srcCopyRect.size()); } } KisPainter gc(dst); gc.setCompositeOp(dst->colorSpace()->compositeOp(COMPOSITE_COPY)); if (useOldData) { gc.bitBltOldData(dstCopyRect.topLeft(), src, srcCopyRect); } else { gc.bitBlt(dstCopyRect.topLeft(), src, srcCopyRect); } } } } void KisPainter::copyAreaOptimized(const QPoint &dstPt, KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect &srcRect) { copyAreaOptimizedImpl(dstPt, src, dst, srcRect); } void KisPainter::copyAreaOptimizedOldData(const QPoint &dstPt, KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect &srcRect) { copyAreaOptimizedImpl(dstPt, src, dst, srcRect); } void KisPainter::copyAreaOptimized(const QPoint &dstPt, KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect &originalSrcRect, KisSelectionSP selection) { if (!selection) { copyAreaOptimized(dstPt, src, dst, originalSrcRect); return; } const QRect selectionRect = selection->selectedRect(); const QRect srcRect = originalSrcRect & selectionRect; const QPoint dstOffset = srcRect.topLeft() - originalSrcRect.topLeft(); const QRect dstRect = QRect(dstPt + dstOffset, srcRect.size()); const bool srcEmpty = (src->extent() & srcRect).isEmpty(); const bool dstEmpty = (dst->extent() & dstRect).isEmpty(); if (!srcEmpty || !dstEmpty) { //if (srcEmpty) { // doesn't support dstRect // dst->clearSelection(selection); // } else */ { KisPainter gc(dst); gc.setSelection(selection); gc.setCompositeOp(dst->colorSpace()->compositeOp(COMPOSITE_COPY)); gc.bitBlt(dstRect.topLeft(), src, srcRect); } } } KisPaintDeviceSP KisPainter::convertToAlphaAsAlpha(KisPaintDeviceSP src) { const KoColorSpace *srcCS = src->colorSpace(); const QRect processRect = src->extent(); KisPaintDeviceSP dst(new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8())); if (processRect.isEmpty()) return dst; KisSequentialConstIterator srcIt(src, processRect); KisSequentialIterator dstIt(dst, processRect); while (srcIt.nextPixel() && dstIt.nextPixel()) { const quint8 *srcPtr = srcIt.rawDataConst(); quint8 *alpha8Ptr = dstIt.rawData(); const quint8 white = srcCS->intensity8(srcPtr); const quint8 alpha = srcCS->opacityU8(srcPtr); *alpha8Ptr = KoColorSpaceMaths::multiply(alpha, KoColorSpaceMathsTraits::unitValue - white); } return dst; } KisPaintDeviceSP KisPainter::convertToAlphaAsGray(KisPaintDeviceSP src) { const KoColorSpace *srcCS = src->colorSpace(); const QRect processRect = src->extent(); KisPaintDeviceSP dst(new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8())); if (processRect.isEmpty()) return dst; KisSequentialConstIterator srcIt(src, processRect); KisSequentialIterator dstIt(dst, processRect); while (srcIt.nextPixel() && dstIt.nextPixel()) { const quint8 *srcPtr = srcIt.rawDataConst(); quint8 *alpha8Ptr = dstIt.rawData(); *alpha8Ptr = srcCS->intensity8(srcPtr); } return dst; } bool KisPainter::checkDeviceHasTransparency(KisPaintDeviceSP dev) { const QRect deviceBounds = dev->exactBounds(); const QRect imageBounds = dev->defaultBounds()->bounds(); if (deviceBounds.isEmpty() || (deviceBounds & imageBounds) != imageBounds) { return true; } const KoColorSpace *cs = dev->colorSpace(); KisSequentialConstIterator it(dev, deviceBounds); while(it.nextPixel()) { if (cs->opacityU8(it.rawDataConst()) != OPACITY_OPAQUE_U8) { return true; } } return false; } void KisPainter::begin(KisPaintDeviceSP device) { begin(device, d->selection); } void KisPainter::begin(KisPaintDeviceSP device, KisSelectionSP selection) { if (!device) return; d->selection = selection; Q_ASSERT(device->colorSpace()); end(); d->device = device; d->colorSpace = device->colorSpace(); d->compositeOp = d->colorSpace->compositeOp(COMPOSITE_OVER); d->pixelSize = device->pixelSize(); } void KisPainter::end() { Q_ASSERT_X(!d->transaction, "KisPainter::end()", "end() was called for the painter having a transaction. " "Please use end/deleteTransaction() instead"); } void KisPainter::beginTransaction(const KUndo2MagicString& transactionName,int timedID) { Q_ASSERT_X(!d->transaction, "KisPainter::beginTransaction()", "You asked for a new transaction while still having " "another one. Please finish the first one with " "end/deleteTransaction() first"); d->transaction = new KisTransaction(transactionName, d->device); Q_CHECK_PTR(d->transaction); d->transaction->undoCommand()->setTimedID(timedID); } void KisPainter::revertTransaction() { Q_ASSERT_X(d->transaction, "KisPainter::revertTransaction()", "No transaction is in progress"); d->transaction->revert(); delete d->transaction; d->transaction = 0; } void KisPainter::endTransaction(KisUndoAdapter *undoAdapter) { Q_ASSERT_X(d->transaction, "KisPainter::endTransaction()", "No transaction is in progress"); d->transaction->commit(undoAdapter); delete d->transaction; d->transaction = 0; } void KisPainter::endTransaction(KisPostExecutionUndoAdapter *undoAdapter) { Q_ASSERT_X(d->transaction, "KisPainter::endTransaction()", "No transaction is in progress"); d->transaction->commit(undoAdapter); delete d->transaction; d->transaction = 0; } KUndo2Command* KisPainter::endAndTakeTransaction() { Q_ASSERT_X(d->transaction, "KisPainter::endTransaction()", "No transaction is in progress"); KUndo2Command *transactionData = d->transaction->endAndTake(); delete d->transaction; d->transaction = 0; return transactionData; } void KisPainter::deleteTransaction() { if (!d->transaction) return; delete d->transaction; d->transaction = 0; } void KisPainter::putTransaction(KisTransaction* transaction) { Q_ASSERT_X(!d->transaction, "KisPainter::putTransaction()", "You asked for a new transaction while still having " "another one. Please finish the first one with " "end/deleteTransaction() first"); d->transaction = transaction; } KisTransaction* KisPainter::takeTransaction() { Q_ASSERT_X(d->transaction, "KisPainter::takeTransaction()", "No transaction is in progress"); KisTransaction *temp = d->transaction; d->transaction = 0; return temp; } QVector KisPainter::takeDirtyRegion() { QVector vrect = d->dirtyRects; d->dirtyRects.clear(); return vrect; } void KisPainter::addDirtyRect(const QRect & rc) { QRect r = rc.normalized(); if (r.isValid()) { d->dirtyRects.append(rc); } } void KisPainter::addDirtyRects(const QVector &rects) { d->dirtyRects.reserve(d->dirtyRects.size() + rects.size()); Q_FOREACH (const QRect &rc, rects) { const QRect r = rc.normalized(); if (r.isValid()) { d->dirtyRects.append(rc); } } } inline bool KisPainter::Private::tryReduceSourceRect(const KisPaintDevice *srcDev, QRect *srcRect, qint32 *srcX, qint32 *srcY, qint32 *srcWidth, qint32 *srcHeight, qint32 *dstX, qint32 *dstY) { /** * In case of COMPOSITE_COPY and Wrap Around Mode even the pixels * outside the device extent matter, because they will be either * directly copied (former case) or cloned from another area of * the image. */ if (compositeOp->id() != COMPOSITE_COPY && compositeOp->id() != COMPOSITE_DESTINATION_IN && compositeOp->id() != COMPOSITE_DESTINATION_ATOP && !srcDev->defaultBounds()->wrapAroundMode()) { /** * If srcDev->extent() (the area of the tiles containing * srcDev) is smaller than srcRect, then shrink srcRect to * that size. This is done as a speed optimization, useful for * stack recomposition in KisImage. srcRect won't grow if * srcDev->extent() is larger. */ *srcRect &= srcDev->extent(); if (srcRect->isEmpty()) return true; // Readjust the function paramenters to the new dimensions. *dstX += srcRect->x() - *srcX; // This will only add, not subtract *dstY += srcRect->y() - *srcY; // Idem srcRect->getRect(srcX, srcY, srcWidth, srcHeight); } return false; } void KisPainter::bitBltWithFixedSelection(qint32 dstX, qint32 dstY, const KisPaintDeviceSP srcDev, const KisFixedPaintDeviceSP selection, qint32 selX, qint32 selY, qint32 srcX, qint32 srcY, qint32 srcWidth, qint32 srcHeight) { // TODO: get selX and selY working as intended /* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just initializing they perform some dummy passes with those parameters, and it must not crash */ if (srcWidth == 0 || srcHeight == 0) return; if (srcDev.isNull()) return; if (d->device.isNull()) return; // Check that selection has an alpha colorspace, crash if false Q_ASSERT(selection->colorSpace() == KoColorSpaceRegistry::instance()->alpha8()); QRect srcRect = QRect(srcX, srcY, srcWidth, srcHeight); QRect selRect = QRect(selX, selY, srcWidth, srcHeight); /* Trying to read outside a KisFixedPaintDevice is inherently wrong and shouldn't be done, so crash if someone attempts to do this. Don't resize YET as it would obfuscate the mistake. */ Q_ASSERT(selection->bounds().contains(selRect)); Q_UNUSED(selRect); // only used by the above Q_ASSERT /** * An optimization, which crops the source rect by the bounds of * the source device when it is possible */ if (d->tryReduceSourceRect(srcDev, &srcRect, &srcX, &srcY, &srcWidth, &srcHeight, &dstX, &dstY)) return; /* Create an intermediate byte array to hold information before it is written to the current paint device (d->device) */ quint8* dstBytes = 0; try { dstBytes = new quint8[srcWidth * srcHeight * d->device->pixelSize()]; } catch (const std::bad_alloc&) { warnKrita << "KisPainter::bitBltWithFixedSelection std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "dst bytes"; return; } d->device->readBytes(dstBytes, dstX, dstY, srcWidth, srcHeight); // Copy the relevant bytes of raw data from srcDev quint8* srcBytes = 0; try { srcBytes = new quint8[srcWidth * srcHeight * srcDev->pixelSize()]; } catch (const std::bad_alloc&) { warnKrita << "KisPainter::bitBltWithFixedSelection std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "src bytes"; return; } srcDev->readBytes(srcBytes, srcX, srcY, srcWidth, srcHeight); QRect selBounds = selection->bounds(); const quint8 *selRowStart = selection->data() + (selBounds.width() * (selY - selBounds.top()) + (selX - selBounds.left())) * selection->pixelSize(); /* * This checks whether there is nothing selected. */ if (!d->selection) { /* As there's nothing selected, blit to dstBytes (intermediary bit array), ignoring d->selection (the user selection)*/ d->paramInfo.dstRowStart = dstBytes; d->paramInfo.dstRowStride = srcWidth * d->device->pixelSize(); d->paramInfo.srcRowStart = srcBytes; d->paramInfo.srcRowStride = srcWidth * srcDev->pixelSize(); d->paramInfo.maskRowStart = selRowStart; d->paramInfo.maskRowStride = selBounds.width() * selection->pixelSize(); d->paramInfo.rows = srcHeight; d->paramInfo.cols = srcWidth; d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags); } else { /* Read the user selection (d->selection) bytes into an array, ready to merge in the next block*/ quint32 totalBytes = srcWidth * srcHeight * selection->pixelSize(); quint8* mergedSelectionBytes = 0; try { mergedSelectionBytes = new quint8[ totalBytes ]; } catch (const std::bad_alloc&) { warnKrita << "KisPainter::bitBltWithFixedSelection std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "total bytes"; return; } d->selection->projection()->readBytes(mergedSelectionBytes, dstX, dstY, srcWidth, srcHeight); // Merge selections here by multiplying them - compositeOP(COMPOSITE_MULT) d->paramInfo.dstRowStart = mergedSelectionBytes; d->paramInfo.dstRowStride = srcWidth * selection->pixelSize(); d->paramInfo.srcRowStart = selRowStart; d->paramInfo.srcRowStride = selBounds.width() * selection->pixelSize(); d->paramInfo.maskRowStart = 0; d->paramInfo.maskRowStride = 0; d->paramInfo.rows = srcHeight; d->paramInfo.cols = srcWidth; KoColorSpaceRegistry::instance()->alpha8()->compositeOp(COMPOSITE_MULT)->composite(d->paramInfo); // Blit to dstBytes (intermediary bit array) d->paramInfo.dstRowStart = dstBytes; d->paramInfo.dstRowStride = srcWidth * d->device->pixelSize(); d->paramInfo.srcRowStart = srcBytes; d->paramInfo.srcRowStride = srcWidth * srcDev->pixelSize(); d->paramInfo.maskRowStart = mergedSelectionBytes; d->paramInfo.maskRowStride = srcWidth * selection->pixelSize(); d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags); delete[] mergedSelectionBytes; } d->device->writeBytes(dstBytes, dstX, dstY, srcWidth, srcHeight); delete[] dstBytes; delete[] srcBytes; addDirtyRect(QRect(dstX, dstY, srcWidth, srcHeight)); } void KisPainter::bitBltWithFixedSelection(qint32 dstX, qint32 dstY, const KisPaintDeviceSP srcDev, const KisFixedPaintDeviceSP selection, qint32 srcWidth, qint32 srcHeight) { bitBltWithFixedSelection(dstX, dstY, srcDev, selection, 0, 0, 0, 0, srcWidth, srcHeight); } template void KisPainter::bitBltImpl(qint32 dstX, qint32 dstY, const KisPaintDeviceSP srcDev, qint32 srcX, qint32 srcY, qint32 srcWidth, qint32 srcHeight) { /* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just initializing they perform some dummy passes with those parameters, and it must not crash */ if (srcWidth == 0 || srcHeight == 0) return; if (srcDev.isNull()) return; if (d->device.isNull()) return; QRect srcRect = QRect(srcX, srcY, srcWidth, srcHeight); if (d->compositeOp->id() == COMPOSITE_COPY) { if(!d->selection && d->isOpacityUnit && srcX == dstX && srcY == dstY && d->device->fastBitBltPossible(srcDev)) { if(useOldSrcData) { d->device->fastBitBltOldData(srcDev, srcRect); } else { d->device->fastBitBlt(srcDev, srcRect); } addDirtyRect(srcRect); return; } } else { /** * An optimization, which crops the source rect by the bounds of * the source device when it is possible */ if (d->tryReduceSourceRect(srcDev, &srcRect, &srcX, &srcY, &srcWidth, &srcHeight, &dstX, &dstY)) return; } qint32 dstY_ = dstY; qint32 srcY_ = srcY; qint32 rowsRemaining = srcHeight; // Read below KisRandomConstAccessorSP srcIt = srcDev->createRandomConstAccessorNG(srcX, srcY); KisRandomAccessorSP dstIt = d->device->createRandomAccessorNG(dstX, dstY); /* Here be a huge block of verbose code that does roughly the same than the other bit blit operations. This one is longer than the rest in an effort to optimize speed and memory use */ if (d->selection) { KisPaintDeviceSP selectionProjection(d->selection->projection()); KisRandomConstAccessorSP maskIt = selectionProjection->createRandomConstAccessorNG(dstX, dstY); while (rowsRemaining > 0) { qint32 dstX_ = dstX; qint32 srcX_ = srcX; qint32 columnsRemaining = srcWidth; qint32 numContiguousDstRows = dstIt->numContiguousRows(dstY_); qint32 numContiguousSrcRows = srcIt->numContiguousRows(srcY_); qint32 numContiguousSelRows = maskIt->numContiguousRows(dstY_); qint32 rows = qMin(numContiguousDstRows, numContiguousSrcRows); rows = qMin(rows, numContiguousSelRows); rows = qMin(rows, rowsRemaining); while (columnsRemaining > 0) { qint32 numContiguousDstColumns = dstIt->numContiguousColumns(dstX_); qint32 numContiguousSrcColumns = srcIt->numContiguousColumns(srcX_); qint32 numContiguousSelColumns = maskIt->numContiguousColumns(dstX_); qint32 columns = qMin(numContiguousDstColumns, numContiguousSrcColumns); columns = qMin(columns, numContiguousSelColumns); columns = qMin(columns, columnsRemaining); qint32 srcRowStride = srcIt->rowStride(srcX_, srcY_); srcIt->moveTo(srcX_, srcY_); qint32 dstRowStride = dstIt->rowStride(dstX_, dstY_); dstIt->moveTo(dstX_, dstY_); qint32 maskRowStride = maskIt->rowStride(dstX_, dstY_); maskIt->moveTo(dstX_, dstY_); d->paramInfo.dstRowStart = dstIt->rawData(); d->paramInfo.dstRowStride = dstRowStride; // if we don't use the oldRawData, we need to access the rawData of the source device. d->paramInfo.srcRowStart = useOldSrcData ? srcIt->oldRawData() : static_cast(srcIt.data())->rawData(); d->paramInfo.srcRowStride = srcRowStride; d->paramInfo.maskRowStart = static_cast(maskIt.data())->rawData(); d->paramInfo.maskRowStride = maskRowStride; d->paramInfo.rows = rows; d->paramInfo.cols = columns; d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags); srcX_ += columns; dstX_ += columns; columnsRemaining -= columns; } srcY_ += rows; dstY_ += rows; rowsRemaining -= rows; } } else { while (rowsRemaining > 0) { qint32 dstX_ = dstX; qint32 srcX_ = srcX; qint32 columnsRemaining = srcWidth; qint32 numContiguousDstRows = dstIt->numContiguousRows(dstY_); qint32 numContiguousSrcRows = srcIt->numContiguousRows(srcY_); qint32 rows = qMin(numContiguousDstRows, numContiguousSrcRows); rows = qMin(rows, rowsRemaining); while (columnsRemaining > 0) { qint32 numContiguousDstColumns = dstIt->numContiguousColumns(dstX_); qint32 numContiguousSrcColumns = srcIt->numContiguousColumns(srcX_); qint32 columns = qMin(numContiguousDstColumns, numContiguousSrcColumns); columns = qMin(columns, columnsRemaining); qint32 srcRowStride = srcIt->rowStride(srcX_, srcY_); srcIt->moveTo(srcX_, srcY_); qint32 dstRowStride = dstIt->rowStride(dstX_, dstY_); dstIt->moveTo(dstX_, dstY_); d->paramInfo.dstRowStart = dstIt->rawData(); d->paramInfo.dstRowStride = dstRowStride; // if we don't use the oldRawData, we need to access the rawData of the source device. d->paramInfo.srcRowStart = useOldSrcData ? srcIt->oldRawData() : static_cast(srcIt.data())->rawData(); d->paramInfo.srcRowStride = srcRowStride; d->paramInfo.maskRowStart = 0; d->paramInfo.maskRowStride = 0; d->paramInfo.rows = rows; d->paramInfo.cols = columns; d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags); srcX_ += columns; dstX_ += columns; columnsRemaining -= columns; } srcY_ += rows; dstY_ += rows; rowsRemaining -= rows; } } addDirtyRect(QRect(dstX, dstY, srcWidth, srcHeight)); } void KisPainter::bitBlt(qint32 dstX, qint32 dstY, const KisPaintDeviceSP srcDev, qint32 srcX, qint32 srcY, qint32 srcWidth, qint32 srcHeight) { bitBltImpl(dstX, dstY, srcDev, srcX, srcY, srcWidth, srcHeight); } void KisPainter::bitBlt(const QPoint & pos, const KisPaintDeviceSP srcDev, const QRect & srcRect) { bitBlt(pos.x(), pos.y(), srcDev, srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height()); } void KisPainter::bitBltOldData(qint32 dstX, qint32 dstY, const KisPaintDeviceSP srcDev, qint32 srcX, qint32 srcY, qint32 srcWidth, qint32 srcHeight) { bitBltImpl(dstX, dstY, srcDev, srcX, srcY, srcWidth, srcHeight); } void KisPainter::bitBltOldData(const QPoint & pos, const KisPaintDeviceSP srcDev, const QRect & srcRect) { bitBltOldData(pos.x(), pos.y(), srcDev, srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height()); } void KisPainter::fill(qint32 x, qint32 y, qint32 width, qint32 height, const KoColor& color) { /* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just * initializing they perform some dummy passes with those parameters, and it must not crash */ if(width == 0 || height == 0 || d->device.isNull()) return; KoColor srcColor(color, d->device->compositionSourceColorSpace()); qint32 dstY = y; qint32 rowsRemaining = height; KisRandomAccessorSP dstIt = d->device->createRandomAccessorNG(x, y); if(d->selection) { KisPaintDeviceSP selectionProjection(d->selection->projection()); KisRandomConstAccessorSP maskIt = selectionProjection->createRandomConstAccessorNG(x, y); while(rowsRemaining > 0) { qint32 dstX = x; qint32 columnsRemaining = width; qint32 numContiguousDstRows = dstIt->numContiguousRows(dstY); qint32 numContiguousSelRows = maskIt->numContiguousRows(dstY); qint32 rows = qMin(numContiguousDstRows, numContiguousSelRows); rows = qMin(rows, rowsRemaining); while (columnsRemaining > 0) { qint32 numContiguousDstColumns = dstIt->numContiguousColumns(dstX); qint32 numContiguousSelColumns = maskIt->numContiguousColumns(dstX); qint32 columns = qMin(numContiguousDstColumns, numContiguousSelColumns); columns = qMin(columns, columnsRemaining); qint32 dstRowStride = dstIt->rowStride(dstX, dstY); dstIt->moveTo(dstX, dstY); qint32 maskRowStride = maskIt->rowStride(dstX, dstY); maskIt->moveTo(dstX, dstY); d->paramInfo.dstRowStart = dstIt->rawData(); d->paramInfo.dstRowStride = dstRowStride; d->paramInfo.srcRowStart = srcColor.data(); d->paramInfo.srcRowStride = 0; // srcRowStride is set to zero to use the compositeOp with only a single color pixel d->paramInfo.maskRowStart = maskIt->oldRawData(); d->paramInfo.maskRowStride = maskRowStride; d->paramInfo.rows = rows; d->paramInfo.cols = columns; d->colorSpace->bitBlt(srcColor.colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags); dstX += columns; columnsRemaining -= columns; } dstY += rows; rowsRemaining -= rows; } } else { while(rowsRemaining > 0) { qint32 dstX = x; qint32 columnsRemaining = width; qint32 numContiguousDstRows = dstIt->numContiguousRows(dstY); qint32 rows = qMin(numContiguousDstRows, rowsRemaining); while(columnsRemaining > 0) { qint32 numContiguousDstColumns = dstIt->numContiguousColumns(dstX); qint32 columns = qMin(numContiguousDstColumns, columnsRemaining); qint32 dstRowStride = dstIt->rowStride(dstX, dstY); dstIt->moveTo(dstX, dstY); d->paramInfo.dstRowStart = dstIt->rawData(); d->paramInfo.dstRowStride = dstRowStride; d->paramInfo.srcRowStart = srcColor.data(); d->paramInfo.srcRowStride = 0; // srcRowStride is set to zero to use the compositeOp with only a single color pixel d->paramInfo.maskRowStart = 0; d->paramInfo.maskRowStride = 0; d->paramInfo.rows = rows; d->paramInfo.cols = columns; d->colorSpace->bitBlt(srcColor.colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags); dstX += columns; columnsRemaining -= columns; } dstY += rows; rowsRemaining -= rows; } } addDirtyRect(QRect(x, y, width, height)); } void KisPainter::bltFixed(qint32 dstX, qint32 dstY, const KisFixedPaintDeviceSP srcDev, qint32 srcX, qint32 srcY, qint32 srcWidth, qint32 srcHeight) { /* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just initializing they perform some dummy passes with those parameters, and it must not crash */ if (srcWidth == 0 || srcHeight == 0) return; if (srcDev.isNull()) return; if (d->device.isNull()) return; QRect srcRect = QRect(srcX, srcY, srcWidth, srcHeight); QRect srcBounds = srcDev->bounds(); /* Trying to read outside a KisFixedPaintDevice is inherently wrong and shouldn't be done, so crash if someone attempts to do this. Don't resize as it would obfuscate the mistake. */ KIS_SAFE_ASSERT_RECOVER_RETURN(srcBounds.contains(srcRect)); Q_UNUSED(srcRect); // only used in above assertion /* Create an intermediate byte array to hold information before it is written to the current paint device (aka: d->device) */ quint8* dstBytes = 0; try { dstBytes = new quint8[srcWidth * srcHeight * d->device->pixelSize()]; } catch (const std::bad_alloc&) { warnKrita << "KisPainter::bltFixed std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "total bytes"; return; } d->device->readBytes(dstBytes, dstX, dstY, srcWidth, srcHeight); const quint8 *srcRowStart = srcDev->data() + (srcBounds.width() * (srcY - srcBounds.top()) + (srcX - srcBounds.left())) * srcDev->pixelSize(); d->paramInfo.dstRowStart = dstBytes; d->paramInfo.dstRowStride = srcWidth * d->device->pixelSize(); d->paramInfo.srcRowStart = srcRowStart; d->paramInfo.srcRowStride = srcBounds.width() * srcDev->pixelSize(); d->paramInfo.maskRowStart = 0; d->paramInfo.maskRowStride = 0; d->paramInfo.rows = srcHeight; d->paramInfo.cols = srcWidth; if (d->selection) { /* d->selection is a KisPaintDevice, so first a readBytes is performed to get the area of interest... */ KisPaintDeviceSP selectionProjection(d->selection->projection()); quint8* selBytes = 0; try { selBytes = new quint8[srcWidth * srcHeight * selectionProjection->pixelSize()]; } catch (const std::bad_alloc&) { delete[] dstBytes; return; } selectionProjection->readBytes(selBytes, dstX, dstY, srcWidth, srcHeight); d->paramInfo.maskRowStart = selBytes; d->paramInfo.maskRowStride = srcWidth * selectionProjection->pixelSize(); } // ...and then blit. d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags); d->device->writeBytes(dstBytes, dstX, dstY, srcWidth, srcHeight); delete[] d->paramInfo.maskRowStart; delete[] dstBytes; addDirtyRect(QRect(dstX, dstY, srcWidth, srcHeight)); } void KisPainter::bltFixed(const QPoint & pos, const KisFixedPaintDeviceSP srcDev, const QRect & srcRect) { bltFixed(pos.x(), pos.y(), srcDev, srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height()); } void KisPainter::bltFixedWithFixedSelection(qint32 dstX, qint32 dstY, const KisFixedPaintDeviceSP srcDev, const KisFixedPaintDeviceSP selection, qint32 selX, qint32 selY, qint32 srcX, qint32 srcY, quint32 srcWidth, quint32 srcHeight) { // TODO: get selX and selY working as intended /* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just initializing they perform some dummy passes with those parameters, and it must not crash */ if (srcWidth == 0 || srcHeight == 0) return; if (srcDev.isNull()) return; if (d->device.isNull()) return; // Check that selection has an alpha colorspace, crash if false Q_ASSERT(selection->colorSpace() == KoColorSpaceRegistry::instance()->alpha8()); QRect srcRect = QRect(srcX, srcY, srcWidth, srcHeight); QRect selRect = QRect(selX, selY, srcWidth, srcHeight); QRect srcBounds = srcDev->bounds(); QRect selBounds = selection->bounds(); /* Trying to read outside a KisFixedPaintDevice is inherently wrong and shouldn't be done, so crash if someone attempts to do this. Don't resize as it would obfuscate the mistake. */ Q_ASSERT(srcBounds.contains(srcRect)); Q_UNUSED(srcRect); // only used in above assertion Q_ASSERT(selBounds.contains(selRect)); Q_UNUSED(selRect); // only used in above assertion /* Create an intermediate byte array to hold information before it is written to the current paint device (aka: d->device) */ quint8* dstBytes = 0; try { dstBytes = new quint8[srcWidth * srcHeight * d->device->pixelSize()]; } catch (const std::bad_alloc&) { warnKrita << "KisPainter::bltFixedWithFixedSelection std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "total bytes"; return; } d->device->readBytes(dstBytes, dstX, dstY, srcWidth, srcHeight); const quint8 *srcRowStart = srcDev->data() + (srcBounds.width() * (srcY - srcBounds.top()) + (srcX - srcBounds.left())) * srcDev->pixelSize(); const quint8 *selRowStart = selection->data() + (selBounds.width() * (selY - selBounds.top()) + (selX - selBounds.left())) * selection->pixelSize(); if (!d->selection) { /* As there's nothing selected, blit to dstBytes (intermediary bit array), ignoring d->selection (the user selection)*/ d->paramInfo.dstRowStart = dstBytes; d->paramInfo.dstRowStride = srcWidth * d->device->pixelSize(); d->paramInfo.srcRowStart = srcRowStart; d->paramInfo.srcRowStride = srcBounds.width() * srcDev->pixelSize(); d->paramInfo.maskRowStart = selRowStart; d->paramInfo.maskRowStride = selBounds.width() * selection->pixelSize(); d->paramInfo.rows = srcHeight; d->paramInfo.cols = srcWidth; d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags); } else { /* Read the user selection (d->selection) bytes into an array, ready to merge in the next block*/ quint32 totalBytes = srcWidth * srcHeight * selection->pixelSize(); quint8 * mergedSelectionBytes = 0; try { mergedSelectionBytes = new quint8[ totalBytes ]; } catch (const std::bad_alloc&) { warnKrita << "KisPainter::bltFixedWithFixedSelection std::bad_alloc for " << totalBytes << "total bytes"; delete[] dstBytes; return; } d->selection->projection()->readBytes(mergedSelectionBytes, dstX, dstY, srcWidth, srcHeight); // Merge selections here by multiplying them - compositeOp(COMPOSITE_MULT) d->paramInfo.dstRowStart = mergedSelectionBytes; d->paramInfo.dstRowStride = srcWidth * selection->pixelSize(); d->paramInfo.srcRowStart = selRowStart; d->paramInfo.srcRowStride = selBounds.width() * selection->pixelSize(); d->paramInfo.maskRowStart = 0; d->paramInfo.maskRowStride = 0; d->paramInfo.rows = srcHeight; d->paramInfo.cols = srcWidth; KoColorSpaceRegistry::instance()->alpha8()->compositeOp(COMPOSITE_MULT)->composite(d->paramInfo); // Blit to dstBytes (intermediary bit array) d->paramInfo.dstRowStart = dstBytes; d->paramInfo.dstRowStride = srcWidth * d->device->pixelSize(); d->paramInfo.srcRowStart = srcRowStart; d->paramInfo.srcRowStride = srcBounds.width() * srcDev->pixelSize(); d->paramInfo.maskRowStart = mergedSelectionBytes; d->paramInfo.maskRowStride = srcWidth * selection->pixelSize(); d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags); delete[] mergedSelectionBytes; } d->device->writeBytes(dstBytes, dstX, dstY, srcWidth, srcHeight); delete[] dstBytes; addDirtyRect(QRect(dstX, dstY, srcWidth, srcHeight)); } void KisPainter::bltFixedWithFixedSelection(qint32 dstX, qint32 dstY, const KisFixedPaintDeviceSP srcDev, const KisFixedPaintDeviceSP selection, quint32 srcWidth, quint32 srcHeight) { bltFixedWithFixedSelection(dstX, dstY, srcDev, selection, 0, 0, 0, 0, srcWidth, srcHeight); } void KisPainter::paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) { if (d->device && d->paintOp && d->paintOp->canPaint()) { d->paintOp->paintLine(pi1, pi2, currentDistance); } } void KisPainter::paintPolyline(const vQPointF &points, int index, int numPoints) { if (d->fillStyle != FillStyleNone) { fillPolygon(points, d->fillStyle); } if (d->strokeStyle == StrokeStyleNone) return; if (index >= points.count()) return; if (numPoints < 0) numPoints = points.count(); if (index + numPoints > points.count()) numPoints = points.count() - index; if (numPoints > 1) { KisDistanceInformation saveDist(points[0], KisAlgebra2D::directionBetweenPoints(points[0], points[1], 0.0)); for (int i = index; i < index + numPoints - 1; i++) { paintLine(points [i], points [i + 1], &saveDist); } } } static void getBezierCurvePoints(const KisVector2D &pos1, const KisVector2D &control1, const KisVector2D &control2, const KisVector2D &pos2, vQPointF& points) { LineEquation line = LineEquation::Through(pos1, pos2); qreal d1 = line.absDistance(control1); qreal d2 = line.absDistance(control2); if (d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) { points.push_back(toQPointF(pos1)); } else { // Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508 KisVector2D l2 = (pos1 + control1) / 2; KisVector2D h = (control1 + control2) / 2; KisVector2D l3 = (l2 + h) / 2; KisVector2D r3 = (control2 + pos2) / 2; KisVector2D r2 = (h + r3) / 2; KisVector2D l4 = (l3 + r2) / 2; getBezierCurvePoints(pos1, l2, l3, l4, points); getBezierCurvePoints(l4, r2, r3, pos2, points); } } void KisPainter::getBezierCurvePoints(const QPointF &pos1, const QPointF &control1, const QPointF &control2, const QPointF &pos2, vQPointF& points) const { ::getBezierCurvePoints(toKisVector2D(pos1), toKisVector2D(control1), toKisVector2D(control2), toKisVector2D(pos2), points); } void KisPainter::paintBezierCurve(const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) { if (d->paintOp && d->paintOp->canPaint()) { d->paintOp->paintBezierCurve(pi1, control1, control2, pi2, currentDistance); } } void KisPainter::paintRect(const QRectF &rect) { QRectF normalizedRect = rect.normalized(); vQPointF points; points.push_back(normalizedRect.topLeft()); points.push_back(normalizedRect.bottomLeft()); points.push_back(normalizedRect.bottomRight()); points.push_back(normalizedRect.topRight()); paintPolygon(points); } void KisPainter::paintRect(const qreal x, const qreal y, const qreal w, const qreal h) { paintRect(QRectF(x, y, w, h)); } void KisPainter::paintEllipse(const QRectF &rect) { QRectF r = rect.normalized(); // normalize before checking as negative width and height are empty too if (r.isEmpty()) return; // See http://www.whizkidtech.redprince.net/bezier/circle/ for explanation. // kappa = (4/3*(sqrt(2)-1)) const qreal kappa = 0.5522847498; const qreal lx = (r.width() / 2) * kappa; const qreal ly = (r.height() / 2) * kappa; QPointF center = r.center(); QPointF p0(r.left(), center.y()); QPointF p1(r.left(), center.y() - ly); QPointF p2(center.x() - lx, r.top()); QPointF p3(center.x(), r.top()); vQPointF points; getBezierCurvePoints(p0, p1, p2, p3, points); QPointF p4(center.x() + lx, r.top()); QPointF p5(r.right(), center.y() - ly); QPointF p6(r.right(), center.y()); getBezierCurvePoints(p3, p4, p5, p6, points); QPointF p7(r.right(), center.y() + ly); QPointF p8(center.x() + lx, r.bottom()); QPointF p9(center.x(), r.bottom()); getBezierCurvePoints(p6, p7, p8, p9, points); QPointF p10(center.x() - lx, r.bottom()); QPointF p11(r.left(), center.y() + ly); getBezierCurvePoints(p9, p10, p11, p0, points); paintPolygon(points); } void KisPainter::paintEllipse(const qreal x, const qreal y, const qreal w, const qreal h) { paintEllipse(QRectF(x, y, w, h)); } void KisPainter::paintAt(const KisPaintInformation& pi, KisDistanceInformation *savedDist) { if (d->paintOp && d->paintOp->canPaint()) { d->paintOp->paintAt(pi, savedDist); } } void KisPainter::fillPolygon(const vQPointF& points, FillStyle fillStyle) { if (points.count() < 3) { return; } if (fillStyle == FillStyleNone) { return; } QPainterPath polygonPath; polygonPath.moveTo(points.at(0)); for (int pointIndex = 1; pointIndex < points.count(); pointIndex++) { polygonPath.lineTo(points.at(pointIndex)); } polygonPath.closeSubpath(); d->fillStyle = fillStyle; fillPainterPath(polygonPath); } void KisPainter::paintPolygon(const vQPointF& points) { if (d->fillStyle != FillStyleNone) { fillPolygon(points, d->fillStyle); } if (d->strokeStyle != StrokeStyleNone) { if (points.count() > 1) { KisDistanceInformation distance(points[0], KisAlgebra2D::directionBetweenPoints(points[0], points[1], 0.0)); for (int i = 0; i < points.count() - 1; i++) { paintLine(KisPaintInformation(points[i]), KisPaintInformation(points[i + 1]), &distance); } paintLine(points[points.count() - 1], points[0], &distance); } } } void KisPainter::paintPainterPath(const QPainterPath& path) { if (d->fillStyle != FillStyleNone) { fillPainterPath(path); } if (d->strokeStyle == StrokeStyleNone) return; QPointF lastPoint, nextPoint; int elementCount = path.elementCount(); KisDistanceInformation saveDist; for (int i = 0; i < elementCount; i++) { QPainterPath::Element element = path.elementAt(i); switch (element.type) { case QPainterPath::MoveToElement: lastPoint = QPointF(element.x, element.y); break; case QPainterPath::LineToElement: nextPoint = QPointF(element.x, element.y); paintLine(KisPaintInformation(lastPoint), KisPaintInformation(nextPoint), &saveDist); lastPoint = nextPoint; break; case QPainterPath::CurveToElement: nextPoint = QPointF(path.elementAt(i + 2).x, path.elementAt(i + 2).y); paintBezierCurve(KisPaintInformation(lastPoint), QPointF(path.elementAt(i).x, path.elementAt(i).y), QPointF(path.elementAt(i + 1).x, path.elementAt(i + 1).y), KisPaintInformation(nextPoint), &saveDist); lastPoint = nextPoint; break; default: continue; } } } void KisPainter::fillPainterPath(const QPainterPath& path) { fillPainterPath(path, QRect()); } void KisPainter::fillPainterPath(const QPainterPath& path, const QRect &requestedRect) { if (d->mirrorHorizontally || d->mirrorVertically) { KisLodTransform lod(d->device); QPointF effectiveAxesCenter = lod.map(d->axesCenter); QTransform C1 = QTransform::fromTranslate(-effectiveAxesCenter.x(), -effectiveAxesCenter.y()); QTransform C2 = QTransform::fromTranslate(effectiveAxesCenter.x(), effectiveAxesCenter.y()); QTransform t; QPainterPath newPath; QRect newRect; if (d->mirrorHorizontally) { t = C1 * QTransform::fromScale(-1,1) * C2; newPath = t.map(path); newRect = t.mapRect(requestedRect); d->fillPainterPathImpl(newPath, newRect); } if (d->mirrorVertically) { t = C1 * QTransform::fromScale(1,-1) * C2; newPath = t.map(path); newRect = t.mapRect(requestedRect); d->fillPainterPathImpl(newPath, newRect); } if (d->mirrorHorizontally && d->mirrorVertically) { t = C1 * QTransform::fromScale(-1,-1) * C2; newPath = t.map(path); newRect = t.mapRect(requestedRect); d->fillPainterPathImpl(newPath, newRect); } } d->fillPainterPathImpl(path, requestedRect); } void KisPainter::Private::fillPainterPathImpl(const QPainterPath& path, const QRect &requestedRect) { if (fillStyle == FillStyleNone) { return; } // Fill the polygon bounding rectangle with the required contents then we'll // create a mask for the actual polygon coverage. if (!fillPainter) { polygon = device->createCompositionSourceDevice(); fillPainter = new KisFillPainter(polygon); } else { polygon->clear(); } Q_CHECK_PTR(polygon); QRectF boundingRect = path.boundingRect(); QRect fillRect = boundingRect.toAlignedRect(); // Expand the rectangle to allow for anti-aliasing. fillRect.adjust(-1, -1, 1, 1); if (requestedRect.isValid()) { fillRect &= requestedRect; } switch (fillStyle) { default: Q_FALLTHROUGH(); case FillStyleForegroundColor: fillPainter->fillRect(fillRect, q->paintColor(), OPACITY_OPAQUE_U8); break; case FillStyleBackgroundColor: fillPainter->fillRect(fillRect, q->backgroundColor(), OPACITY_OPAQUE_U8); break; case FillStylePattern: if (pattern) { // if the user hasn't got any patterns installed, we shouldn't crash... fillPainter->fillRect(fillRect, pattern); } break; case FillStyleGenerator: if (generator) { // if the user hasn't got any generators, we shouldn't crash... fillPainter->fillRect(fillRect.x(), fillRect.y(), fillRect.width(), fillRect.height(), q->generator()); } break; } if (polygonMaskImage.isNull() || (maskPainter == 0)) { polygonMaskImage = QImage(maskImageWidth, maskImageHeight, QImage::Format_ARGB32_Premultiplied); maskPainter = new QPainter(&polygonMaskImage); maskPainter->setRenderHint(QPainter::Antialiasing, q->antiAliasPolygonFill()); } // Break the mask up into chunks so we don't have to allocate a potentially very large QImage. const QColor black(Qt::black); const QBrush brush(Qt::white); for (qint32 x = fillRect.x(); x < fillRect.x() + fillRect.width(); x += maskImageWidth) { for (qint32 y = fillRect.y(); y < fillRect.y() + fillRect.height(); y += maskImageHeight) { polygonMaskImage.fill(black.rgb()); maskPainter->translate(-x, -y); maskPainter->fillPath(path, brush); maskPainter->translate(x, y); qint32 rectWidth = qMin(fillRect.x() + fillRect.width() - x, maskImageWidth); qint32 rectHeight = qMin(fillRect.y() + fillRect.height() - y, maskImageHeight); KisHLineIteratorSP lineIt = polygon->createHLineIteratorNG(x, y, rectWidth); quint8 tmp; for (int row = y; row < y + rectHeight; row++) { QRgb* line = reinterpret_cast(polygonMaskImage.scanLine(row - y)); do { tmp = qRed(line[lineIt->x() - x]); polygon->colorSpace()->applyAlphaU8Mask(lineIt->rawData(), &tmp, 1); } while (lineIt->nextPixel()); lineIt->nextRow(); } } } QRect bltRect = !requestedRect.isEmpty() ? requestedRect : fillRect; q->bitBlt(bltRect.x(), bltRect.y(), polygon, bltRect.x(), bltRect.y(), bltRect.width(), bltRect.height()); } void KisPainter::drawPainterPath(const QPainterPath& path, const QPen& pen) { drawPainterPath(path, pen, QRect()); } void KisPainter::drawPainterPath(const QPainterPath& path, const QPen& _pen, const QRect &requestedRect) { // we are drawing mask, it has to be white // color of the path is given by paintColor() KIS_SAFE_ASSERT_RECOVER_NOOP(_pen.color() == Qt::white); QPen pen(_pen); pen.setColor(Qt::white); if (!d->fillPainter) { d->polygon = d->device->createCompositionSourceDevice(); d->fillPainter = new KisFillPainter(d->polygon); } else { d->polygon->clear(); } Q_CHECK_PTR(d->polygon); QRectF boundingRect = path.boundingRect(); QRect fillRect = boundingRect.toAlignedRect(); // take width of the pen into account int penWidth = qRound(pen.widthF()); fillRect.adjust(-penWidth, -penWidth, penWidth, penWidth); // Expand the rectangle to allow for anti-aliasing. fillRect.adjust(-1, -1, 1, 1); if (!requestedRect.isNull()) { fillRect &= requestedRect; } d->fillPainter->fillRect(fillRect, paintColor(), OPACITY_OPAQUE_U8); if (d->polygonMaskImage.isNull() || (d->maskPainter == 0)) { d->polygonMaskImage = QImage(d->maskImageWidth, d->maskImageHeight, QImage::Format_ARGB32_Premultiplied); d->maskPainter = new QPainter(&d->polygonMaskImage); d->maskPainter->setRenderHint(QPainter::Antialiasing, antiAliasPolygonFill()); } // Break the mask up into chunks so we don't have to allocate a potentially very large QImage. const QColor black(Qt::black); QPen oldPen = d->maskPainter->pen(); d->maskPainter->setPen(pen); for (qint32 x = fillRect.x(); x < fillRect.x() + fillRect.width(); x += d->maskImageWidth) { for (qint32 y = fillRect.y(); y < fillRect.y() + fillRect.height(); y += d->maskImageHeight) { d->polygonMaskImage.fill(black.rgb()); d->maskPainter->translate(-x, -y); d->maskPainter->drawPath(path); d->maskPainter->translate(x, y); qint32 rectWidth = qMin(fillRect.x() + fillRect.width() - x, d->maskImageWidth); qint32 rectHeight = qMin(fillRect.y() + fillRect.height() - y, d->maskImageHeight); KisHLineIteratorSP lineIt = d->polygon->createHLineIteratorNG(x, y, rectWidth); quint8 tmp; for (int row = y; row < y + rectHeight; row++) { QRgb* line = reinterpret_cast(d->polygonMaskImage.scanLine(row - y)); do { tmp = qRed(line[lineIt->x() - x]); d->polygon->colorSpace()->applyAlphaU8Mask(lineIt->rawData(), &tmp, 1); } while (lineIt->nextPixel()); lineIt->nextRow(); } } } d->maskPainter->setPen(oldPen); QRect r = d->polygon->extent(); bitBlt(r.x(), r.y(), d->polygon, r.x(), r.y(), r.width(), r.height()); } inline void KisPainter::compositeOnePixel(quint8 *dst, const KoColor &color) { d->paramInfo.dstRowStart = dst; d->paramInfo.dstRowStride = 0; d->paramInfo.srcRowStart = color.data(); d->paramInfo.srcRowStride = 0; d->paramInfo.maskRowStart = 0; d->paramInfo.maskRowStride = 0; d->paramInfo.rows = 1; d->paramInfo.cols = 1; d->colorSpace->bitBlt(color.colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags); } /**/ void KisPainter::drawLine(const QPointF& start, const QPointF& end, qreal width, bool antialias){ int x1 = qFloor(start.x()); int y1 = qFloor(start.y()); int x2 = qFloor(end.x()); int y2 = qFloor(end.y()); if ((x2 == x1 ) && (y2 == y1)) return; int dstX = x2-x1; int dstY = y2-y1; qreal uniC = dstX*y1 - dstY*x1; qreal projectionDenominator = 1.0 / (pow((double)dstX, 2) + pow((double)dstY, 2)); qreal subPixel; if (qAbs(dstX) > qAbs(dstY)){ subPixel = start.x() - x1; }else{ subPixel = start.y() - y1; } qreal halfWidth = width * 0.5 + subPixel; int W_ = qRound(halfWidth) + 1; // save the state int X1_ = x1; int Y1_ = y1; int X2_ = x2; int Y2_ = y2; if (x2device->createRandomAccessorNG(x1, y1); KisRandomConstAccessorSP selectionAccessor; if (d->selection) { selectionAccessor = d->selection->projection()->createRandomConstAccessorNG(x1, y1); } for (int y = y1-W_; y < y2+W_ ; y++){ for (int x = x1-W_; x < x2+W_; x++){ projection = ( (x-X1_)* dstX + (y-Y1_)*dstY ) * projectionDenominator; scanX = X1_ + projection * dstX; scanY = Y1_ + projection * dstY; if (((scanX < x1) || (scanX > x2)) || ((scanY < y1) || (scanY > y2))) { AA_ = qMin( sqrt( pow((double)x - X1_, 2) + pow((double)y - Y1_, 2) ), sqrt( pow((double)x - X2_, 2) + pow((double)y - Y2_, 2) )); }else{ AA_ = qAbs(dstY*x - dstX*y + uniC) * denominator; } if (AA_>halfWidth) { continue; } accessor->moveTo(x, y); if (selectionAccessor) selectionAccessor->moveTo(x,y); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { KoColor mycolor = d->paintColor; if (antialias && AA_ > halfWidth-1.0) { mycolor.colorSpace()->multiplyAlpha(mycolor.data(), 1.0 - (AA_-(halfWidth-1.0)), 1); } compositeOnePixel(accessor->rawData(), mycolor); } } } } /**/ void KisPainter::drawLine(const QPointF & start, const QPointF & end) { drawThickLine(start, end, 1, 1); } void KisPainter::drawDDALine(const QPointF & start, const QPointF & end) { int x = qFloor(start.x()); int y = qFloor(start.y()); int x2 = qFloor(end.x()); int y2 = qFloor(end.y()); // Width and height of the line int xd = x2 - x; int yd = y2 - y; - float m = (float)yd / (float)xd; + float m = 0; + bool lockAxis = true; + + if (xd == 0) { + m = 2.0; + } else if ( yd != 0) { + lockAxis = false; + m = (float)yd / (float)xd; + } float fx = x; float fy = y; int inc; KisRandomAccessorSP accessor = d->device->createRandomAccessorNG(x, y); KisRandomConstAccessorSP selectionAccessor; if (d->selection) { selectionAccessor = d->selection->projection()->createRandomConstAccessorNG(x, y); } accessor->moveTo(x, y); if (selectionAccessor) selectionAccessor->moveTo(x,y); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { compositeOnePixel(accessor->rawData(), d->paintColor); } if (fabs(m) > 1.0f) { inc = (yd > 0) ? 1 : -1; - m = 1.0f / m; + m = (lockAxis)? 0 : 1.0f / m; m *= inc; while (y != y2) { y = y + inc; fx = fx + m; x = qRound(fx); accessor->moveTo(x, y); if (selectionAccessor) selectionAccessor->moveTo(x, y); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { compositeOnePixel(accessor->rawData(), d->paintColor); } } } else { inc = (xd > 0) ? 1 : -1; m *= inc; while (x != x2) { x = x + inc; fy = fy + m; y = qRound(fy); accessor->moveTo(x, y); if (selectionAccessor) selectionAccessor->moveTo(x, y); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { compositeOnePixel(accessor->rawData(), d->paintColor); } } } } void KisPainter::drawWobblyLine(const QPointF & start, const QPointF & end) { KoColor mycolor(d->paintColor); int x1 = qFloor(start.x()); int y1 = qFloor(start.y()); int x2 = qFloor(end.x()); int y2 = qFloor(end.y()); KisRandomAccessorSP accessor = d->device->createRandomAccessorNG(x1, y1); KisRandomConstAccessorSP selectionAccessor; if (d->selection) { selectionAccessor = d->selection->projection()->createRandomConstAccessorNG(x1, y1); } // Width and height of the line int xd = (x2 - x1); int yd = (y2 - y1); int x; int y; float fx = (x = x1); float fy = (y = y1); float m = (float)yd / (float)xd; int inc; if (fabs(m) > 1) { inc = (yd > 0) ? 1 : -1; m = 1.0f / m; m *= inc; while (y != y2) { fx = fx + m; y = y + inc; x = qRound(fx); float br1 = qFloor(fx + 1) - fx; float br2 = fx - qFloor(fx); accessor->moveTo(x, y); if (selectionAccessor) selectionAccessor->moveTo(x, y); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { mycolor.setOpacity((quint8)(255*br1)); compositeOnePixel(accessor->rawData(), mycolor); } accessor->moveTo(x + 1, y); if (selectionAccessor) selectionAccessor->moveTo(x + 1, y); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { mycolor.setOpacity((quint8)(255*br2)); compositeOnePixel(accessor->rawData(), mycolor); } } } else { inc = (xd > 0) ? 1 : -1; m *= inc; while (x != x2) { fy = fy + m; x = x + inc; y = qRound(fy); float br1 = qFloor(fy + 1) - fy; float br2 = fy - qFloor(fy); accessor->moveTo(x, y); if (selectionAccessor) selectionAccessor->moveTo(x, y); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { mycolor.setOpacity((quint8)(255*br1)); compositeOnePixel(accessor->rawData(), mycolor); } accessor->moveTo(x, y + 1); if (selectionAccessor) selectionAccessor->moveTo(x, y + 1); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { mycolor.setOpacity((quint8)(255*br2)); compositeOnePixel(accessor->rawData(), mycolor); } } } } void KisPainter::drawWuLine(const QPointF & start, const QPointF & end) { KoColor lineColor(d->paintColor); int x1 = qFloor(start.x()); int y1 = qFloor(start.y()); int x2 = qFloor(end.x()); int y2 = qFloor(end.y()); KisRandomAccessorSP accessor = d->device->createRandomAccessorNG(x1, y1); KisRandomConstAccessorSP selectionAccessor; if (d->selection) { selectionAccessor = d->selection->projection()->createRandomConstAccessorNG(x1, y1); } float grad, xd, yd; float xgap, ygap, xend, yend, yf, xf; float brightness1, brightness2; int ix1, ix2, iy1, iy2; quint8 c1, c2; // gradient of line xd = (x2 - x1); yd = (y2 - y1); if (yd == 0) { /* Horizontal line */ int incr = (x1 < x2) ? 1 : -1; ix1 = x1; ix2 = x2; iy1 = y1; while (ix1 != ix2) { ix1 = ix1 + incr; accessor->moveTo(ix1, iy1); if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { compositeOnePixel(accessor->rawData(), lineColor); } } return; } if (xd == 0) { /* Vertical line */ int incr = (y1 < y2) ? 1 : -1; iy1 = y1; iy2 = y2; ix1 = x1; while (iy1 != iy2) { iy1 = iy1 + incr; accessor->moveTo(ix1, iy1); if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { compositeOnePixel(accessor->rawData(), lineColor); } } return; } if (fabs(xd) > fabs(yd)) { // horizontal line // line have to be paint from left to right if (x1 > x2) { std::swap(x1, x2); std::swap(y1, y2); xd = (x2 - x1); yd = (y2 - y1); } grad = yd / xd; // nearest X,Y integer coordinates xend = x1; yend = y1 + grad * (xend - x1); xgap = invertFrac(x1 + 0.5f); ix1 = x1; iy1 = qFloor(yend); // calc the intensity of the other end point pixel pair. brightness1 = invertFrac(yend) * xgap; brightness2 = frac(yend) * xgap; c1 = (int)(brightness1 * OPACITY_OPAQUE_U8); c2 = (int)(brightness2 * OPACITY_OPAQUE_U8); accessor->moveTo(ix1, iy1); if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { lineColor.setOpacity(c1); compositeOnePixel(accessor->rawData(), lineColor); } accessor->moveTo(ix1, iy1 + 1); if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1 + 1); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { lineColor.setOpacity(c2); compositeOnePixel(accessor->rawData(), lineColor); } // calc first Y-intersection for main loop yf = yend + grad; xend = x2; yend = y2 + grad * (xend - x2); xgap = invertFrac(x2 - 0.5f); ix2 = x2; iy2 = qFloor(yend); brightness1 = invertFrac(yend) * xgap; brightness2 = frac(yend) * xgap; c1 = (int)(brightness1 * OPACITY_OPAQUE_U8); c2 = (int)(brightness2 * OPACITY_OPAQUE_U8); accessor->moveTo(ix2, iy2); if (selectionAccessor) selectionAccessor->moveTo(ix2, iy2); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { lineColor.setOpacity(c1); compositeOnePixel(accessor->rawData(), lineColor); } accessor->moveTo(ix2, iy2 + 1); if (selectionAccessor) selectionAccessor->moveTo(ix2, iy2 + 1); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { lineColor.setOpacity(c2); compositeOnePixel(accessor->rawData(), lineColor); } // main loop for (int x = ix1 + 1; x <= ix2 - 1; x++) { brightness1 = invertFrac(yf); brightness2 = frac(yf); c1 = (int)(brightness1 * OPACITY_OPAQUE_U8); c2 = (int)(brightness2 * OPACITY_OPAQUE_U8); accessor->moveTo(x, qFloor(yf)); if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yf)); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { lineColor.setOpacity(c1); compositeOnePixel(accessor->rawData(), lineColor); } accessor->moveTo(x, qFloor(yf) + 1); if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yf) + 1); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { lineColor.setOpacity(c2); compositeOnePixel(accessor->rawData(), lineColor); } yf = yf + grad; } } else { //vertical // line have to be painted from left to right if (y1 > y2) { std::swap(x1, x2); std::swap(y1, y2); xd = (x2 - x1); yd = (y2 - y1); } grad = xd / yd; // nearest X,Y integer coordinates yend = y1; xend = x1 + grad * (yend - y1); ygap = y1; ix1 = qFloor(xend); iy1 = y1; // calc the intensity of the other end point pixel pair. brightness1 = invertFrac(xend) * ygap; brightness2 = frac(xend) * ygap; c1 = (int)(brightness1 * OPACITY_OPAQUE_U8); c2 = (int)(brightness2 * OPACITY_OPAQUE_U8); accessor->moveTo(ix1, iy1); if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { lineColor.setOpacity(c1); compositeOnePixel(accessor->rawData(), lineColor); } accessor->moveTo(x1 + 1, y1); if (selectionAccessor) selectionAccessor->moveTo(x1 + 1, y1); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { lineColor.setOpacity(c2); compositeOnePixel(accessor->rawData(), lineColor); } // calc first Y-intersection for main loop xf = xend + grad; yend = y2; xend = x2 + grad * (yend - y2); ygap = invertFrac(y2 - 0.5f); ix2 = qFloor(xend); iy2 = y2; brightness1 = invertFrac(xend) * ygap; brightness2 = frac(xend) * ygap; c1 = (int)(brightness1 * OPACITY_OPAQUE_U8); c2 = (int)(brightness2 * OPACITY_OPAQUE_U8); accessor->moveTo(ix2, iy2); if (selectionAccessor) selectionAccessor->moveTo(ix2, iy2); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { lineColor.setOpacity(c1); compositeOnePixel(accessor->rawData(), lineColor); } accessor->moveTo(ix2 + 1, iy2); if (selectionAccessor) selectionAccessor->moveTo(ix2 + 1, iy2); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { lineColor.setOpacity(c2); compositeOnePixel(accessor->rawData(), lineColor); } // main loop for (int y = iy1 + 1; y <= iy2 - 1; y++) { brightness1 = invertFrac(xf); brightness2 = frac(xf); c1 = (int)(brightness1 * OPACITY_OPAQUE_U8); c2 = (int)(brightness2 * OPACITY_OPAQUE_U8); accessor->moveTo(qFloor(xf), y); if (selectionAccessor) selectionAccessor->moveTo(qFloor(xf), y); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { lineColor.setOpacity(c1); compositeOnePixel(accessor->rawData(), lineColor); } accessor->moveTo(qFloor(xf) + 1, y); if (selectionAccessor) selectionAccessor->moveTo(qFloor(xf) + 1, y); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { lineColor.setOpacity(c2); compositeOnePixel(accessor->rawData(), lineColor); } xf = xf + grad; } }//end-of-else } void KisPainter::drawThickLine(const QPointF & start, const QPointF & end, int startWidth, int endWidth) { KisRandomAccessorSP accessor = d->device->createRandomAccessorNG(start.x(), start.y()); KisRandomConstAccessorSP selectionAccessor; if (d->selection) { selectionAccessor = d->selection->projection()->createRandomConstAccessorNG(start.x(), start.y()); } const KoColorSpace *cs = d->device->colorSpace(); KoColor c1(d->paintColor); KoColor c2(d->paintColor); KoColor c3(d->paintColor); KoColor col1(c1); KoColor col2(c1); float grada, gradb, dxa, dxb, dya, dyb, fraca, fracb, xfa, yfa, xfb, yfb, b1a, b2a, b1b, b2b, dstX, dstY; int x, y, ix1, ix2, iy1, iy2; int x0a, y0a, x1a, y1a, x0b, y0b, x1b, y1b; int tp0, tn0, tp1, tn1; int horizontal = 0; float opacity = 1.0; tp0 = startWidth / 2; tn0 = startWidth / 2; if (startWidth % 2 == 0) // even width startWidth tn0--; tp1 = endWidth / 2; tn1 = endWidth / 2; if (endWidth % 2 == 0) // even width endWidth tn1--; int x0 = qRound(start.x()); int y0 = qRound(start.y()); int x1 = qRound(end.x()); int y1 = qRound(end.y()); dstX = x1 - x0; // run of general line dstY = y1 - y0; // rise of general line if (dstY < 0) dstY = -dstY; if (dstX < 0) dstX = -dstX; if (dstX > dstY) { // horizontalish horizontal = 1; x0a = x0; y0a = y0 - tn0; x0b = x0; y0b = y0 + tp0; x1a = x1; y1a = y1 - tn1; x1b = x1; y1b = y1 + tp1; } else { x0a = x0 - tn0; y0a = y0; x0b = x0 + tp0; y0b = y0; x1a = x1 - tn1; y1a = y1; x1b = x1 + tp1; y1b = y1; } if (horizontal) { // draw endpoints for (int i = y0a; i <= y0b; i++) { accessor->moveTo(x0, i); if (selectionAccessor) selectionAccessor->moveTo(x0, i); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { compositeOnePixel(accessor->rawData(), c1); } } for (int i = y1a; i <= y1b; i++) { accessor->moveTo(x1, i); if (selectionAccessor) selectionAccessor->moveTo(x1, i); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { compositeOnePixel(accessor->rawData(), c1); } } } else { for (int i = x0a; i <= x0b; i++) { accessor->moveTo(i, y0); if (selectionAccessor) selectionAccessor->moveTo(i, y0); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { compositeOnePixel(accessor->rawData(), c1); } } for (int i = x1a; i <= x1b; i++) { accessor->moveTo(i, y1); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { compositeOnePixel(accessor->rawData(), c1); } } } //antialias endpoints if (x1 != x0 && y1 != y0) { if (horizontal) { accessor->moveTo(x0a, y0a - 1); if (selectionAccessor) selectionAccessor->moveTo(x0a, y0a - 1); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { qreal alpha = cs->opacityF(accessor->rawData()); opacity = .25 * c1.opacityF() + (1 - .25) * alpha; col1.setOpacity(opacity); compositeOnePixel(accessor->rawData(), col1); } accessor->moveTo(x1b, y1b + 1); if (selectionAccessor) selectionAccessor->moveTo(x1b, y1b + 1); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { qreal alpha = cs->opacityF(accessor->rawData()); opacity = .25 * c2.opacityF() + (1 - .25) * alpha; col1.setOpacity(opacity); compositeOnePixel(accessor->rawData(), col1); } } else { accessor->moveTo(x0a - 1, y0a); if (selectionAccessor) selectionAccessor->moveTo(x0a - 1, y0a); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { qreal alpha = cs->opacityF(accessor->rawData()); opacity = .25 * c1.opacityF() + (1 - .25) * alpha; col1.setOpacity(opacity); compositeOnePixel(accessor->rawData(), col1); } accessor->moveTo(x1b + 1, y1b); if (selectionAccessor) selectionAccessor->moveTo(x1b + 1, y1b); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { qreal alpha = cs->opacityF(accessor->rawData()); opacity = .25 * c2.opacityF() + (1 - .25) * alpha; col1.setOpacity(opacity); compositeOnePixel(accessor->rawData(), col1); } } } dxa = x1a - x0a; // run of a dya = y1a - y0a; // rise of a dxb = x1b - x0b; // run of b dyb = y1b - y0b; // rise of b if (horizontal) { // horizontal-ish lines if (x1 < x0) { int xt, yt, wt; KoColor tmp; xt = x1a; x1a = x0a; x0a = xt; yt = y1a; y1a = y0a; y0a = yt; xt = x1b; x1b = x0b; x0b = xt; yt = y1b; y1b = y0b; y0b = yt; xt = x1; x1 = x0; x0 = xt; yt = y1; y1 = y0; y0 = yt; tmp = c1; c1 = c2; c2 = tmp; wt = startWidth; startWidth = endWidth; endWidth = wt; } grada = dya / dxa; gradb = dyb / dxb; ix1 = x0; iy1 = y0; ix2 = x1; iy2 = y1; yfa = y0a + grada; yfb = y0b + gradb; for (x = ix1 + 1; x <= ix2 - 1; x++) { fraca = yfa - qFloor(yfa); b1a = 1 - fraca; b2a = fraca; fracb = yfb - qFloor(yfb); b1b = 1 - fracb; b2b = fracb; // color first pixel of bottom line opacity = ((x - ix1) / dstX) * c2.opacityF() + (1 - (x - ix1) / dstX) * c1.opacityF(); c3.setOpacity(opacity); accessor->moveTo(x, qFloor(yfa)); if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yfa)); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { qreal alpha = cs->opacityF(accessor->rawData()); opacity = b1a * c3.opacityF() + (1 - b1a) * alpha; col1.setOpacity(opacity); compositeOnePixel(accessor->rawData(), col1); } // color first pixel of top line if (!(startWidth == 1 && endWidth == 1)) { accessor->moveTo(x, qFloor(yfb)); if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yfb)); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { qreal alpha = cs->opacityF(accessor->rawData()); opacity = b1b * c3.opacityF() + (1 - b1b) * alpha; col1.setOpacity(opacity); compositeOnePixel(accessor->rawData(), col1); } } // color second pixel of bottom line if (grada != 0 && grada != 1) { // if not flat or exact diagonal accessor->moveTo(x, qFloor(yfa) + 1); if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yfa) + 1); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { qreal alpha = cs->opacityF(accessor->rawData()); opacity = b2a * c3.opacityF() + (1 - b2a) * alpha; col2.setOpacity(opacity); compositeOnePixel(accessor->rawData(), col2); } } // color second pixel of top line if (gradb != 0 && gradb != 1 && !(startWidth == 1 && endWidth == 1)) { accessor->moveTo(x, qFloor(yfb) + 1); if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yfb) + 1); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { qreal alpha = cs->opacityF(accessor->rawData()); opacity = b2b * c3.opacityF() + (1 - b2b) * alpha; col2.setOpacity(opacity); compositeOnePixel(accessor->rawData(), col2); } } // fill remaining pixels if (!(startWidth == 1 && endWidth == 1)) { if (yfa < yfb) for (int i = qFloor(yfa) + 1; i <= qFloor(yfb); i++) { accessor->moveTo(x, i); if (selectionAccessor) selectionAccessor->moveTo(x, i); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { compositeOnePixel(accessor->rawData(), c3); } } else for (int i = qFloor(yfa) + 1; i >= qFloor(yfb); i--) { accessor->moveTo(x, i); if (selectionAccessor) selectionAccessor->moveTo(x, i); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { compositeOnePixel(accessor->rawData(), c3); } } } yfa += grada; yfb += gradb; } } else { // vertical-ish lines if (y1 < y0) { int xt, yt, wt; xt = x1a; x1a = x0a; x0a = xt; yt = y1a; y1a = y0a; y0a = yt; xt = x1b; x1b = x0b; x0b = xt; yt = y1b; y1b = y0b; y0b = yt; xt = x1; x1 = x0; x0 = xt; yt = y1; y1 = y0; y0 = yt; KoColor tmp; tmp = c1; c1 = c2; c2 = tmp; wt = startWidth; startWidth = endWidth; endWidth = wt; } grada = dxa / dya; gradb = dxb / dyb; ix1 = x0; iy1 = y0; ix2 = x1; iy2 = y1; xfa = x0a + grada; xfb = x0b + gradb; for (y = iy1 + 1; y <= iy2 - 1; y++) { fraca = xfa - qFloor(xfa); b1a = 1 - fraca; b2a = fraca; fracb = xfb - qFloor(xfb); b1b = 1 - fracb; b2b = fracb; // color first pixel of left line opacity = ((y - iy1) / dstY) * c2.opacityF() + (1 - (y - iy1) / dstY) * c1.opacityF(); c3.setOpacity(opacity); accessor->moveTo(qFloor(xfa), y); if (selectionAccessor) selectionAccessor->moveTo(qFloor(xfa), y); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { qreal alpha = cs->opacityF(accessor->rawData()); opacity = b1a * c3.opacityF() + (1 - b1a) * alpha; col1.setOpacity(opacity); compositeOnePixel(accessor->rawData(), col1); } // color first pixel of right line if (!(startWidth == 1 && endWidth == 1)) { accessor->moveTo(qFloor(xfb), y); if (selectionAccessor) selectionAccessor->moveTo(qFloor(xfb), y); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { qreal alpha = cs->opacityF(accessor->rawData()); opacity = b1b * c3.opacityF() + (1 - b1b) * alpha; col1.setOpacity(opacity); compositeOnePixel(accessor->rawData(), col1); } } // color second pixel of left line if (grada != 0 && grada != 1) { // if not flat or exact diagonal accessor->moveTo(qFloor(xfa) + 1, y); if (selectionAccessor) selectionAccessor->moveTo(qFloor(xfa) + 1, y); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { qreal alpha = cs->opacityF(accessor->rawData()); opacity = b2a * c3.opacityF() + (1 - b2a) * alpha; col2.setOpacity(opacity); compositeOnePixel(accessor->rawData(), col2); } } // color second pixel of right line if (gradb != 0 && gradb != 1 && !(startWidth == 1 && endWidth == 1)) { accessor->moveTo(qFloor(xfb) + 1, y); if (selectionAccessor) selectionAccessor->moveTo(qFloor(xfb) + 1, y); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { qreal alpha = cs->opacityF(accessor->rawData()); opacity = b2b * c3.opacityF() + (1 - b2b) * alpha; col2.setOpacity(opacity); compositeOnePixel(accessor->rawData(), col2); } } // fill remaining pixels between current xfa,xfb if (!(startWidth == 1 && endWidth == 1)) { if (xfa < xfb) for (int i = qFloor(xfa) + 1; i <= qFloor(xfb); i++) { accessor->moveTo(i, y); if (selectionAccessor) selectionAccessor->moveTo(i, y); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { compositeOnePixel(accessor->rawData(), c3); } } else for (int i = qFloor(xfb); i <= qFloor(xfa) + 1; i++) { accessor->moveTo(i, y); if (selectionAccessor) selectionAccessor->moveTo(i, y); if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) { compositeOnePixel(accessor->rawData(), c3); } } } xfa += grada; xfb += gradb; } } } void KisPainter::setProgress(KoUpdater * progressUpdater) { d->progressUpdater = progressUpdater; } const KisPaintDeviceSP KisPainter::device() const { return d->device; } KisPaintDeviceSP KisPainter::device() { return d->device; } void KisPainter::setChannelFlags(QBitArray channelFlags) { // Q_ASSERT(channelFlags.isEmpty() || quint32(channelFlags.size()) == d->colorSpace->channelCount()); // Now, if all bits in the channelflags are true, pass an empty channel flags bitarray // because otherwise the compositeops cannot optimize. d->paramInfo.channelFlags = channelFlags; if (!channelFlags.isEmpty() && channelFlags == QBitArray(channelFlags.size(), true)) { d->paramInfo.channelFlags = QBitArray(); } } QBitArray KisPainter::channelFlags() { return d->paramInfo.channelFlags; } void KisPainter::setPattern(const KoPattern * pattern) { d->pattern = pattern; } const KoPattern * KisPainter::pattern() const { return d->pattern; } void KisPainter::setPaintColor(const KoColor& color) { d->paintColor = color; if (d->device) { d->paintColor.convertTo(d->device->compositionSourceColorSpace()); } } const KoColor &KisPainter::paintColor() const { return d->paintColor; } void KisPainter::setBackgroundColor(const KoColor& color) { d->backgroundColor = color; if (d->device) { d->backgroundColor.convertTo(d->device->compositionSourceColorSpace()); } } const KoColor &KisPainter::backgroundColor() const { return d->backgroundColor; } void KisPainter::setGenerator(KisFilterConfigurationSP generator) { d->generator = generator; } const KisFilterConfigurationSP KisPainter::generator() const { return d->generator; } void KisPainter::setFillStyle(FillStyle fillStyle) { d->fillStyle = fillStyle; } KisPainter::FillStyle KisPainter::fillStyle() const { return d->fillStyle; } void KisPainter::setAntiAliasPolygonFill(bool antiAliasPolygonFill) { d->antiAliasPolygonFill = antiAliasPolygonFill; } bool KisPainter::antiAliasPolygonFill() { return d->antiAliasPolygonFill; } void KisPainter::setStrokeStyle(KisPainter::StrokeStyle strokeStyle) { d->strokeStyle = strokeStyle; } KisPainter::StrokeStyle KisPainter::strokeStyle() const { return d->strokeStyle; } void KisPainter::setFlow(quint8 flow) { d->paramInfo.flow = float(flow) / 255.0f; } quint8 KisPainter::flow() const { return quint8(d->paramInfo.flow * 255.0f); } void KisPainter::setOpacityUpdateAverage(quint8 opacity) { d->isOpacityUnit = opacity == OPACITY_OPAQUE_U8; d->paramInfo.updateOpacityAndAverage(float(opacity) / 255.0f); } void KisPainter::setAverageOpacity(qreal averageOpacity) { d->paramInfo.setOpacityAndAverage(d->paramInfo.opacity, averageOpacity); } qreal KisPainter::blendAverageOpacity(qreal opacity, qreal averageOpacity) { const float exponent = 0.1; return averageOpacity < opacity ? opacity : exponent * opacity + (1.0 - exponent) * (averageOpacity); } void KisPainter::setOpacity(quint8 opacity) { d->isOpacityUnit = opacity == OPACITY_OPAQUE_U8; d->paramInfo.opacity = float(opacity) / 255.0f; } quint8 KisPainter::opacity() const { return quint8(d->paramInfo.opacity * 255.0f); } void KisPainter::setCompositeOp(const KoCompositeOp * op) { d->compositeOp = op; } const KoCompositeOp * KisPainter::compositeOp() { return d->compositeOp; } /** * TODO: Rename this setCompositeOpId(). See KoCompositeOpRegistry.h */ void KisPainter::setCompositeOp(const QString& op) { d->compositeOp = d->colorSpace->compositeOp(op); } void KisPainter::setSelection(KisSelectionSP selection) { d->selection = selection; } KisSelectionSP KisPainter::selection() { return d->selection; } KoUpdater * KisPainter::progressUpdater() { return d->progressUpdater; } void KisPainter::setGradient(const KoAbstractGradient* gradient) { d->gradient = gradient; } const KoAbstractGradient* KisPainter::gradient() const { return d->gradient; } void KisPainter::setPaintOpPreset(KisPaintOpPresetSP preset, KisNodeSP node, KisImageSP image) { d->paintOpPreset = preset; KisPaintOp *paintop = KisPaintOpRegistry::instance()->paintOp(preset, this, node, image); Q_ASSERT(paintop); if (paintop) { delete d->paintOp; d->paintOp = paintop; } else { warnKrita << "Could not create paintop for preset " << preset->name(); } } KisPaintOpPresetSP KisPainter::preset() const { return d->paintOpPreset; } KisPaintOp* KisPainter::paintOp() const { return d->paintOp; } void KisPainter::setMirrorInformation(const QPointF& axesCenter, bool mirrorHorizontally, bool mirrorVertically) { d->axesCenter = axesCenter; d->mirrorHorizontally = mirrorHorizontally; d->mirrorVertically = mirrorVertically; } void KisPainter::copyMirrorInformationFrom(const KisPainter *other) { d->axesCenter = other->d->axesCenter; d->mirrorHorizontally = other->d->mirrorHorizontally; d->mirrorVertically = other->d->mirrorVertically; } bool KisPainter::hasMirroring() const { return d->mirrorHorizontally || d->mirrorVertically; } bool KisPainter::hasHorizontalMirroring() const { return d->mirrorHorizontally; } bool KisPainter::hasVerticalMirroring() const { return d->mirrorVertically; } void KisPainter::setMaskImageSize(qint32 width, qint32 height) { d->maskImageWidth = qBound(1, width, 256); d->maskImageHeight = qBound(1, height, 256); d->fillPainter = 0; d->polygonMaskImage = QImage(); } //void KisPainter::setLockAlpha(bool protect) //{ // if(d->paramInfo.channelFlags.isEmpty()) { // d->paramInfo.channelFlags = d->colorSpace->channelFlags(true, true); // } // QBitArray switcher = // d->colorSpace->channelFlags(protect, !protect); // if(protect) { // d->paramInfo.channelFlags &= switcher; // } // else { // d->paramInfo.channelFlags |= switcher; // } // Q_ASSERT(quint32(d->paramInfo.channelFlags.size()) == d->colorSpace->channelCount()); //} //bool KisPainter::alphaLocked() const //{ // QBitArray switcher = d->colorSpace->channelFlags(false, true); // return !(d->paramInfo.channelFlags & switcher).count(true); //} void KisPainter::setRenderingIntent(KoColorConversionTransformation::Intent intent) { d->renderingIntent = intent; } void KisPainter::setColorConversionFlags(KoColorConversionTransformation::ConversionFlags conversionFlags) { d->conversionFlags = conversionFlags; } void KisPainter::setRunnableStrokeJobsInterface(KisRunnableStrokeJobsInterface *interface) { d->runnableStrokeJobsInterface = interface; } KisRunnableStrokeJobsInterface *KisPainter::runnableStrokeJobsInterface() const { if (!d->runnableStrokeJobsInterface) { if (!d->fakeRunnableStrokeJobsInterface) { d->fakeRunnableStrokeJobsInterface.reset(new KisFakeRunnableStrokeJobsExecutor()); } return d->fakeRunnableStrokeJobsInterface.data(); } return d->runnableStrokeJobsInterface; } void KisPainter::renderMirrorMaskSafe(QRect rc, KisFixedPaintDeviceSP dab, bool preserveDab) { if (!d->mirrorHorizontally && !d->mirrorVertically) return; KisFixedPaintDeviceSP dabToProcess = dab; if (preserveDab) { dabToProcess = new KisFixedPaintDevice(*dab); } renderMirrorMask(rc, dabToProcess); } void KisPainter::renderMirrorMaskSafe(QRect rc, KisPaintDeviceSP dab, int sx, int sy, KisFixedPaintDeviceSP mask, bool preserveMask) { if (!d->mirrorHorizontally && !d->mirrorVertically) return; KisFixedPaintDeviceSP maskToProcess = mask; if (preserveMask) { maskToProcess = new KisFixedPaintDevice(*mask); } renderMirrorMask(rc, dab, sx, sy, maskToProcess); } void KisPainter::renderMirrorMask(QRect rc, KisFixedPaintDeviceSP dab) { int x = rc.topLeft().x(); int y = rc.topLeft().y(); KisLodTransform t(d->device); QPoint effectiveAxesCenter = t.map(d->axesCenter).toPoint(); int mirrorX = -((x+rc.width()) - effectiveAxesCenter.x()) + effectiveAxesCenter.x(); int mirrorY = -((y+rc.height()) - effectiveAxesCenter.y()) + effectiveAxesCenter.y(); if (d->mirrorHorizontally && d->mirrorVertically){ dab->mirror(true, false); bltFixed(mirrorX, y, dab, 0,0,rc.width(),rc.height()); dab->mirror(false,true); bltFixed(mirrorX, mirrorY, dab, 0,0,rc.width(),rc.height()); dab->mirror(true, false); bltFixed(x, mirrorY, dab, 0,0,rc.width(),rc.height()); } else if (d->mirrorHorizontally){ dab->mirror(true, false); bltFixed(mirrorX, y, dab, 0,0,rc.width(),rc.height()); } else if (d->mirrorVertically){ dab->mirror(false, true); bltFixed(x, mirrorY, dab, 0,0,rc.width(),rc.height()); } } void KisPainter::renderMirrorMask(QRect rc, KisFixedPaintDeviceSP dab, KisFixedPaintDeviceSP mask) { int x = rc.topLeft().x(); int y = rc.topLeft().y(); KisLodTransform t(d->device); QPoint effectiveAxesCenter = t.map(d->axesCenter).toPoint(); int mirrorX = -((x+rc.width()) - effectiveAxesCenter.x()) + effectiveAxesCenter.x(); int mirrorY = -((y+rc.height()) - effectiveAxesCenter.y()) + effectiveAxesCenter.y(); if (d->mirrorHorizontally && d->mirrorVertically){ dab->mirror(true, false); mask->mirror(true, false); bltFixedWithFixedSelection(mirrorX,y, dab, mask, rc.width() ,rc.height() ); dab->mirror(false,true); mask->mirror(false, true); bltFixedWithFixedSelection(mirrorX,mirrorY, dab, mask, rc.width() ,rc.height() ); dab->mirror(true, false); mask->mirror(true, false); bltFixedWithFixedSelection(x,mirrorY, dab, mask, rc.width() ,rc.height() ); }else if (d->mirrorHorizontally){ dab->mirror(true, false); mask->mirror(true, false); bltFixedWithFixedSelection(mirrorX,y, dab, mask, rc.width() ,rc.height() ); }else if (d->mirrorVertically){ dab->mirror(false, true); mask->mirror(false, true); bltFixedWithFixedSelection(x,mirrorY, dab, mask, rc.width() ,rc.height() ); } } void KisPainter::renderMirrorMask(QRect rc, KisPaintDeviceSP dab){ if (d->mirrorHorizontally || d->mirrorVertically){ KisFixedPaintDeviceSP mirrorDab(new KisFixedPaintDevice(dab->colorSpace())); QRect dabRc( QPoint(0,0), QSize(rc.width(),rc.height()) ); mirrorDab->setRect(dabRc); mirrorDab->lazyGrowBufferWithoutInitialization(); dab->readBytes(mirrorDab->data(),rc); renderMirrorMask( QRect(rc.topLeft(),dabRc.size()), mirrorDab); } } void KisPainter::renderMirrorMask(QRect rc, KisPaintDeviceSP dab, int sx, int sy, KisFixedPaintDeviceSP mask) { if (d->mirrorHorizontally || d->mirrorVertically){ KisFixedPaintDeviceSP mirrorDab(new KisFixedPaintDevice(dab->colorSpace())); QRect dabRc( QPoint(0,0), QSize(rc.width(),rc.height()) ); mirrorDab->setRect(dabRc); mirrorDab->lazyGrowBufferWithoutInitialization(); dab->readBytes(mirrorDab->data(),QRect(QPoint(sx,sy),rc.size())); renderMirrorMask(rc, mirrorDab, mask); } } void KisPainter::renderDabWithMirroringNonIncremental(QRect rc, KisPaintDeviceSP dab) { QVector rects; int x = rc.topLeft().x(); int y = rc.topLeft().y(); KisLodTransform t(d->device); QPoint effectiveAxesCenter = t.map(d->axesCenter).toPoint(); int mirrorX = -((x+rc.width()) - effectiveAxesCenter.x()) + effectiveAxesCenter.x(); int mirrorY = -((y+rc.height()) - effectiveAxesCenter.y()) + effectiveAxesCenter.y(); rects << rc; if (d->mirrorHorizontally && d->mirrorVertically){ rects << QRect(mirrorX, y, rc.width(), rc.height()); rects << QRect(mirrorX, mirrorY, rc.width(), rc.height()); rects << QRect(x, mirrorY, rc.width(), rc.height()); } else if (d->mirrorHorizontally) { rects << QRect(mirrorX, y, rc.width(), rc.height()); } else if (d->mirrorVertically) { rects << QRect(x, mirrorY, rc.width(), rc.height()); } Q_FOREACH (const QRect &rc, rects) { d->device->clear(rc); } QRect resultRect = dab->extent() | rc; bool intersects = false; for (int i = 1; i < rects.size(); i++) { if (rects[i].intersects(resultRect)) { intersects = true; break; } } /** * If there are no cross-intersections, we can use a fast path * and do no cycling recompositioning */ if (!intersects) { rects.resize(1); } Q_FOREACH (const QRect &rc, rects) { bitBlt(rc.topLeft(), dab, rc); } Q_FOREACH (const QRect &rc, rects) { renderMirrorMask(rc, dab); } } bool KisPainter::hasDirtyRegion() const { return !d->dirtyRects.isEmpty(); } void KisPainter::mirrorRect(Qt::Orientation direction, QRect *rc) const { KisLodTransform t(d->device); QPoint effectiveAxesCenter = t.map(d->axesCenter).toPoint(); KritaUtils::mirrorRect(direction, effectiveAxesCenter, rc); } void KisPainter::mirrorDab(Qt::Orientation direction, KisRenderedDab *dab) const { KisLodTransform t(d->device); QPoint effectiveAxesCenter = t.map(d->axesCenter).toPoint(); KritaUtils::mirrorDab(direction, effectiveAxesCenter, dab); } namespace { inline void mirrorOneObject(Qt::Orientation dir, const QPoint ¢er, QRect *rc) { KritaUtils::mirrorRect(dir, center, rc); } inline void mirrorOneObject(Qt::Orientation dir, const QPoint ¢er, QPointF *pt) { KritaUtils::mirrorPoint(dir, center, pt); } inline void mirrorOneObject(Qt::Orientation dir, const QPoint ¢er, QPair *pair) { KritaUtils::mirrorPoint(dir, center, &pair->first); KritaUtils::mirrorPoint(dir, center, &pair->second); } } template QVector KisPainter::Private::calculateMirroredObjects(const T &object) { QVector result; KisLodTransform t(this->device); const QPoint effectiveAxesCenter = t.map(this->axesCenter).toPoint(); T baseObject = object; result << baseObject; if (this->mirrorHorizontally && this->mirrorVertically){ mirrorOneObject(Qt::Horizontal, effectiveAxesCenter, &baseObject); result << baseObject; mirrorOneObject(Qt::Vertical, effectiveAxesCenter, &baseObject); result << baseObject; mirrorOneObject(Qt::Horizontal, effectiveAxesCenter, &baseObject); result << baseObject; } else if (this->mirrorHorizontally) { mirrorOneObject(Qt::Horizontal, effectiveAxesCenter, &baseObject); result << baseObject; } else if (this->mirrorVertically) { mirrorOneObject(Qt::Vertical, effectiveAxesCenter, &baseObject); result << baseObject; } return result; } const QVector KisPainter::calculateAllMirroredRects(const QRect &rc) { return d->calculateMirroredObjects(rc); } const QVector KisPainter::calculateAllMirroredPoints(const QPointF &pos) { return d->calculateMirroredObjects(pos); } const QVector> KisPainter::calculateAllMirroredPoints(const QPair &pair) { return d->calculateMirroredObjects(pair); } diff --git a/libs/image/kis_properties_configuration.h b/libs/image/kis_properties_configuration.h index 60ef3727e7..886d5c283b 100644 --- a/libs/image/kis_properties_configuration.h +++ b/libs/image/kis_properties_configuration.h @@ -1,224 +1,224 @@ /* * Copyright (c) 2006 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_PROPERTIES_CONFIGURATION_H_ #define _KIS_PROPERTIES_CONFIGURATION_H_ #include #include #include #include #include #include class QDomElement; class QDomDocument; #include "kis_serializable_configuration.h" #include "kritaimage_export.h" #include "kis_types.h" /** * KisPropertiesConfiguration is a map-based properties class that can * be serialized and deserialized. * * It differs from the base class KisSerializableConfiguration in that * it provides a number of convenience methods to get at the data and */ class KRITAIMAGE_EXPORT KisPropertiesConfiguration : public KisSerializableConfiguration { public: /** * Create a new properties config. */ KisPropertiesConfiguration(); ~KisPropertiesConfiguration() override; /** * Deep copy the properties \p rhs */ KisPropertiesConfiguration(const KisPropertiesConfiguration& rhs); /** * Deep copy the properties \p rhs */ KisPropertiesConfiguration& operator=(const KisPropertiesConfiguration& rhs); public: /** * Fill the properties configuration object from the XML encoded representation in s. * This function use the "Legacy" style XML of the 1.x .kra file format. * @param xml the string that will be parsed as xml * @param clear if true, the properties map will be emptied. * @return true is the xml document could be parsed */ bool fromXML(const QString& xml, bool clear = true) override; /** * Fill the properties configuration object from the XML encoded representation in s. * This function use the "Legacy" style XML of the 1.x .kra file format. * * Note: the existing properties will not be cleared */ void fromXML(const QDomElement&) override; /** * Create a serialized version of this properties config * This function use the "Legacy" style XML of the 1.x .kra file format. */ void toXML(QDomDocument&, QDomElement&) const override; /** * Create a serialized version of this properties config * This function use the "Legacy" style XML of the 1.x .kra file format. */ QString toXML() const override; /** * @return true if the map contains a property with the specified name */ virtual bool hasProperty(const QString& name) const; /** * Set the property with name to value. */ virtual void setProperty(const QString & name, const QVariant & value); /** * Set value to the value associated with property name * * XXX: API alert: a setter that is prefixed with get? * * @return false if the specified property did not exist. */ virtual bool getProperty(const QString & name, QVariant & value) const; virtual QVariant getProperty(const QString & name) const; template T getPropertyLazy(const QString & name, const T &defaultValue) const { QVariant value = getProperty(name); return value.isValid() ? value.value() : defaultValue; } QString getPropertyLazy(const QString & name, const char *defaultValue) const { return getPropertyLazy(name, QString(defaultValue)); } int getInt(const QString & name, int def = 0) const; double getDouble(const QString & name, double def = 0.0) const; float getFloat(const QString& name, float def = 0.0) const; bool getBool(const QString & name, bool def = false) const; QString getString(const QString & name, const QString & def = QString()) const; KisCubicCurve getCubicCurve(const QString & name, const KisCubicCurve & curve = KisCubicCurve()) const; /** * @brief getColor fetch the given property as a KoColor. * * The color can be stored as *
    *
  • A KoColor *
  • A QColor *
  • A string that can be parsed as an XML color definition - *
  • A string that QColor can convert to a color (see http://doc.qt.io/qt-5/qcolor.html#setNamedColor) + *
  • A string that QColor can convert to a color (see https://doc.qt.io/qt-5/qcolor.html#setNamedColor) *
  • An integer that QColor can convert to a color *
* * @param name the name of the property * @param color the default value to be returned if the @param name does not exist. * @return returns the named property as a KoColor if the value can be converted to a color, * otherwise a empty KoColor is returned. */ KoColor getColor(const QString& name, const KoColor& color = KoColor()) const; QMap getProperties() const; /// Clear the map of properties void clearProperties(); /// Marks a property that should not be saved by toXML void setPropertyNotSaved(const QString & name); void removeProperty(const QString & name); /** * Get the keys of all the properties in the object */ virtual QList getPropertiesKeys() const; /** * Get a set of properties, which keys are prefixed with \p prefix. The settings object * \p config will have all these properties with the prefix stripped from them. */ void getPrefixedProperties(const QString &prefix, KisPropertiesConfiguration *config) const; /** * A convenience override */ void getPrefixedProperties(const QString &prefix, KisPropertiesConfigurationSP config) const; /** * Takes all the properties from \p config, adds \p prefix to all their keys and puths them * into this properties object */ void setPrefixedProperties(const QString &prefix, const KisPropertiesConfiguration *config); /** * A convenience override */ void setPrefixedProperties(const QString &prefix, const KisPropertiesConfigurationSP config); static QString escapeString(const QString &string); static QString unescapeString(const QString &string); void setProperty(const QString &name, const QStringList &value); QStringList getStringList(const QString &name, const QStringList &defaultValue = QStringList()) const; QStringList getPropertyLazy(const QString &name, const QStringList &defaultValue) const; public: void dump() const; private: struct Private; Private* const d; }; class KRITAIMAGE_EXPORT KisPropertiesConfigurationFactory : public KisSerializableConfigurationFactory { public: KisPropertiesConfigurationFactory(); ~KisPropertiesConfigurationFactory() override; KisSerializableConfigurationSP createDefault() override; KisSerializableConfigurationSP create(const QDomElement& e) override; private: struct Private; Private* const d; }; #endif diff --git a/libs/image/kis_psd_layer_style.h b/libs/image/kis_psd_layer_style.h index a536b19d8c..2711f296ff 100644 --- a/libs/image/kis_psd_layer_style.h +++ b/libs/image/kis_psd_layer_style.h @@ -1,101 +1,101 @@ /* * Copyright (c) 2014 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_PSD_LAYER_STYLE_H #define KIS_PSD_LAYER_STYLE_H class QIODevice; class QUuid; #include #include #include "kis_types.h" #include "kritaimage_export.h" class KisPSDLayerStyle; typedef QSharedPointer KisPSDLayerStyleSP; /** * @brief The KisPSDLayerStyle class implements loading, saving and applying * the PSD layer effects. * - * See http://www.tonton-pixel.com/Photoshop%20Additional%20File%20Formats/styles-file-format.html + * See https://www.tonton-pixel.com/Photoshop%20Additional%20File%20Formats/styles-file-format.html * */ class KRITAIMAGE_EXPORT KisPSDLayerStyle { public: explicit KisPSDLayerStyle(); virtual ~KisPSDLayerStyle(); KisPSDLayerStyle(const KisPSDLayerStyle& rhs); KisPSDLayerStyle operator=(const KisPSDLayerStyle& rhs); KisPSDLayerStyleSP clone() const; void clear(); QString name() const; void setName(const QString &value); QUuid uuid() const; void setUuid(const QUuid &value) const; QString psdUuid() const; void setPsdUuid(const QString &value) const; /** * \return true if all the styles are disabled */ bool isEmpty() const; bool isEnabled() const; void setEnabled(bool value); const psd_layer_effects_context* context() const; const psd_layer_effects_drop_shadow* dropShadow() const; const psd_layer_effects_inner_shadow* innerShadow() const; const psd_layer_effects_outer_glow* outerGlow() const; const psd_layer_effects_inner_glow* innerGlow() const; const psd_layer_effects_satin* satin() const; const psd_layer_effects_color_overlay* colorOverlay() const; const psd_layer_effects_gradient_overlay* gradientOverlay() const; const psd_layer_effects_pattern_overlay* patternOverlay() const; const psd_layer_effects_stroke* stroke() const; const psd_layer_effects_bevel_emboss* bevelAndEmboss() const; psd_layer_effects_context* context(); psd_layer_effects_drop_shadow* dropShadow(); psd_layer_effects_inner_shadow* innerShadow(); psd_layer_effects_outer_glow* outerGlow(); psd_layer_effects_inner_glow* innerGlow(); psd_layer_effects_satin* satin(); psd_layer_effects_color_overlay* colorOverlay(); psd_layer_effects_gradient_overlay* gradientOverlay(); psd_layer_effects_pattern_overlay* patternOverlay(); psd_layer_effects_stroke* stroke(); psd_layer_effects_bevel_emboss* bevelAndEmboss(); private: struct Private; Private * const d; }; #endif // KIS_PSD_LAYER_STYLE_H diff --git a/libs/image/kis_selection_filters.cpp b/libs/image/kis_selection_filters.cpp index 5c2a4ebacc..dd80a72161 100644 --- a/libs/image/kis_selection_filters.cpp +++ b/libs/image/kis_selection_filters.cpp @@ -1,875 +1,875 @@ /* * Copyright (c) 2005 Michael Thaler * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_selection_filters.h" #include #include #include "kis_convolution_painter.h" #include "kis_convolution_kernel.h" #include "kis_pixel_selection.h" #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define RINT(x) floor ((x) + 0.5) KisSelectionFilter::~KisSelectionFilter() { } KUndo2MagicString KisSelectionFilter::name() { return KUndo2MagicString(); } QRect KisSelectionFilter::changeRect(const QRect &rect, KisDefaultBoundsBaseSP defaultBounds) { Q_UNUSED(defaultBounds); return rect; } void KisSelectionFilter::computeBorder(qint32* circ, qint32 xradius, qint32 yradius) { qint32 i; qint32 diameter = xradius * 2 + 1; double tmp; for (i = 0; i < diameter; i++) { if (i > xradius) tmp = (i - xradius) - 0.5; else if (i < xradius) tmp = (xradius - i) - 0.5; else tmp = 0.0; - double divisor = (double) xradius * sqrt(xradius * xradius - tmp * tmp); + double divisor = (double) xradius; if (divisor == 0.0) { divisor = 1.0; } - circ[i] = (qint32) RINT(yradius / divisor); + circ[i] = (qint32) RINT(yradius * sqrt(xradius * xradius - tmp * tmp) / divisor); } } void KisSelectionFilter::rotatePointers(quint8** p, quint32 n) { quint32 i; quint8 *p0 = p[0]; for (i = 0; i < n - 1; i++) { p[i] = p[i + 1]; } p[i] = p0; } void KisSelectionFilter::computeTransition(quint8* transition, quint8** buf, qint32 width) { qint32 x = 0; if (width == 1) { if (buf[1][x] > 127 && (buf[0][x] < 128 || buf[2][x] < 128)) transition[x] = 255; else transition[x] = 0; return; } if (buf[1][x] > 127) { if (buf[0][x] < 128 || buf[0][x + 1] < 128 || buf[1][x + 1] < 128 || buf[2][x] < 128 || buf[2][x + 1] < 128) transition[x] = 255; else transition[x] = 0; } else transition[x] = 0; for (qint32 x = 1; x < width - 1; x++) { if (buf[1][x] >= 128) { if (buf[0][x - 1] < 128 || buf[0][x] < 128 || buf[0][x + 1] < 128 || buf[1][x - 1] < 128 || buf[1][x + 1] < 128 || buf[2][x - 1] < 128 || buf[2][x] < 128 || buf[2][x + 1] < 128) transition[x] = 255; else transition[x] = 0; } else transition[x] = 0; } if (buf[1][x] >= 128) { if (buf[0][x - 1] < 128 || buf[0][x] < 128 || buf[1][x - 1] < 128 || buf[2][x - 1] < 128 || buf[2][x] < 128) transition[x] = 255; else transition[x] = 0; } else transition[x] = 0; } KUndo2MagicString KisErodeSelectionFilter::name() { return kundo2_i18n("Erode Selection"); } QRect KisErodeSelectionFilter::changeRect(const QRect& rect, KisDefaultBoundsBaseSP defaultBounds) { Q_UNUSED(defaultBounds); const qint32 radius = 1; return rect.adjusted(-radius, -radius, radius, radius); } void KisErodeSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { // Erode (radius 1 pixel) a mask (1bpp) quint8* buf[3]; qint32 width = rect.width(); qint32 height = rect.height(); quint8* out = new quint8[width]; for (qint32 i = 0; i < 3; i++) buf[i] = new quint8[width + 2]; // load top of image pixelSelection->readBytes(buf[0] + 1, rect.x(), rect.y(), width, 1); buf[0][0] = buf[0][1]; buf[0][width + 1] = buf[0][width]; memcpy(buf[1], buf[0], width + 2); for (qint32 y = 0; y < height; y++) { if (y + 1 < height) { pixelSelection->readBytes(buf[2] + 1, rect.x(), rect.y() + y + 1, width, 1); buf[2][0] = buf[2][1]; buf[2][width + 1] = buf[2][width]; } else { memcpy(buf[2], buf[1], width + 2); } for (qint32 x = 0 ; x < width; x++) { qint32 min = 255; if (buf[0][x+1] < min) min = buf[0][x+1]; if (buf[1][x] < min) min = buf[1][x]; if (buf[1][x+1] < min) min = buf[1][x+1]; if (buf[1][x+2] < min) min = buf[1][x+2]; if (buf[2][x+1] < min) min = buf[2][x+1]; out[x] = min; } pixelSelection->writeBytes(out, rect.x(), rect.y() + y, width, 1); rotatePointers(buf, 3); } for (qint32 i = 0; i < 3; i++) delete[] buf[i]; delete[] out; } KUndo2MagicString KisDilateSelectionFilter::name() { return kundo2_i18n("Dilate Selection"); } QRect KisDilateSelectionFilter::changeRect(const QRect& rect, KisDefaultBoundsBaseSP defaultBounds) { Q_UNUSED(defaultBounds); const qint32 radius = 1; return rect.adjusted(-radius, -radius, radius, radius); } void KisDilateSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { // dilate (radius 1 pixel) a mask (1bpp) quint8* buf[3]; qint32 width = rect.width(); qint32 height = rect.height(); quint8* out = new quint8[width]; for (qint32 i = 0; i < 3; i++) buf[i] = new quint8[width + 2]; // load top of image pixelSelection->readBytes(buf[0] + 1, rect.x(), rect.y(), width, 1); buf[0][0] = buf[0][1]; buf[0][width + 1] = buf[0][width]; memcpy(buf[1], buf[0], width + 2); for (qint32 y = 0; y < height; y++) { if (y + 1 < height) { pixelSelection->readBytes(buf[2] + 1, rect.x(), rect.y() + y + 1, width, 1); buf[2][0] = buf[2][1]; buf[2][width + 1] = buf[2][width]; } else { memcpy(buf[2], buf[1], width + 2); } for (qint32 x = 0 ; x < width; x++) { qint32 max = 0; if (buf[0][x+1] > max) max = buf[0][x+1]; if (buf[1][x] > max) max = buf[1][x]; if (buf[1][x+1] > max) max = buf[1][x+1]; if (buf[1][x+2] > max) max = buf[1][x+2]; if (buf[2][x+1] > max) max = buf[2][x+1]; out[x] = max; } pixelSelection->writeBytes(out, rect.x(), rect.y() + y, width, 1); rotatePointers(buf, 3); } for (qint32 i = 0; i < 3; i++) delete[] buf[i]; delete[] out; } KisBorderSelectionFilter::KisBorderSelectionFilter(qint32 xRadius, qint32 yRadius) : m_xRadius(xRadius), m_yRadius(yRadius) { } KUndo2MagicString KisBorderSelectionFilter::name() { return kundo2_i18n("Border Selection"); } QRect KisBorderSelectionFilter::changeRect(const QRect& rect, KisDefaultBoundsBaseSP defaultBounds) { Q_UNUSED(defaultBounds); return rect.adjusted(-m_xRadius, -m_yRadius, m_xRadius, m_yRadius); } void KisBorderSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { if (m_xRadius <= 0 || m_yRadius <= 0) return; quint8 *buf[3]; quint8 **density; quint8 **transition; if (m_xRadius == 1 && m_yRadius == 1) { // optimize this case specifically quint8* source[3]; for (qint32 i = 0; i < 3; i++) source[i] = new quint8[rect.width()]; quint8* transition = new quint8[rect.width()]; pixelSelection->readBytes(source[0], rect.x(), rect.y(), rect.width(), 1); memcpy(source[1], source[0], rect.width()); if (rect.height() > 1) pixelSelection->readBytes(source[2], rect.x(), rect.y() + 1, rect.width(), 1); else memcpy(source[2], source[1], rect.width()); computeTransition(transition, source, rect.width()); pixelSelection->writeBytes(transition, rect.x(), rect.y(), rect.width(), 1); for (qint32 y = 1; y < rect.height(); y++) { rotatePointers(source, 3); if (y + 1 < rect.height()) pixelSelection->readBytes(source[2], rect.x(), rect.y() + y + 1, rect.width(), 1); else memcpy(source[2], source[1], rect.width()); computeTransition(transition, source, rect.width()); pixelSelection->writeBytes(transition, rect.x(), rect.y() + y, rect.width(), 1); } for (qint32 i = 0; i < 3; i++) delete[] source[i]; delete[] transition; return; } qint32* max = new qint32[rect.width() + 2 * m_xRadius]; for (qint32 i = 0; i < (rect.width() + 2 * m_xRadius); i++) max[i] = m_yRadius + 2; max += m_xRadius; for (qint32 i = 0; i < 3; i++) buf[i] = new quint8[rect.width()]; transition = new quint8*[m_yRadius + 1]; for (qint32 i = 0; i < m_yRadius + 1; i++) { transition[i] = new quint8[rect.width() + 2 * m_xRadius]; memset(transition[i], 0, rect.width() + 2 * m_xRadius); transition[i] += m_xRadius; } quint8* out = new quint8[rect.width()]; density = new quint8*[2 * m_xRadius + 1]; density += m_xRadius; for (qint32 x = 0; x < (m_xRadius + 1); x++) { // allocate density[][] density[ x] = new quint8[2 * m_yRadius + 1]; density[ x] += m_yRadius; density[-x] = density[x]; } for (qint32 x = 0; x < (m_xRadius + 1); x++) { // compute density[][] double tmpx, tmpy, dist; quint8 a; tmpx = x > 0.0 ? x - 0.5 : 0.0; for (qint32 y = 0; y < (m_yRadius + 1); y++) { tmpy = y > 0.0 ? y - 0.5 : 0.0; dist = ((tmpy * tmpy) / (m_yRadius * m_yRadius) + (tmpx * tmpx) / (m_xRadius * m_xRadius)); if (dist < 1.0) a = (quint8)(255 * (1.0 - sqrt(dist))); else a = 0; density[ x][ y] = a; density[ x][-y] = a; density[-x][ y] = a; density[-x][-y] = a; } } pixelSelection->readBytes(buf[0], rect.x(), rect.y(), rect.width(), 1); memcpy(buf[1], buf[0], rect.width()); if (rect.height() > 1) pixelSelection->readBytes(buf[2], rect.x(), rect.y() + 1, rect.width(), 1); else memcpy(buf[2], buf[1], rect.width()); computeTransition(transition[1], buf, rect.width()); for (qint32 y = 1; y < m_yRadius && y + 1 < rect.height(); y++) { // set up top of image rotatePointers(buf, 3); pixelSelection->readBytes(buf[2], rect.x(), rect.y() + y + 1, rect.width(), 1); computeTransition(transition[y + 1], buf, rect.width()); } for (qint32 x = 0; x < rect.width(); x++) { // set up max[] for top of image max[x] = -(m_yRadius + 7); for (qint32 j = 1; j < m_yRadius + 1; j++) if (transition[j][x]) { max[x] = j; break; } } for (qint32 y = 0; y < rect.height(); y++) { // main calculation loop rotatePointers(buf, 3); rotatePointers(transition, m_yRadius + 1); if (y < rect.height() - (m_yRadius + 1)) { pixelSelection->readBytes(buf[2], rect.x(), rect.y() + y + m_yRadius + 1, rect.width(), 1); computeTransition(transition[m_yRadius], buf, rect.width()); } else memcpy(transition[m_yRadius], transition[m_yRadius - 1], rect.width()); for (qint32 x = 0; x < rect.width(); x++) { // update max array if (max[x] < 1) { if (max[x] <= -m_yRadius) { if (transition[m_yRadius][x]) max[x] = m_yRadius; else max[x]--; } else if (transition[-max[x]][x]) max[x] = -max[x]; else if (transition[-max[x] + 1][x]) max[x] = -max[x] + 1; else max[x]--; } else max[x]--; if (max[x] < -m_yRadius - 1) max[x] = -m_yRadius - 1; } quint8 last_max = max[0][density[-1]]; qint32 last_index = 1; for (qint32 x = 0 ; x < rect.width(); x++) { // render scan line last_index--; if (last_index >= 0) { last_max = 0; for (qint32 i = m_xRadius; i >= 0; i--) if (max[x + i] <= m_yRadius && max[x + i] >= -m_yRadius && density[i][max[x+i]] > last_max) { last_max = density[i][max[x + i]]; last_index = i; } out[x] = last_max; } else { last_max = 0; for (qint32 i = m_xRadius; i >= -m_xRadius; i--) if (max[x + i] <= m_yRadius && max[x + i] >= -m_yRadius && density[i][max[x + i]] > last_max) { last_max = density[i][max[x + i]]; last_index = i; } out[x] = last_max; } if (last_max == 0) { qint32 i; for (i = x + 1; i < rect.width(); i++) { if (max[i] >= -m_yRadius) break; } if (i - x > m_xRadius) { for (; x < i - m_xRadius; x++) out[x] = 0; x--; } last_index = m_xRadius; } } pixelSelection->writeBytes(out, rect.x(), rect.y() + y, rect.width(), 1); } delete [] out; for (qint32 i = 0; i < 3; i++) delete[] buf[i]; max -= m_xRadius; delete[] max; for (qint32 i = 0; i < m_yRadius + 1; i++) { transition[i] -= m_xRadius; delete transition[i]; } delete[] transition; for (qint32 i = 0; i < m_xRadius + 1 ; i++) { density[i] -= m_yRadius; delete density[i]; } density -= m_xRadius; delete[] density; } KisFeatherSelectionFilter::KisFeatherSelectionFilter(qint32 radius) : m_radius(radius) { } KUndo2MagicString KisFeatherSelectionFilter::name() { return kundo2_i18n("Feather Selection"); } QRect KisFeatherSelectionFilter::changeRect(const QRect& rect, KisDefaultBoundsBaseSP defaultBounds) { Q_UNUSED(defaultBounds); return rect.adjusted(-m_radius, -m_radius, m_radius, m_radius); } void KisFeatherSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { // compute horizontal kernel const uint kernelSize = m_radius * 2 + 1; Eigen::Matrix gaussianMatrix(1, kernelSize); const qreal multiplicand = 1.0 / (2.0 * M_PI * m_radius * m_radius); const qreal exponentMultiplicand = 1.0 / (2.0 * m_radius * m_radius); for (uint x = 0; x < kernelSize; x++) { uint xDistance = qAbs((int)m_radius - (int)x); gaussianMatrix(0, x) = multiplicand * exp( -(qreal)((xDistance * xDistance) + (m_radius * m_radius)) * exponentMultiplicand ); } KisConvolutionKernelSP kernelHoriz = KisConvolutionKernel::fromMatrix(gaussianMatrix, 0, gaussianMatrix.sum()); KisConvolutionKernelSP kernelVertical = KisConvolutionKernel::fromMatrix(gaussianMatrix.transpose(), 0, gaussianMatrix.sum()); KisPaintDeviceSP interm = new KisPaintDevice(pixelSelection->colorSpace()); interm->prepareClone(pixelSelection); KisConvolutionPainter horizPainter(interm); horizPainter.setChannelFlags(interm->colorSpace()->channelFlags(false, true)); horizPainter.applyMatrix(kernelHoriz, pixelSelection, rect.topLeft(), rect.topLeft(), rect.size(), BORDER_REPEAT); horizPainter.end(); KisConvolutionPainter verticalPainter(pixelSelection); verticalPainter.setChannelFlags(pixelSelection->colorSpace()->channelFlags(false, true)); verticalPainter.applyMatrix(kernelVertical, interm, rect.topLeft(), rect.topLeft(), rect.size(), BORDER_REPEAT); verticalPainter.end(); } KisGrowSelectionFilter::KisGrowSelectionFilter(qint32 xRadius, qint32 yRadius) : m_xRadius(xRadius), m_yRadius(yRadius) { } KUndo2MagicString KisGrowSelectionFilter::name() { return kundo2_i18n("Grow Selection"); } QRect KisGrowSelectionFilter::changeRect(const QRect& rect, KisDefaultBoundsBaseSP defaultBounds) { Q_UNUSED(defaultBounds); return rect.adjusted(-m_xRadius, -m_yRadius, m_xRadius, m_yRadius); } void KisGrowSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { if (m_xRadius <= 0 || m_yRadius <= 0) return; /** * Much code resembles Shrink filter, so please fix bugs * in both filters */ quint8 **buf; // caches the region's pixel data quint8 **max; // caches the largest values for each column max = new quint8* [rect.width() + 2 * m_xRadius]; buf = new quint8* [m_yRadius + 1]; for (qint32 i = 0; i < m_yRadius + 1; i++) { buf[i] = new quint8[rect.width()]; } quint8* buffer = new quint8[(rect.width() + 2 * m_xRadius) *(m_yRadius + 1)]; for (qint32 i = 0; i < rect.width() + 2 * m_xRadius; i++) { if (i < m_xRadius) max[i] = buffer; else if (i < rect.width() + m_xRadius) max[i] = &buffer[(m_yRadius + 1) * (i - m_xRadius)]; else max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius - 1)]; for (qint32 j = 0; j < m_xRadius + 1; j++) max[i][j] = 0; } /* offset the max pointer by m_xRadius so the range of the array is [-m_xRadius] to [region->w + m_xRadius] */ max += m_xRadius; quint8* out = new quint8[ rect.width()]; // holds the new scan line we are computing qint32* circ = new qint32[ 2 * m_xRadius + 1 ]; // holds the y coords of the filter's mask computeBorder(circ, m_xRadius, m_yRadius); /* offset the circ pointer by m_xRadius so the range of the array is [-m_xRadius] to [m_xRadius] */ circ += m_xRadius; memset(buf[0], 0, rect.width()); for (qint32 i = 0; i < m_yRadius && i < rect.height(); i++) { // load top of image pixelSelection->readBytes(buf[i + 1], rect.x(), rect.y() + i, rect.width(), 1); } for (qint32 x = 0; x < rect.width() ; x++) { // set up max for top of image max[x][0] = 0; // buf[0][x] is always 0 max[x][1] = buf[1][x]; // MAX (buf[1][x], max[x][0]) always = buf[1][x] for (qint32 j = 2; j < m_yRadius + 1; j++) { max[x][j] = MAX(buf[j][x], max[x][j-1]); } } for (qint32 y = 0; y < rect.height(); y++) { rotatePointers(buf, m_yRadius + 1); if (y < rect.height() - (m_yRadius)) pixelSelection->readBytes(buf[m_yRadius], rect.x(), rect.y() + y + m_yRadius, rect.width(), 1); else memset(buf[m_yRadius], 0, rect.width()); for (qint32 x = 0; x < rect.width(); x++) { /* update max array */ for (qint32 i = m_yRadius; i > 0; i--) { max[x][i] = MAX(MAX(max[x][i - 1], buf[i - 1][x]), buf[i][x]); } max[x][0] = buf[0][x]; } qint32 last_max = max[0][circ[-1]]; qint32 last_index = 1; for (qint32 x = 0; x < rect.width(); x++) { /* render scan line */ last_index--; if (last_index >= 0) { if (last_max == 255) out[x] = 255; else { last_max = 0; for (qint32 i = m_xRadius; i >= 0; i--) if (last_max < max[x + i][circ[i]]) { last_max = max[x + i][circ[i]]; last_index = i; } out[x] = last_max; } } else { last_index = m_xRadius; last_max = max[x + m_xRadius][circ[m_xRadius]]; for (qint32 i = m_xRadius - 1; i >= -m_xRadius; i--) if (last_max < max[x + i][circ[i]]) { last_max = max[x + i][circ[i]]; last_index = i; } out[x] = last_max; } } pixelSelection->writeBytes(out, rect.x(), rect.y() + y, rect.width(), 1); } /* undo the offsets to the pointers so we can free the malloced memory */ circ -= m_xRadius; max -= m_xRadius; delete[] circ; delete[] buffer; delete[] max; for (qint32 i = 0; i < m_yRadius + 1; i++) delete[] buf[i]; delete[] buf; delete[] out; } KisShrinkSelectionFilter::KisShrinkSelectionFilter(qint32 xRadius, qint32 yRadius, bool edgeLock) : m_xRadius(xRadius), m_yRadius(yRadius), m_edgeLock(edgeLock) { } KUndo2MagicString KisShrinkSelectionFilter::name() { return kundo2_i18n("Shrink Selection"); } QRect KisShrinkSelectionFilter::changeRect(const QRect& rect, KisDefaultBoundsBaseSP defaultBounds) { Q_UNUSED(defaultBounds); return rect.adjusted(-m_xRadius, -m_yRadius, m_xRadius, m_yRadius); } void KisShrinkSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { if (m_xRadius <= 0 || m_yRadius <= 0) return; /* pretty much the same as fatten_region only different blame all bugs in this function on jaycox@gimp.org */ /* If edge_lock is true we assume that pixels outside the region we are passed are identical to the edge pixels. If edge_lock is false, we assume that pixels outside the region are 0 */ quint8 **buf; // caches the region's pixels quint8 **max; // caches the smallest values for each column qint32 last_max, last_index; max = new quint8* [rect.width() + 2 * m_xRadius]; buf = new quint8* [m_yRadius + 1]; for (qint32 i = 0; i < m_yRadius + 1; i++) { buf[i] = new quint8[rect.width()]; } qint32 buffer_size = (rect.width() + 2 * m_xRadius + 1) * (m_yRadius + 1); quint8* buffer = new quint8[buffer_size]; if (m_edgeLock) memset(buffer, 255, buffer_size); else memset(buffer, 0, buffer_size); for (qint32 i = 0; i < rect.width() + 2 * m_xRadius; i++) { if (i < m_xRadius) if (m_edgeLock) max[i] = buffer; else max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius)]; else if (i < rect.width() + m_xRadius) max[i] = &buffer[(m_yRadius + 1) * (i - m_xRadius)]; else if (m_edgeLock) max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius - 1)]; else max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius)]; } if (!m_edgeLock) for (qint32 j = 0 ; j < m_xRadius + 1; j++) max[0][j] = 0; // offset the max pointer by m_xRadius so the range of the array is [-m_xRadius] to [region->w + m_xRadius] max += m_xRadius; quint8* out = new quint8[rect.width()]; // holds the new scan line we are computing qint32* circ = new qint32[2 * m_xRadius + 1]; // holds the y coords of the filter's mask computeBorder(circ, m_xRadius, m_yRadius); // offset the circ pointer by m_xRadius so the range of the array is [-m_xRadius] to [m_xRadius] circ += m_xRadius; for (qint32 i = 0; i < m_yRadius && i < rect.height(); i++) // load top of image pixelSelection->readBytes(buf[i + 1], rect.x(), rect.y() + i, rect.width(), 1); if (m_edgeLock) memcpy(buf[0], buf[1], rect.width()); else memset(buf[0], 0, rect.width()); for (qint32 x = 0; x < rect.width(); x++) { // set up max for top of image max[x][0] = buf[0][x]; for (qint32 j = 1; j < m_yRadius + 1; j++) max[x][j] = MIN(buf[j][x], max[x][j-1]); } for (qint32 y = 0; y < rect.height(); y++) { rotatePointers(buf, m_yRadius + 1); if (y < rect.height() - m_yRadius) pixelSelection->readBytes(buf[m_yRadius], rect.x(), rect.y() + y + m_yRadius, rect.width(), 1); else if (m_edgeLock) memcpy(buf[m_yRadius], buf[m_yRadius - 1], rect.width()); else memset(buf[m_yRadius], 0, rect.width()); for (qint32 x = 0 ; x < rect.width(); x++) { // update max array for (qint32 i = m_yRadius; i > 0; i--) { max[x][i] = MIN(MIN(max[x][i - 1], buf[i - 1][x]), buf[i][x]); } max[x][0] = buf[0][x]; } last_max = max[0][circ[-1]]; last_index = 0; for (qint32 x = 0 ; x < rect.width(); x++) { // render scan line last_index--; if (last_index >= 0) { if (last_max == 0) out[x] = 0; else { last_max = 255; for (qint32 i = m_xRadius; i >= 0; i--) if (last_max > max[x + i][circ[i]]) { last_max = max[x + i][circ[i]]; last_index = i; } out[x] = last_max; } } else { last_index = m_xRadius; last_max = max[x + m_xRadius][circ[m_xRadius]]; for (qint32 i = m_xRadius - 1; i >= -m_xRadius; i--) if (last_max > max[x + i][circ[i]]) { last_max = max[x + i][circ[i]]; last_index = i; } out[x] = last_max; } } pixelSelection->writeBytes(out, rect.x(), rect.y() + y, rect.width(), 1); } // undo the offsets to the pointers so we can free the malloced memory circ -= m_xRadius; max -= m_xRadius; delete[] circ; delete[] buffer; delete[] max; for (qint32 i = 0; i < m_yRadius + 1; i++) delete[] buf[i]; delete[] buf; delete[] out; } KUndo2MagicString KisSmoothSelectionFilter::name() { return kundo2_i18n("Smooth Selection"); } QRect KisSmoothSelectionFilter::changeRect(const QRect& rect, KisDefaultBoundsBaseSP defaultBounds) { Q_UNUSED(defaultBounds); const qint32 radius = 1; return rect.adjusted(-radius, -radius, radius, radius); } void KisSmoothSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { // Simple convolution filter to smooth a mask (1bpp) quint8 *buf[3]; qint32 width = rect.width(); qint32 height = rect.height(); quint8* out = new quint8[width]; for (qint32 i = 0; i < 3; i++) buf[i] = new quint8[width + 2]; // load top of image pixelSelection->readBytes(buf[0] + 1, rect.x(), rect.y(), width, 1); buf[0][0] = buf[0][1]; buf[0][width + 1] = buf[0][width]; memcpy(buf[1], buf[0], width + 2); for (qint32 y = 0; y < height; y++) { if (y + 1 < height) { pixelSelection->readBytes(buf[2] + 1, rect.x(), rect.y() + y + 1, width, 1); buf[2][0] = buf[2][1]; buf[2][width + 1] = buf[2][width]; } else { memcpy(buf[2], buf[1], width + 2); } for (qint32 x = 0 ; x < width; x++) { qint32 value = (buf[0][x] + buf[0][x+1] + buf[0][x+2] + buf[1][x] + buf[2][x+1] + buf[1][x+2] + buf[2][x] + buf[1][x+1] + buf[2][x+2]); out[x] = value / 9; } pixelSelection->writeBytes(out, rect.x(), rect.y() + y, width, 1); rotatePointers(buf, 3); } for (qint32 i = 0; i < 3; i++) delete[] buf[i]; delete[] out; } KUndo2MagicString KisInvertSelectionFilter::name() { return kundo2_i18n("Invert Selection"); } QRect KisInvertSelectionFilter::changeRect(const QRect &rect, KisDefaultBoundsBaseSP defaultBounds) { Q_UNUSED(rect); return defaultBounds->bounds(); } void KisInvertSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { Q_UNUSED(rect); pixelSelection->invert(); } diff --git a/libs/image/lazybrush/patched_boykov_kolmogorov_max_flow.hpp b/libs/image/lazybrush/patched_boykov_kolmogorov_max_flow.hpp index b2a13e70de..0373186de3 100644 --- a/libs/image/lazybrush/patched_boykov_kolmogorov_max_flow.hpp +++ b/libs/image/lazybrush/patched_boykov_kolmogorov_max_flow.hpp @@ -1,882 +1,882 @@ // Copyright (c) 2006, Stephan Diederich // // This code may be used under either of the following two licences: // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. OF SUCH DAMAGE. // // Or: // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) +// https://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_BOYKOV_KOLMOGOROV_MAX_FLOW_HPP #define BOOST_BOYKOV_KOLMOGOROV_MAX_FLOW_HPP #include #include #include #include #include #include #include // for std::min and std::max #include #include #include #include #include #include #include #include // The algorithm impelemented here is described in: // // Boykov, Y., Kolmogorov, V. "An Experimental Comparison of Min-Cut/Max-Flow // Algorithms for Energy Minimization in Vision", In IEEE Transactions on // Pattern Analysis and Machine Intelligence, vol. 26, no. 9, pp. 1124-1137, // Sep 2004. // // For further reading, also see: // // Kolmogorov, V. "Graph Based Algorithms for Scene Reconstruction from Two or // More Views". PhD thesis, Cornell University, Sep 2003. namespace boost { namespace detail { template class bk_max_flow { typedef typename property_traits::value_type tEdgeVal; typedef graph_traits tGraphTraits; typedef typename tGraphTraits::vertex_iterator vertex_iterator; typedef typename tGraphTraits::vertex_descriptor vertex_descriptor; typedef typename tGraphTraits::edge_descriptor edge_descriptor; typedef typename tGraphTraits::edge_iterator edge_iterator; typedef typename tGraphTraits::out_edge_iterator out_edge_iterator; typedef boost::queue tQueue; //queue of vertices, used in adoption-stage typedef typename property_traits::value_type tColorValue; typedef color_traits tColorTraits; typedef typename property_traits::value_type tDistanceVal; public: bk_max_flow(Graph& g, EdgeCapacityMap cap, ResidualCapacityEdgeMap res, ReverseEdgeMap rev, PredecessorMap pre, ColorMap color, DistanceMap dist, IndexMap idx, vertex_descriptor src, vertex_descriptor sink): m_g(g), m_index_map(idx), m_cap_map(cap), m_res_cap_map(res), m_rev_edge_map(rev), m_pre_map(pre), m_tree_map(color), m_dist_map(dist), m_source(src), m_sink(sink), m_active_nodes(), m_in_active_list_vec(num_vertices(g), false), m_in_active_list_map(make_iterator_property_map(m_in_active_list_vec.begin(), m_index_map)), m_has_parent_vec(num_vertices(g), false), m_has_parent_map(make_iterator_property_map(m_has_parent_vec.begin(), m_index_map)), m_time_vec(num_vertices(g), 0), m_time_map(make_iterator_property_map(m_time_vec.begin(), m_index_map)), m_flow(0), m_time(1), m_last_grow_vertex(graph_traits::null_vertex()){ // initialize the color-map with gray-values vertex_iterator vi, v_end; for(boost::tie(vi, v_end) = vertices(m_g); vi != v_end; ++vi){ set_tree(*vi, tColorTraits::gray()); } // Initialize flow to zero which means initializing // the residual capacity equal to the capacity edge_iterator ei, e_end; for(boost::tie(ei, e_end) = edges(m_g); ei != e_end; ++ei) { put(m_res_cap_map, *ei, get(m_cap_map, *ei)); BOOST_ASSERT(get(m_rev_edge_map, get(m_rev_edge_map, *ei)) == *ei); //check if the reverse edge map is build up properly } //init the search trees with the two terminals set_tree(m_source, tColorTraits::black()); set_tree(m_sink, tColorTraits::white()); put(m_time_map, m_source, 1); put(m_time_map, m_sink, 1); } tEdgeVal max_flow(){ //augment direct paths from SOURCE->SINK and SOURCE->VERTEX->SINK augment_direct_paths(); //start the main-loop while(true){ bool path_found; edge_descriptor connecting_edge; boost::tie(connecting_edge, path_found) = grow(); //find a path from source to sink if(!path_found){ //we're finished, no more paths were found break; } ++m_time; augment(connecting_edge); //augment that path adopt(); //rebuild search tree structure } return m_flow; } // the complete class is protected, as we want access to members in // derived test-class (see test/boykov_kolmogorov_max_flow_test.cpp) protected: void augment_direct_paths(){ // in a first step, we augment all direct paths from source->NODE->sink // and additionally paths from source->sink. This improves especially // graphcuts for segmentation, as most of the nodes have source/sink // connects but shouldn't have an impact on other maxflow problems // (this is done in grow() anyway) out_edge_iterator ei, e_end; for(boost::tie(ei, e_end) = out_edges(m_source, m_g); ei != e_end; ++ei){ edge_descriptor from_source = *ei; vertex_descriptor current_node = target(from_source, m_g); if(current_node == m_sink){ tEdgeVal cap = get(m_res_cap_map, from_source); put(m_res_cap_map, from_source, 0); m_flow += cap; continue; } edge_descriptor to_sink; bool is_there; boost::tie(to_sink, is_there) = lookup_edge(current_node, m_sink, m_g); if(is_there){ tEdgeVal cap_from_source = get(m_res_cap_map, from_source); tEdgeVal cap_to_sink = get(m_res_cap_map, to_sink); if(cap_from_source > cap_to_sink){ set_tree(current_node, tColorTraits::black()); add_active_node(current_node); set_edge_to_parent(current_node, from_source); put(m_dist_map, current_node, 1); put(m_time_map, current_node, 1); // add stuff to flow and update residuals. we don't need to // update reverse_edges, as incoming/outgoing edges to/from // source/sink don't count for max-flow put(m_res_cap_map, from_source, get(m_res_cap_map, from_source) - cap_to_sink); put(m_res_cap_map, to_sink, 0); m_flow += cap_to_sink; } else if(cap_to_sink > 0){ set_tree(current_node, tColorTraits::white()); add_active_node(current_node); set_edge_to_parent(current_node, to_sink); put(m_dist_map, current_node, 1); put(m_time_map, current_node, 1); // add stuff to flow and update residuals. we don't need to update // reverse_edges, as incoming/outgoing edges to/from source/sink // don't count for max-flow put(m_res_cap_map, to_sink, get(m_res_cap_map, to_sink) - cap_from_source); put(m_res_cap_map, from_source, 0); m_flow += cap_from_source; } } else if(get(m_res_cap_map, from_source)){ // there is no sink connect, so we can't augment this path, but to // avoid adding m_source to the active nodes, we just activate this // node and set the appropriate things set_tree(current_node, tColorTraits::black()); set_edge_to_parent(current_node, from_source); put(m_dist_map, current_node, 1); put(m_time_map, current_node, 1); add_active_node(current_node); } } for(boost::tie(ei, e_end) = out_edges(m_sink, m_g); ei != e_end; ++ei){ edge_descriptor to_sink = get(m_rev_edge_map, *ei); vertex_descriptor current_node = source(to_sink, m_g); if(get(m_res_cap_map, to_sink)){ set_tree(current_node, tColorTraits::white()); set_edge_to_parent(current_node, to_sink); put(m_dist_map, current_node, 1); put(m_time_map, current_node, 1); add_active_node(current_node); } } } /** * Returns a pair of an edge and a boolean. if the bool is true, the * edge is a connection of a found path from s->t , read "the link" and * source(returnVal, m_g) is the end of the path found in the source-tree * target(returnVal, m_g) is the beginning of the path found in the sink-tree */ std::pair grow(){ BOOST_ASSERT(m_orphans.empty()); vertex_descriptor current_node; while((current_node = get_next_active_node()) != graph_traits::null_vertex()){ //if there is one BOOST_ASSERT(get_tree(current_node) != tColorTraits::gray() && (has_parent(current_node) || current_node == m_source || current_node == m_sink)); if(get_tree(current_node) == tColorTraits::black()){ //source tree growing out_edge_iterator ei, e_end; if(current_node != m_last_grow_vertex){ m_last_grow_vertex = current_node; boost::tie(m_last_grow_edge_it, m_last_grow_edge_end) = out_edges(current_node, m_g); } for(; m_last_grow_edge_it != m_last_grow_edge_end; ++m_last_grow_edge_it) { edge_descriptor out_edge = *m_last_grow_edge_it; if(get(m_res_cap_map, out_edge) > 0){ //check if we have capacity left on this edge vertex_descriptor other_node = target(out_edge, m_g); if(get_tree(other_node) == tColorTraits::gray()){ //it's a free node set_tree(other_node, tColorTraits::black()); //acquire other node to our search tree set_edge_to_parent(other_node, out_edge); //set us as parent put(m_dist_map, other_node, get(m_dist_map, current_node) + 1); //and update the distance-heuristic put(m_time_map, other_node, get(m_time_map, current_node)); add_active_node(other_node); } else if(get_tree(other_node) == tColorTraits::black()) { // we do this to get shorter paths. check if we are nearer to // the source as its parent is if(is_closer_to_terminal(current_node, other_node)){ set_edge_to_parent(other_node, out_edge); put(m_dist_map, other_node, get(m_dist_map, current_node) + 1); put(m_time_map, other_node, get(m_time_map, current_node)); } } else{ BOOST_ASSERT(get_tree(other_node)==tColorTraits::white()); //kewl, found a path from one to the other search tree, return // the connecting edge in src->sink dir return std::make_pair(out_edge, true); } } } //for all out-edges } //source-tree-growing else{ BOOST_ASSERT(get_tree(current_node) == tColorTraits::white()); out_edge_iterator ei, e_end; if(current_node != m_last_grow_vertex){ m_last_grow_vertex = current_node; boost::tie(m_last_grow_edge_it, m_last_grow_edge_end) = out_edges(current_node, m_g); } for(; m_last_grow_edge_it != m_last_grow_edge_end; ++m_last_grow_edge_it){ edge_descriptor in_edge = get(m_rev_edge_map, *m_last_grow_edge_it); if(get(m_res_cap_map, in_edge) > 0){ //check if there is capacity left vertex_descriptor other_node = source(in_edge, m_g); if(get_tree(other_node) == tColorTraits::gray()){ //it's a free node set_tree(other_node, tColorTraits::white()); //acquire that node to our search tree set_edge_to_parent(other_node, in_edge); //set us as parent add_active_node(other_node); //activate that node put(m_dist_map, other_node, get(m_dist_map, current_node) + 1); //set its distance put(m_time_map, other_node, get(m_time_map, current_node));//and time } else if(get_tree(other_node) == tColorTraits::white()){ if(is_closer_to_terminal(current_node, other_node)){ //we are closer to the sink than its parent is, so we "adopt" him set_edge_to_parent(other_node, in_edge); put(m_dist_map, other_node, get(m_dist_map, current_node) + 1); put(m_time_map, other_node, get(m_time_map, current_node)); } } else{ BOOST_ASSERT(get_tree(other_node)==tColorTraits::black()); //kewl, found a path from one to the other search tree, // return the connecting edge in src->sink dir return std::make_pair(in_edge, true); } } } //for all out-edges } //sink-tree growing //all edges of that node are processed, and no more paths were found. // remove if from the front of the active queue finish_node(current_node); } //while active_nodes not empty //no active nodes anymore and no path found, we're done return std::make_pair(edge_descriptor(), false); } /** * augments path from s->t and updates residual graph * source(e, m_g) is the end of the path found in the source-tree * target(e, m_g) is the beginning of the path found in the sink-tree * this phase generates orphans on satured edges, if the attached verts are * from different search-trees orphans are ordered in distance to * sink/source. first the farest from the source are front_inserted into * the orphans list, and after that the sink-tree-orphans are * front_inserted. when going to adoption stage the orphans are popped_front, * and so we process the nearest verts to the terminals first */ void augment(edge_descriptor e) { BOOST_ASSERT(get_tree(target(e, m_g)) == tColorTraits::white()); BOOST_ASSERT(get_tree(source(e, m_g)) == tColorTraits::black()); BOOST_ASSERT(m_orphans.empty()); const tEdgeVal bottleneck = find_bottleneck(e); //now we push the found flow through the path //for each edge we saturate we have to look for the verts that belong to that edge, one of them becomes an orphans //now process the connecting edge put(m_res_cap_map, e, get(m_res_cap_map, e) - bottleneck); BOOST_ASSERT(get(m_res_cap_map, e) >= 0); put(m_res_cap_map, get(m_rev_edge_map, e), get(m_res_cap_map, get(m_rev_edge_map, e)) + bottleneck); //now we follow the path back to the source vertex_descriptor current_node = source(e, m_g); while(current_node != m_source){ edge_descriptor pred = get_edge_to_parent(current_node); const tEdgeVal new_pred_cap = get(m_res_cap_map, pred) - bottleneck; put(m_res_cap_map, pred, new_pred_cap); BOOST_ASSERT(get(m_res_cap_map, pred) >= 0); const edge_descriptor pred_rev = get(m_rev_edge_map, pred); put(m_res_cap_map, pred_rev, get(m_res_cap_map, pred_rev) + bottleneck); if(new_pred_cap == 0){ set_no_parent(current_node); m_orphans.push_front(current_node); } current_node = source(pred, m_g); } //then go forward in the sink-tree current_node = target(e, m_g); while(current_node != m_sink){ edge_descriptor pred = get_edge_to_parent(current_node); const tEdgeVal new_pred_cap = get(m_res_cap_map, pred) - bottleneck; put(m_res_cap_map, pred, new_pred_cap); BOOST_ASSERT(get(m_res_cap_map, pred) >= 0); const edge_descriptor pred_rev = get(m_rev_edge_map, pred); put(m_res_cap_map, pred_rev, get(m_res_cap_map, pred_rev) + bottleneck); if(new_pred_cap == 0){ set_no_parent(current_node); m_orphans.push_front(current_node); } current_node = target(pred, m_g); } //and add it to the max-flow m_flow += bottleneck; } /** * returns the bottleneck of a s->t path (end_of_path is last vertex in * source-tree, begin_of_path is first vertex in sink-tree) */ inline tEdgeVal find_bottleneck(edge_descriptor e){ BOOST_USING_STD_MIN(); tEdgeVal minimum_cap = get(m_res_cap_map, e); vertex_descriptor current_node = source(e, m_g); //first go back in the source tree while(current_node != m_source){ edge_descriptor pred = get_edge_to_parent(current_node); minimum_cap = min BOOST_PREVENT_MACRO_SUBSTITUTION(minimum_cap, get(m_res_cap_map, pred)); current_node = source(pred, m_g); } //then go forward in the sink-tree current_node = target(e, m_g); while(current_node != m_sink){ edge_descriptor pred = get_edge_to_parent(current_node); minimum_cap = min BOOST_PREVENT_MACRO_SUBSTITUTION(minimum_cap, get(m_res_cap_map, pred)); current_node = target(pred, m_g); } return minimum_cap; } /** * rebuild search trees * empty the queue of orphans, and find new parents for them or just drop * them from the search trees */ void adopt(){ while(!m_orphans.empty() || !m_child_orphans.empty()){ vertex_descriptor current_node; if(m_child_orphans.empty()){ //get the next orphan from the main-queue and remove it current_node = m_orphans.front(); m_orphans.pop_front(); } else{ current_node = m_child_orphans.front(); m_child_orphans.pop(); } if(get_tree(current_node) == tColorTraits::black()){ //we're in the source-tree tDistanceVal min_distance = (std::numeric_limits::max)(); edge_descriptor new_parent_edge; out_edge_iterator ei, e_end; for(boost::tie(ei, e_end) = out_edges(current_node, m_g); ei != e_end; ++ei){ const edge_descriptor in_edge = get(m_rev_edge_map, *ei); BOOST_ASSERT(target(in_edge, m_g) == current_node); //we should be the target of this edge if(get(m_res_cap_map, in_edge) > 0){ vertex_descriptor other_node = source(in_edge, m_g); if(get_tree(other_node) == tColorTraits::black() && has_source_connect(other_node)){ if(get(m_dist_map, other_node) < min_distance){ min_distance = get(m_dist_map, other_node); new_parent_edge = in_edge; } } } } if(min_distance != (std::numeric_limits::max)()){ set_edge_to_parent(current_node, new_parent_edge); put(m_dist_map, current_node, min_distance + 1); put(m_time_map, current_node, m_time); } else{ put(m_time_map, current_node, 0); for(boost::tie(ei, e_end) = out_edges(current_node, m_g); ei != e_end; ++ei){ edge_descriptor in_edge = get(m_rev_edge_map, *ei); vertex_descriptor other_node = source(in_edge, m_g); if(get_tree(other_node) == tColorTraits::black() && other_node != m_source){ if(get(m_res_cap_map, in_edge) > 0){ add_active_node(other_node); } if(has_parent(other_node) && source(get_edge_to_parent(other_node), m_g) == current_node){ //we are the parent of that node //it has to find a new parent, too set_no_parent(other_node); m_child_orphans.push(other_node); } } } set_tree(current_node, tColorTraits::gray()); } //no parent found } //source-tree-adoption else{ //now we should be in the sink-tree, check that... BOOST_ASSERT(get_tree(current_node) == tColorTraits::white()); out_edge_iterator ei, e_end; edge_descriptor new_parent_edge; tDistanceVal min_distance = (std::numeric_limits::max)(); for(boost::tie(ei, e_end) = out_edges(current_node, m_g); ei != e_end; ++ei){ const edge_descriptor out_edge = *ei; if(get(m_res_cap_map, out_edge) > 0){ const vertex_descriptor other_node = target(out_edge, m_g); if(get_tree(other_node) == tColorTraits::white() && has_sink_connect(other_node)) if(get(m_dist_map, other_node) < min_distance){ min_distance = get(m_dist_map, other_node); new_parent_edge = out_edge; } } } if(min_distance != (std::numeric_limits::max)()){ set_edge_to_parent(current_node, new_parent_edge); put(m_dist_map, current_node, min_distance + 1); put(m_time_map, current_node, m_time); } else{ put(m_time_map, current_node, 0); for(boost::tie(ei, e_end) = out_edges(current_node, m_g); ei != e_end; ++ei){ const edge_descriptor out_edge = *ei; const vertex_descriptor other_node = target(out_edge, m_g); if(get_tree(other_node) == tColorTraits::white() && other_node != m_sink){ if(get(m_res_cap_map, out_edge) > 0){ add_active_node(other_node); } if(has_parent(other_node) && target(get_edge_to_parent(other_node), m_g) == current_node){ //we were it's parent, so it has to find a new one, too set_no_parent(other_node); m_child_orphans.push(other_node); } } } set_tree(current_node, tColorTraits::gray()); } //no parent found } //sink-tree adoption } //while !orphans.empty() } //adopt /** * return next active vertex if there is one, otherwise a null_vertex */ inline vertex_descriptor get_next_active_node(){ while(true){ if(m_active_nodes.empty()) return graph_traits::null_vertex(); vertex_descriptor v = m_active_nodes.front(); //if it has no parent, this node can't be active (if its not source or sink) if(!has_parent(v) && v != m_source && v != m_sink){ m_active_nodes.pop(); put(m_in_active_list_map, v, false); } else{ BOOST_ASSERT(get_tree(v) == tColorTraits::black() || get_tree(v) == tColorTraits::white()); return v; } } } /** * adds v as an active vertex, but only if its not in the list already */ inline void add_active_node(vertex_descriptor v){ BOOST_ASSERT(get_tree(v) != tColorTraits::gray()); if(get(m_in_active_list_map, v)){ if (m_last_grow_vertex == v) { m_last_grow_vertex = graph_traits::null_vertex(); } return; } else{ put(m_in_active_list_map, v, true); m_active_nodes.push(v); } } /** * finish_node removes a node from the front of the active queue (its called in grow phase, if no more paths can be found using this node) */ inline void finish_node(vertex_descriptor v){ BOOST_ASSERT(m_active_nodes.front() == v); m_active_nodes.pop(); put(m_in_active_list_map, v, false); m_last_grow_vertex = graph_traits::null_vertex(); } /** * removes a vertex from the queue of active nodes (actually this does nothing, * but checks if this node has no parent edge, as this is the criteria for * being no more active) */ inline void remove_active_node(vertex_descriptor v){ (void)v; // disable unused parameter warning BOOST_ASSERT(!has_parent(v)); } /** * returns the search tree of v; tColorValue::black() for source tree, * white() for sink tree, gray() for no tree */ inline tColorValue get_tree(vertex_descriptor v) const { return get(m_tree_map, v); } /** * sets search tree of v; tColorValue::black() for source tree, white() * for sink tree, gray() for no tree */ inline void set_tree(vertex_descriptor v, tColorValue t){ put(m_tree_map, v, t); } /** * returns edge to parent vertex of v; */ inline edge_descriptor get_edge_to_parent(vertex_descriptor v) const{ return get(m_pre_map, v); } /** * returns true if the edge stored in m_pre_map[v] is a valid entry */ inline bool has_parent(vertex_descriptor v) const{ return get(m_has_parent_map, v); } /** * sets edge to parent vertex of v; */ inline void set_edge_to_parent(vertex_descriptor v, edge_descriptor f_edge_to_parent){ BOOST_ASSERT(get(m_res_cap_map, f_edge_to_parent) > 0); put(m_pre_map, v, f_edge_to_parent); put(m_has_parent_map, v, true); } /** * removes the edge to parent of v (this is done by invalidating the * entry an additional map) */ inline void set_no_parent(vertex_descriptor v){ put(m_has_parent_map, v, false); } /** * checks if vertex v has a connect to the sink-vertex (@p m_sink) * @param v the vertex which is checked * @return true if a path to the sink was found, false if not */ inline bool has_sink_connect(vertex_descriptor v){ tDistanceVal current_distance = 0; vertex_descriptor current_vertex = v; while(true){ if(get(m_time_map, current_vertex) == m_time){ //we found a node which was already checked this round. use it for distance calculations current_distance += get(m_dist_map, current_vertex); break; } if(current_vertex == m_sink){ put(m_time_map, m_sink, m_time); break; } if(has_parent(current_vertex)){ //it has a parent, so get it current_vertex = target(get_edge_to_parent(current_vertex), m_g); ++current_distance; } else{ //no path found return false; } } current_vertex=v; while(get(m_time_map, current_vertex) != m_time){ put(m_dist_map, current_vertex, current_distance); --current_distance; put(m_time_map, current_vertex, m_time); current_vertex = target(get_edge_to_parent(current_vertex), m_g); } return true; } /** * checks if vertex v has a connect to the source-vertex (@p m_source) * @param v the vertex which is checked * @return true if a path to the source was found, false if not */ inline bool has_source_connect(vertex_descriptor v){ tDistanceVal current_distance = 0; vertex_descriptor current_vertex = v; while(true){ if(get(m_time_map, current_vertex) == m_time){ //we found a node which was already checked this round. use it for distance calculations current_distance += get(m_dist_map, current_vertex); break; } if(current_vertex == m_source){ put(m_time_map, m_source, m_time); break; } if(has_parent(current_vertex)){ //it has a parent, so get it current_vertex = source(get_edge_to_parent(current_vertex), m_g); ++current_distance; } else{ //no path found return false; } } current_vertex=v; while(get(m_time_map, current_vertex) != m_time){ put(m_dist_map, current_vertex, current_distance); --current_distance; put(m_time_map, current_vertex, m_time); current_vertex = source(get_edge_to_parent(current_vertex), m_g); } return true; } /** * returns true, if p is closer to a terminal than q */ inline bool is_closer_to_terminal(vertex_descriptor p, vertex_descriptor q){ //checks the timestamps first, to build no cycles, and after that the real distance return (get(m_time_map, q) <= get(m_time_map, p) && get(m_dist_map, q) > get(m_dist_map, p)+1); } //////// // member vars //////// Graph& m_g; IndexMap m_index_map; EdgeCapacityMap m_cap_map; ResidualCapacityEdgeMap m_res_cap_map; ReverseEdgeMap m_rev_edge_map; PredecessorMap m_pre_map; //stores paths found in the growth stage ColorMap m_tree_map; //maps each vertex into one of the two search tree or none (gray()) DistanceMap m_dist_map; //stores distance to source/sink nodes vertex_descriptor m_source; vertex_descriptor m_sink; tQueue m_active_nodes; std::vector m_in_active_list_vec; iterator_property_map::iterator, IndexMap> m_in_active_list_map; std::list m_orphans; tQueue m_child_orphans; // we use a second queuqe for child orphans, as they are FIFO processed std::vector m_has_parent_vec; iterator_property_map::iterator, IndexMap> m_has_parent_map; std::vector m_time_vec; //timestamp of each node, used for sink/source-path calculations iterator_property_map::iterator, IndexMap> m_time_map; tEdgeVal m_flow; long m_time; vertex_descriptor m_last_grow_vertex; out_edge_iterator m_last_grow_edge_it; out_edge_iterator m_last_grow_edge_end; }; } //namespace boost::detail /** * non-named-parameter version, given everything * this is the catch all version */ template typename property_traits::value_type boykov_kolmogorov_max_flow(Graph& g, CapacityEdgeMap cap, ResidualCapacityEdgeMap res_cap, ReverseEdgeMap rev_map, PredecessorMap pre_map, ColorMap color, DistanceMap dist, IndexMap idx, typename graph_traits::vertex_descriptor src, typename graph_traits::vertex_descriptor sink) { typedef typename graph_traits::vertex_descriptor vertex_descriptor; typedef typename graph_traits::edge_descriptor edge_descriptor; //as this method is the last one before we instantiate the solver, we do the concept checks here BOOST_CONCEPT_ASSERT(( VertexListGraphConcept )); //to have vertices(), num_vertices(), BOOST_CONCEPT_ASSERT(( EdgeListGraphConcept )); //to have edges() BOOST_CONCEPT_ASSERT(( IncidenceGraphConcept )); //to have source(), target() and out_edges() BOOST_CONCEPT_ASSERT(( ReadablePropertyMapConcept )); //read flow-values from edges BOOST_CONCEPT_ASSERT(( ReadWritePropertyMapConcept )); //write flow-values to residuals BOOST_CONCEPT_ASSERT(( ReadablePropertyMapConcept )); //read out reverse edges BOOST_CONCEPT_ASSERT(( ReadWritePropertyMapConcept )); //store predecessor there BOOST_CONCEPT_ASSERT(( ReadWritePropertyMapConcept )); //write corresponding tree BOOST_CONCEPT_ASSERT(( ReadWritePropertyMapConcept )); //write distance to source/sink BOOST_CONCEPT_ASSERT(( ReadablePropertyMapConcept )); //get index 0...|V|-1 BOOST_ASSERT(num_vertices(g) >= 2 && src != sink); detail::bk_max_flow< Graph, CapacityEdgeMap, ResidualCapacityEdgeMap, ReverseEdgeMap, PredecessorMap, ColorMap, DistanceMap, IndexMap > algo(g, cap, res_cap, rev_map, pre_map, color, dist, idx, src, sink); return algo.max_flow(); } /** * non-named-parameter version, given capacity, residucal_capacity, * reverse_edges, and an index map. */ template typename property_traits::value_type boykov_kolmogorov_max_flow(Graph& g, CapacityEdgeMap cap, ResidualCapacityEdgeMap res_cap, ReverseEdgeMap rev, IndexMap idx, typename graph_traits::vertex_descriptor src, typename graph_traits::vertex_descriptor sink) { typename graph_traits::vertices_size_type n_verts = num_vertices(g); std::vector::edge_descriptor> predecessor_vec(n_verts); std::vector color_vec(n_verts); std::vector::vertices_size_type> distance_vec(n_verts); return boykov_kolmogorov_max_flow( g, cap, res_cap, rev, make_iterator_property_map(predecessor_vec.begin(), idx), make_iterator_property_map(color_vec.begin(), idx), make_iterator_property_map(distance_vec.begin(), idx), idx, src, sink); } /** * non-named-parameter version, some given: capacity, residual_capacity, * reverse_edges, color_map and an index map. Use this if you are interested in * the minimum cut, as the color map provides that info. */ template typename property_traits::value_type boykov_kolmogorov_max_flow(Graph& g, CapacityEdgeMap cap, ResidualCapacityEdgeMap res_cap, ReverseEdgeMap rev, ColorMap color, IndexMap idx, typename graph_traits::vertex_descriptor src, typename graph_traits::vertex_descriptor sink) { typename graph_traits::vertices_size_type n_verts = num_vertices(g); std::vector::edge_descriptor> predecessor_vec(n_verts); std::vector::vertices_size_type> distance_vec(n_verts); return boykov_kolmogorov_max_flow( g, cap, res_cap, rev, make_iterator_property_map(predecessor_vec.begin(), idx), color, make_iterator_property_map(distance_vec.begin(), idx), idx, src, sink); } /** * named-parameter version, some given */ template typename property_traits::const_type>::value_type boykov_kolmogorov_max_flow(Graph& g, typename graph_traits::vertex_descriptor src, typename graph_traits::vertex_descriptor sink, const bgl_named_params& params) { return boykov_kolmogorov_max_flow( g, choose_const_pmap(get_param(params, edge_capacity), g, edge_capacity), choose_pmap(get_param(params, edge_residual_capacity), g, edge_residual_capacity), choose_const_pmap(get_param(params, edge_reverse), g, edge_reverse), choose_pmap(get_param(params, vertex_predecessor), g, vertex_predecessor), choose_pmap(get_param(params, vertex_color), g, vertex_color), choose_pmap(get_param(params, vertex_distance), g, vertex_distance), choose_const_pmap(get_param(params, vertex_index), g, vertex_index), src, sink); } /** * named-parameter version, none given */ template typename property_traits::const_type>::value_type boykov_kolmogorov_max_flow(Graph& g, typename graph_traits::vertex_descriptor src, typename graph_traits::vertex_descriptor sink) { bgl_named_params params(0); // bogus empty param return boykov_kolmogorov_max_flow(g, src, sink, params); } } // namespace boost #endif // BOOST_BOYKOV_KOLMOGOROV_MAX_FLOW_HPP diff --git a/libs/image/tiles3/kis_lockless_stack.h b/libs/image/tiles3/kis_lockless_stack.h index 6145af836a..2ec0cbc947 100644 --- a/libs/image/tiles3/kis_lockless_stack.h +++ b/libs/image/tiles3/kis_lockless_stack.h @@ -1,235 +1,235 @@ /* * Copyright (c) 2010 Dmitry Kazakov * * References: * * Maged M. Michael, Safe memory reclamation for dynamic * lock-free objects using atomic reads and writes, * Proceedings of the twenty-first annual symposium on * Principles of distributed computing, July 21-24, 2002, * Monterey, California * * * Idea of m_deleteBlockers is taken from Andrey Gulin's blog - * http://users.livejournal.com/_foreseer/34284.html + * https://users.livejournal.com/-foreseer/34284.html * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_LOCKLESS_STACK_H #define __KIS_LOCKLESS_STACK_H #include template class KisLocklessStack { private: struct Node { Node *next; T data; }; public: KisLocklessStack() { } ~KisLocklessStack() { freeList(m_top.fetchAndStoreOrdered(0)); freeList(m_freeNodes.fetchAndStoreOrdered(0)); } void push(T data) { Node *newNode = new Node(); newNode->data = data; Node *top; do { top = m_top; newNode->next = top; } while (!m_top.testAndSetOrdered(top, newNode)); m_numNodes.ref(); } bool pop(T &value) { bool result = false; m_deleteBlockers.ref(); while(1) { Node *top = (Node*) m_top; if(!top) break; // This is safe as we ref'ed m_deleteBlockers Node *next = top->next; if(m_top.testAndSetOrdered(top, next)) { m_numNodes.deref(); result = true; value = top->data; /** * Test if we are the only delete blocker left * (it means that we are the only owner of 'top') * If there is someone else in "delete-blocked section", * then just add the struct to the list of free nodes. */ if (m_deleteBlockers == 1) { cleanUpNodes(); delete top; } else { releaseNode(top); } break; } } m_deleteBlockers.deref(); return result; } void clear() { // a fast-path without write ops if(!m_top) return; m_deleteBlockers.ref(); Node *top = m_top.fetchAndStoreOrdered(0); int removedChunkSize = 0; Node *tmp = top; while(tmp) { removedChunkSize++; tmp = tmp->next; } m_numNodes.fetchAndAddOrdered(-removedChunkSize); while(top) { Node *next = top->next; if (m_deleteBlockers == 1) { /** * We are the only owner of top contents. * So we can delete it freely. */ cleanUpNodes(); freeList(top); next = 0; } else { releaseNode(top); } top = next; } m_deleteBlockers.deref(); } void mergeFrom(KisLocklessStack &other) { Node *otherTop = other.m_top.fetchAndStoreOrdered(0); if (!otherTop) return; int removedChunkSize = 1; Node *last = otherTop; while(last->next) { removedChunkSize++; last = last->next; } other.m_numNodes.fetchAndAddOrdered(-removedChunkSize); Node *top; do { top = m_top; last->next = top; } while (!m_top.testAndSetOrdered(top, otherTop)); m_numNodes.fetchAndAddOrdered(removedChunkSize); } /** * This is impossible to measure the size of the stack * in highly concurrent environment. So we return approximate * value! Do not rely on this value much! */ qint32 size() const { return m_numNodes; } bool isEmpty() const { return !m_numNodes; } private: inline void releaseNode(Node *node) { Node *top; do { top = m_freeNodes; node->next = top; } while (!m_freeNodes.testAndSetOrdered(top, node)); } inline void cleanUpNodes() { Node *cleanChain = m_freeNodes.fetchAndStoreOrdered(0); if (!cleanChain) return; /** * If we are the only users of the objects is cleanChain, * then just free it. Otherwise, push them back into the * recycling list and keep them there till another * chance comes. */ if (m_deleteBlockers == 1) { freeList(cleanChain); } else { Node *last = cleanChain; while (last->next) last = last->next; Node *freeTop; do { freeTop = m_freeNodes; last->next = freeTop; } while (!m_freeNodes.testAndSetOrdered(freeTop, cleanChain)); } } inline void freeList(Node *first) { Node *next; while (first) { next = first->next; delete first; first = next; } } private: Q_DISABLE_COPY(KisLocklessStack) QAtomicPointer m_top; QAtomicPointer m_freeNodes; QAtomicInt m_deleteBlockers; QAtomicInt m_numNodes; }; #endif /* __KIS_LOCKLESS_STACK_H */ diff --git a/libs/libkis/FillLayer.h b/libs/libkis/FillLayer.h index 07740b2a08..209b682c17 100644 --- a/libs/libkis/FillLayer.h +++ b/libs/libkis/FillLayer.h @@ -1,100 +1,100 @@ /* * Copyright (c) 2017 Wolthera van Hövell tot Westerflier * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef LIBKIS_FILLLAYER_H #define LIBKIS_FILLLAYER_H #include #include "Node.h" #include #include #include #include "kritalibkis_export.h" #include "libkis.h" /** * @brief The FillLayer class * A fill layer is much like a filter layer in that it takes a name * and filter. It however specializes in filters that fill the whole canvas, * such as a pattern or full color fill. */ class KRITALIBKIS_EXPORT FillLayer : public Node { Q_OBJECT Q_DISABLE_COPY(FillLayer) public: /** * @brief FillLayer Create a new fill layer with the given generator plugin * @param image the image this fill layer will belong to * @param name "pattern" or "color" * @param filterConfig a configuration object appropriate to the given generator plugin * * For a "pattern" fill layer, the InfoObject can contain a single "pattern" parameter with * the name of a pattern as known to the resource system: "pattern" = "Cross01.pat". * * For a "color" fill layer, the InfoObject can contain a single "color" parameter with - * a QColor, a string that QColor can parse (see http://doc.qt.io/qt-5/qcolor.html#setNamedColor) + * a QColor, a string that QColor can parse (see https://doc.qt.io/qt-5/qcolor.html#setNamedColor) * or an XML description of the color, which can be derived from a @see ManagedColor. * * @param selection a selection object, can be empty * @param parent */ explicit FillLayer(KisImageSP image, QString name, KisFilterConfigurationSP filterConfig, Selection &selection, QObject *parent = 0); explicit FillLayer(KisGeneratorLayerSP layer, QObject *parent = 0); ~FillLayer() override; public Q_SLOTS: /** * @brief type Krita has several types of nodes, split in layers and masks. Group * layers can contain other layers, any layer can contain masks. * * @return The type of the node. Valid types are: *
    *
  • paintlayer *
  • grouplayer *
  • filelayer *
  • filterlayer *
  • filllayer *
  • clonelayer *
  • vectorlayer *
  • transparencymask *
  • filtermask *
  • transformmask *
  • selectionmask *
  • colorizemask *
* * If the Node object isn't wrapping a valid Krita layer or mask object, and * empty string is returned. */ virtual QString type() const override; /** * @brief setGenerator set the given generator for this fill layer * @param generatorName "pattern" or "color" * @param filterConfig a configuration object appropriate to the given generator plugin * @return true if the generator was correctly created and set on the layer */ bool setGenerator(const QString &generatorName, InfoObject *filterConfig); QString generatorName(); InfoObject *filterConfig(); }; #endif // LIBKIS_FILLLAYER_H diff --git a/libs/libkis/InfoObject.h b/libs/libkis/InfoObject.h index ee8c4b9ff7..b3beb91ef0 100644 --- a/libs/libkis/InfoObject.h +++ b/libs/libkis/InfoObject.h @@ -1,88 +1,88 @@ /* * Copyright (c) 2016 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef LIBKIS_INFOOBJECT_H #define LIBKIS_INFOOBJECT_H #include #include #include "kritalibkis_export.h" #include "libkis.h" /** * InfoObject wrap a properties map. These maps can be used to set the * configuration for filters. */ class KRITALIBKIS_EXPORT InfoObject : public QObject { Q_OBJECT public: InfoObject(KisPropertiesConfigurationSP configuration); /** * Create a new, empty InfoObject. */ explicit InfoObject(QObject *parent = 0); ~InfoObject() override; bool operator==(const InfoObject &other) const; bool operator!=(const InfoObject &other) const; /** * Return all properties this InfoObject manages. */ QMap properties() const; /** * Add all properties in the @p propertyMap to this InfoObject */ void setProperties(QMap propertyMap); public Q_SLOTS: /** * set the property identified by @p key to @p value * * If you want create a property that represents a color, you can use a QColor - * or hex string, as defined in http://doc.qt.io/qt-5/qcolor.html#setNamedColor. + * or hex string, as defined in https://doc.qt.io/qt-5/qcolor.html#setNamedColor. * */ void setProperty(const QString &key, QVariant value); /** * return the value for the property identified by key, or None if there is no such key. */ QVariant property(const QString &key); private: friend class Filter; friend class Document; friend class Node; /** * @brief configuration gives access to the internal configuration object. Must * be used used internally in libkis * @return the internal configuration object. */ KisPropertiesConfigurationSP configuration() const; struct Private; Private *d; }; #endif // LIBKIS_INFOOBJECT_H diff --git a/libs/libkis/ManagedColor.h b/libs/libkis/ManagedColor.h index 5b5837ee17..1e57c6be3e 100644 --- a/libs/libkis/ManagedColor.h +++ b/libs/libkis/ManagedColor.h @@ -1,211 +1,211 @@ /* * Copyright (C) 2017 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef MANAGEDCOLOR_H #define MANAGEDCOLOR_H #include #include #include #include "kritalibkis_export.h" #include "libkis.h" class KoColor; /** * @brief The ManagedColor class is a class to handle colors that are color managed. * A managed color is a color of which we know the model(RGB, LAB, CMYK, etc), the bitdepth and * the specific properties of its colorspace, such as the whitepoint, chromacities, trc, etc, as represented * by the color profile. * * Krita has two color management systems. LCMS and OCIO. * LCMS is the one handling the ICC profile stuff, and the major one handling that ManagedColor deals with. * OCIO support is only in the display of the colors. ManagedColor has some support for it in colorForCanvas() * * All colors in Krita are color managed. QColors are understood as RGB-type colors in the sRGB space. * * We recommend you make a color like this: * * @code * colorYellow = ManagedColor("RGBA", "U8", "") * QVector yellowComponents = colorYellow.components() * yellowComponents[0] = 1.0 * yellowComponents[1] = 1.0 * yellowComponents[2] = 0 * yellowComponents[3] = 1.0 * * colorYellow.setComponents(yellowComponents) * QColor yellow = colorYellow.colorForCanvas(canvas) * @endcode */ class KRITALIBKIS_EXPORT ManagedColor : public QObject { Q_OBJECT public: /** * @brief ManagedColor * Create a ManagedColor that is black and transparent. */ explicit ManagedColor(QObject *parent = 0); /** * @brief ManagedColor create a managed color with the given color space properties. * @see setColorModel() for more details. */ ManagedColor(const QString &colorModel, const QString &colorDepth, const QString &colorProfile, QObject *parent = 0); ManagedColor(KoColor color, QObject *parent = 0); ~ManagedColor() override; bool operator==(const ManagedColor &other) const; /** * @brief colorForCanvas * @param canvas the canvas whose color management you'd like to use. In Krita, different views have * separate canvasses, and these can have different OCIO configurations active. * @return the QColor as it would be displaying on the canvas. This result can be used to draw widgets with * the correct configuration applied. */ QColor colorForCanvas(Canvas *canvas) const; /** * colorDepth A string describing the color depth of the image: *
    *
  • U8: unsigned 8 bits integer, the most common type
  • *
  • U16: unsigned 16 bits integer
  • *
  • F16: half, 16 bits floating point. Only available if Krita was built with OpenEXR
  • *
  • F32: 32 bits floating point
  • *
* @return the color depth. */ QString colorDepth() const; /** * @brief colorModel retrieve the current color model of this document: *
    *
  • A: Alpha mask
  • *
  • RGBA: RGB with alpha channel (The actual order of channels is most often BGR!)
  • *
  • XYZA: XYZ with alpha channel
  • *
  • LABA: LAB with alpha channel
  • *
  • CMYKA: CMYK with alpha channel
  • *
  • GRAYA: Gray with alpha channel
  • *
  • YCbCrA: YCbCr with alpha channel
  • *
* @return the internal color model string. */ QString colorModel() const; /** * @return the name of the current color profile */ QString colorProfile() const; /** * @brief setColorProfile set the color profile of the image to the given profile. The profile has to * be registered with krita and be compatible with the current color model and depth; the image data * is not converted. * @param colorProfile * @return false if the colorProfile name does not correspond to to a registered profile or if assigning * the profile failed. */ bool setColorProfile(const QString &colorProfile); /** * @brief setColorSpace convert the nodes and the image to the given colorspace. The conversion is * done with Perceptual as intent, High Quality and No LCMS Optimizations as flags and no blackpoint * compensation. * * @param colorModel A string describing the color model of the image: *
    *
  • A: Alpha mask
  • *
  • RGBA: RGB with alpha channel (The actual order of channels is most often BGR!)
  • *
  • XYZA: XYZ with alpha channel
  • *
  • LABA: LAB with alpha channel
  • *
  • CMYKA: CMYK with alpha channel
  • *
  • GRAYA: Gray with alpha channel
  • *
  • YCbCrA: YCbCr with alpha channel
  • *
* @param colorDepth A string describing the color depth of the image: *
    *
  • U8: unsigned 8 bits integer, the most common type
  • *
  • U16: unsigned 16 bits integer
  • *
  • F16: half, 16 bits floating point. Only available if Krita was built with OpenEXR
  • *
  • F32: 32 bits floating point
  • *
* @param colorProfile a valid color profile for this color model and color depth combination. * @return false the combination of these arguments does not correspond to a colorspace. */ bool setColorSpace(const QString &colorModel, const QString &colorDepth, const QString &colorProfile); /** * @brief components * @return a QVector containing the channel/components of this color normalized. This includes the alphachannel. */ QVector components() const; /** * @brief componentsOrdered() * @return same as Components, except the values are ordered to the display. */ QVector componentsOrdered() const; /** * @brief setComponents * Set the channel/components with normalized values. For integer colorspace, this obviously means the limit * is between 0.0-1.0, but for floating point colorspaces, 2.4 or 103.5 are still meaningful (if bright) values. * @param values the QVector containing the new channel/component values. These should be normalized. */ void setComponents(const QVector &values); /** * Serialize this color following Create's swatch color specification available - * at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format + * at https://web.archive.org/web/20110826002520/http://create.freedesktop.org/wiki/Swatches_-_colour_file_format/Draft */ QString toXML() const; /** * Unserialize a color following Create's swatch color specification available - * at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format + * at https://web.archive.org/web/20110826002520/http://create.freedesktop.org/wiki/Swatches_-_colour_file_format/Draft * * @param xml an XML color * * @return the unserialized color, or an empty color object if the function failed * to unserialize the color */ void fromXML(const QString &xml); /** * @brief toQString create a user-visible string of the channel names and the channel values * @return a string that can be used to display the values of this color to the user. */ QString toQString(); private: friend class View; friend class PaletteView; friend class Swatch; KoColor color() const; struct Private; const QScopedPointer d; }; #endif // MANAGEDCOLOR_H diff --git a/libs/odf/KoPageFormat.cpp b/libs/odf/KoPageFormat.cpp index ee6be9342b..686e79c34f 100644 --- a/libs/odf/KoPageFormat.cpp +++ b/libs/odf/KoPageFormat.cpp @@ -1,181 +1,181 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright 2002, 2003 David Faure Copyright 2003 Nicolas GOUTTE Copyright 2007 Thomas Zander 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 "KoPageFormat.h" #include #include // paper formats ( mm ) #define PG_A3_WIDTH 297.0 #define PG_A3_HEIGHT 420.0 #define PG_A4_WIDTH 210.0 #define PG_A4_HEIGHT 297.0 #define PG_A5_WIDTH 148.0 #define PG_A5_HEIGHT 210.0 #define PG_B5_WIDTH 182.0 #define PG_B5_HEIGHT 257.0 #define PG_US_LETTER_WIDTH 216.0 #define PG_US_LETTER_HEIGHT 279.0 #define PG_US_LEGAL_WIDTH 216.0 #define PG_US_LEGAL_HEIGHT 356.0 #define PG_US_EXECUTIVE_WIDTH 191.0 #define PG_US_EXECUTIVE_HEIGHT 254.0 struct PageFormatInfo { KoPageFormat::Format format; QPrinter::PageSize qprinter; const char* shortName; // Short name const char* descriptiveName; // Full name, which will be translated qreal width; // in mm qreal height; // in mm }; // NOTES: // - the width and height of non-ISO formats are rounded -// http://en.wikipedia.org/wiki/Paper_size can help +// https://en.wikipedia.org/wiki/Paper_size can help // - the comments "should be..." indicates the exact values if the inch sizes would be multiplied by 25.4 mm/inch const PageFormatInfo pageFormatInfo[] = { { KoPageFormat::IsoA3Size, QPrinter::A3, "A3", I18N_NOOP2("Page size", "ISO A3"), 297.0, 420.0 }, { KoPageFormat::IsoA4Size, QPrinter::A4, "A4", I18N_NOOP2("Page size", "ISO A4"), 210.0, 297.0 }, { KoPageFormat::IsoA5Size, QPrinter::A5, "A5", I18N_NOOP2("Page size", "ISO A5"), 148.0, 210.0 }, { KoPageFormat::UsLetterSize, QPrinter::Letter, "Letter", I18N_NOOP2("Page size", "US Letter"), 215.9, 279.4 }, { KoPageFormat::UsLegalSize, QPrinter::Legal, "Legal", I18N_NOOP2("Page size", "US Legal"), 215.9, 355.6 }, { KoPageFormat::ScreenSize, QPrinter::A4, "Screen", I18N_NOOP2("Page size", "Screen"), PG_A4_HEIGHT, PG_A4_WIDTH }, // Custom, so fall back to A4 { KoPageFormat::CustomSize, QPrinter::A4, "Custom", I18N_NOOP2("Page size", "Custom"), PG_A4_WIDTH, PG_A4_HEIGHT }, // Custom, so fall back to A4 { KoPageFormat::IsoB5Size, QPrinter::B5, "B5", I18N_NOOP2("Page size", "ISO B5"), 182.0, 257.0 }, { KoPageFormat::UsExecutiveSize, QPrinter::Executive, "Executive", I18N_NOOP2("Page size", "US Executive"), 191.0, 254.0 }, // should be 190.5 mm x 254.0 mm { KoPageFormat::IsoA0Size, QPrinter::A0, "A0", I18N_NOOP2("Page size", "ISO A0"), 841.0, 1189.0 }, { KoPageFormat::IsoA1Size, QPrinter::A1, "A1", I18N_NOOP2("Page size", "ISO A1"), 594.0, 841.0 }, { KoPageFormat::IsoA2Size, QPrinter::A2, "A2", I18N_NOOP2("Page size", "ISO A2"), 420.0, 594.0 }, { KoPageFormat::IsoA6Size, QPrinter::A6, "A6", I18N_NOOP2("Page size", "ISO A6"), 105.0, 148.0 }, { KoPageFormat::IsoA7Size, QPrinter::A7, "A7", I18N_NOOP2("Page size", "ISO A7"), 74.0, 105.0 }, { KoPageFormat::IsoA8Size, QPrinter::A8, "A8", I18N_NOOP2("Page size", "ISO A8"), 52.0, 74.0 }, { KoPageFormat::IsoA9Size, QPrinter::A9, "A9", I18N_NOOP2("Page size", "ISO A9"), 37.0, 52.0 }, { KoPageFormat::IsoB0Size, QPrinter::B0, "B0", I18N_NOOP2("Page size", "ISO B0"), 1030.0, 1456.0 }, { KoPageFormat::IsoB1Size, QPrinter::B1, "B1", I18N_NOOP2("Page size", "ISO B1"), 728.0, 1030.0 }, { KoPageFormat::IsoB10Size, QPrinter::B10, "B10", I18N_NOOP2("Page size", "ISO B10"), 32.0, 45.0 }, { KoPageFormat::IsoB2Size, QPrinter::B2, "B2", I18N_NOOP2("Page size", "ISO B2"), 515.0, 728.0 }, { KoPageFormat::IsoB3Size, QPrinter::B3, "B3", I18N_NOOP2("Page size", "ISO B3"), 364.0, 515.0 }, { KoPageFormat::IsoB4Size, QPrinter::B4, "B4", I18N_NOOP2("Page size", "ISO B4"), 257.0, 364.0 }, { KoPageFormat::IsoB6Size, QPrinter::B6, "B6", I18N_NOOP2("Page size", "ISO B6"), 128.0, 182.0 }, { KoPageFormat::IsoC5Size, QPrinter::C5E, "C5", I18N_NOOP2("Page size", "ISO C5"), 163.0, 229.0 }, // Some sources tells: 162 mm x 228 mm { KoPageFormat::UsComm10Size, QPrinter::Comm10E, "Comm10", I18N_NOOP2("Page size", "US Common 10"), 105.0, 241.0 }, // should be 104.775 mm x 241.3 mm { KoPageFormat::IsoDLSize, QPrinter::DLE, "DL", I18N_NOOP2("Page size", "ISO DL"), 110.0, 220.0 }, { KoPageFormat::UsFolioSize, QPrinter::Folio, "Folio", I18N_NOOP2("Page size", "US Folio"), 210.0, 330.0 }, // should be 209.54 mm x 330.2 mm { KoPageFormat::UsLedgerSize, QPrinter::Ledger, "Ledger", I18N_NOOP2("Page size", "US Ledger"), 432.0, 279.0 }, // should be 431.8 mm x 297.4 mm { KoPageFormat::UsTabloidSize, QPrinter::Tabloid, "Tabloid", I18N_NOOP2("Page size", "US Tabloid"), 279.0, 432.0 }, // should be 297.4 mm x 431.8 mm { KoPageFormat::Invalid, QPrinter::Custom, "", "", -1, -1 } }; QPrinter::PageSize KoPageFormat::printerPageSize(KoPageFormat::Format format) { if (format == ScreenSize) { warnOdf << "You use the page layout SCREEN. Printing in ISO A4 Landscape."; return QPrinter::A4; } if (format == CustomSize) { warnOdf << "The used page layout (Custom) is not supported by KQPrinter. Printing in A4."; return QPrinter::A4; } return pageFormatInfo[format].qprinter; } qreal KoPageFormat::width(Format format, Orientation orientation) { if (orientation == Landscape) return height(format, Portrait); return pageFormatInfo[format].width; } qreal KoPageFormat::height(Format format, Orientation orientation) { if (orientation == Landscape) return width(format, Portrait); return pageFormatInfo[format].height; } KoPageFormat::Format KoPageFormat::guessFormat(qreal width, qreal height) { for (int i = 0; pageFormatInfo[i].format != KoPageFormat::Invalid ;i++) { // We have some tolerance. 1pt is a third of a mm, this is // barely noticeable for a page size. if (qAbs(width - pageFormatInfo[i].width) < 1.0 && qAbs(height - pageFormatInfo[i].height) < 1.0) return pageFormatInfo[i].format; } return CustomSize; } QString KoPageFormat::formatString(Format format) { return QString::fromLatin1(pageFormatInfo[format].shortName); } KoPageFormat::Format KoPageFormat::formatFromString(const QString & string) { for (int i = 0; pageFormatInfo[i].format != KoPageFormat::Invalid ;i++) { if (string == QString::fromLatin1(pageFormatInfo[i].shortName)) return pageFormatInfo[i].format; } // We do not know the format name, so we have a custom format return CustomSize; } KoPageFormat::Format KoPageFormat::defaultFormat() { int qprinter; if (QLocale().measurementSystem() == QLocale::ImperialSystem) { qprinter = static_cast(QPageSize::Letter); } else { qprinter = static_cast(QPageSize::A4); } for (int i = 0; pageFormatInfo[i].format != KoPageFormat::Invalid ;i++) { if (pageFormatInfo[i].qprinter == qprinter) return static_cast(i); } return IsoA4Size; } QString KoPageFormat::name(Format format) { return i18nc("Page size", pageFormatInfo[format].descriptiveName); } QStringList KoPageFormat::localizedPageFormatNames() { QStringList lst; for (int i = 0; pageFormatInfo[i].format != KoPageFormat::Invalid ;i++) { lst << i18nc("Page size", pageFormatInfo[i].descriptiveName); } return lst; } QStringList KoPageFormat::pageFormatNames() { QStringList lst; for (int i = 0; pageFormatInfo[i].format != KoPageFormat::Invalid ;i++) { lst << pageFormatInfo[i].shortName; } return lst; } diff --git a/libs/pigment/KoColor.h b/libs/pigment/KoColor.h index b231e484fc..cc8fd2d8f7 100644 --- a/libs/pigment/KoColor.h +++ b/libs/pigment/KoColor.h @@ -1,293 +1,293 @@ /* * Copyright (c) 2005 Boudewijn Rempt * Copyright (C) 2007 Thomas Zander * * 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 KOCOLOR_H #define KOCOLOR_H #include #include #include #include "kritapigment_export.h" #include "KoColorConversionTransformation.h" #include "KoColorSpaceRegistry.h" #include "KoColorSpaceTraits.h" #include class QDomDocument; class QDomElement; class KoColorProfile; class KoColorSpace; /** * A KoColor describes a color in a certain colorspace. The color is stored in a buffer * that can be manipulated by the function of the color space. */ class KRITAPIGMENT_EXPORT KoColor : public boost::equality_comparable { public: /// Create an empty KoColor. It will be valid, but also black and transparent KoColor(); /// Create a null KoColor. It will be valid, but all channels will be set to 0 explicit KoColor(const KoColorSpace * colorSpace); /// Create a KoColor from a QColor. The QColor is immediately converted to native. The QColor /// is assumed to have the current monitor profile. KoColor(const QColor & color, const KoColorSpace * colorSpace); /// Create a KoColor using a native color strategy. The data is copied. KoColor(const quint8 * data, const KoColorSpace * colorSpace); /// Create a KoColor by converting src into another colorspace KoColor(const KoColor &src, const KoColorSpace * colorSpace); /// Copy constructor -- deep copies the colors. KoColor(const KoColor & rhs) { *this = rhs; } /** * assignment operator to copy the data from the param color into this one. * @param rhs the color we are going to copy * @return this color */ inline KoColor &operator=(const KoColor &rhs) { if (&rhs == this) { return *this; } m_colorSpace = rhs.m_colorSpace; m_size = rhs.m_size; memcpy(m_data, rhs.m_data, m_size); assertPermanentColorspace(); return *this; } bool operator==(const KoColor &other) const { if (*colorSpace() != *other.colorSpace()) { return false; } if (m_size != other.m_size) { return false; } return memcmp(m_data, other.m_data, m_size) == 0; } /// return the current colorSpace const KoColorSpace * colorSpace() const { return m_colorSpace; } /// return the current profile const KoColorProfile *profile() const; /// Convert this KoColor to the specified colorspace. If the specified colorspace is the /// same as the original colorspace, do nothing void convertTo(const KoColorSpace * cs, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags); void convertTo(const KoColorSpace * cs); /// Copies this color and converts it to the specified colorspace. If the specified colorspace is the /// same as the original colorspace, just returns a copy KoColor convertedTo(const KoColorSpace * cs, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const; /// Copies this color and converts it to the specified colorspace. If the specified colorspace is the /// same as the original colorspace, just returns a copy KoColor convertedTo(const KoColorSpace * cs) const; /// assign new profile without converting pixel data void setProfile(const KoColorProfile *profile); /// Replace the existing color data, and colorspace with the specified data. /// The data is copied. void setColor(const quint8 * data, const KoColorSpace * colorSpace = 0); /// Convert the color from src and replace the value of the current color with the converted data. /// Don't convert the color if src and this have the same colorspace. void fromKoColor(const KoColor& src); /// a convenience method for the above. void toQColor(QColor *c) const; /// a convenience method for the above. QColor toQColor() const; /** * Convenient function to set the opacity of the color. */ void setOpacity(quint8 alpha); void setOpacity(qreal alpha); /** * Convenient function that return the opacity of the color */ quint8 opacityU8() const; qreal opacityF() const; /// Convenient function for converting from a QColor void fromQColor(const QColor& c); /** * @return the buffer associated with this color object to be used with the * transformation object created by the color space of this KoColor * or to copy to a different buffer from the same color space */ quint8 * data() { return m_data; } /** * @return the buffer associated with this color object to be used with the * transformation object created by the color space of this KoColor * or to copy to a different buffer from the same color space */ const quint8 * data() const { return m_data; } /** * Channelwise subtracts \p value from *this and stores the result in *this * * Throws a safe assert if the colorspaces of the two colors are different */ void subtract(const KoColor &value); /** * Channelwise subtracts \p value from a copy of *this and returns the result * * Throws a safe assert if the colorspaces of the two colors are different */ KoColor subtracted(const KoColor &value) const; /** * Channelwise adds \p value to *this and stores the result in *this * * Throws a safe assert if the colorspaces of the two colors are different */ void add(const KoColor &value); /** * Channelwise adds \p value to a copy of *this and returns the result * * Throws a safe assert if the colorspaces of the two colors are different */ KoColor added(const KoColor &value) const; /** * Serialize this color following Create's swatch color specification available - * at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format + * at https://web.archive.org/web/20110826002520/http://create.freedesktop.org/wiki/Swatches_-_colour_file_format/Draft * * This function doesn't create the \ element but rather the \, * \, \ ... elements. It is assumed that colorElt is the \ * element. * * @param colorElt root element for the serialization, it is assumed that this * element is \ * @param doc is the document containing colorElt */ void toXML(QDomDocument& doc, QDomElement& colorElt) const; /** * Unserialize a color following Create's swatch color specification available - * at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format + * at https://web.archive.org/web/20110826002520/http://create.freedesktop.org/wiki/Swatches_-_colour_file_format/Draft * * @param elt the element to unserialize (\, \, \) * @param channelDepthId the bit depth is unspecified by the spec, this allow to select * a preferred bit depth for creating the KoColor object (if that * bit depth isn't available, this function will randomly select * an other bit depth) * @return the unserialize color, or an empty color object if the function failed * to unserialize the color */ static KoColor fromXML(const QDomElement& elt, const QString & channelDepthId); /** * Unserialize a color following Create's swatch color specification available - * at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format + * at https://web.archive.org/web/20110826002520/http://create.freedesktop.org/wiki/Swatches_-_colour_file_format/Draft * * @param elt the element to unserialize (\, \, \) * @param channelDepthId the bit depth is unspecified by the spec, this allow to select * a preferred bit depth for creating the KoColor object (if that * bit depth isn't available, this function will randomly select * an other bit depth) * @param ok If a an error occurs, *ok is set to false; otherwise it's set to true * @return the unserialize color, or an empty color object if the function failed * to unserialize the color */ static KoColor fromXML(const QDomElement& elt, const QString & channelDepthId, bool* ok); /** * @brief toXML creates a string with XML that represents the current color. The XML * is extended with a "channeldepth" attribute so we can restore the color to the same * channel depth. * @return a valid XML document in a string */ QString toXML() const; /** * @brief fromXML restores a KoColor from a string saved with toXML(). If the * string does not contain the "channeldepth" attribute, 16 bit integer is assumed. * @param xml a valid XML document * @return a new KoColor object */ static KoColor fromXML(const QString &xml); /** * @brief toQString create a user-visible string of the channel names and the channel values * @param color the color to create the string from * @return a string that can be used to display the values of this color to the user. */ static QString toQString(const KoColor &color); #ifndef NODEBUG /// use qDebug calls to print internal info void dump() const; #endif private: inline void assertPermanentColorspace() { #ifndef NODEBUG if (m_colorSpace) { Q_ASSERT(*m_colorSpace == *KoColorSpaceRegistry::instance()->permanentColorspace(m_colorSpace)); } #endif } const KoColorSpace *m_colorSpace; quint8 m_data[MAX_PIXEL_SIZE]; quint8 m_size; }; Q_DECLARE_METATYPE(KoColor) KRITAPIGMENT_EXPORT QDebug operator<<(QDebug dbg, const KoColor &color); #endif diff --git a/libs/pigment/KoColorSpace.h b/libs/pigment/KoColorSpace.h index 036760e00d..df314198c4 100644 --- a/libs/pigment/KoColorSpace.h +++ b/libs/pigment/KoColorSpace.h @@ -1,646 +1,646 @@ /* * Copyright (c) 2005 Boudewijn Rempt * Copyright (c) 2006-2007 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this 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 KOCOLORSPACE_H #define KOCOLORSPACE_H #include #include #include #include #include #include #include "KoColorSpaceConstants.h" #include "KoColorConversionTransformation.h" #include "KoColorProofingConversionTransformation.h" #include "KoCompositeOp.h" #include #include "kritapigment_export.h" class QDomDocument; class QDomElement; class KoChannelInfo; class KoColorProfile; class KoColorTransformation; class QBitArray; enum Deletability { OwnedByRegistryDoNotDelete, OwnedByRegistryRegistryDeletes, NotOwnedByRegistry }; enum ColorSpaceIndependence { FULLY_INDEPENDENT, TO_LAB16, TO_RGBA8, TO_RGBA16 }; class KoMixColorsOp; class KoConvolutionOp; /** * A KoColorSpace is the definition of a certain color space. * * A color model and a color space are two related concepts. A color * model is more general in that it describes the channels involved and * how they in broad terms combine to describe a color. Examples are * RGB, HSV, CMYK. * * A color space is more specific in that it also describes exactly how * the channels are combined. So for each color model there can be a * number of specific color spaces. So RGB is the model and sRGB, * adobeRGB, etc are colorspaces. * * In Pigment KoColorSpace acts as both a color model and a color space. * You can think of the class definition as the color model, but the * instance of the class as representing a colorspace. * * A third concept is the profile represented by KoColorProfile. It * represents the info needed to specialize a color model into a color * space. * * KoColorSpace is an abstract class serving as an interface. * * Subclasses implement actual color spaces * Some subclasses implement only some parts and are named Traits * */ class KRITAPIGMENT_EXPORT KoColorSpace : public boost::equality_comparable { friend class KoColorSpaceRegistry; friend class KoColorSpaceFactory; protected: /// Only for use by classes that serve as baseclass for real color spaces KoColorSpace(); public: /// Should be called by real color spaces KoColorSpace(const QString &id, const QString &name, KoMixColorsOp* mixColorsOp, KoConvolutionOp* convolutionOp); virtual bool operator==(const KoColorSpace& rhs) const; protected: virtual ~KoColorSpace(); public: //========== Gamut and other basic info ===================================// /* * @returns QPolygonF with 5*channel samples converted to xyY. * maybe convert to 3d space in future? */ QPolygonF gamutXYY() const; /* * @returns a polygon with 5 samples per channel converted to xyY, but unlike * gamutxyY it focuses on the luminance. This then can be used to visualise * the approximate trc of a given colorspace. */ QPolygonF estimatedTRCXYY() const; QVector lumaCoefficients() const; //========== Channels =====================================================// /// Return a list describing all the channels this color model has. The order /// of the channels in the list is the order of channels in the pixel. To find /// out the preferred display position, use KoChannelInfo::displayPosition. QList channels() const; /** * The total number of channels for a single pixel in this color model */ virtual quint32 channelCount() const = 0; /** * Position of the alpha channel in a pixel */ virtual quint32 alphaPos() const = 0; /** * The total number of color channels (excludes alpha) for a single * pixel in this color model. */ virtual quint32 colorChannelCount() const = 0; /** * returns a QBitArray that contains true for the specified * channel types: * * @param color if true, set all color channels to true * @param alpha if true, set all alpha channels to true * * The order of channels is the colorspace descriptive order, * not the pixel order. */ QBitArray channelFlags(bool color = true, bool alpha = false) const; /** * The size in bytes of a single pixel in this color model */ virtual quint32 pixelSize() const = 0; /** * Return a string with the channel's value suitable for display in the gui. */ virtual QString channelValueText(const quint8 *pixel, quint32 channelIndex) const = 0; /** * Return a string with the channel's value with integer * channels normalised to the floating point range 0 to 1, if * appropriate. */ virtual QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) const = 0; /** * Return a QVector of floats with channels' values normalized * to floating point range 0 to 1. */ virtual void normalisedChannelsValue(const quint8 *pixel, QVector &channels) const = 0; /** * Write in the pixel the value from the normalized vector. */ virtual void fromNormalisedChannelsValue(quint8 *pixel, const QVector &values) const = 0; /** * Convert the value of the channel at the specified position into * an 8-bit value. The position is not the number of bytes, but * the position of the channel as defined in the channel info list. */ virtual quint8 scaleToU8(const quint8 * srcPixel, qint32 channelPos) const = 0; /** * Set dstPixel to the pixel containing only the given channel of srcPixel. The remaining channels * should be set to whatever makes sense for 'empty' channels of this color space, * with the intent being that the pixel should look like it only has the given channel. */ virtual void singleChannelPixel(quint8 *dstPixel, const quint8 *srcPixel, quint32 channelIndex) const = 0; //========== Identification ===============================================// /** * ID for use in files and internally: unchanging name. As the id must be unique * it is usually the concatenation of the id of the color model and of the color * depth, for instance "RGBA8" or "CMYKA16" or "XYZA32f". */ QString id() const; /** * User visible name which contains the name of the color model and of the color depth. * For instance "RGBA (8-bits)" or "CMYKA (16-bits)". */ QString name() const; /** * @return a string that identify the color model (for instance "RGB" or "CMYK" ...) * @see KoColorModelStandardIds.h */ virtual KoID colorModelId() const = 0; /** * @return a string that identify the bit depth (for instance "U8" or "F16" ...) * @see KoColorModelStandardIds.h */ virtual KoID colorDepthId() const = 0; /** * @return true if the profile given in argument can be used by this color space */ virtual bool profileIsCompatible(const KoColorProfile* profile) const = 0; /** * If false, images in this colorspace will degrade considerably by * functions, tools and filters that have the given measure of colorspace * independence. * * @param independence the measure to which this colorspace will suffer * from the manipulations of the tool or filter asking * @return false if no degradation will take place, true if degradation will * take place */ virtual bool willDegrade(ColorSpaceIndependence independence) const = 0; //========== Capabilities =================================================// /** * Tests if the colorspace offers the specific composite op. */ virtual bool hasCompositeOp(const QString & id) const; /** * Returns the list of user-visible composite ops supported by this colorspace. */ virtual QList compositeOps() const; /** * Retrieve a single composite op from the ones this colorspace offers. * If the requeste composite op does not exist, COMPOSITE_OVER is returned. */ const KoCompositeOp * compositeOp(const QString & id) const; /** * add a composite op to this colorspace. */ virtual void addCompositeOp(const KoCompositeOp * op); /** * Returns true if the colorspace supports channel values outside the * (normalised) range 0 to 1. */ virtual bool hasHighDynamicRange() const = 0; //========== Display profiles =============================================// /** * Return the profile of this color space. */ virtual const KoColorProfile * profile() const = 0; //================= Conversion functions ==================================// /** * The fromQColor methods take a given color defined as an RGB QColor * and fills a byte array with the corresponding color in the * the colorspace managed by this strategy. * * @param color the QColor that will be used to fill dst * @param dst a pointer to a pixel * @param profile the optional profile that describes the color values of QColor */ virtual void fromQColor(const QColor& color, quint8 *dst, const KoColorProfile * profile = 0) const = 0; /** * The toQColor methods take a byte array that is at least pixelSize() long * and converts the contents to a QColor, using the given profile as a source * profile and the optional profile as a destination profile. * * @param src a pointer to the source pixel * @param c the QColor that will be filled with the color at src * @param profile the optional profile that describes the color in c, for instance the monitor profile */ virtual void toQColor(const quint8 *src, QColor *c, const KoColorProfile * profile = 0) const = 0; /** * Convert the pixels in data to (8-bit BGRA) QImage using the specified profiles. * * @param data A pointer to a contiguous memory region containing width * height pixels * @param width in pixels * @param height in pixels * @param dstProfile destination profile * @param renderingIntent the rendering intent * @param conversionFlags conversion flags */ virtual QImage convertToQImage(const quint8 *data, qint32 width, qint32 height, const KoColorProfile * dstProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const; /** * Convert the specified data to Lab (D50). All colorspaces are guaranteed to support this * * @param src the source data * @param dst the destination data * @param nPixels the number of source pixels */ virtual void toLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const; /** * Convert the specified data from Lab (D50). to this colorspace. All colorspaces are * guaranteed to support this. * * @param src the pixels in 16 bit lab format * @param dst the destination data * @param nPixels the number of pixels in the array */ virtual void fromLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const; /** * Convert the specified data to sRGB 16 bits. All colorspaces are guaranteed to support this * * @param src the source data * @param dst the destination data * @param nPixels the number of source pixels */ virtual void toRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const; /** * Convert the specified data from sRGB 16 bits. to this colorspace. All colorspaces are * guaranteed to support this. * * @param src the pixels in 16 bit rgb format * @param dst the destination data * @param nPixels the number of pixels in the array */ virtual void fromRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const; /** * Create a color conversion transformation. */ virtual KoColorConversionTransformation* createColorConverter(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const; /** * Convert a byte array of srcLen pixels *src to the specified color space * and put the converted bytes into the prepared byte array *dst. * * Returns false if the conversion failed, true if it succeeded * * This function is not thread-safe. If you want to apply multiple conversion * in different threads at the same time, you need to create one color converter * per-thread using createColorConverter. */ virtual bool convertPixelsTo(const quint8 * src, quint8 * dst, const KoColorSpace * dstColorSpace, quint32 numPixels, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const; virtual KoColorConversionTransformation *createProofingTransform(const KoColorSpace * dstColorSpace, const KoColorSpace * proofingSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::Intent proofingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags, quint8 *gamutWarning, double adaptationState) const; /** * @brief proofPixelsTo * @param src source * @param dst destination * @param numPixels the amount of pixels. * @param proofingTransform the intent used for proofing. * @return */ virtual bool proofPixelsTo(const quint8 * src, quint8 * dst, quint32 numPixels, KoColorConversionTransformation *proofingTransform) const; //============================== Manipulation functions ==========================// // // The manipulation functions have default implementations that _convert_ the pixel // to a QColor and back. Reimplement these methods in your color strategy! // /** * Get the alpha value of the given pixel, downscaled to an 8-bit value. */ virtual quint8 opacityU8(const quint8 * pixel) const = 0; virtual qreal opacityF(const quint8 * pixel) const = 0; /** * Set the alpha channel of the given run of pixels to the given value. * * pixels -- a pointer to the pixels that will have their alpha set to this value * alpha -- a downscaled 8-bit value for opacity * nPixels -- the number of pixels * */ virtual void setOpacity(quint8 * pixels, quint8 alpha, qint32 nPixels) const = 0; virtual void setOpacity(quint8 * pixels, qreal alpha, qint32 nPixels) const = 0; /** * Multiply the alpha channel of the given run of pixels by the given value. * * pixels -- a pointer to the pixels that will have their alpha set to this value * alpha -- a downscaled 8-bit value for opacity * nPixels -- the number of pixels * */ virtual void multiplyAlpha(quint8 * pixels, quint8 alpha, qint32 nPixels) const = 0; /** * Applies the specified 8-bit alpha mask to the pixels. We assume that there are just * as many alpha values as pixels but we do not check this; the alpha values * are assumed to be 8-bits. */ virtual void applyAlphaU8Mask(quint8 * pixels, const quint8 * alpha, qint32 nPixels) const = 0; /** * Applies the inverted 8-bit alpha mask to the pixels. We assume that there are just * as many alpha values as pixels but we do not check this; the alpha values * are assumed to be 8-bits. */ virtual void applyInverseAlphaU8Mask(quint8 * pixels, const quint8 * alpha, qint32 nPixels) const = 0; /** * Applies the specified float alpha mask to the pixels. We assume that there are just * as many alpha values as pixels but we do not check this; alpha values have to be between 0.0 and 1.0 */ virtual void applyAlphaNormedFloatMask(quint8 * pixels, const float * alpha, qint32 nPixels) const = 0; /** * Applies the inverted specified float alpha mask to the pixels. We assume that there are just * as many alpha values as pixels but we do not check this; alpha values have to be between 0.0 and 1.0 */ virtual void applyInverseNormedFloatMask(quint8 * pixels, const float * alpha, qint32 nPixels) const = 0; /** * Create an adjustment object for adjusting the brightness and contrast * transferValues is a 256 bins array with values from 0 to 0xFFFF * This function is thread-safe, but you need to create one KoColorTransformation per thread. */ virtual KoColorTransformation *createBrightnessContrastAdjustment(const quint16 *transferValues) const = 0; /** * Create an adjustment object for adjusting individual channels * transferValues is an array of colorChannelCount number of 256 bins array with values from 0 to 0xFFFF * This function is thread-safe, but you need to create one KoColorTransformation per thread. * * The layout of the channels must be the following: * * 0..N-2 - color channels of the pixel; * N-1 - alpha channel of the pixel (if exists) */ virtual KoColorTransformation *createPerChannelAdjustment(const quint16 * const* transferValues) const = 0; /** * Darken all color channels with the given amount. If compensate is true, * the compensation factor will be used to limit the darkening. * */ virtual KoColorTransformation *createDarkenAdjustment(qint32 shade, bool compensate, qreal compensation) const = 0; /** * Invert color channels of the given pixels * This function is thread-safe, but you need to create one KoColorTransformation per thread. */ virtual KoColorTransformation *createInvertTransformation() const = 0; /** * Get the difference between 2 colors, normalized in the range (0,255). Only completely * opaque and completely transparent are taken into account when computing the difference; * other transparency levels are not regarded when finding the difference. */ virtual quint8 difference(const quint8* src1, const quint8* src2) const = 0; /** * Get the difference between 2 colors, normalized in the range (0,255). This function * takes the Alpha channel of the pixel into account. Alpha channel has the same * weight as Lightness channel. */ virtual quint8 differenceA(const quint8* src1, const quint8* src2) const = 0; /** * @return the mix color operation of this colorspace (do not delete it locally, it's deleted by the colorspace). */ virtual KoMixColorsOp* mixColorsOp() const; /** * @return the convolution operation of this colorspace (do not delete it locally, it's deleted by the colorspace). */ virtual KoConvolutionOp* convolutionOp() const; /** * Calculate the intensity of the given pixel, scaled down to the range 0-255. XXX: Maybe this should be more flexible */ virtual quint8 intensity8(const quint8 * src) const = 0; /* *increase luminosity by step */ virtual void increaseLuminosity(quint8 * pixel, qreal step) const; virtual void decreaseLuminosity(quint8 * pixel, qreal step) const; virtual void increaseSaturation(quint8 * pixel, qreal step) const; virtual void decreaseSaturation(quint8 * pixel, qreal step) const; virtual void increaseHue(quint8 * pixel, qreal step) const; virtual void decreaseHue(quint8 * pixel, qreal step) const; virtual void increaseRed(quint8 * pixel, qreal step) const; virtual void increaseGreen(quint8 * pixel, qreal step) const; virtual void increaseBlue(quint8 * pixel, qreal step) const; virtual void increaseYellow(quint8 * pixel, qreal step) const; virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const = 0; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const = 0; virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const = 0; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const = 0; /** * Compose two arrays of pixels together. If source and target * are not the same color model, the source pixels will be * converted to the target model. We're "dst" -- "dst" pixels are always in _this_ * colorspace. * * @param srcSpace the colorspace of the source pixels that will be composited onto "us" * @param params the information needed for blitting e.g. the source and destination pixel data, * the opacity and flow, ... * @param op the composition operator to use, e.g. COPY_OVER * @param renderingIntent the rendering intent * @param conversionFlags the conversion flags. * */ virtual void bitBlt(const KoColorSpace* srcSpace, const KoCompositeOp::ParameterInfo& params, const KoCompositeOp* op, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const; /** * Serialize this color following Create's swatch color specification available - * at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format + * at https://web.archive.org/web/20110826002520/http://create.freedesktop.org/wiki/Swatches_-_colour_file_format/Draft * * This function doesn't create the \ element but rather the \, * \, \ ... elements. It is assumed that colorElt is the \ * element. * * @param pixel buffer to serialized * @param colorElt root element for the serialization, it is assumed that this * element is \ * @param doc is the document containing colorElt */ virtual void colorToXML(const quint8* pixel, QDomDocument& doc, QDomElement& colorElt) const = 0; /** * Unserialize a color following Create's swatch color specification available - * at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format + * at https://web.archive.org/web/20110826002520/http://create.freedesktop.org/wiki/Swatches_-_colour_file_format/Draft * * @param pixel buffer where the color will be unserialized * @param elt the element to unserialize (\, \, \) * @return the unserialize color, or an empty color object if the function failed * to unserialize the color */ virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const = 0; KoColorTransformation* createColorTransformation(const QString & id, const QHash & parameters) const; protected: /** * Use this function in the constructor of your colorspace to add the information about a channel. * @param ci a pointer to the information about a channel */ virtual void addChannel(KoChannelInfo * ci); const KoColorConversionTransformation* toLabA16Converter() const; const KoColorConversionTransformation* fromLabA16Converter() const; const KoColorConversionTransformation* toRgbA16Converter() const; const KoColorConversionTransformation* fromRgbA16Converter() const; /** * Returns the thread-local conversion cache. If it doesn't exist * yet, it is created. If it is currently too small, it is resized. */ QVector * threadLocalConversionCache(quint32 size) const; /** * This function defines the behavior of the bitBlt function * when the composition of pixels in different colorspaces is * requested, that is in case: * * srcCS == any * dstCS == this * * 1) preferCompositionInSourceColorSpace() == false, * * the source pixels are first converted to *this color space * and then composition is performed. * * 2) preferCompositionInSourceColorSpace() == true, * * the destination pixels are first converted into *srcCS color * space, then the composition is done, and the result is finally * converted into *this colorspace. * * This is used by alpha8() color space mostly, because it has * weaker representation of the color, so the composition * should be done in CS with richer functionality. */ virtual bool preferCompositionInSourceColorSpace() const; struct Private; Private * const d; }; inline QDebug operator<<(QDebug dbg, const KoColorSpace *cs) { if (cs) { dbg.nospace() << cs->name() << " (" << cs->colorModelId().id() << "," << cs->colorDepthId().id() << " )"; } else { dbg.nospace() << "0x0"; } return dbg.space(); } #endif // KOCOLORSPACE_H diff --git a/libs/pigment/KoLut.h b/libs/pigment/KoLut.h index a92a88d5fd..e9213ad1c7 100644 --- a/libs/pigment/KoLut.h +++ b/libs/pigment/KoLut.h @@ -1,33 +1,33 @@ /* * Copyright (c) 2010 Cyrille Berger #define _USE_QT_TYPES_ namespace Ko { #include "lut.h" } #endif diff --git a/libs/pigment/compositeops/KoCompositeOpFunctions.h b/libs/pigment/compositeops/KoCompositeOpFunctions.h index 7357238e0e..80c242c356 100644 --- a/libs/pigment/compositeops/KoCompositeOpFunctions.h +++ b/libs/pigment/compositeops/KoCompositeOpFunctions.h @@ -1,952 +1,952 @@ /* * Copyright (c) 2011 Silvio Heinrich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KOCOMPOSITEOP_FUNCTIONS_H_ #define KOCOMPOSITEOP_FUNCTIONS_H_ #include template inline void cfReorientedNormalMapCombine(TReal srcR, TReal srcG, TReal srcB, TReal& dstR, TReal& dstG, TReal& dstB) { - // see http://blog.selfshadow.com/publications/blending-in-detail/ by Barre-Brisebois and Hill + // see https://blog.selfshadow.com/publications/blending-in-detail/ by Barre-Brisebois and Hill TReal tx = 2*srcR-1; TReal ty = 2*srcG-1; TReal tz = 2*srcB; TReal ux = -2*dstR+1; TReal uy = -2*dstG+1; TReal uz = 2*dstB-1; TReal k = (tx*ux+ty*uy+tz*uz)/tz; // dot(t,u)/t.z TReal rx = tx*k-ux; TReal ry = ty*k-uy; TReal rz = tz*k-uz; k = 1/sqrt(rx*rx+ry*ry+rz*rz); // normalize result rx *= k; ry *= k; rz *= k; dstR = rx*0.5+0.5; dstG = ry*0.5+0.5; dstB = rz*0.5+0.5; } template inline void cfColor(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) { TReal lum = getLightness(dr, dg, db); dr = sr; dg = sg; db = sb; setLightness(dr, dg, db, lum); } template inline void cfLightness(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) { setLightness(dr, dg, db, getLightness(sr, sg, sb)); } template inline void cfIncreaseLightness(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) { addLightness(dr, dg, db, getLightness(sr, sg, sb)); } template inline void cfDecreaseLightness(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) { addLightness(dr, dg, db, getLightness(sr, sg, sb) - TReal(1.0)); } template inline void cfSaturation(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) { TReal sat = getSaturation(sr, sg, sb); TReal light = getLightness(dr, dg, db); setSaturation(dr, dg, db, sat); setLightness(dr, dg, db, light); } template inline void cfIncreaseSaturation(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) { using namespace Arithmetic; TReal sat = lerp(getSaturation(dr,dg,db), unitValue(), getSaturation(sr,sg,sb)); TReal light = getLightness(dr, dg, db); setSaturation(dr, dg, db, sat); setLightness(dr, dg, db, light); } template inline void cfDecreaseSaturation(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) { using namespace Arithmetic; TReal sat = lerp(zeroValue(), getSaturation(dr,dg,db), getSaturation(sr,sg,sb)); TReal light = getLightness(dr, dg, db); setSaturation(dr, dg, db, sat); setLightness(dr, dg, db, light); } template inline void cfHue(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) { TReal sat = getSaturation(dr, dg, db); TReal lum = getLightness(dr, dg, db); dr = sr; dg = sg; db = sb; setSaturation(dr, dg, db, sat); setLightness(dr, dg, db, lum); } template inline void cfTangentNormalmap(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) { using namespace Arithmetic; TReal half=halfValue(); dr = sr+(dr-half); dg = sg+(dg-half); db = sb+(db-unitValue()); } template inline void cfDarkerColor(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) { TReal lum = getLightness(dr, dg, db); TReal lum2 = getLightness(sr, sg, sb); if (lum inline void cfLighterColor(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) { TReal lum = getLightness(dr, dg, db); TReal lum2 = getLightness(sr, sg, sb); if (lum>lum2) { sr = dr; sg = dg; sb = db; } else { dr = sr; dg = sg; db = sb; } } template inline T cfColorBurn(T src, T dst) { using namespace Arithmetic; if(dst == unitValue()) return unitValue(); T invDst = inv(dst); if(src < invDst) return zeroValue(); return inv(clamp(div(invDst, src))); } template inline T cfLinearBurn(T src, T dst) { using namespace Arithmetic; typedef typename KoColorSpaceMathsTraits::compositetype composite_type; return clamp(composite_type(src) + dst - unitValue()); } template inline T cfColorDodge(T src, T dst) { using namespace Arithmetic; //Fixing Color Dodge to avoid ZX Colors on bright area. if(src == unitValue()) return unitValue(); T invSrc = inv(src); if(invSrc == zeroValue()) return unitValue(); return Arithmetic::clamp(div(dst, invSrc)); } template inline T cfAddition(T src, T dst) { typedef typename KoColorSpaceMathsTraits::compositetype composite_type; return Arithmetic::clamp(composite_type(src) + dst); } template inline T cfSubtract(T src, T dst) { using namespace Arithmetic; typedef typename KoColorSpaceMathsTraits::compositetype composite_type; return clamp(composite_type(dst) - src); } template inline T cfInverseSubtract(T src, T dst) { using namespace Arithmetic; typedef typename KoColorSpaceMathsTraits::compositetype composite_type; return clamp(composite_type(dst) - inv(src)); } template inline T cfExclusion(T src, T dst) { using namespace Arithmetic; typedef typename KoColorSpaceMathsTraits::compositetype composite_type; composite_type x = mul(src, dst); return clamp(composite_type(dst) + src - (x + x)); } template inline T cfDivide(T src, T dst) { using namespace Arithmetic; //typedef typename KoColorSpaceMathsTraits::compositetype composite_type; if(src == zeroValue()) return (dst == zeroValue()) ? zeroValue() : unitValue(); return clamp(div(dst, src)); } template inline T cfHardLight(T src, T dst) { using namespace Arithmetic; typedef typename KoColorSpaceMathsTraits::compositetype composite_type; composite_type src2 = composite_type(src) + src; if(src > halfValue()) { // screen(src*2.0 - 1.0, dst) src2 -= unitValue(); // src2 is guaranteed to be smaller than unitValue() now return Arithmetic::unionShapeOpacity(T(src2), dst); } // src2 is guaranteed to be smaller than unitValue() due to 'if' return Arithmetic::mul(T(src2), dst); } template inline T cfSoftLightSvg(T src, T dst) { using namespace Arithmetic; qreal fsrc = scale(src); qreal fdst = scale(dst); if(fsrc > 0.5f) { qreal D = (fdst > 0.25f) ? sqrt(fdst) : ((16.0f*fdst - 12.0)*fdst + 4.0f)*fdst; return scale(fdst + (2.0f*fsrc - 1.0f) * (D - fdst)); } return scale(fdst - (1.0f - 2.0f * fsrc) * fdst * (1.0f - fdst)); } template inline T cfSoftLight(T src, T dst) { using namespace Arithmetic; qreal fsrc = scale(src); qreal fdst = scale(dst); if(fsrc > 0.5f) { return scale(fdst + (2.0f * fsrc - 1.0f) * (sqrt(fdst) - fdst)); } return scale(fdst - (1.0f - 2.0f*fsrc) * fdst * (1.0f - fdst)); } template inline T cfVividLight(T src, T dst) { using namespace Arithmetic; typedef typename KoColorSpaceMathsTraits::compositetype composite_type; if(src < halfValue()) { if(src == zeroValue()) return (dst == unitValue()) ? unitValue() : zeroValue(); // min(1,max(0,1-(1-dst) / (2*src))) composite_type src2 = composite_type(src) + src; composite_type dsti = inv(dst); return clamp(unitValue() - (dsti * unitValue() / src2)); } if(src == unitValue()) return (dst == zeroValue()) ? zeroValue() : unitValue(); // min(1,max(0, dst / (2*(1-src))) composite_type srci2 = inv(src); srci2 += srci2; return clamp(composite_type(dst) * unitValue() / srci2); } template inline T cfPinLight(T src, T dst) { typedef typename KoColorSpaceMathsTraits::compositetype composite_type; // TODO: verify that the formula is correct (the first max would be useless here) // max(0, max(2*src-1, min(dst, 2*src))) composite_type src2 = composite_type(src) + src; composite_type a = qMin(dst, src2); composite_type b = qMax(src2-Arithmetic::unitValue(), a); return T(b); } template inline T cfArcTangent(T src, T dst) { using namespace Arithmetic; if(dst == zeroValue()) return (src == zeroValue()) ? zeroValue() : unitValue(); return scale(2.0 * atan(scale(src) / scale(dst)) / Arithmetic::pi); } template inline T cfAllanon(T src, T dst) { using namespace Arithmetic; typedef typename KoColorSpaceMathsTraits::compositetype composite_type; // (dst + src) / 2 [or (dst + src) * 0.5] return T((composite_type(src) + dst) * halfValue() / unitValue()); } template inline T cfLinearLight(T src, T dst) { using namespace Arithmetic; typedef typename KoColorSpaceMathsTraits::compositetype composite_type; // min(1,max(0,(dst + 2*src)-1)) return clamp((composite_type(src) + src + dst) - unitValue()); } template inline T cfParallel(T src, T dst) { using namespace Arithmetic; typedef typename KoColorSpaceMathsTraits::compositetype composite_type; // min(max(2 / (1/dst + 1/src), 0), 1) composite_type unit = unitValue(); composite_type s = (src != zeroValue()) ? div(unit, src) : unit; composite_type d = (dst != zeroValue()) ? div(unit, dst) : unit; if (src == zeroValue()) { return zeroValue(); } if (dst == zeroValue()) { return zeroValue(); } return clamp((unit+unit) * unit / (d+s)); } template inline T cfEquivalence(T src, T dst) { typedef typename KoColorSpaceMathsTraits::compositetype composite_type; // 1 - abs(dst - src) composite_type x = composite_type(dst) - src; return (x < Arithmetic::zeroValue()) ? T(-x) : T(x); } template inline T cfGrainMerge(T src, T dst) { using namespace Arithmetic; typedef typename KoColorSpaceMathsTraits::compositetype composite_type; return clamp(composite_type(dst) + src - halfValue()); } template inline T cfGrainExtract(T src, T dst) { using namespace Arithmetic; typedef typename KoColorSpaceMathsTraits::compositetype composite_type; return clamp(composite_type(dst) - src + halfValue()); } template inline T cfHardMix(T src, T dst) { return (dst > Arithmetic::halfValue()) ? cfColorDodge(src,dst) : cfColorBurn(src,dst); } template inline T cfHardMixPhotoshop(T src, T dst) { using namespace Arithmetic; typedef typename KoColorSpaceMathsTraits::compositetype composite_type; const composite_type sum = composite_type(src) + dst; return sum > unitValue() ? unitValue() : zeroValue(); } template inline T cfAdditiveSubtractive(T src, T dst) { using namespace Arithmetic; // min(1,max(0,abs(sqr(CB)-sqr(CT)))) qreal x = sqrt(scale(dst)) - sqrt(scale(src)); return scale((x < 0.0) ? -x : x); } template inline T cfGammaDark(T src, T dst) { using namespace Arithmetic; if(src == zeroValue()) return zeroValue(); // power(dst, 1/src) return scale(pow(scale(dst), 1.0/scale(src))); } template inline T cfGammaLight(T src, T dst) { using namespace Arithmetic; return scale(pow(scale(dst), scale(src))); } template inline T cfGammaIllumination(T src, T dst) { using namespace Arithmetic; return inv(cfGammaDark(inv(src),inv(dst))); } template inline T cfGeometricMean(T src, T dst) { using namespace Arithmetic; return scale(sqrt(scale(dst) * scale(src))); } template inline T cfOver(T src, T dst) { Q_UNUSED(dst); return src; } template inline T cfOverlay(T src, T dst) { return cfHardLight(dst, src); } template inline T cfMultiply(T src, T dst) { return Arithmetic::mul(src, dst); } template inline T cfHardOverlay(T src, T dst) { using namespace Arithmetic; qreal fsrc = scale(src); qreal fdst = scale(dst); if (fsrc == 1.0) { return scale(1.0);} if(fsrc > 0.5f) { return scale(cfDivide(inv(2.0 * fsrc - 1.0f), fdst)); } return scale(mul(2.0 * fsrc, fdst)); } template inline T cfDifference(T src, T dst) { return qMax(src,dst) - qMin(src,dst); } template inline T cfScreen(T src, T dst) { return Arithmetic::unionShapeOpacity(src, dst); } template inline T cfDarkenOnly(T src, T dst) { return qMin(src, dst); } template inline T cfLightenOnly(T src, T dst) { return qMax(src, dst); } template inline T cfGlow(T src, T dst) { using namespace Arithmetic; // see http://www.pegtop.net/delphi/articles/blendmodes/quadratic.htm for formulas of Quadratic Blending Modes like Glow, Reflect, Freeze, and Heat if (dst == unitValue()) { return unitValue(); } return clamp(div(mul(src, src), inv(dst))); } template inline T cfReflect(T src, T dst) { using namespace Arithmetic; return clamp(cfGlow(dst,src)); } template inline T cfHeat(T src, T dst) { using namespace Arithmetic; if(src == unitValue()) { return unitValue(); } if(dst == zeroValue()) { return zeroValue(); } return inv(clamp(div(mul(inv(src), inv(src)),dst))); } template inline T cfFreeze(T src, T dst) { using namespace Arithmetic; return (cfHeat(dst,src)); } template inline T cfHelow(T src, T dst) { using namespace Arithmetic; // see http://www.pegtop.net/delphi/articles/blendmodes/quadratic.htm for formulas of Quadratic Blending Modes like Glow, Reflect, Freeze, and Heat if (cfHardMixPhotoshop(src,dst) == unitValue()) { return cfHeat(src,dst); } if (src == zeroValue()) { return zeroValue(); } return (cfGlow(src,dst)); } template inline T cfFrect(T src, T dst) { using namespace Arithmetic; if (cfHardMixPhotoshop(src,dst) == unitValue()) { return cfFreeze(src,dst); } if (dst == zeroValue()) { return zeroValue(); } return (cfReflect(src,dst)); } template inline T cfGleat(T src, T dst) { using namespace Arithmetic; // see http://www.pegtop.net/delphi/articles/blendmodes/quadratic.htm for formulas of Quadratic Blending Modes like Glow, Reflect, Freeze, and Heat if(dst == unitValue()) { return unitValue(); } if(cfHardMixPhotoshop(src,dst) == unitValue()) { return cfGlow(src,dst); } return (cfHeat(src,dst)); } template inline T cfReeze(T src, T dst) { using namespace Arithmetic; return (cfGleat(dst,src)); } template inline T cfFhyrd(T src, T dst) { using namespace Arithmetic; return (cfAllanon(cfFrect(src,dst),cfHelow(src,dst))); } template inline T cfInterpolation(T src, T dst) { using namespace Arithmetic; qreal fsrc = scale(src); qreal fdst = scale(dst); if(dst == zeroValue() && src == zeroValue()) { return zeroValue(); } return scale(.5f-.25f*cos(pi*(fsrc))-.25f*cos(pi*(fdst))); } template inline T cfInterpolationB(T src, T dst) { using namespace Arithmetic; return cfInterpolation(cfInterpolation(src,dst),cfInterpolation(src,dst)); } template inline T cfPenumbraB(T src, T dst) { using namespace Arithmetic; if (dst == unitValue()) { return unitValue(); } if (dst + src < unitValue()) { return (cfColorDodge(dst,src)/2); } if (src == zeroValue()) { return zeroValue(); } return inv(clamp(div(inv(dst),src)/2)); } template inline T cfPenumbraD(T src, T dst) { using namespace Arithmetic; if (dst == unitValue()) { return unitValue(); } return cfArcTangent(src,inv(dst)); } template inline T cfPenumbraC(T src, T dst) { using namespace Arithmetic; return cfPenumbraD(dst,src); } template inline T cfPenumbraA(T src, T dst) { using namespace Arithmetic; return (cfPenumbraB(dst,src)); } template inline T cfSoftLightIFSIllusions(T src, T dst) { using namespace Arithmetic; qreal fsrc = scale(src); qreal fdst = scale(dst); return scale(pow(fdst,pow(2.0,(mul(2.0,.5f-fsrc))))); } template inline T cfSoftLightPegtopDelphi(T src, T dst) { using namespace Arithmetic; return clamp(cfAddition(mul(dst,cfScreen(src,dst)),mul(mul(src,dst),inv(dst)))); } template inline T cfNegation(T src, T dst) { using namespace Arithmetic; typedef typename KoColorSpaceMathsTraits::compositetype composite_type; composite_type unit = unitValue(); composite_type a = unit - src - dst; composite_type s = abs(a); composite_type d = unit - s; return T(d); } template inline T cfNor(T src, T dst) { using namespace Arithmetic; return and(src,dst); } template inline T cfNand(T src, T dst) { using namespace Arithmetic; return or(src,dst); } template inline T cfXor(T src, T dst) { using namespace Arithmetic; return xor(src,dst); } template inline T cfXnor(T src, T dst) { using namespace Arithmetic; return cfXor(src,inv(dst)); } template inline T cfAnd(T src, T dst) { using namespace Arithmetic; return cfNor(inv(src),inv(dst)); } template inline T cfOr(T src, T dst) { using namespace Arithmetic; return cfNand(inv(src),inv(dst)); } template inline T cfConverse(T src, T dst) { using namespace Arithmetic; return cfOr(inv(src),dst); } template inline T cfNotConverse(T src, T dst) { using namespace Arithmetic; return cfAnd(src,inv(dst)); } template inline T cfImplies(T src, T dst) { using namespace Arithmetic; return cfOr(src,inv(dst)); } template inline T cfNotImplies(T src, T dst) { using namespace Arithmetic; return cfAnd(inv(src),dst); } template inline T cfPNormA(T src, T dst) { using namespace Arithmetic; //This is also known as P-Norm mode with factor of 2.3333 See IMBLEND image blending mode samples, and please see imblend.m file found on Additional Blending Mode thread at Phabricator. 1/2.3333 is .42875... return clamp(pow(pow((float)dst, 2.3333333333333333) + pow((float)src, 2.3333333333333333), 0.428571428571434)); } template inline T cfPNormB(T src, T dst) { using namespace Arithmetic; //This is also known as P-Norm mode with factor of 2.3333 See IMBLEND image blending mode samples, and please see imblend.m file found on Additional Blending Mode thread at Phabricator. 1/2.3333 is .42875... return clamp(pow(pow(dst,4)+pow(src,4),0.25)); } template inline T cfSuperLight(T src, T dst) { using namespace Arithmetic; //4.0 can be adjusted to taste. 4.0 is picked for being the best in terms of contrast and details. See imblend.m file. qreal fsrc = scale(src); qreal fdst = scale(dst); if (fsrc < .5) { return scale(inv(pow(pow(inv(fdst),2.875)+pow(inv(2.0*fsrc),2.875),1.0/2.875))); } return scale(pow(pow(fdst,2.875)+pow(2.0*fsrc-1.0,2.875),1.0/2.875)); } template inline T cfTintIFSIllusions(T src, T dst) { using namespace Arithmetic; //Known as Light Blending mode found in IFS Illusions. Picked this name because it results into a very strong tint, and has better naming convention. qreal fsrc = scale(src); qreal fdst = scale(dst); return scale(fsrc*inv(fdst)+sqrt(fdst)); } template inline T cfShadeIFSIllusions(T src, T dst) { using namespace Arithmetic; //Known as Shadow Blending mode found in IFS Illusions. Picked this name because it is the opposite of Tint (IFS Illusion Blending mode). qreal fsrc = scale(src); qreal fdst = scale(dst); return scale(inv((inv(fdst)*fsrc)+sqrt(inv(fsrc)))); } template inline T cfFogLightenIFSIllusions(T src, T dst) { using namespace Arithmetic; //Known as Bright Blending mode found in IFS Illusions. Picked this name because the shading reminds me of fog when overlaying with a gradient. qreal fsrc = scale(src); qreal fdst = scale(dst); if (fsrc < .5) { return scale(inv(inv(fsrc)*fsrc)-inv(fdst)*inv(fsrc)); } return scale(fsrc-inv(fdst)*inv(fsrc)+pow(inv(fsrc),2)); } template inline T cfFogDarkenIFSIllusions(T src, T dst) { using namespace Arithmetic; //Known as Dark Blending mode found in IFS Illusions. Picked this name because the shading reminds me of fog when overlaying with a gradient. qreal fsrc = scale(src); qreal fdst = scale(dst); if (fsrc < .5) { return scale(inv(fsrc)*fsrc+fsrc*fdst); } return scale(fsrc*fdst+fsrc-pow(fsrc,2)); } template inline T cfModulo(T src, T dst) { using namespace Arithmetic; return mod(dst,src); } template inline T cfModuloShift(T src, T dst) { using namespace Arithmetic; qreal fsrc = scale(src); qreal fdst = scale(dst); if (fsrc == 1.0 && fdst == 0.0) { return scale(0.0); } return scale(mod((fdst+fsrc),1.0000000000)); } template inline T cfModuloShiftContinuous(T src, T dst) { using namespace Arithmetic; //This blending mode do not behave like difference/equivalent with destination layer inverted if you use group layer on addition while the content of group layer contains several addition-mode layers, it works as expected on float images. So, no need to change this. qreal fsrc = scale(src); qreal fdst = scale(dst); if (fsrc == 1.0 && fdst == 0.0) { return scale(1.0); } return scale((int(ceil(fdst+fsrc)) % 2 != 0) || (fdst == zeroValue()) ? cfModuloShift(fsrc,fdst) : inv(cfModuloShift(fsrc,fdst))); } template inline T cfDivisiveModulo(T src, T dst) { using namespace Arithmetic; //I have to use 1.00000 as unitValue failed to work for those area. qreal fsrc = scale(src); qreal fdst = scale(dst); if (fsrc == zeroValue()) { return scale(mod(((1.0000000000/epsilon()) * fdst),1.0000000000)); } return scale(mod(((1.0000000000/fsrc) * fdst),1.0000000000)); } template inline T cfDivisiveModuloContinuous(T src, T dst) { using namespace Arithmetic; qreal fsrc = scale(src); qreal fdst = scale(dst); if (fdst == zeroValue()) { return zeroValue(); } if (fsrc == zeroValue()) { return cfDivisiveModulo(fsrc,fdst); } return scale( int(ceil(fdst/fsrc)) % 2 != 0 ? cfDivisiveModulo(fsrc,fdst) : inv(cfDivisiveModulo(fsrc,fdst))); } template inline T cfModuloContinuous(T src, T dst) { using namespace Arithmetic; return cfMultiply(cfDivisiveModuloContinuous(src,dst),src); } template inline T cfEasyDodge(T src, T dst) { using namespace Arithmetic; // The 13 divided by 15 can be adjusted to taste. See imgblend.m qreal fsrc = scale(src); qreal fdst = scale(dst); if (fsrc == 1.0) { return scale(1.0);} return scale(pow(fdst,mul(inv(fsrc != 1.0 ? fsrc : .999999999999),1.039999999))); } template inline T cfEasyBurn(T src, T dst) { using namespace Arithmetic; // The 13 divided by 15 can be adjusted to taste. See imgblend.m qreal fsrc = scale(src); qreal fdst = scale(dst); return scale(inv(pow(inv(fsrc != 1.0 ? fsrc : .999999999999),mul(fdst,1.039999999)))); } template inline T cfFlatLight(T src, T dst) { using namespace Arithmetic; if (src == zeroValue()) { return zeroValue(); } return clamp(cfHardMixPhotoshop(inv(src),dst)==unitValue() ? cfPenumbraB(src,dst) : cfPenumbraA(src,dst)); } template inline void cfAdditionSAI(TReal src, TReal sa, TReal& dst, TReal& da) { using namespace Arithmetic; typedef typename KoColorSpaceMathsTraits::compositetype composite_type; Q_UNUSED(da); composite_type newsrc; newsrc = mul(src, sa); dst = clamp(newsrc + dst); } #endif // KOCOMPOSITEOP_FUNCTIONS_H_ diff --git a/libs/pigment/lut.h b/libs/pigment/lut.h index 4812876feb..6f0c885c86 100644 --- a/libs/pigment/lut.h +++ b/libs/pigment/lut.h @@ -1,334 +1,334 @@ /* * Copyright (c) 2010 Cyrille Berger * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _LUT_H_ #define _LUT_H_ template class LutKey; template class FullLutKey; /** * Provide an implementation for a look-up table for a function. Do not use directly, instead * use @ref Lut when you are not interested in having the full look-up table or for floating * point, or use @ref FullLut for 8bits and 16bits when you want to have a full Lut. * * @code * struct MyFunction { * inline static int compute(int i) * { * return 1-i; * } * } * Lut myLut; * @endcode */ template class BaseLut { private: /** * Initialization of the table. */ inline void init() { int size = m_key.size(); m_table = new _OutputT_[size]; for(int i = 0; i < size; ++i) { m_table[i] = m_function(m_key.keyToInput(i)); } } public: /** * Create the lut with the specific key. */ inline BaseLut(_LutKeyT_ key, _FunctionT_ function = _FunctionT_()) : m_key(key), m_function(function) { init(); } inline ~BaseLut() { // just leak on exit -- we get into trouble for explicitly // deleting stuff from static objects, like registries //delete[] m_table; } public: /** * @return the function value for parameter @p i */ inline _OutputT_ operator()(_InputT_ i) const { if(m_key.inrange(i)) { return m_table[m_key.inputToKey(i)]; } return m_function(i); } private: _OutputT_* m_table; _LutKeyT_ m_key; _FunctionT_ m_function; }; /** * This Lut is limited to a range of values. */ template class Lut : public BaseLut<_FunctionT_, _OutputT_, _InputT_, LutKey<_InputT_> > { public: /** * Create the lut between @p _min and @p _max . */ inline Lut(_InputT_ _min, _InputT_ _max, _FunctionT_ function = _FunctionT_()) : BaseLut<_FunctionT_, _OutputT_, _InputT_, LutKey<_InputT_> >( LutKey<_InputT_>(_min, _max), function) { } inline Lut(LutKey<_InputT_> key, _FunctionT_ function = _FunctionT_()) : BaseLut<_FunctionT_, _OutputT_, _InputT_, LutKey<_InputT_> >( key, function) { } }; /** * This Lut has precomputed values for all elements. */ template class FullLut : public BaseLut<_FunctionT_, _OutputT_, _InputT_, FullLutKey<_InputT_> > { public: inline FullLut( _FunctionT_ function = _FunctionT_()) : BaseLut<_FunctionT_, _OutputT_, _InputT_, FullLutKey<_InputT_> >( FullLutKey<_InputT_>(), function) { } }; #ifdef _USE_QT_TYPES_ typedef quint8 lut_uint8; typedef quint16 lut_uint16; typedef quint32 lut_uint32; #else #include typedef uint8_t lut_uint8; typedef uint16_t lut_uint16; typedef uint32_t lut_uint32; #endif // integer specialization #define PARTIAL_LUT_INT_SPECIALIZATION(_INT_TYPE_) \ template<> \ class LutKey<_INT_TYPE_> { \ public: \ LutKey<_INT_TYPE_>(_INT_TYPE_ min, _INT_TYPE_ max) : m_min(min), m_max(max) \ { \ } \ public: \ inline int inputToKey(_INT_TYPE_ i) const \ { \ return i - m_min; \ } \ inline _INT_TYPE_ keyToInput(int k) const \ { \ return k + m_min; \ } \ inline bool inrange(_INT_TYPE_ i) const \ { \ return i >= m_min && i <= m_max; \ } \ inline _INT_TYPE_ minimum() const \ { \ return m_min; \ } \ inline _INT_TYPE_ maximum() const \ { \ return m_max; \ } \ inline int size() const \ { \ return m_max - m_min + 1; \ } \ private: \ _INT_TYPE_ m_min, m_max; \ }; PARTIAL_LUT_INT_SPECIALIZATION(lut_uint8) PARTIAL_LUT_INT_SPECIALIZATION(lut_uint16) PARTIAL_LUT_INT_SPECIALIZATION(lut_uint32) #define FULL_LUT_INT_SPECIALIZATION(_INT_TYPE_, _MIN_, _MAX_) \ template<> \ class FullLutKey<_INT_TYPE_> { \ public: \ FullLutKey<_INT_TYPE_>() \ { \ } \ public: \ inline int inputToKey(_INT_TYPE_ i) const \ { \ return i - _MIN_; \ } \ inline _INT_TYPE_ keyToInput(int k) const \ { \ return k + _MIN_; \ } \ inline bool inrange(_INT_TYPE_ ) const \ { \ return true; \ } \ inline _INT_TYPE_ minimum() const \ { \ return _MIN_; \ } \ inline _INT_TYPE_ maximum() const \ { \ return _MAX_; \ } \ inline int size() const \ { \ return _MAX_ - _MIN_ + 1; \ } \ private: \ }; FULL_LUT_INT_SPECIALIZATION(lut_uint8, 0, 255) FULL_LUT_INT_SPECIALIZATION(lut_uint16, 0, 65535) // float specialization /** * This provide an implementation for a LutKey for floating point input values. * * Based on "High-speed Conversion of Floating Point Images to 8-bit" by Bill Spitzaks - * (http://mysite.verizon.net/spitzak/conversion/) + * (https://spitzak.github.io/conversion/sketches_0265.pdf) */ template<> class LutKey { public: union IFNumber { lut_uint32 i; float f; }; public: LutKey(float min, float max, float precision) : m_min(min), m_max(max), m_precision(precision) { // Those values where computed using the test_linear and setting the shift and then using // the standard deviation. if (precision <= 0.000011809f) { m_min = 1; m_max = -1; } else if (precision <= 0.0000237291f) m_shift = 8; else if (precision <= 0.0000475024f) m_shift = 9; else if (precision <= 0.0000948575f) m_shift = 10; else if (precision <= 0.00019013f) m_shift = 11; else if (precision <= 0.000379523f) m_shift = 12; else if (precision <= 0.000758431f) m_shift = 13; else if (precision <= 0.00151891f) m_shift = 14; else if (precision <= 0.00303725f) m_shift = 15; else m_shift = 16; if ( 0.0 <= m_min && m_min <= precision) m_min = precision; if ( -precision <= m_max && m_max <= 0.0) m_max = -precision; IFNumber uf; if(m_min > 0 && m_max > 0) { uf.f = m_min; m_tMin_p = uf.i >> m_shift; uf.f = m_max; m_tMax_p = uf.i >> m_shift; m_tMin_n = m_tMax_p; m_tMax_n = m_tMax_p; } else if( m_max < 0) { uf.f = m_min; m_tMax_n = uf.i >> m_shift; uf.f = m_max; m_tMin_n = uf.i >> m_shift; m_tMin_p = m_tMax_n; m_tMax_p = m_tMax_n; } else { // m_min <0 && m_max > 0 uf.f = precision; m_tMin_p = uf.i >> m_shift; uf.f = m_max; m_tMax_p = uf.i >> m_shift; uf.f = -precision; m_tMin_n = uf.i >> m_shift; uf.f = m_min; m_tMax_n = uf.i >> m_shift; } m_diff_p = m_tMax_p - m_tMin_p; } public: inline int inputToKey(float i) const { IFNumber uf; uf.f = i; int k = (uf.i >> m_shift); if(k <= m_tMax_p) { return k - m_tMin_p; } else { return k - m_tMin_n + m_diff_p; } } inline float keyToInput(int k) const { IFNumber uf; if( k <= m_diff_p ) { uf.i = ((k + m_tMin_p) << m_shift); } else { uf.i = ((k + m_tMin_n - m_diff_p ) << m_shift); } return uf.f; } inline bool inrange(float i) const { return i >= m_min && i <= m_max && (i < -m_precision || i > m_precision); } inline float minimum() const { return m_min; } inline float maximum() const { return m_max; } inline int size() const { return m_diff_p + m_tMax_n - m_tMin_p + 1; } private: float m_min, m_max, m_precision; int m_tMin_p, m_tMax_p, m_tMin_n, m_tMax_n, m_diff_p; int m_shift; }; #endif diff --git a/libs/pigment/resources/KoColorSet.cpp b/libs/pigment/resources/KoColorSet.cpp index 722f221884..b30ab028e8 100644 --- a/libs/pigment/resources/KoColorSet.cpp +++ b/libs/pigment/resources/KoColorSet.cpp @@ -1,1646 +1,1646 @@ /* This file is part of the KDE project Copyright (c) 2005 Boudewijn Rempt Copyright (c) 2016 L. E. Segovia 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // qFromLittleEndian #include #include #include #include #include #include #include #include #include "KisSwatch.h" #include "KoColorSet.h" #include "KoColorSet_p.h" namespace { /** * readAllLinesSafe() reads all the lines in the byte array * using the automated UTF8 and CR/LF transformations. That * might be necessary for opening GPL palettes created on Linux * in Windows environment. */ QStringList readAllLinesSafe(QByteArray *data) { QStringList lines; QBuffer buffer(data); buffer.open(QBuffer::ReadOnly); QTextStream stream(&buffer); QString line; while (stream.readLineInto(&line)) { lines << line; } return lines; } } const QString KoColorSet::GLOBAL_GROUP_NAME = QString(); const QString KoColorSet::KPL_VERSION_ATTR = "version"; const QString KoColorSet::KPL_GROUP_ROW_COUNT_ATTR = "rows"; const QString KoColorSet::KPL_PALETTE_COLUMN_COUNT_ATTR = "columns"; const QString KoColorSet::KPL_PALETTE_NAME_ATTR = "name"; const QString KoColorSet::KPL_PALETTE_COMMENT_ATTR = "comment"; const QString KoColorSet::KPL_PALETTE_FILENAME_ATTR = "filename"; const QString KoColorSet::KPL_PALETTE_READONLY_ATTR = "readonly"; const QString KoColorSet::KPL_COLOR_MODEL_ID_ATTR = "colorModelId"; const QString KoColorSet::KPL_COLOR_DEPTH_ID_ATTR = "colorDepthId"; const QString KoColorSet::KPL_GROUP_NAME_ATTR = "name"; const QString KoColorSet::KPL_SWATCH_ROW_ATTR = "row"; const QString KoColorSet::KPL_SWATCH_COL_ATTR = "column"; const QString KoColorSet::KPL_SWATCH_NAME_ATTR = "name"; const QString KoColorSet::KPL_SWATCH_ID_ATTR = "id"; const QString KoColorSet::KPL_SWATCH_SPOT_ATTR = "spot"; const QString KoColorSet::KPL_SWATCH_BITDEPTH_ATTR = "bitdepth"; const QString KoColorSet::KPL_PALETTE_PROFILE_TAG = "Profile"; const QString KoColorSet::KPL_SWATCH_POS_TAG = "Position"; const QString KoColorSet::KPL_SWATCH_TAG = "ColorSetEntry"; const QString KoColorSet::KPL_GROUP_TAG = "Group"; const QString KoColorSet::KPL_PALETTE_TAG = "ColorSet"; const int MAXIMUM_ALLOWED_COLUMNS = 4096; KoColorSet::KoColorSet(const QString& filename) : KoResource(filename) , d(new Private(this)) { if (!filename.isEmpty()) { QFileInfo f(filename); setIsEditable(f.isWritable()); } } /// Create an copied palette KoColorSet::KoColorSet(const KoColorSet& rhs) : QObject(0) , KoResource(rhs) , d(new Private(this)) { d->paletteType = rhs.d->paletteType; d->data = rhs.d->data; d->comment = rhs.d->comment; d->groupNames = rhs.d->groupNames; d->groups = rhs.d->groups; d->isGlobal = rhs.d->isGlobal; d->isEditable = rhs.d->isEditable; } KoColorSet::~KoColorSet() { } bool KoColorSet::load() { QFile file(filename()); if (file.size() == 0) return false; if (!file.open(QIODevice::ReadOnly)) { warnPigment << "Can't open file " << filename(); return false; } bool res = loadFromDevice(&file); file.close(); if (!QFileInfo(filename()).isWritable()) { setIsEditable(false); } return res; } bool KoColorSet::loadFromDevice(QIODevice *dev) { if (!dev->isOpen()) dev->open(QIODevice::ReadOnly); d->data = dev->readAll(); Q_ASSERT(d->data.size() != 0); return d->init(); } bool KoColorSet::save() { if (d->isGlobal) { // save to resource dir QFile file(filename()); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { return false; } saveToDevice(&file); file.close(); return true; } else { return true; // palette is not global, but still indicate that it's saved } } bool KoColorSet::saveToDevice(QIODevice *dev) const { bool res; switch(d->paletteType) { case GPL: res = d->saveGpl(dev); break; default: res = d->saveKpl(dev); } if (res) { KoResource::saveToDevice(dev); } return res; } QByteArray KoColorSet::toByteArray() const { QBuffer s; s.open(QIODevice::WriteOnly); if (!saveToDevice(&s)) { warnPigment << "saving palette failed:" << name(); return QByteArray(); } s.close(); s.open(QIODevice::ReadOnly); QByteArray res = s.readAll(); s.close(); return res; } bool KoColorSet::fromByteArray(QByteArray &data) { QBuffer buf(&data); buf.open(QIODevice::ReadOnly); return loadFromDevice(&buf); } KoColorSet::PaletteType KoColorSet::paletteType() const { return d->paletteType; } void KoColorSet::setPaletteType(PaletteType paletteType) { d->paletteType = paletteType; QString suffix; switch(d->paletteType) { case GPL: suffix = ".gpl"; break; case ACT: suffix = ".act"; break; case RIFF_PAL: case PSP_PAL: suffix = ".pal"; break; case ACO: suffix = ".aco"; break; case XML: suffix = ".xml"; break; case KPL: suffix = ".kpl"; break; case SBZ: suffix = ".sbz"; break; default: suffix = defaultFileExtension(); } QStringList fileName = filename().split("."); fileName.last() = suffix.replace(".", ""); setFilename(fileName.join(".")); } quint32 KoColorSet::colorCount() const { int colorCount = 0; for (KisSwatchGroup &g : d->groups.values()) { colorCount += g.colorCount(); } return colorCount; } void KoColorSet::add(const KisSwatch &c, const QString &groupName) { KisSwatchGroup &modifiedGroup = d->groups.contains(groupName) ? d->groups[groupName] : d->global(); modifiedGroup.addEntry(c); } void KoColorSet::setEntry(const KisSwatch &e, int x, int y, const QString &groupName) { KisSwatchGroup &modifiedGroup = d->groups.contains(groupName) ? d->groups[groupName] : d->global(); modifiedGroup.setEntry(e, x, y); } void KoColorSet::clear() { d->groups.clear(); d->groupNames.clear(); d->groups[GLOBAL_GROUP_NAME] = KisSwatchGroup(); d->groupNames.append(GLOBAL_GROUP_NAME); } KisSwatch KoColorSet::getColorGlobal(quint32 x, quint32 y) const { for (const QString &groupName : getGroupNames()) { if (d->groups.contains(groupName)) { if ((int)y < d->groups[groupName].rowCount()) { return d->groups[groupName].getEntry(x, y); } else { y -= d->groups[groupName].rowCount(); } } } return KisSwatch(); } KisSwatch KoColorSet::getColorGroup(quint32 x, quint32 y, QString groupName) { KisSwatch e; const KisSwatchGroup &sourceGroup = groupName == QString() ? d->global() : d->groups[groupName]; if (sourceGroup.checkEntry(x, y)) { e = sourceGroup.getEntry(x, y); } return e; } QStringList KoColorSet::getGroupNames() const { if (d->groupNames.size() != d->groups.size()) { warnPigment << "mismatch between groups and the groupnames list."; return QStringList(d->groups.keys()); } return d->groupNames; } bool KoColorSet::changeGroupName(const QString &oldGroupName, const QString &newGroupName) { if (!d->groups.contains(oldGroupName)) { return false; } if (oldGroupName == newGroupName) { return true; } d->groups[newGroupName] = d->groups[oldGroupName]; d->groups.remove(oldGroupName); d->groups[newGroupName].setName(newGroupName); //rename the string in the stringlist; int index = d->groupNames.indexOf(oldGroupName); d->groupNames.replace(index, newGroupName); return true; } void KoColorSet::setColumnCount(int columns) { d->groups[GLOBAL_GROUP_NAME].setColumnCount(columns); for (KisSwatchGroup &g : d->groups.values()) { g.setColumnCount(columns); } } int KoColorSet::columnCount() const { return d->groups[GLOBAL_GROUP_NAME].columnCount(); } QString KoColorSet::comment() { return d->comment; } void KoColorSet::setComment(QString comment) { d->comment = comment; } bool KoColorSet::addGroup(const QString &groupName) { if (d->groups.contains(groupName) || getGroupNames().contains(groupName)) { return false; } d->groupNames.append(groupName); d->groups[groupName] = KisSwatchGroup(); d->groups[groupName].setName(groupName); return true; } bool KoColorSet::moveGroup(const QString &groupName, const QString &groupNameInsertBefore) { if (!d->groupNames.contains(groupName) || d->groupNames.contains(groupNameInsertBefore)==false) { return false; } if (groupNameInsertBefore != GLOBAL_GROUP_NAME && groupName != GLOBAL_GROUP_NAME) { d->groupNames.removeAt(d->groupNames.indexOf(groupName)); int index = d->groupNames.indexOf(groupNameInsertBefore); d->groupNames.insert(index, groupName); } return true; } bool KoColorSet::removeGroup(const QString &groupName, bool keepColors) { if (!d->groups.contains(groupName)) { return false; } if (groupName == GLOBAL_GROUP_NAME) { return false; } if (keepColors) { // put all colors directly below global int startingRow = d->groups[GLOBAL_GROUP_NAME].rowCount(); for (const KisSwatchGroup::SwatchInfo &info : d->groups[groupName].infoList()) { d->groups[GLOBAL_GROUP_NAME].setEntry(info.swatch, info.column, info.row + startingRow); } } d->groupNames.removeAt(d->groupNames.indexOf(groupName)); d->groups.remove(groupName); return true; } QString KoColorSet::defaultFileExtension() const { return QString(".kpl"); } int KoColorSet::rowCount() const { int res = 0; for (const QString &name : getGroupNames()) { res += d->groups[name].rowCount(); } return res; } KisSwatchGroup *KoColorSet::getGroup(const QString &name) { if (!d->groups.contains(name)) { return 0; } return &(d->groups[name]); } KisSwatchGroup *KoColorSet::getGlobalGroup() { return getGroup(GLOBAL_GROUP_NAME); } bool KoColorSet::isGlobal() const { return d->isGlobal; } void KoColorSet::setIsGlobal(bool isGlobal) { d->isGlobal = isGlobal; } bool KoColorSet::isEditable() const { return d->isEditable; } void KoColorSet::setIsEditable(bool isEditable) { d->isEditable = isEditable; } KisSwatchGroup::SwatchInfo KoColorSet::getClosestColorInfo(KoColor compare, bool useGivenColorSpace) { KisSwatchGroup::SwatchInfo res; quint8 highestPercentage = 0; quint8 testPercentage = 0; for (const QString &groupName : getGroupNames()) { KisSwatchGroup *group = getGroup(groupName); for (const KisSwatchGroup::SwatchInfo &currInfo : group->infoList()) { KoColor color = currInfo.swatch.color(); if (useGivenColorSpace == true && compare.colorSpace() != color.colorSpace()) { color.convertTo(compare.colorSpace()); } else if (compare.colorSpace() != color.colorSpace()) { compare.convertTo(color.colorSpace()); } testPercentage = (255 - compare.colorSpace()->difference(compare.data(), color.data())); if (testPercentage > highestPercentage) { highestPercentage = testPercentage; res = currInfo; } } } return res; } /********************************KoColorSet::Private**************************/ KoColorSet::Private::Private(KoColorSet *a_colorSet) : colorSet(a_colorSet) { groups[KoColorSet::GLOBAL_GROUP_NAME] = KisSwatchGroup(); groupNames.append(KoColorSet::GLOBAL_GROUP_NAME); } KoColorSet::PaletteType KoColorSet::Private::detectFormat(const QString &fileName, const QByteArray &ba) { QFileInfo fi(fileName); // .pal if (ba.startsWith("RIFF") && ba.indexOf("PAL data", 8)) { return KoColorSet::RIFF_PAL; } // .gpl else if (ba.startsWith("GIMP Palette")) { return KoColorSet::GPL; } // .pal else if (ba.startsWith("JASC-PAL")) { return KoColorSet::PSP_PAL; } else if (fi.suffix().toLower() == "aco") { return KoColorSet::ACO; } else if (fi.suffix().toLower() == "act") { return KoColorSet::ACT; } else if (fi.suffix().toLower() == "xml") { return KoColorSet::XML; } else if (fi.suffix().toLower() == "kpl") { return KoColorSet::KPL; } else if (fi.suffix().toLower() == "sbz") { return KoColorSet::SBZ; } return KoColorSet::UNKNOWN; } void KoColorSet::Private::scribusParseColor(KoColorSet *set, QXmlStreamReader *xml) { KisSwatch colorEntry; // It's a color, retrieve it QXmlStreamAttributes colorProperties = xml->attributes(); QStringRef colorName = colorProperties.value("NAME"); colorEntry.setName(colorName.isEmpty() || colorName.isNull() ? i18n("Untitled") : colorName.toString()); // RGB or CMYK? if (colorProperties.hasAttribute("RGB")) { dbgPigment << "Color " << colorProperties.value("NAME") << ", RGB " << colorProperties.value("RGB"); KoColor currentColor(KoColorSpaceRegistry::instance()->rgb8()); QStringRef colorValue = colorProperties.value("RGB"); if (colorValue.length() != 7 && colorValue.at(0) != '#') { // Color is a hexadecimal number xml->raiseError("Invalid rgb8 color (malformed): " + colorValue); return; } else { bool rgbOk; quint32 rgb = colorValue.mid(1).toUInt(&rgbOk, 16); if (!rgbOk) { xml->raiseError("Invalid rgb8 color (unable to convert): " + colorValue); return; } quint8 r = rgb >> 16 & 0xff; quint8 g = rgb >> 8 & 0xff; quint8 b = rgb & 0xff; dbgPigment << "Color parsed: "<< r << g << b; currentColor.data()[0] = r; currentColor.data()[1] = g; currentColor.data()[2] = b; currentColor.setOpacity(OPACITY_OPAQUE_U8); colorEntry.setColor(currentColor); set->add(colorEntry); while(xml->readNextStartElement()) { //ignore - these are all unknown or the /> element tag xml->skipCurrentElement(); } return; } } else if (colorProperties.hasAttribute("CMYK")) { dbgPigment << "Color " << colorProperties.value("NAME") << ", CMYK " << colorProperties.value("CMYK"); KoColor currentColor(KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Integer8BitsColorDepthID.id(), QString())); QStringRef colorValue = colorProperties.value("CMYK"); if (colorValue.length() != 9 && colorValue.at(0) != '#') { // Color is a hexadecimal number xml->raiseError("Invalid cmyk color (malformed): " % colorValue); return; } else { bool cmykOk; quint32 cmyk = colorValue.mid(1).toUInt(&cmykOk, 16); // cmyk uses the full 32 bits if (!cmykOk) { xml->raiseError("Invalid cmyk color (unable to convert): " % colorValue); return; } quint8 c = cmyk >> 24 & 0xff; quint8 m = cmyk >> 16 & 0xff; quint8 y = cmyk >> 8 & 0xff; quint8 k = cmyk & 0xff; dbgPigment << "Color parsed: "<< c << m << y << k; currentColor.data()[0] = c; currentColor.data()[1] = m; currentColor.data()[2] = y; currentColor.data()[3] = k; currentColor.setOpacity(OPACITY_OPAQUE_U8); colorEntry.setColor(currentColor); set->add(colorEntry); while(xml->readNextStartElement()) { //ignore - these are all unknown or the /> element tag xml->skipCurrentElement(); } return; } } else { xml->raiseError("Unknown color space for color " + colorEntry.name()); } } bool KoColorSet::Private::loadScribusXmlPalette(KoColorSet *set, QXmlStreamReader *xml) { //1. Get name QXmlStreamAttributes paletteProperties = xml->attributes(); QStringRef paletteName = paletteProperties.value("Name"); dbgPigment << "Processed name of palette:" << paletteName; set->setName(paletteName.toString()); //2. Inside the SCRIBUSCOLORS, there are lots of colors. Retrieve them while(xml->readNextStartElement()) { QStringRef currentElement = xml->name(); if(QStringRef::compare(currentElement, "COLOR", Qt::CaseInsensitive) == 0) { scribusParseColor(set, xml); } else { xml->skipCurrentElement(); } } if(xml->hasError()) { return false; } return true; } quint16 KoColorSet::Private::readShort(QIODevice *io) { quint16 val; quint64 read = io->read((char*)&val, 2); if (read != 2) return false; return qFromBigEndian(val); } bool KoColorSet::Private::init() { // just in case this is a reload (eg by KoEditColorSetDialog), groupNames.clear(); groups.clear(); groupNames.append(KoColorSet::GLOBAL_GROUP_NAME); groups[KoColorSet::GLOBAL_GROUP_NAME] = KisSwatchGroup(); if (colorSet->filename().isNull()) { warnPigment << "Cannot load palette" << colorSet->name() << "there is no filename set"; return false; } if (data.isNull()) { QFile file(colorSet->filename()); if (file.size() == 0) { warnPigment << "Cannot load palette" << colorSet->name() << "there is no data available"; return false; } file.open(QIODevice::ReadOnly); data = file.readAll(); file.close(); } bool res = false; paletteType = detectFormat(colorSet->filename(), data); switch(paletteType) { case GPL: res = loadGpl(); break; case ACT: res = loadAct(); break; case RIFF_PAL: res = loadRiff(); break; case PSP_PAL: res = loadPsp(); break; case ACO: res = loadAco(); break; case XML: res = loadXml(); break; case KPL: res = loadKpl(); break; case SBZ: res = loadSbz(); break; default: res = false; } colorSet->setValid(res); QImage img(global().columnCount() * 4, global().rowCount() * 4, QImage::Format_ARGB32); QPainter gc(&img); gc.fillRect(img.rect(), Qt::darkGray); for (const KisSwatchGroup::SwatchInfo &info : global().infoList()) { QColor c = info.swatch.color().toQColor(); gc.fillRect(info.column * 4, info.row * 4, 4, 4, c); } colorSet->setImage(img); colorSet->setValid(res); data.clear(); return res; } bool KoColorSet::Private::saveGpl(QIODevice *dev) const { Q_ASSERT(dev->isOpen()); Q_ASSERT(dev->isWritable()); QTextStream stream(dev); stream << "GIMP Palette\nName: " << colorSet->name() << "\nColumns: " << colorSet->columnCount() << "\n#\n"; /* * Qt doesn't provide an interface to get a const reference to a QHash, that is * the underlying data structure of groups. Therefore, directly use * groups[KoColorSet::GLOBAL_GROUP_NAME] so that saveGpl can stay const */ for (int y = 0; y < groups[KoColorSet::GLOBAL_GROUP_NAME].rowCount(); y++) { for (int x = 0; x < colorSet->columnCount(); x++) { if (!groups[KoColorSet::GLOBAL_GROUP_NAME].checkEntry(x, y)) { continue; } const KisSwatch& entry = groups[KoColorSet::GLOBAL_GROUP_NAME].getEntry(x, y); QColor c = entry.color().toQColor(); stream << c.red() << " " << c.green() << " " << c.blue() << "\t"; if (entry.name().isEmpty()) stream << "Untitled\n"; else stream << entry.name() << "\n"; } } return true; } bool KoColorSet::Private::loadGpl() { if (data.isEmpty() || data.isNull() || data.length() < 50) { warnPigment << "Illegal Gimp palette file: " << colorSet->filename(); return false; } quint32 index = 0; QStringList lines = readAllLinesSafe(&data); if (lines.size() < 3) { warnPigment << "Not enough lines in palette file: " << colorSet->filename(); return false; } QString columnsText; qint32 r, g, b; KisSwatch e; // Read name if (!lines[0].startsWith("GIMP") || !lines[1].toLower().contains("name")) { warnPigment << "Illegal Gimp palette file: " << colorSet->filename(); return false; } colorSet->setName(i18n(lines[1].split(":")[1].trimmed().toLatin1())); index = 2; // Read columns int columns = 0; if (lines[index].toLower().contains("columns")) { columnsText = lines[index].split(":")[1].trimmed(); columns = columnsText.toInt(); if (columns > MAXIMUM_ALLOWED_COLUMNS) { warnPigment << "Refusing to set unreasonable number of columns (" << columns << ") in GIMP Palette file " << colorSet->filename() << " - using maximum number of allowed columns instead"; global().setColumnCount(MAXIMUM_ALLOWED_COLUMNS); } else { global().setColumnCount(columns); } index = 3; } for (qint32 i = index; i < lines.size(); i++) { if (lines[i].startsWith('#')) { comment += lines[i].mid(1).trimmed() + ' '; } else if (!lines[i].isEmpty()) { QStringList a = lines[i].replace('\t', ' ').split(' ', QString::SkipEmptyParts); if (a.count() < 3) { continue; } r = qBound(0, a[0].toInt(), 255); g = qBound(0, a[1].toInt(), 255); b = qBound(0, a[2].toInt(), 255); e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); for (int i = 0; i != 3; i++) { a.pop_front(); } QString name = a.join(" "); e.setName(name.isEmpty() || name == "Untitled" ? i18n("Untitled") : name); global().addEntry(e); } } int rowCount = global().colorCount()/ global().columnCount(); if (global().colorCount() % global().columnCount()>0) { rowCount ++; } global().setRowCount(rowCount); return true; } bool KoColorSet::Private::loadAct() { QFileInfo info(colorSet->filename()); colorSet->setName(info.completeBaseName()); KisSwatch e; for (int i = 0; i < data.size(); i += 3) { quint8 r = data[i]; quint8 g = data[i+1]; quint8 b = data[i+2]; e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); global().addEntry(e); } return true; } bool KoColorSet::Private::loadRiff() { - // http://worms2d.info/Palette_file + // https://worms2d.info/Palette_file QFileInfo info(colorSet->filename()); colorSet->setName(info.completeBaseName()); KisSwatch e; RiffHeader header; memcpy(&header, data.constData(), sizeof(RiffHeader)); header.colorcount = qFromBigEndian(header.colorcount); for (int i = sizeof(RiffHeader); (i < (int)(sizeof(RiffHeader) + header.colorcount) && i < data.size()); i += 4) { quint8 r = data[i]; quint8 g = data[i+1]; quint8 b = data[i+2]; e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e); } return true; } bool KoColorSet::Private::loadPsp() { QFileInfo info(colorSet->filename()); colorSet->setName(info.completeBaseName()); KisSwatch e; qint32 r, g, b; QStringList l = readAllLinesSafe(&data); if (l.size() < 4) return false; if (l[0] != "JASC-PAL") return false; if (l[1] != "0100") return false; int entries = l[2].toInt(); for (int i = 0; i < entries; ++i) { QStringList a = l[i + 3].replace('\t', ' ').split(' ', QString::SkipEmptyParts); if (a.count() != 3) { continue; } r = qBound(0, a[0].toInt(), 255); g = qBound(0, a[1].toInt(), 255); b = qBound(0, a[2].toInt(), 255); e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); QString name = a.join(" "); e.setName(name.isEmpty() ? i18n("Untitled") : name); groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e); } return true; } bool KoColorSet::Private::loadKpl() { QBuffer buf(&data); buf.open(QBuffer::ReadOnly); QScopedPointer store(KoStore::createStore(&buf, KoStore::Read, "krita/x-colorset", KoStore::Zip)); if (!store || store->bad()) { return false; } if (store->hasFile("profiles.xml")) { if (!store->open("profiles.xml")) { return false; } QByteArray data; data.resize(store->size()); QByteArray ba = store->read(store->size()); store->close(); QDomDocument doc; doc.setContent(ba); QDomElement e = doc.documentElement(); QDomElement c = e.firstChildElement(KPL_PALETTE_PROFILE_TAG); while (!c.isNull()) { QString name = c.attribute(KPL_PALETTE_NAME_ATTR); QString filename = c.attribute(KPL_PALETTE_FILENAME_ATTR); QString colorModelId = c.attribute(KPL_COLOR_MODEL_ID_ATTR); QString colorDepthId = c.attribute(KPL_COLOR_DEPTH_ID_ATTR); if (!KoColorSpaceRegistry::instance()->profileByName(name)) { store->open(filename); QByteArray data; data.resize(store->size()); data = store->read(store->size()); store->close(); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(colorModelId, colorDepthId, data); if (profile && profile->valid()) { KoColorSpaceRegistry::instance()->addProfile(profile); } } c = c.nextSiblingElement(); } } { if (!store->open("colorset.xml")) { return false; } QByteArray data; data.resize(store->size()); QByteArray ba = store->read(store->size()); store->close(); int desiredColumnCount; QDomDocument doc; doc.setContent(ba); QDomElement e = doc.documentElement(); colorSet->setName(e.attribute(KPL_PALETTE_NAME_ATTR)); colorSet->setIsEditable(e.attribute(KPL_PALETTE_READONLY_ATTR) != "true"); comment = e.attribute(KPL_PALETTE_COMMENT_ATTR); desiredColumnCount = e.attribute(KPL_PALETTE_COLUMN_COUNT_ATTR).toInt(); if (desiredColumnCount > MAXIMUM_ALLOWED_COLUMNS) { warnPigment << "Refusing to set unreasonable number of columns (" << desiredColumnCount << ") in KPL palette file " << colorSet->filename() << " - setting maximum allowed column count instead."; colorSet->setColumnCount(MAXIMUM_ALLOWED_COLUMNS); } else { colorSet->setColumnCount(desiredColumnCount); } loadKplGroup(doc, e, colorSet->getGlobalGroup()); QDomElement g = e.firstChildElement(KPL_GROUP_TAG); while (!g.isNull()) { QString groupName = g.attribute(KPL_GROUP_NAME_ATTR); colorSet->addGroup(groupName); loadKplGroup(doc, g, colorSet->getGroup(groupName)); g = g.nextSiblingElement(KPL_GROUP_TAG); } } buf.close(); return true; } bool KoColorSet::Private::loadAco() { QFileInfo info(colorSet->filename()); colorSet->setName(info.completeBaseName()); QBuffer buf(&data); buf.open(QBuffer::ReadOnly); quint16 version = readShort(&buf); quint16 numColors = readShort(&buf); KisSwatch e; if (version == 1 && buf.size() > 4+numColors*10) { buf.seek(4+numColors*10); version = readShort(&buf); numColors = readShort(&buf); } const quint16 quint16_MAX = 65535; for (int i = 0; i < numColors && !buf.atEnd(); ++i) { quint16 colorSpace = readShort(&buf); quint16 ch1 = readShort(&buf); quint16 ch2 = readShort(&buf); quint16 ch3 = readShort(&buf); quint16 ch4 = readShort(&buf); bool skip = false; if (colorSpace == 0) { // RGB const KoColorProfile *srgb = KoColorSpaceRegistry::instance()->rgb8()->profile(); KoColor c(KoColorSpaceRegistry::instance()->rgb16(srgb)); reinterpret_cast(c.data())[0] = ch3; reinterpret_cast(c.data())[1] = ch2; reinterpret_cast(c.data())[2] = ch1; c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else if (colorSpace == 1) { // HSB QColor qc; qc.setHsvF(ch1 / 65536.0, ch2 / 65536.0, ch3 / 65536.0); KoColor c(qc, KoColorSpaceRegistry::instance()->rgb16()); c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else if (colorSpace == 2) { // CMYK KoColor c(KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Integer16BitsColorDepthID.id(), QString())); reinterpret_cast(c.data())[0] = quint16_MAX - ch1; reinterpret_cast(c.data())[1] = quint16_MAX - ch2; reinterpret_cast(c.data())[2] = quint16_MAX - ch3; reinterpret_cast(c.data())[3] = quint16_MAX - ch4; c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else if (colorSpace == 7) { // LAB KoColor c = KoColor(KoColorSpaceRegistry::instance()->lab16()); reinterpret_cast(c.data())[0] = ch3; reinterpret_cast(c.data())[1] = ch2; reinterpret_cast(c.data())[2] = ch1; c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else if (colorSpace == 8) { // GRAY KoColor c(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), QString())); reinterpret_cast(c.data())[0] = ch1 * (quint16_MAX / 10000); c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else { warnPigment << "Unsupported colorspace in palette" << colorSet->filename() << "(" << colorSpace << ")"; skip = true; } if (version == 2) { quint16 v2 = readShort(&buf); //this isn't a version, it's a marker and needs to be skipped. Q_UNUSED(v2); quint16 size = readShort(&buf) -1; //then comes the length if (size>0) { QByteArray ba = buf.read(size*2); if (ba.size() == size*2) { QTextCodec *Utf16Codec = QTextCodec::codecForName("UTF-16BE"); e.setName(Utf16Codec->toUnicode(ba)); } else { warnPigment << "Version 2 name block is the wrong size" << colorSet->filename(); } } v2 = readShort(&buf); //end marker also needs to be skipped. Q_UNUSED(v2); } if (!skip) { groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e); } } return true; } bool KoColorSet::Private::loadSbz() { QBuffer buf(&data); buf.open(QBuffer::ReadOnly); // &buf is a subclass of QIODevice QScopedPointer store(KoStore::createStore(&buf, KoStore::Read, "application/x-swatchbook", KoStore::Zip)); if (!store || store->bad()) return false; if (store->hasFile("swatchbook.xml")) { // Try opening... if (!store->open("swatchbook.xml")) { return false; } QByteArray data; data.resize(store->size()); QByteArray ba = store->read(store->size()); store->close(); dbgPigment << "XML palette: " << colorSet->filename() << ", SwatchBooker format"; QDomDocument doc; int errorLine, errorColumn; QString errorMessage; bool status = doc.setContent(ba, &errorMessage, &errorLine, &errorColumn); if (!status) { warnPigment << "Illegal XML palette:" << colorSet->filename(); warnPigment << "Error (line" << errorLine << ", column" << errorColumn << "):" << errorMessage; return false; } QDomElement e = doc.documentElement(); // SwatchBook // Start reading properties... QDomElement metadata = e.firstChildElement("metadata"); if (e.isNull()) { warnPigment << "Palette metadata not found"; return false; } QDomElement title = metadata.firstChildElement("dc:title"); QString colorName = title.text(); colorName = colorName.isEmpty() ? i18n("Untitled") : colorName; colorSet->setName(colorName); dbgPigment << "Processed name of palette:" << colorSet->name(); // End reading properties // Now read colors... QDomElement materials = e.firstChildElement("materials"); if (materials.isNull()) { warnPigment << "Materials (color definitions) not found"; return false; } // This one has lots of "color" elements QDomElement colorElement = materials.firstChildElement("color"); if (colorElement.isNull()) { warnPigment << "Color definitions not found (line" << materials.lineNumber() << ", column" << materials.columnNumber() << ")"; return false; } // Also read the swatch book... QDomElement book = e.firstChildElement("book"); if (book.isNull()) { warnPigment << "Palette book (swatch composition) not found (line" << e.lineNumber() << ", column" << e.columnNumber() << ")"; return false; } // Which has lots of "swatch"es (todo: support groups) QDomElement swatch = book.firstChildElement(); if (swatch.isNull()) { warnPigment << "Swatches/groups definition not found (line" << book.lineNumber() << ", column" << book.columnNumber() << ")"; return false; } // We'll store colors here, and as we process swatches // we'll add them to the palette QHash materialsBook; QHash fileColorSpaces; // Color processing for(; !colorElement.isNull(); colorElement = colorElement.nextSiblingElement("color")) { KisSwatch currentEntry; // Set if color is spot currentEntry.setSpotColor(colorElement.attribute("usage") == "spot"); // inside contains id and name // one or more define the color QDomElement currentColorMetadata = colorElement.firstChildElement("metadata"); QDomNodeList currentColorValues = colorElement.elementsByTagName("values"); // Get color name QDomElement colorTitle = currentColorMetadata.firstChildElement("dc:title"); QDomElement colorId = currentColorMetadata.firstChildElement("dc:identifier"); // Is there an id? (we need that at the very least for identifying a color) if (colorId.text().isEmpty()) { warnPigment << "Unidentified color (line" << colorId.lineNumber()<< ", column" << colorId.columnNumber() << ")"; return false; } if (materialsBook.contains(colorId.text())) { warnPigment << "Duplicated color definition (line" << colorId.lineNumber()<< ", column" << colorId.columnNumber() << ")"; return false; } // Get a valid color name currentEntry.setId(colorId.text()); currentEntry.setName(colorTitle.text().isEmpty() ? colorId.text() : colorTitle.text()); // Get a valid color definition if (currentColorValues.isEmpty()) { warnPigment << "Color definitions not found (line" << colorElement.lineNumber() << ", column" << colorElement.columnNumber() << ")"; return false; } bool firstDefinition = false; const KoColorProfile *srgb = KoColorSpaceRegistry::instance()->rgb8()->profile(); // Priority: Lab, otherwise the first definition found for(int j = 0; j < currentColorValues.size(); j++) { QDomNode colorValue = currentColorValues.at(j); QDomElement colorValueE = colorValue.toElement(); QString model = colorValueE.attribute("model", QString()); // sRGB,RGB,HSV,HSL,CMY,CMYK,nCLR: 0 -> 1 // YIQ: Y 0 -> 1 : IQ -0.5 -> 0.5 // Lab: L 0 -> 100 : ab -128 -> 127 // XYZ: 0 -> ~100 if (model == "Lab") { QStringList lab = colorValueE.text().split(" "); if (lab.length() != 3) { warnPigment << "Invalid Lab color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float l = lab.at(0).toFloat(&status); float a = lab.at(1).toFloat(&status); float b = lab.at(2).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } KoColor c(KoColorSpaceRegistry::instance()->colorSpace(LABAColorModelID.id(), Float32BitsColorDepthID.id(), QString())); reinterpret_cast(c.data())[0] = l; reinterpret_cast(c.data())[1] = a; reinterpret_cast(c.data())[2] = b; c.setOpacity(OPACITY_OPAQUE_F); firstDefinition = true; currentEntry.setColor(c); break; // Immediately add this one } else if (model == "sRGB" && !firstDefinition) { QStringList rgb = colorValueE.text().split(" "); if (rgb.length() != 3) { warnPigment << "Invalid sRGB color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float r = rgb.at(0).toFloat(&status); float g = rgb.at(1).toFloat(&status); float b = rgb.at(2).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } KoColor c(KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), srgb)); reinterpret_cast(c.data())[0] = r; reinterpret_cast(c.data())[1] = g; reinterpret_cast(c.data())[2] = b; c.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(c); firstDefinition = true; } else if (model == "XYZ" && !firstDefinition) { QStringList xyz = colorValueE.text().split(" "); if (xyz.length() != 3) { warnPigment << "Invalid XYZ color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float x = xyz.at(0).toFloat(&status); float y = xyz.at(1).toFloat(&status); float z = xyz.at(2).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } KoColor c(KoColorSpaceRegistry::instance()->colorSpace(XYZAColorModelID.id(), Float32BitsColorDepthID.id(), QString())); reinterpret_cast(c.data())[0] = x; reinterpret_cast(c.data())[1] = y; reinterpret_cast(c.data())[2] = z; c.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(c); firstDefinition = true; } // The following color spaces admit an ICC profile (in SwatchBooker) else if (model == "CMYK" && !firstDefinition) { QStringList cmyk = colorValueE.text().split(" "); if (cmyk.length() != 4) { warnPigment << "Invalid CMYK color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float c = cmyk.at(0).toFloat(&status); float m = cmyk.at(1).toFloat(&status); float y = cmyk.at(2).toFloat(&status); float k = cmyk.at(3).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } QString space = colorValueE.attribute("space"); const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), QString()); if (!space.isEmpty()) { // Try loading the profile and add it to the registry if (!fileColorSpaces.contains(space)) { store->enterDirectory("profiles"); store->open(space); QByteArray data; data.resize(store->size()); data = store->read(store->size()); store->close(); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), data); if (profile && profile->valid()) { KoColorSpaceRegistry::instance()->addProfile(profile); colorSpace = KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), profile); fileColorSpaces.insert(space, colorSpace); } } else { colorSpace = fileColorSpaces.value(space); } } KoColor color(colorSpace); reinterpret_cast(color.data())[0] = c; reinterpret_cast(color.data())[1] = m; reinterpret_cast(color.data())[2] = y; reinterpret_cast(color.data())[3] = k; color.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(color); firstDefinition = true; } else if (model == "GRAY" && !firstDefinition) { QString gray = colorValueE.text(); float g = gray.toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } QString space = colorValueE.attribute("space"); const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Float32BitsColorDepthID.id(), QString()); if (!space.isEmpty()) { // Try loading the profile and add it to the registry if (!fileColorSpaces.contains(space)) { store->enterDirectory("profiles"); store->open(space); QByteArray data; data.resize(store->size()); data = store->read(store->size()); store->close(); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), data); if (profile && profile->valid()) { KoColorSpaceRegistry::instance()->addProfile(profile); colorSpace = KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), profile); fileColorSpaces.insert(space, colorSpace); } } else { colorSpace = fileColorSpaces.value(space); } } KoColor c(colorSpace); reinterpret_cast(c.data())[0] = g; c.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(c); firstDefinition = true; } else if (model == "RGB" && !firstDefinition) { QStringList rgb = colorValueE.text().split(" "); if (rgb.length() != 3) { warnPigment << "Invalid RGB color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float r = rgb.at(0).toFloat(&status); float g = rgb.at(1).toFloat(&status); float b = rgb.at(2).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } QString space = colorValueE.attribute("space"); const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), srgb); if (!space.isEmpty()) { // Try loading the profile and add it to the registry if (!fileColorSpaces.contains(space)) { store->enterDirectory("profiles"); store->open(space); QByteArray data; data.resize(store->size()); data = store->read(store->size()); store->close(); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), data); if (profile && profile->valid()) { KoColorSpaceRegistry::instance()->addProfile(profile); colorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), profile); fileColorSpaces.insert(space, colorSpace); } } else { colorSpace = fileColorSpaces.value(space); } } KoColor c(colorSpace); reinterpret_cast(c.data())[0] = r; reinterpret_cast(c.data())[1] = g; reinterpret_cast(c.data())[2] = b; c.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(c); firstDefinition = true; } else { warnPigment << "Color space not implemented:" << model << "(line" << colorValueE.lineNumber() << ", column "<< colorValueE.columnNumber() << ")"; } } if (firstDefinition) { materialsBook.insert(currentEntry.id(), currentEntry); } else { warnPigment << "No supported color spaces for the current color (line" << colorElement.lineNumber() << ", column "<< colorElement.columnNumber() << ")"; return false; } } // End colors // Now decide which ones will go into the palette for(;!swatch.isNull(); swatch = swatch.nextSiblingElement()) { QString type = swatch.tagName(); if (type.isEmpty() || type.isNull()) { warnPigment << "Invalid swatch/group definition (no id) (line" << swatch.lineNumber() << ", column" << swatch.columnNumber() << ")"; return false; } else if (type == "swatch") { QString id = swatch.attribute("material"); if (id.isEmpty() || id.isNull()) { warnPigment << "Invalid swatch definition (no material id) (line" << swatch.lineNumber() << ", column" << swatch.columnNumber() << ")"; return false; } if (materialsBook.contains(id)) { groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(materialsBook.value(id)); } else { warnPigment << "Invalid swatch definition (material not found) (line" << swatch.lineNumber() << ", column" << swatch.columnNumber() << ")"; return false; } } else if (type == "group") { QDomElement groupMetadata = swatch.firstChildElement("metadata"); if (groupMetadata.isNull()) { warnPigment << "Invalid group definition (missing metadata) (line" << groupMetadata.lineNumber() << ", column" << groupMetadata.columnNumber() << ")"; return false; } QDomElement groupTitle = metadata.firstChildElement("dc:title"); if (groupTitle.isNull()) { warnPigment << "Invalid group definition (missing title) (line" << groupTitle.lineNumber() << ", column" << groupTitle.columnNumber() << ")"; return false; } QString currentGroupName = groupTitle.text(); QDomElement groupSwatch = swatch.firstChildElement("swatch"); while(!groupSwatch.isNull()) { QString id = groupSwatch.attribute("material"); if (id.isEmpty() || id.isNull()) { warnPigment << "Invalid swatch definition (no material id) (line" << groupSwatch.lineNumber() << ", column" << groupSwatch.columnNumber() << ")"; return false; } if (materialsBook.contains(id)) { groups[currentGroupName].addEntry(materialsBook.value(id)); } else { warnPigment << "Invalid swatch definition (material not found) (line" << groupSwatch.lineNumber() << ", column" << groupSwatch.columnNumber() << ")"; return false; } groupSwatch = groupSwatch.nextSiblingElement("swatch"); } } } // End palette } buf.close(); return true; } bool KoColorSet::Private::loadXml() { bool res = false; QXmlStreamReader *xml = new QXmlStreamReader(data); if (xml->readNextStartElement()) { QStringRef paletteId = xml->name(); if (QStringRef::compare(paletteId, "SCRIBUSCOLORS", Qt::CaseInsensitive) == 0) { // Scribus dbgPigment << "XML palette: " << colorSet->filename() << ", Scribus format"; res = loadScribusXmlPalette(colorSet, xml); } else { // Unknown XML format xml->raiseError("Unknown XML palette format. Expected SCRIBUSCOLORS, found " + paletteId); } } // If there is any error (it should be returned through the stream) if (xml->hasError() || !res) { warnPigment << "Illegal XML palette:" << colorSet->filename(); warnPigment << "Error (line"<< xml->lineNumber() << ", column" << xml->columnNumber() << "):" << xml->errorString(); return false; } else { dbgPigment << "XML palette parsed successfully:" << colorSet->filename(); return true; } } bool KoColorSet::Private::saveKpl(QIODevice *dev) const { QScopedPointer store(KoStore::createStore(dev, KoStore::Write, "krita/x-colorset", KoStore::Zip)); if (!store || store->bad()) return false; QSet colorSpaces; { QDomDocument doc; QDomElement root = doc.createElement(KPL_PALETTE_TAG); root.setAttribute(KPL_VERSION_ATTR, "1.0"); root.setAttribute(KPL_PALETTE_NAME_ATTR, colorSet->name()); root.setAttribute(KPL_PALETTE_COMMENT_ATTR, comment); root.setAttribute(KPL_PALETTE_READONLY_ATTR, (colorSet->isEditable() || !colorSet->isGlobal()) ? "false" : "true"); root.setAttribute(KPL_PALETTE_COLUMN_COUNT_ATTR, colorSet->columnCount()); root.setAttribute(KPL_GROUP_ROW_COUNT_ATTR, groups[KoColorSet::GLOBAL_GROUP_NAME].rowCount()); saveKplGroup(doc, root, colorSet->getGroup(KoColorSet::GLOBAL_GROUP_NAME), colorSpaces); for (const QString &groupName : groupNames) { if (groupName == KoColorSet::GLOBAL_GROUP_NAME) { continue; } QDomElement gl = doc.createElement(KPL_GROUP_TAG); gl.setAttribute(KPL_GROUP_NAME_ATTR, groupName); root.appendChild(gl); saveKplGroup(doc, gl, colorSet->getGroup(groupName), colorSpaces); } doc.appendChild(root); if (!store->open("colorset.xml")) { return false; } QByteArray ba = doc.toByteArray(); if (store->write(ba) != ba.size()) { return false; } if (!store->close()) { return false; } } QDomDocument doc; QDomElement profileElement = doc.createElement("Profiles"); for (const KoColorSpace *colorSpace : colorSpaces) { QString fn = QFileInfo(colorSpace->profile()->fileName()).fileName(); if (!store->open(fn)) { return false; } QByteArray profileRawData = colorSpace->profile()->rawData(); if (!store->write(profileRawData)) { return false; } if (!store->close()) { return false; } QDomElement el = doc.createElement(KPL_PALETTE_PROFILE_TAG); el.setAttribute(KPL_PALETTE_FILENAME_ATTR, fn); el.setAttribute(KPL_PALETTE_NAME_ATTR, colorSpace->profile()->name()); el.setAttribute(KPL_COLOR_MODEL_ID_ATTR, colorSpace->colorModelId().id()); el.setAttribute(KPL_COLOR_DEPTH_ID_ATTR, colorSpace->colorDepthId().id()); profileElement.appendChild(el); } doc.appendChild(profileElement); if (!store->open("profiles.xml")) { return false; } QByteArray ba = doc.toByteArray(); if (store->write(ba) != ba.size()) { return false; } if (!store->close()) { return false; } return store->finalize(); } void KoColorSet::Private::saveKplGroup(QDomDocument &doc, QDomElement &groupEle, const KisSwatchGroup *group, QSet &colorSetSet) const { groupEle.setAttribute(KPL_GROUP_ROW_COUNT_ATTR, QString::number(group->rowCount())); for (const SwatchInfoType &info : group->infoList()) { const KoColorProfile *profile = info.swatch.color().colorSpace()->profile(); // Only save non-builtin profiles.= if (!profile->fileName().isEmpty()) { colorSetSet.insert(info.swatch.color().colorSpace()); } QDomElement swatchEle = doc.createElement(KPL_SWATCH_TAG); swatchEle.setAttribute(KPL_SWATCH_NAME_ATTR, info.swatch.name()); swatchEle.setAttribute(KPL_SWATCH_ID_ATTR, info.swatch.id()); swatchEle.setAttribute(KPL_SWATCH_SPOT_ATTR, info.swatch.spotColor() ? "true" : "false"); swatchEle.setAttribute(KPL_SWATCH_BITDEPTH_ATTR, info.swatch.color().colorSpace()->colorDepthId().id()); info.swatch.color().toXML(doc, swatchEle); QDomElement positionEle = doc.createElement(KPL_SWATCH_POS_TAG); positionEle.setAttribute(KPL_SWATCH_ROW_ATTR, info.row); positionEle.setAttribute(KPL_SWATCH_COL_ATTR, info.column); swatchEle.appendChild(positionEle); groupEle.appendChild(swatchEle); } } void KoColorSet::Private::loadKplGroup(const QDomDocument &doc, const QDomElement &parentEle, KisSwatchGroup *group) { Q_UNUSED(doc); if (!parentEle.attribute(KPL_GROUP_ROW_COUNT_ATTR).isNull()) { group->setRowCount(parentEle.attribute(KPL_GROUP_ROW_COUNT_ATTR).toInt()); } group->setColumnCount(colorSet->columnCount()); for (QDomElement swatchEle = parentEle.firstChildElement(KPL_SWATCH_TAG); !swatchEle.isNull(); swatchEle = swatchEle.nextSiblingElement(KPL_SWATCH_TAG)) { QString colorDepthId = swatchEle.attribute(KPL_SWATCH_BITDEPTH_ATTR, Integer8BitsColorDepthID.id()); KisSwatch entry; entry.setColor(KoColor::fromXML(swatchEle.firstChildElement(), colorDepthId)); entry.setName(swatchEle.attribute(KPL_SWATCH_NAME_ATTR)); entry.setId(swatchEle.attribute(KPL_SWATCH_ID_ATTR)); entry.setSpotColor(swatchEle.attribute(KPL_SWATCH_SPOT_ATTR, "false") == "true" ? true : false); QDomElement positionEle = swatchEle.firstChildElement(KPL_SWATCH_POS_TAG); if (!positionEle.isNull()) { int rowNumber = positionEle.attribute(KPL_SWATCH_ROW_ATTR).toInt(); int columnNumber = positionEle.attribute(KPL_SWATCH_COL_ATTR).toInt(); if (columnNumber < 0 || columnNumber >= colorSet->columnCount() || rowNumber < 0 ) { warnPigment << "Swatch" << entry.name() << "of palette" << colorSet->name() << "has invalid position."; continue; } group->setEntry(entry, columnNumber, rowNumber); } else { group->addEntry(entry); } } if (parentEle.attribute(KPL_GROUP_ROW_COUNT_ATTR).isNull() && group->colorCount() > 0 && group->columnCount() > 0 && (group->colorCount() / (group->columnCount()) + 1) < 20) { group->setRowCount((group->colorCount() / group->columnCount()) + 1); } } diff --git a/libs/pigment/resources/KoSegmentGradient.cpp b/libs/pigment/resources/KoSegmentGradient.cpp index 16fef332cc..9a063b52c3 100644 --- a/libs/pigment/resources/KoSegmentGradient.cpp +++ b/libs/pigment/resources/KoSegmentGradient.cpp @@ -1,983 +1,984 @@ /* Copyright (c) 2000 Matthias Elter 2001 John Califf 2004 Boudewijn Rempt 2004 Adrian Page 2004, 2007 Sven Langkamp 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 #include #include #include #include #include #include #include #include #include #include #include "KoColorSpaceRegistry.h" #include "KoColorSpace.h" #include "KoMixColorsOp.h" #include #include #include KoGradientSegment::RGBColorInterpolationStrategy *KoGradientSegment::RGBColorInterpolationStrategy::m_instance = 0; KoGradientSegment::HSVCWColorInterpolationStrategy *KoGradientSegment::HSVCWColorInterpolationStrategy::m_instance = 0; KoGradientSegment::HSVCCWColorInterpolationStrategy *KoGradientSegment::HSVCCWColorInterpolationStrategy::m_instance = 0; KoGradientSegment::LinearInterpolationStrategy *KoGradientSegment::LinearInterpolationStrategy::m_instance = 0; KoGradientSegment::CurvedInterpolationStrategy *KoGradientSegment::CurvedInterpolationStrategy::m_instance = 0; KoGradientSegment::SineInterpolationStrategy *KoGradientSegment::SineInterpolationStrategy::m_instance = 0; KoGradientSegment::SphereIncreasingInterpolationStrategy *KoGradientSegment::SphereIncreasingInterpolationStrategy::m_instance = 0; KoGradientSegment::SphereDecreasingInterpolationStrategy *KoGradientSegment::SphereDecreasingInterpolationStrategy::m_instance = 0; KoSegmentGradient::KoSegmentGradient(const QString& file) : KoAbstractGradient(file) { } KoSegmentGradient::~KoSegmentGradient() { for (int i = 0; i < m_segments.count(); i++) { delete m_segments[i]; m_segments[i] = 0; } } KoSegmentGradient::KoSegmentGradient(const KoSegmentGradient &rhs) : KoAbstractGradient(rhs) { Q_FOREACH (KoGradientSegment *segment, rhs.m_segments) { pushSegment(new KoGradientSegment(*segment)); } } KoAbstractGradient* KoSegmentGradient::clone() const { return new KoSegmentGradient(*this); } bool KoSegmentGradient::load() { QFile file(filename()); if (!file.open(QIODevice::ReadOnly)) { warnPigment << "Can't open file " << filename(); return false; } bool res = loadFromDevice(&file); file.close(); return res; } bool KoSegmentGradient::loadFromDevice(QIODevice *dev) { QByteArray data = dev->readAll(); QTextStream fileContent(data, QIODevice::ReadOnly); fileContent.setAutoDetectUnicode(true); QString header = fileContent.readLine(); if (header != "GIMP Gradient") { return false; } QString nameDefinition = fileContent.readLine(); QString numSegmentsText; if (nameDefinition.startsWith("Name: ")) { QString nameText = nameDefinition.right(nameDefinition.length() - 6); setName(nameText); numSegmentsText = fileContent.readLine(); } else { // Older format without name. numSegmentsText = nameDefinition; } dbgPigment << "Loading gradient: " << name(); int numSegments; bool ok; numSegments = numSegmentsText.toInt(&ok); if (!ok || numSegments < 1) { return false; } dbgPigment << "Number of segments = " << numSegments; const KoColorSpace* rgbColorSpace = KoColorSpaceRegistry::instance()->rgb8(); for (int i = 0; i < numSegments; i++) { QString segmentText = fileContent.readLine(); QTextStream segmentFields(&segmentText); QStringList values = segmentText.split(' '); qreal leftOffset = values[0].toDouble(); qreal middleOffset = values[1].toDouble(); qreal rightOffset = values[2].toDouble(); qreal leftRed = values[3].toDouble(); qreal leftGreen = values[4].toDouble(); qreal leftBlue = values[5].toDouble(); qreal leftAlpha = values[6].toDouble(); qreal rightRed = values[7].toDouble(); qreal rightGreen = values[8].toDouble(); qreal rightBlue = values[9].toDouble(); qreal rightAlpha = values[10].toDouble(); int interpolationType = values[11].toInt(); int colorInterpolationType = values[12].toInt(); quint8 data[4]; data[2] = static_cast(leftRed * 255 + 0.5); data[1] = static_cast(leftGreen * 255 + 0.5); data[0] = static_cast(leftBlue * 255 + 0.5); data[3] = static_cast(leftAlpha * OPACITY_OPAQUE_U8 + 0.5); KoColor leftColor(data, rgbColorSpace); data[2] = static_cast(rightRed * 255 + 0.5); data[1] = static_cast(rightGreen * 255 + 0.5); data[0] = static_cast(rightBlue * 255 + 0.5); data[3] = static_cast(rightAlpha * OPACITY_OPAQUE_U8 + 0.5); KoColor rightColor(data, rgbColorSpace); KoGradientSegment *segment = new KoGradientSegment(interpolationType, colorInterpolationType, leftOffset, middleOffset, rightOffset, leftColor, rightColor); Q_CHECK_PTR(segment); if (!segment -> isValid()) { delete segment; return false; } m_segments.push_back(segment); } if (!m_segments.isEmpty()) { updatePreview(); setValid(true); return true; } else { return false; } } bool KoSegmentGradient::save() { QFile file(filename()); if (!file.open(QIODevice::WriteOnly)) { return false; } saveToDevice(&file); file.close(); return true; } bool KoSegmentGradient::saveToDevice(QIODevice *dev) const { QTextStream fileContent(dev); fileContent << "GIMP Gradient\n"; fileContent << "Name: " << name() << "\n"; fileContent << m_segments.count() << "\n"; Q_FOREACH (KoGradientSegment* segment, m_segments) { fileContent << QString::number(segment->startOffset(), 'f') << " " << QString::number(segment->middleOffset(), 'f') << " " << QString::number(segment->endOffset(), 'f') << " "; QColor startColor = segment->startColor().toQColor(); QColor endColor = segment->endColor().toQColor(); fileContent << QString::number(startColor.redF(), 'f') << " " << QString::number(startColor.greenF(), 'f') << " " << QString::number(startColor.blueF(), 'f') << " " << QString::number(startColor.alphaF(), 'f') << " "; fileContent << QString::number(endColor.redF(), 'f') << " " << QString::number(endColor.greenF(), 'f') << " " << QString::number(endColor.blueF(), 'f') << " " << QString::number(endColor.alphaF(), 'f') << " "; fileContent << (int)segment->interpolation() << " " << (int)segment->colorInterpolation() << "\n"; } KoResource::saveToDevice(dev); return true; } KoGradientSegment *KoSegmentGradient::segmentAt(qreal t) const { - Q_ASSERT(t >= 0 || t <= 1); - Q_ASSERT(!m_segments.empty()); + if (t < 0.0) return 0; + if (t > 1.0) return 0; + if (m_segments.isEmpty()) return 0; for (QList::const_iterator it = m_segments.begin(); it != m_segments.end(); ++it) { if (t > (*it)->startOffset() - DBL_EPSILON && t < (*it)->endOffset() + DBL_EPSILON) { return *it; } } return 0; } void KoSegmentGradient::colorAt(KoColor& dst, qreal t) const { const KoGradientSegment *segment = segmentAt(t); Q_ASSERT(segment != 0); if (segment) { segment->colorAt(dst, t); } } QGradient* KoSegmentGradient::toQGradient() const { QGradient* gradient = new QLinearGradient(); QColor color; Q_FOREACH (KoGradientSegment* segment, m_segments) { segment->startColor().toQColor(&color); gradient->setColorAt(segment->startOffset() , color); segment->endColor().toQColor(&color); gradient->setColorAt(segment->endOffset() , color); } return gradient; } QString KoSegmentGradient::defaultFileExtension() const { return QString(".ggr"); } void KoSegmentGradient::toXML(QDomDocument &doc, QDomElement &gradientElt) const { gradientElt.setAttribute("type", "segment"); Q_FOREACH(KoGradientSegment *segment, this->segments()) { QDomElement segmentElt = doc.createElement("segment"); QDomElement start = doc.createElement("start"); QDomElement end = doc.createElement("end"); segmentElt.setAttribute("start-offset", KisDomUtils::toString(segment->startOffset())); const KoColor startColor = segment->startColor(); segmentElt.setAttribute("start-bitdepth", startColor.colorSpace()->colorDepthId().id()); segmentElt.setAttribute("start-alpha", KisDomUtils::toString(startColor.opacityF())); startColor.toXML(doc, start); segmentElt.setAttribute("middle-offset", KisDomUtils::toString(segment->middleOffset())); segmentElt.setAttribute("end-offset", KisDomUtils::toString(segment->endOffset())); const KoColor endColor = segment->endColor(); segmentElt.setAttribute("end-bitdepth", endColor.colorSpace()->colorDepthId().id()); segmentElt.setAttribute("end-alpha", KisDomUtils::toString(endColor.opacityF())); endColor.toXML(doc, end); segmentElt.setAttribute("interpolation", KisDomUtils::toString(segment->interpolation())); segmentElt.setAttribute("color-interpolation", KisDomUtils::toString(segment->colorInterpolation())); segmentElt.appendChild(start); segmentElt.appendChild(end); gradientElt.appendChild(segmentElt); } } KoSegmentGradient KoSegmentGradient::fromXML(const QDomElement &elt) { KoSegmentGradient gradient; QDomElement segmentElt = elt.firstChildElement("segment"); while (!segmentElt.isNull()) { int interpolation = KisDomUtils::toInt(segmentElt.attribute("interpolation", "0.0")); int colorInterpolation = KisDomUtils::toInt(segmentElt.attribute("color-interpolation", "0.0")); double startOffset = KisDomUtils::toDouble(segmentElt.attribute("start-offset", "0.0")); qreal middleOffset = KisDomUtils::toDouble(segmentElt.attribute("middle-offset", "0.0")); qreal endOffset = KisDomUtils::toDouble(segmentElt.attribute("end-offset", "0.0")); QDomElement start = segmentElt.firstChildElement("start"); QString startBitdepth = segmentElt.attribute("start-bitdepth", Integer8BitsColorDepthID.id()); QColor left = KoColor::fromXML(start.firstChildElement(), startBitdepth).toQColor(); left.setAlphaF(KisDomUtils::toDouble(segmentElt.attribute("start-alpha", "1.0"))); QString endBitdepth = segmentElt.attribute("end-bitdepth", Integer8BitsColorDepthID.id()); QDomElement end = segmentElt.firstChildElement("end"); QColor right = KoColor::fromXML(end.firstChildElement(), endBitdepth).toQColor(); right.setAlphaF(KisDomUtils::toDouble(segmentElt.attribute("end-alpha", "1.0"))); gradient.createSegment(interpolation, colorInterpolation, startOffset, endOffset, middleOffset, left, right); segmentElt = segmentElt.nextSiblingElement("segment"); } return gradient; } KoGradientSegment::KoGradientSegment(int interpolationType, int colorInterpolationType, qreal startOffset, qreal middleOffset, qreal endOffset, const KoColor& startColor, const KoColor& endColor) { m_interpolator = 0; switch (interpolationType) { case INTERP_LINEAR: m_interpolator = LinearInterpolationStrategy::instance(); break; case INTERP_CURVED: m_interpolator = CurvedInterpolationStrategy::instance(); break; case INTERP_SINE: m_interpolator = SineInterpolationStrategy::instance(); break; case INTERP_SPHERE_INCREASING: m_interpolator = SphereIncreasingInterpolationStrategy::instance(); break; case INTERP_SPHERE_DECREASING: m_interpolator = SphereDecreasingInterpolationStrategy::instance(); break; } m_colorInterpolator = 0; switch (colorInterpolationType) { case COLOR_INTERP_RGB: m_colorInterpolator = RGBColorInterpolationStrategy::instance(); break; case COLOR_INTERP_HSV_CCW: m_colorInterpolator = HSVCCWColorInterpolationStrategy::instance(); break; case COLOR_INTERP_HSV_CW: m_colorInterpolator = HSVCWColorInterpolationStrategy::instance(); break; } if (startOffset < DBL_EPSILON) { m_startOffset = 0; } else if (startOffset > 1 - DBL_EPSILON) { m_startOffset = 1; } else { m_startOffset = startOffset; } if (middleOffset < m_startOffset + DBL_EPSILON) { m_middleOffset = m_startOffset; } else if (middleOffset > 1 - DBL_EPSILON) { m_middleOffset = 1; } else { m_middleOffset = middleOffset; } if (endOffset < m_middleOffset + DBL_EPSILON) { m_endOffset = m_middleOffset; } else if (endOffset > 1 - DBL_EPSILON) { m_endOffset = 1; } else { m_endOffset = endOffset; } m_length = m_endOffset - m_startOffset; if (m_length < DBL_EPSILON) { m_middleT = 0.5; } else { m_middleT = (m_middleOffset - m_startOffset) / m_length; } m_startColor = startColor; m_endColor = endColor; } const KoColor& KoGradientSegment::startColor() const { return m_startColor; } const KoColor& KoGradientSegment::endColor() const { return m_endColor; } qreal KoGradientSegment::startOffset() const { return m_startOffset; } qreal KoGradientSegment::middleOffset() const { return m_middleOffset; } qreal KoGradientSegment::endOffset() const { return m_endOffset; } void KoGradientSegment::setStartOffset(qreal t) { m_startOffset = t; m_length = m_endOffset - m_startOffset; if (m_length < DBL_EPSILON) { m_middleT = 0.5; } else { m_middleT = (m_middleOffset - m_startOffset) / m_length; } } void KoGradientSegment::setMiddleOffset(qreal t) { m_middleOffset = t; if (m_length < DBL_EPSILON) { m_middleT = 0.5; } else { m_middleT = (m_middleOffset - m_startOffset) / m_length; } } void KoGradientSegment::setEndOffset(qreal t) { m_endOffset = t; m_length = m_endOffset - m_startOffset; if (m_length < DBL_EPSILON) { m_middleT = 0.5; } else { m_middleT = (m_middleOffset - m_startOffset) / m_length; } } int KoGradientSegment::interpolation() const { return m_interpolator->type(); } void KoGradientSegment::setInterpolation(int interpolationType) { switch (interpolationType) { case INTERP_LINEAR: m_interpolator = LinearInterpolationStrategy::instance(); break; case INTERP_CURVED: m_interpolator = CurvedInterpolationStrategy::instance(); break; case INTERP_SINE: m_interpolator = SineInterpolationStrategy::instance(); break; case INTERP_SPHERE_INCREASING: m_interpolator = SphereIncreasingInterpolationStrategy::instance(); break; case INTERP_SPHERE_DECREASING: m_interpolator = SphereDecreasingInterpolationStrategy::instance(); break; } } int KoGradientSegment::colorInterpolation() const { return m_colorInterpolator->type(); } void KoGradientSegment::setColorInterpolation(int colorInterpolationType) { switch (colorInterpolationType) { case COLOR_INTERP_RGB: m_colorInterpolator = RGBColorInterpolationStrategy::instance(); break; case COLOR_INTERP_HSV_CCW: m_colorInterpolator = HSVCCWColorInterpolationStrategy::instance(); break; case COLOR_INTERP_HSV_CW: m_colorInterpolator = HSVCWColorInterpolationStrategy::instance(); break; } } void KoGradientSegment::colorAt(KoColor& dst, qreal t) const { Q_ASSERT(t > m_startOffset - DBL_EPSILON && t < m_endOffset + DBL_EPSILON); qreal segmentT; if (m_length < DBL_EPSILON) { segmentT = 0.5; } else { segmentT = (t - m_startOffset) / m_length; } qreal colorT = m_interpolator->valueAt(segmentT, m_middleT); m_colorInterpolator->colorAt(dst, colorT, m_startColor, m_endColor); } bool KoGradientSegment::isValid() const { if (m_interpolator == 0 || m_colorInterpolator == 0) return false; return true; } KoGradientSegment::RGBColorInterpolationStrategy::RGBColorInterpolationStrategy() : m_colorSpace(KoColorSpaceRegistry::instance()->rgb8()) { } KoGradientSegment::RGBColorInterpolationStrategy *KoGradientSegment::RGBColorInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new RGBColorInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } void KoGradientSegment::RGBColorInterpolationStrategy::colorAt(KoColor& dst, qreal t, const KoColor& _start, const KoColor& _end) const { KoColor buffer(m_colorSpace); KoColor start(m_colorSpace); KoColor end(m_colorSpace); KoColor startDummy, endDummy; //hack to get a color space with the bitdepth of the gradients(8bit), but with the colour profile of the image// const KoColorSpace* mixSpace = KoColorSpaceRegistry::instance()->rgb8(dst.colorSpace()->profile()); //convert to the right colorspace for the start and end if we have our mixSpace. if (mixSpace){ startDummy = KoColor(_start, mixSpace); endDummy = KoColor(_end, mixSpace); } else { startDummy = _start; endDummy = _end; } start.fromKoColor(_start); end.fromKoColor(_end); const quint8 *colors[2]; colors[0] = startDummy.data(); colors[1] = endDummy.data(); qint16 colorWeights[2]; colorWeights[0] = static_cast((1.0 - t) * 255 + 0.5); colorWeights[1] = 255 - colorWeights[0]; //check if our mixspace exists, it doesn't at startup. if (mixSpace){ if (*buffer.colorSpace() != *mixSpace) { buffer = KoColor(mixSpace); } mixSpace->mixColorsOp()->mixColors(colors, colorWeights, 2, buffer.data()); } else { buffer = KoColor(m_colorSpace); m_colorSpace->mixColorsOp()->mixColors(colors, colorWeights, 2, buffer.data()); } dst.fromKoColor(buffer); } KoGradientSegment::HSVCWColorInterpolationStrategy::HSVCWColorInterpolationStrategy() : m_colorSpace(KoColorSpaceRegistry::instance()->rgb8()) { } KoGradientSegment::HSVCWColorInterpolationStrategy *KoGradientSegment::HSVCWColorInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new HSVCWColorInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } void KoGradientSegment::HSVCWColorInterpolationStrategy::colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const { QColor sc; QColor ec; start.toQColor(&sc); end.toQColor(&ec); int s = static_cast(sc.saturation() + t * (ec.saturation() - sc.saturation()) + 0.5); int v = static_cast(sc.value() + t * (ec.value() - sc.value()) + 0.5); int h; if (ec.hue() < sc.hue()) { h = static_cast(ec.hue() + (1 - t) * (sc.hue() - ec.hue()) + 0.5); } else { h = static_cast(ec.hue() + (1 - t) * (360 - ec.hue() + sc.hue()) + 0.5); if (h > 359) { h -= 360; } } // XXX: added an explicit cast. Is this correct? quint8 opacity = static_cast(sc.alpha() + t * (ec.alpha() - sc.alpha())); QColor result; result.setHsv(h, s, v); result.setAlpha(opacity); dst.fromQColor(result); } KoGradientSegment::HSVCCWColorInterpolationStrategy::HSVCCWColorInterpolationStrategy() : m_colorSpace(KoColorSpaceRegistry::instance()->rgb8()) { } KoGradientSegment::HSVCCWColorInterpolationStrategy *KoGradientSegment::HSVCCWColorInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new HSVCCWColorInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } void KoGradientSegment::HSVCCWColorInterpolationStrategy::colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const { QColor sc; QColor se; start.toQColor(&sc); end.toQColor(&se); int s = static_cast(sc.saturation() + t * (se.saturation() - sc.saturation()) + 0.5); int v = static_cast(sc.value() + t * (se.value() - sc.value()) + 0.5); int h; if (sc.hue() < se.hue()) { h = static_cast(sc.hue() + t * (se.hue() - sc.hue()) + 0.5); } else { h = static_cast(sc.hue() + t * (360 - sc.hue() + se.hue()) + 0.5); if (h > 359) { h -= 360; } } // XXX: Added an explicit static cast quint8 opacity = static_cast(sc.alpha() + t * (se.alpha() - sc.alpha())); QColor result; result.setHsv(h, s, v); result.setAlpha(opacity); dst.fromQColor(result); } KoGradientSegment::LinearInterpolationStrategy *KoGradientSegment::LinearInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new LinearInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } qreal KoGradientSegment::LinearInterpolationStrategy::calcValueAt(qreal t, qreal middle) { Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON); Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON); qreal value = 0; if (t <= middle) { if (middle < DBL_EPSILON) { value = 0; } else { value = (t / middle) * 0.5; } } else { if (middle > 1 - DBL_EPSILON) { value = 1; } else { value = ((t - middle) / (1 - middle)) * 0.5 + 0.5; } } return value; } qreal KoGradientSegment::LinearInterpolationStrategy::valueAt(qreal t, qreal middle) const { return calcValueAt(t, middle); } KoGradientSegment::CurvedInterpolationStrategy::CurvedInterpolationStrategy() { m_logHalf = log(0.5); } KoGradientSegment::CurvedInterpolationStrategy *KoGradientSegment::CurvedInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new CurvedInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } qreal KoGradientSegment::CurvedInterpolationStrategy::valueAt(qreal t, qreal middle) const { Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON); Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON); qreal value = 0; if (middle < DBL_EPSILON) { middle = DBL_EPSILON; } value = pow(t, m_logHalf / log(middle)); return value; } KoGradientSegment::SineInterpolationStrategy *KoGradientSegment::SineInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new SineInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } qreal KoGradientSegment::SineInterpolationStrategy::valueAt(qreal t, qreal middle) const { qreal lt = LinearInterpolationStrategy::calcValueAt(t, middle); qreal value = (sin(-M_PI_2 + M_PI * lt) + 1.0) / 2.0; return value; } KoGradientSegment::SphereIncreasingInterpolationStrategy *KoGradientSegment::SphereIncreasingInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new SphereIncreasingInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } qreal KoGradientSegment::SphereIncreasingInterpolationStrategy::valueAt(qreal t, qreal middle) const { qreal lt = LinearInterpolationStrategy::calcValueAt(t, middle) - 1; qreal value = sqrt(1 - lt * lt); return value; } KoGradientSegment::SphereDecreasingInterpolationStrategy *KoGradientSegment::SphereDecreasingInterpolationStrategy::instance() { if (m_instance == 0) { m_instance = new SphereDecreasingInterpolationStrategy(); Q_CHECK_PTR(m_instance); } return m_instance; } qreal KoGradientSegment::SphereDecreasingInterpolationStrategy::valueAt(qreal t, qreal middle) const { qreal lt = LinearInterpolationStrategy::calcValueAt(t, middle); qreal value = 1 - sqrt(1 - lt * lt); return value; } void KoSegmentGradient::createSegment(int interpolation, int colorInterpolation, double startOffset, double endOffset, double middleOffset, const QColor & left, const QColor & right) { pushSegment(new KoGradientSegment(interpolation, colorInterpolation, startOffset, middleOffset, endOffset, KoColor(left, colorSpace()), KoColor(right, colorSpace()))); } const QList KoSegmentGradient::getHandlePositions() const { QList handlePositions; handlePositions.push_back(m_segments[0]->startOffset()); for (int i = 0; i < m_segments.count(); i++) { handlePositions.push_back(m_segments[i]->endOffset()); } return handlePositions; } const QList KoSegmentGradient::getMiddleHandlePositions() const { QList middleHandlePositions; for (int i = 0; i < m_segments.count(); i++) { middleHandlePositions.push_back(m_segments[i]->middleOffset()); } return middleHandlePositions; } void KoSegmentGradient::moveSegmentStartOffset(KoGradientSegment* segment, double t) { QList::iterator it = std::find(m_segments.begin(), m_segments.end(), segment); if (it != m_segments.end()) { if (it == m_segments.begin()) { segment->setStartOffset(0.0); return; } KoGradientSegment* previousSegment = (*(it - 1)); if (t > segment->startOffset()) { if (t > segment->middleOffset()) t = segment->middleOffset(); } else { if (t < previousSegment->middleOffset()) t = previousSegment->middleOffset(); } previousSegment->setEndOffset(t); segment->setStartOffset(t); } } void KoSegmentGradient::moveSegmentEndOffset(KoGradientSegment* segment, double t) { QList::iterator it = std::find(m_segments.begin(), m_segments.end(), segment); if (it != m_segments.end()) { if (it + 1 == m_segments.end()) { segment->setEndOffset(1.0); return; } KoGradientSegment* followingSegment = (*(it + 1)); if (t < segment->endOffset()) { if (t < segment->middleOffset()) t = segment->middleOffset(); } else { if (t > followingSegment->middleOffset()) t = followingSegment->middleOffset(); } followingSegment->setStartOffset(t); segment->setEndOffset(t); } } void KoSegmentGradient::moveSegmentMiddleOffset(KoGradientSegment* segment, double t) { if (segment) { if (t > segment->endOffset()) segment->setMiddleOffset(segment->endOffset()); else if (t < segment->startOffset()) segment->setMiddleOffset(segment->startOffset()); else segment->setMiddleOffset(t); } } void KoSegmentGradient::splitSegment(KoGradientSegment* segment) { Q_ASSERT(segment != 0); QList::iterator it = std::find(m_segments.begin(), m_segments.end(), segment); if (it != m_segments.end()) { KoColor midleoffsetColor(segment->endColor().colorSpace()); segment->colorAt(midleoffsetColor, segment->middleOffset()); KoGradientSegment* newSegment = new KoGradientSegment( segment->interpolation(), segment->colorInterpolation(), segment ->startOffset(), (segment->middleOffset() - segment->startOffset()) / 2 + segment->startOffset(), segment->middleOffset(), segment->startColor(), midleoffsetColor); m_segments.insert(it, newSegment); segment->setStartColor(midleoffsetColor); segment->setStartOffset(segment->middleOffset()); segment->setMiddleOffset((segment->endOffset() - segment->startOffset()) / 2 + segment->startOffset()); } } void KoSegmentGradient::duplicateSegment(KoGradientSegment* segment) { Q_ASSERT(segment != 0); QList::iterator it = std::find(m_segments.begin(), m_segments.end(), segment); if (it != m_segments.end()) { double middlePostionPercentage = (segment->middleOffset() - segment->startOffset()) / segment->length(); double center = segment->startOffset() + segment->length() / 2; KoGradientSegment* newSegment = new KoGradientSegment( segment->interpolation(), segment->colorInterpolation(), segment ->startOffset(), segment->length() / 2 * middlePostionPercentage + segment->startOffset(), center, segment->startColor(), segment->endColor()); m_segments.insert(it, newSegment); segment->setStartOffset(center); segment->setMiddleOffset(segment->length() * middlePostionPercentage + segment->startOffset()); } } void KoSegmentGradient::mirrorSegment(KoGradientSegment* segment) { Q_ASSERT(segment != 0); KoColor tmpColor = segment->startColor(); segment->setStartColor(segment->endColor()); segment->setEndColor(tmpColor); segment->setMiddleOffset(segment->endOffset() - (segment->middleOffset() - segment->startOffset())); if (segment->interpolation() == INTERP_SPHERE_INCREASING) segment->setInterpolation(INTERP_SPHERE_DECREASING); else if (segment->interpolation() == INTERP_SPHERE_DECREASING) segment->setInterpolation(INTERP_SPHERE_INCREASING); if (segment->colorInterpolation() == COLOR_INTERP_HSV_CW) segment->setColorInterpolation(COLOR_INTERP_HSV_CCW); else if (segment->colorInterpolation() == COLOR_INTERP_HSV_CCW) segment->setColorInterpolation(COLOR_INTERP_HSV_CW); } KoGradientSegment* KoSegmentGradient::removeSegment(KoGradientSegment* segment) { Q_ASSERT(segment != 0); if (m_segments.count() < 2) return 0; QList::iterator it = std::find(m_segments.begin(), m_segments.end(), segment); if (it != m_segments.end()) { double middlePostionPercentage; KoGradientSegment* nextSegment; if (it == m_segments.begin()) { nextSegment = (*(it + 1)); middlePostionPercentage = (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length(); nextSegment->setStartOffset(segment->startOffset()); nextSegment->setMiddleOffset(middlePostionPercentage * nextSegment->length() + nextSegment->startOffset()); } else { nextSegment = (*(it - 1)); middlePostionPercentage = (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length(); nextSegment->setEndOffset(segment->endOffset()); nextSegment->setMiddleOffset(middlePostionPercentage * nextSegment->length() + nextSegment->startOffset()); } delete segment; m_segments.erase(it); return nextSegment; } return 0; } bool KoSegmentGradient::removeSegmentPossible() const { if (m_segments.count() < 2) return false; return true; } const QList& KoSegmentGradient::segments() const { return m_segments; } diff --git a/libs/pigment/tests/CCSGraph.cpp b/libs/pigment/tests/CCSGraph.cpp index 6a574af35e..404508ff02 100644 --- a/libs/pigment/tests/CCSGraph.cpp +++ b/libs/pigment/tests/CCSGraph.cpp @@ -1,119 +1,119 @@ /* * Copyright (c) 2007-2008 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this 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 "KoColorSpaceRegistry.h" #include "KoColorConversionSystem.h" #include #include #include struct FriendOfColorSpaceRegistry { static QString toDot() { return KoColorSpaceRegistry::instance()->colorConversionSystem()->toDot(); } static QString bestPathToDot(const QString &srcKey, const QString &dstKey) { return KoColorSpaceRegistry::instance()->colorConversionSystem()->bestPathToDot(srcKey, dstKey); } }; int main(int argc, char** argv) { QCoreApplication app(argc, argv); QCommandLineParser parser; parser.addVersionOption(); parser.addHelpOption(); // Initialize the list of options parser.addOption(QCommandLineOption(QStringList() << QLatin1String("graphs"), i18n("return the list of available graphs"))); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("graph"), i18n("specify the type of graph (see --graphs to get the full list, the default is full)"), QLatin1String("type"), QLatin1String("full"))); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("key"), i18n("specify the key of the source color space"), QLatin1String("key"), QString())); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("key"), i18n("specify the key of the destination color space"), QLatin1String("key"), QString())); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("output"), i18n("specify the output (can be ps or dot, the default is ps)"), QLatin1String("type"), QLatin1String("ps"))); parser.addPositionalArgument(QLatin1String("outputfile"), i18n("name of the output file")); parser.process(app); // PORTING SCRIPT: move this to after any parser.addOption if (parser.isSet("graphs")) { // Don't change those lines to use dbgPigment derivatives, they need to be outputted // to stdout not stderr. std::cout << "full : show all the connection on the graph" << std::endl; std::cout << "bestpath : show the best path for a given transformation" << std::endl; exit(EXIT_SUCCESS); } QString graphType = parser.value("graph"); QString outputType = parser.value("output"); if (parser.positionalArguments().count() != 1) { errorPigment << "No output file name specified"; parser.showHelp(); exit(EXIT_FAILURE); } QString outputFileName = parser.positionalArguments()[0]; // Generate the graph QString dot; if (graphType == "full") { dot = FriendOfColorSpaceRegistry::toDot(); } else if (graphType == "bestpath") { QString srcKey = parser.value("src-key"); QString dstKey = parser.value("dst-key"); if (srcKey.isEmpty() || dstKey.isEmpty()) { errorPigment << "src-key and dst-key must be specified for the graph bestpath"; exit(EXIT_FAILURE); } else { dot = FriendOfColorSpaceRegistry::bestPathToDot(srcKey, dstKey); } } else { errorPigment << "Unknown graph type : " << graphType.toLatin1(); exit(EXIT_FAILURE); } if (outputType == "dot") { QFile file(outputFileName); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) exit(EXIT_FAILURE); QTextStream out(&file); out << dot; } else if (outputType == "ps" || outputType == "svg") { QTemporaryFile file; if (!file.open()) { exit(EXIT_FAILURE); } QTextStream out(&file); out << dot; QString cmd = QString("dot -T%1 %2 -o %3").arg(outputType).arg(file.fileName()).arg(outputFileName); file.close(); if (QProcess::execute(cmd) != 0) { - errorPigment << "An error has occurred when executing : '" << cmd << "' the most likely cause is that 'dot' command is missing, and that you should install graphviz (from http://www.graphviz.org)"; + errorPigment << "An error has occurred when executing : '" << cmd << "' the most likely cause is that 'dot' command is missing, and that you should install graphviz (from https://www.graphviz.org)"; } } else { errorPigment << "Unknown output type : " << outputType; exit(EXIT_FAILURE); } } diff --git a/libs/psd/psd.h b/libs/psd/psd.h index acb2b937e2..ff7efc1d09 100644 --- a/libs/psd/psd.h +++ b/libs/psd/psd.h @@ -1,1169 +1,1169 @@ /* * Copyright (c) 2010 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. */ /* * Constants and defines taken from gimp and psdparse */ #ifndef PSD_H #define PSD_H #include #include #include #include #include #include #include #include "kritapsd_export.h" class KoPattern; const int MAX_CHANNELS = 56; typedef qint32 Fixed; /* Represents a fixed point implied decimal */ /** * Image color/depth modes */ enum psd_color_mode { Bitmap = 0, Grayscale=1, Indexed=2, RGB=3, CMYK=4, MultiChannel=7, DuoTone=8, Lab=9, Gray16, RGB48, Lab48, CMYK64, DeepMultichannel, Duotone16, COLORMODE_UNKNOWN = 9000 }; /** * Color samplers, apparently distict from PSDColormode */ namespace psd_color_sampler { enum PSDColorSamplers { RGB, HSB, CMYK, PANTONE, // LAB FOCOLTONE, // CMYK TRUMATCH, // CMYK TOYO, // LAB LAB, GRAYSCALE, HKS, // CMYK DIC, // LAB TOTAL_INK, MONITOR_RGB, DUOTONE, OPACITY, ANPA = 3000 // LAB }; } // EFFECTS enum psd_gradient_style { psd_gradient_style_linear, // 'Lnr ' psd_gradient_style_radial, // 'Rdl ' psd_gradient_style_angle, // 'Angl' psd_gradient_style_reflected, // 'Rflc' psd_gradient_style_diamond // 'Dmnd' }; enum psd_color_stop_type { psd_color_stop_type_foreground_color, // 'FrgC' psd_color_stop_type_background_Color, // 'BckC' psd_color_stop_type_user_stop // 'UsrS' }; enum psd_technique_type { psd_technique_softer, psd_technique_precise, psd_technique_slope_limit, }; enum psd_stroke_position { psd_stroke_outside, psd_stroke_inside, psd_stroke_center }; enum psd_fill_type { psd_fill_solid_color, psd_fill_gradient, psd_fill_pattern, }; enum psd_glow_source { psd_glow_center, psd_glow_edge, }; enum psd_bevel_style { psd_bevel_outer_bevel, psd_bevel_inner_bevel, psd_bevel_emboss, psd_bevel_pillow_emboss, psd_bevel_stroke_emboss, }; enum psd_direction { psd_direction_up, psd_direction_down }; enum psd_section_type { psd_other = 0, psd_open_folder, psd_closed_folder, psd_bounding_divider }; // GRADIENT MAP // Each color stop struct psd_gradient_color_stop { qint32 location; // Location of color stop qint32 midpoint; // Midpoint of color stop QColor actual_color; psd_color_stop_type color_stop_type; }; // Each transparency stop struct psd_gradient_transparency_stop { qint32 location; // Location of transparency stop qint32 midpoint; // Midpoint of transparency stop qint8 opacity; // Opacity of transparency stop }; // Gradient settings (Photoshop 6.0) struct psd_layer_gradient_map { bool reverse; // Is gradient reverse bool dithered; // Is gradient dithered qint32 name_length; quint16 *name; // Name of the gradient: Unicode string, padded qint8 number_color_stops; // Number of color stops to follow psd_gradient_color_stop * color_stop; qint8 number_transparency_stops;// Number of transparency stops to follow psd_gradient_transparency_stop * transparency_stop; qint8 expansion_count; // Expansion count ( = 2 for Photoshop 6.0) qint8 interpolation; // Interpolation if length above is non-zero qint8 length; // Length (= 32 for Photoshop 6.0) qint8 mode; // Mode for this gradient qint32 random_number_seed; // Random number seed qint8 showing_transparency_flag;// Flag for showing transparency qint8 using_vector_color_flag;// Flag for using vector color qint32 roughness_factor; // Roughness factor QColor min_color; QColor max_color; QColor lookup_table[256]; }; struct psd_gradient_color { qint32 smoothness; qint32 name_length; quint16 * name; // Name of the gradient: Unicode string, padded qint8 number_color_stops; // Number of color stops to follow psd_gradient_color_stop * color_stop; qint8 number_transparency_stops;// Number of transparency stops to follow psd_gradient_transparency_stop *transparency_stop; }; struct psd_pattern { psd_color_mode color_mode = Bitmap; // The image mode of the file. quint8 height = 0; // Point: vertical, 2 bytes and horizontal, 2 bytes quint8 width = 0; QString name; QString uuid; qint32 version = 0; quint8 top = 0; // Rectangle: top, left, bottom, right quint8 left = 0; quint8 bottom = 0; quint8 right = 0; qint32 max_channel = 0; // Max channels qint32 channel_number = 0; QVector color_table; }; struct psd_layer_effects_context { psd_layer_effects_context() : keep_original(false) { } bool keep_original; }; #define PSD_LOOKUP_TABLE_SIZE 256 -// dsdw, isdw: http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_22203 +// dsdw, isdw: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_22203 class KRITAPSD_EXPORT psd_layer_effects_shadow_base { public: psd_layer_effects_shadow_base() : m_invertsSelection(false) , m_edgeHidden(true) , m_effectEnabled(false) , m_blendMode(COMPOSITE_MULT) , m_color(Qt::black) , m_nativeColor(Qt::black) , m_opacity(75) , m_angle(120) , m_useGlobalLight(true) , m_distance(21) , m_spread(0) , m_size(21) , m_antiAliased(0) , m_noise(0) , m_knocksOut(false) , m_fillType(psd_fill_solid_color) , m_technique(psd_technique_softer) , m_range(100) , m_jitter(0) , m_gradient(0) { for(int i = 0; i < PSD_LOOKUP_TABLE_SIZE; ++i) { m_contourLookupTable[i] = i; } } virtual ~psd_layer_effects_shadow_base() { } QPoint calculateOffset(const psd_layer_effects_context *context) const; void setEffectEnabled(bool value) { m_effectEnabled = value; } bool effectEnabled() const { return m_effectEnabled; } QString blendMode() const { return m_blendMode; } QColor color() const { return m_color; } QColor nativeColor() const { return m_nativeColor; } qint32 opacity() const { return m_opacity; } qint32 angle() const { return m_angle; } bool useGlobalLight() const { return m_useGlobalLight; } qint32 distance() const { return m_distance; } qint32 spread() const { return m_spread; } qint32 size() const { return m_size; } const quint8* contourLookupTable() const { return m_contourLookupTable; } bool antiAliased() const { return m_antiAliased; } qint32 noise() const { return m_noise; } bool knocksOut() const { return m_knocksOut; } bool invertsSelection() const { return m_invertsSelection; } bool edgeHidden() const { return m_edgeHidden; } psd_fill_type fillType() const { return m_fillType; } psd_technique_type technique() const { return m_technique; } qint32 range() const { return m_range; } qint32 jitter() const { return m_jitter; } KoAbstractGradientSP gradient() const { return m_gradient; } public: void setBlendMode(QString value) { m_blendMode = value; } void setColor(QColor value) { m_color = value; } void setNativeColor(QColor value) { m_nativeColor = value; } void setOpacity(qint32 value) { m_opacity = value; } void setAngle(qint32 value) { m_angle = value; } void setUseGlobalLight(bool value) { m_useGlobalLight = value; } void setDistance(qint32 value) { m_distance = value; } void setSpread(qint32 value) { m_spread = value; } void setSize(qint32 value) { m_size = value; } void setContourLookupTable(const quint8* value) { memcpy(m_contourLookupTable, value, PSD_LOOKUP_TABLE_SIZE * sizeof(quint8)); } void setAntiAliased(bool value) { m_antiAliased = value; } void setNoise(qint32 value) { m_noise = value; } void setKnocksOut(bool value) { m_knocksOut = value; } void setInvertsSelection(bool value) { m_invertsSelection = value; } void setEdgeHidden(bool value) { m_edgeHidden = value; } void setFillType(psd_fill_type value) { m_fillType = value; } void setTechnique(psd_technique_type value) { m_technique = value; } void setRange(qint32 value) { m_range = value; } void setJitter(qint32 value) { m_jitter = value; } void setGradient(KoAbstractGradientSP value) { m_gradient = value; } virtual void scaleLinearSizes(qreal scale) { m_distance *= scale; m_size *= scale; } private: // internal bool m_invertsSelection; bool m_edgeHidden; private: bool m_effectEnabled; // Effect enabled QString m_blendMode; // already in Krita format! QColor m_color; QColor m_nativeColor; qint32 m_opacity; // Opacity as a percent (0...100) qint32 m_angle; // Angle in degrees bool m_useGlobalLight; // Use this angle in all of the layer effects qint32 m_distance; // Distance in pixels qint32 m_spread; // Intensity as a percent qint32 m_size; // Blur value in pixels quint8 m_contourLookupTable[PSD_LOOKUP_TABLE_SIZE]; bool m_antiAliased; qint32 m_noise; bool m_knocksOut; // for Outer/Inner Glow psd_fill_type m_fillType; psd_technique_type m_technique; qint32 m_range; qint32 m_jitter; KoAbstractGradientSP m_gradient; }; class KRITAPSD_EXPORT psd_layer_effects_shadow_common : public psd_layer_effects_shadow_base { public: /// FIXME: 'using' is not supported by MSVC, so please refactor in /// some other way to ensure that the setters are not used /// in the classes we don't want // using psd_layer_effects_shadow_base::setBlendMode; // using psd_layer_effects_shadow_base::setColor; // using psd_layer_effects_shadow_base::setOpacity; // using psd_layer_effects_shadow_base::setAngle; // using psd_layer_effects_shadow_base::setUseGlobalLight; // using psd_layer_effects_shadow_base::setDistance; // using psd_layer_effects_shadow_base::setSpread; // using psd_layer_effects_shadow_base::setSize; // using psd_layer_effects_shadow_base::setContourLookupTable; // using psd_layer_effects_shadow_base::setAntiAliased; // using psd_layer_effects_shadow_base::setNoise; }; class KRITAPSD_EXPORT psd_layer_effects_drop_shadow : public psd_layer_effects_shadow_common { public: /// FIXME: 'using' is not supported by MSVC, so please refactor in /// some other way to ensure that the setters are not used /// in the classes we don't want //using psd_layer_effects_shadow_base::setKnocksOut; }; -// isdw: http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_22203 +// isdw: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_22203 class KRITAPSD_EXPORT psd_layer_effects_inner_shadow : public psd_layer_effects_shadow_common { public: psd_layer_effects_inner_shadow() { setKnocksOut(false); setInvertsSelection(true); setEdgeHidden(false); } }; class KRITAPSD_EXPORT psd_layer_effects_glow_common : public psd_layer_effects_shadow_base { public: psd_layer_effects_glow_common() { setKnocksOut(true); setDistance(0); setBlendMode(COMPOSITE_LINEAR_DODGE); setColor(Qt::white); } /// FIXME: 'using' is not supported by MSVC, so please refactor in /// some other way to ensure that the setters are not used /// in the classes we don't want // using psd_layer_effects_shadow_base::setBlendMode; // using psd_layer_effects_shadow_base::setColor; // using psd_layer_effects_shadow_base::setOpacity; // using psd_layer_effects_shadow_base::setSpread; // using psd_layer_effects_shadow_base::setSize; // using psd_layer_effects_shadow_base::setContourLookupTable; // using psd_layer_effects_shadow_base::setAntiAliased; // using psd_layer_effects_shadow_base::setNoise; // using psd_layer_effects_shadow_base::setFillType; // using psd_layer_effects_shadow_base::setTechnique; // using psd_layer_effects_shadow_base::setRange; // using psd_layer_effects_shadow_base::setJitter; // using psd_layer_effects_shadow_base::setGradient; }; -// oglw: http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_25738 +// oglw: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_25738 class KRITAPSD_EXPORT psd_layer_effects_outer_glow : public psd_layer_effects_glow_common { }; -// iglw: http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_27692 +// iglw: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_27692 class KRITAPSD_EXPORT psd_layer_effects_inner_glow : public psd_layer_effects_glow_common { public: psd_layer_effects_inner_glow() : m_source(psd_glow_edge) { setInvertsSelection(true); setEdgeHidden(false); setKnocksOut(false); } psd_glow_source source() const { return m_source; } void setSource(psd_glow_source value) { m_source = value; } private: psd_glow_source m_source; }; struct psd_layer_effects_satin : public psd_layer_effects_shadow_base { psd_layer_effects_satin() { setInvert(false); setUseGlobalLight(false); setDistance(8); setSize(7); setSpread(0); setKnocksOut(true); setEdgeHidden(false); setBlendMode(COMPOSITE_LINEAR_BURN); } /// FIXME: 'using' is not supported by MSVC, so please refactor in /// some other way to ensure that the setters are not used /// in the classes we don't want // using psd_layer_effects_shadow_base::setBlendMode; // using psd_layer_effects_shadow_base::setColor; // using psd_layer_effects_shadow_base::setOpacity; // // NOTE: no global light setting explicitly! // using psd_layer_effects_shadow_base::setAngle; // using psd_layer_effects_shadow_base::setDistance; // using psd_layer_effects_shadow_base::setSize; // using psd_layer_effects_shadow_base::setContourLookupTable; // using psd_layer_effects_shadow_base::setAntiAliased; bool invert() const { return m_invert; } void setInvert(bool value) { m_invert = value; } private: bool m_invert; }; struct psd_pattern_info { qint32 name_length; quint16 * name; quint8 identifier[256]; }; -// bevl: http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_31889 +// bevl: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_31889 struct psd_layer_effects_bevel_emboss : public psd_layer_effects_shadow_base { psd_layer_effects_bevel_emboss() : m_style(psd_bevel_inner_bevel), m_technique(psd_technique_softer), m_depth(100), m_direction(psd_direction_up), m_soften(0), m_altitude(30), m_glossAntiAliased(false), m_highlightBlendMode(COMPOSITE_SCREEN), m_highlightColor(Qt::white), m_highlightOpacity(75), m_shadowBlendMode(COMPOSITE_MULT), m_shadowColor(Qt::black), m_shadowOpacity(75), m_contourEnabled(false), m_contourRange(100), m_textureEnabled(false), m_texturePattern(0), m_textureScale(100), m_textureDepth(100), m_textureInvert(false), m_textureAlignWithLayer(true), m_textureHorizontalPhase(0), m_textureVerticalPhase(0) { for(int i = 0; i < PSD_LOOKUP_TABLE_SIZE; ++i) { m_glossContourLookupTable[i] = i; } } /// FIXME: 'using' is not supported by MSVC, so please refactor in /// some other way to ensure that the setters are not used /// in the classes we don't want // using psd_layer_effects_shadow_base::setSize; // using psd_layer_effects_shadow_base::setAngle; // using psd_layer_effects_shadow_base::setUseGlobalLight; // using psd_layer_effects_shadow_base::setContourLookupTable; // using psd_layer_effects_shadow_base::setAntiAliased; psd_bevel_style style() const { return m_style; } void setStyle(psd_bevel_style value) { m_style = value; } psd_technique_type technique() const { return m_technique; } void setTechnique(psd_technique_type value) { m_technique = value; } int depth() const { return m_depth; } void setDepth(int value) { m_depth = value; } psd_direction direction() const { return m_direction; } void setDirection(psd_direction value) { m_direction = value; } int soften() const { return m_soften; } void setSoften(int value) { m_soften = value; } int altitude() const { return m_altitude; } void setAltitude(int value) { m_altitude = value; } const quint8* glossContourLookupTable() const { return m_glossContourLookupTable; } void setGlossContourLookupTable(const quint8 *value) { memcpy(m_glossContourLookupTable, value, PSD_LOOKUP_TABLE_SIZE * sizeof(quint8)); } bool glossAntiAliased() const { return m_glossAntiAliased; } void setGlossAntiAliased(bool value) { m_glossAntiAliased = value; } QString highlightBlendMode() const { return m_highlightBlendMode; } void setHighlightBlendMode(QString value) { m_highlightBlendMode = value; } QColor highlightColor() const { return m_highlightColor; } void setHighlightColor(QColor value) { m_highlightColor = value; } qint32 highlightOpacity() const { return m_highlightOpacity; } void setHighlightOpacity(qint32 value) { m_highlightOpacity = value; } QString shadowBlendMode() const { return m_shadowBlendMode; } void setShadowBlendMode(QString value) { m_shadowBlendMode = value; } QColor shadowColor() const { return m_shadowColor; } void setShadowColor(QColor value) { m_shadowColor = value; } qint32 shadowOpacity() const { return m_shadowOpacity; } void setShadowOpacity(qint32 value) { m_shadowOpacity = value; } bool contourEnabled() const { return m_contourEnabled; } void setContourEnabled(bool value) { m_contourEnabled = value; } int contourRange() const { return m_contourRange; } void setContourRange(int value) { m_contourRange = value; } bool textureEnabled() const { return m_textureEnabled; } void setTextureEnabled(bool value) { m_textureEnabled = value; } KoPattern* texturePattern() const { return m_texturePattern; } void setTexturePattern(KoPattern *value) { m_texturePattern = value; } int textureScale() const { return m_textureScale; } void setTextureScale(int value) { m_textureScale = value; } int textureDepth() const { return m_textureDepth; } void setTextureDepth(int value) { m_textureDepth = value; } bool textureInvert() const { return m_textureInvert; } void setTextureInvert(bool value) { m_textureInvert = value; } bool textureAlignWithLayer() const { return m_textureAlignWithLayer; } void setTextureAlignWithLayer(bool value) { m_textureAlignWithLayer = value; } void setTexturePhase(const QPointF &phase) { m_textureHorizontalPhase = phase.x(); m_textureVerticalPhase = phase.y(); } QPointF texturePhase() const { return QPointF(m_textureHorizontalPhase, m_textureVerticalPhase); } int textureHorizontalPhase() const { return m_textureHorizontalPhase; } void setTextureHorizontalPhase(int value) { m_textureHorizontalPhase = value; } int textureVerticalPhase() const { return m_textureVerticalPhase; } void setTextureVerticalPhase(int value) { m_textureVerticalPhase = value; } void scaleLinearSizes(qreal scale) override { psd_layer_effects_shadow_base::scaleLinearSizes(scale); m_soften *= scale; m_textureScale *= scale; } private: psd_bevel_style m_style; psd_technique_type m_technique; int m_depth; psd_direction m_direction; // Up or down int m_soften; // Blur value in pixels. int m_altitude; quint8 m_glossContourLookupTable[256]; bool m_glossAntiAliased; QString m_highlightBlendMode; // already in Krita format QColor m_highlightColor; qint32 m_highlightOpacity; // Highlight opacity as a percent QString m_shadowBlendMode; // already in Krita format QColor m_shadowColor; qint32 m_shadowOpacity; // Shadow opacity as a percent bool m_contourEnabled; int m_contourRange; bool m_textureEnabled; KoPattern *m_texturePattern; int m_textureScale; int m_textureDepth; bool m_textureInvert; bool m_textureAlignWithLayer; int m_textureHorizontalPhase; // 0..100% int m_textureVerticalPhase; // 0..100% }; struct psd_layer_effects_overlay_base : public psd_layer_effects_shadow_base { psd_layer_effects_overlay_base() : m_scale(100), m_alignWithLayer(true), m_reverse(false), m_style(psd_gradient_style_linear), m_gradientXOffset(0), m_gradientYOffset(0), m_pattern(0), m_horizontalPhase(0), m_verticalPhase(0) { setUseGlobalLight(false); } /// FIXME: 'using' is not supported by MSVC, so please refactor in /// some other way to ensure that the setters are not used /// in the classes we don't want // using psd_layer_effects_shadow_base::setBlendMode; // using psd_layer_effects_shadow_base::setOpacity; int scale() const { return m_scale; } bool alignWithLayer() const { return m_alignWithLayer; } bool reverse() const { return m_reverse; } psd_gradient_style style() const { return m_style; } int gradientXOffset() const { return m_gradientXOffset; } int gradientYOffset() const { return m_gradientYOffset; } KoPattern* pattern() const { return m_pattern; } int horizontalPhase() const { return m_horizontalPhase; } int verticalPhase() const { return m_verticalPhase; } // refactor that public: void setScale(int value) { m_scale = value; } void setAlignWithLayer(bool value) { m_alignWithLayer = value; } void setReverse(bool value) { m_reverse = value; } void setStyle(psd_gradient_style value) { m_style = value; } void setGradientOffset(const QPointF &pt) { m_gradientXOffset = qRound(pt.x()); m_gradientYOffset = qRound(pt.y()); } QPointF gradientOffset() const { return QPointF(m_gradientXOffset, m_gradientYOffset); } void setPattern(KoPattern *value) { m_pattern = value; } void setPatternPhase(const QPointF &phase) { m_horizontalPhase = phase.x(); m_verticalPhase = phase.y(); } QPointF patternPhase() const { return QPointF(m_horizontalPhase, m_verticalPhase); } void scaleLinearSizes(qreal scale) override { psd_layer_effects_shadow_base::scaleLinearSizes(scale); m_scale *= scale; } private: // Gradient+Pattern int m_scale; bool m_alignWithLayer; // Gradient bool m_reverse; psd_gradient_style m_style; int m_gradientXOffset; // 0..100% int m_gradientYOffset; // 0..100% // Pattern KoPattern *m_pattern; int m_horizontalPhase; // 0..100% int m_verticalPhase; // 0..100% protected: /// FIXME: 'using' is not supported by MSVC, so please refactor in /// some other way to ensure that the setters are not used /// in the classes we don't want // must be called in the derived classes' c-tor // using psd_layer_effects_shadow_base::setFillType; }; -// sofi: http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_70055 +// sofi: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_70055 struct psd_layer_effects_color_overlay : public psd_layer_effects_overlay_base { psd_layer_effects_color_overlay() { setFillType(psd_fill_solid_color); setColor(Qt::white); } /// FIXME: 'using' is not supported by MSVC, so please refactor in /// some other way to ensure that the setters are not used /// in the classes we don't want // using psd_layer_effects_shadow_base::setColor; }; struct psd_layer_effects_gradient_overlay : public psd_layer_effects_overlay_base { psd_layer_effects_gradient_overlay() { setFillType(psd_fill_gradient); setAngle(90); setReverse(false); setScale(100); setAlignWithLayer(true); setStyle(psd_gradient_style_linear); } public: /// FIXME: 'using' is not supported by MSVC, so please refactor in /// some other way to ensure that the setters are not used /// in the classes we don't want // using psd_layer_effects_shadow_base::setGradient; // using psd_layer_effects_shadow_base::setAngle; // using psd_layer_effects_overlay_base::setReverse; // using psd_layer_effects_overlay_base::setScale; // using psd_layer_effects_overlay_base::setAlignWithLayer; // using psd_layer_effects_overlay_base::setStyle; // using psd_layer_effects_overlay_base::setGradientOffset; // using psd_layer_effects_overlay_base::gradientOffset; }; struct psd_layer_effects_pattern_overlay : public psd_layer_effects_overlay_base { psd_layer_effects_pattern_overlay() { setFillType(psd_fill_pattern); setScale(100); setAlignWithLayer(true); } /// FIXME: 'using' is not supported by MSVC, so please refactor in /// some other way to ensure that the setters are not used /// in the classes we don't want // using psd_layer_effects_overlay_base::setScale; // using psd_layer_effects_overlay_base::setAlignWithLayer; // using psd_layer_effects_overlay_base::setPattern; // using psd_layer_effects_overlay_base::setPatternPhase; // using psd_layer_effects_overlay_base::patternPhase; private: // These are unused /*int m_scale; bool m_alignWithLayer; KoPattern *m_pattern; int m_horizontalPhase; int m_verticalPhase;*/ }; struct psd_layer_effects_stroke : public psd_layer_effects_overlay_base { psd_layer_effects_stroke() : m_position(psd_stroke_outside) { setFillType(psd_fill_solid_color); setColor(Qt::black); setAngle(90); setReverse(false); setScale(100); setAlignWithLayer(true); setStyle(psd_gradient_style_linear); setScale(100); setAlignWithLayer(true); } /// FIXME: 'using' is not supported by MSVC, so please refactor in /// some other way to ensure that the setters are not used /// in the classes we don't want // using psd_layer_effects_shadow_base::setFillType; // using psd_layer_effects_shadow_base::setSize; // using psd_layer_effects_shadow_base::setColor; // using psd_layer_effects_shadow_base::setGradient; // using psd_layer_effects_shadow_base::setAngle; // using psd_layer_effects_overlay_base::setReverse; // using psd_layer_effects_overlay_base::setScale; // using psd_layer_effects_overlay_base::setAlignWithLayer; // using psd_layer_effects_overlay_base::setStyle; // using psd_layer_effects_overlay_base::setGradientOffset; // using psd_layer_effects_overlay_base::gradientOffset; // using psd_layer_effects_overlay_base::setPattern; // using psd_layer_effects_overlay_base::setPatternPhase; // using psd_layer_effects_overlay_base::patternPhase; psd_stroke_position position() const { return m_position; } void setPosition(psd_stroke_position value) { m_position = value; } private: psd_stroke_position m_position; }; /** * Convert PsdColorMode to pigment colormodelid and colordepthid. * @see KoColorModelStandardIds * * @return a QPair containing ColorModelId and ColorDepthID */ QPair KRITAPSD_EXPORT psd_colormode_to_colormodelid(psd_color_mode colormode, quint16 channelDepth); /** * Convert the Photoshop blend mode strings to Pigment compositeop id's */ QString KRITAPSD_EXPORT psd_blendmode_to_composite_op(const QString& blendmode); QString KRITAPSD_EXPORT composite_op_to_psd_blendmode(const QString& compositeOp); #endif // PSD_H diff --git a/libs/ui/canvas/kis_canvas_widget_base.cpp b/libs/ui/canvas/kis_canvas_widget_base.cpp index ebf1e5c057..a852f01645 100644 --- a/libs/ui/canvas/kis_canvas_widget_base.cpp +++ b/libs/ui/canvas/kis_canvas_widget_base.cpp @@ -1,275 +1,275 @@ /* * Copyright (C) 2007, 2010 Adrian Page * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_canvas_widget_base.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_coordinates_converter.h" #include "kis_canvas_decoration.h" #include "kis_config.h" #include "kis_canvas2.h" #include "KisViewManager.h" #include "kis_selection_manager.h" #include "KisDocument.h" #include "kis_update_info.h" struct KisCanvasWidgetBase::Private { public: Private(KisCanvas2 *newCanvas, KisCoordinatesConverter *newCoordinatesConverter) : canvas(newCanvas) , coordinatesConverter(newCoordinatesConverter) , viewConverter(newCanvas->viewConverter()) , toolProxy(newCanvas->toolProxy()) , ignorenextMouseEventExceptRightMiddleClick(0) , borderColor(Qt::gray) {} QList decorations; KisCanvas2 * canvas; KisCoordinatesConverter *coordinatesConverter; const KoViewConverter * viewConverter; KoToolProxy * toolProxy; QTimer blockMouseEvent; - bool ignorenextMouseEventExceptRightMiddleClick; // HACK work around Qt bug not sending tablet right/dblclick http://bugreports.qt.nokia.com/browse/QTBUG-8598 + bool ignorenextMouseEventExceptRightMiddleClick; // HACK work around Qt bug not sending tablet right/dblclick https://bugreports.qt.io/browse/QTBUG-8598 QColor borderColor; }; KisCanvasWidgetBase::KisCanvasWidgetBase(KisCanvas2 * canvas, KisCoordinatesConverter *coordinatesConverter) : m_d(new Private(canvas, coordinatesConverter)) { m_d->blockMouseEvent.setSingleShot(true); } KisCanvasWidgetBase::~KisCanvasWidgetBase() { /** * Clear all the attached decoration. Otherwise they might decide * to process some events or signals after the canvas has been * destroyed */ //5qDeleteAll(m_d->decorations); m_d->decorations.clear(); delete m_d; } void KisCanvasWidgetBase::drawDecorations(QPainter & gc, const QRect &updateWidgetRect) const { if (!m_d->canvas) { dbgFile<<"canvas doesn't exist, in canvas widget base!"; return; } gc.save(); // Setup the painter to take care of the offset; all that the // classes that do painting need to keep track of is resolution gc.setRenderHint(QPainter::Antialiasing); gc.setRenderHint(QPainter::TextAntialiasing); // This option does not do anything anymore with Qt4.6, so don't re-enable it since it seems to break display - // http://www.archivum.info/qt-interest@trolltech.com/2010-01/00481/Re:-(Qt-interest)-Is-QPainter::HighQualityAntialiasing-render-hint-broken-in-Qt-4.6.html + // https://lists.qt-project.org/pipermail/qt-interest-old/2009-December/017078.html // gc.setRenderHint(QPainter::HighQualityAntialiasing); gc.setRenderHint(QPainter::SmoothPixmapTransform); gc.save(); gc.setClipRect(updateWidgetRect); QTransform transform = m_d->coordinatesConverter->flakeToWidgetTransform(); gc.setTransform(transform); // Paint the shapes (other than the layers) m_d->canvas->globalShapeManager()->paint(gc, *m_d->viewConverter, false); // draw green selection outlines around text shapes that are edited, so the user sees where they end gc.save(); QTransform worldTransform = gc.worldTransform(); gc.setPen( Qt::green ); Q_FOREACH (KoShape *shape, canvas()->shapeManager()->selection()->selectedShapes()) { if (shape->shapeId() == "ArtisticText" || shape->shapeId() == "TextShapeID") { gc.setWorldTransform(shape->absoluteTransformation(m_d->viewConverter) * worldTransform); KoShape::applyConversion(gc, *m_d->viewConverter); gc.drawRect(QRectF(QPointF(), shape->size())); } } gc.restore(); // Draw text shape over canvas while editing it, that's needs to show the text selection correctly QString toolId = KoToolManager::instance()->activeToolId(); if (toolId == "ArtisticTextTool" || toolId == "TextTool") { gc.save(); gc.setPen(Qt::NoPen); gc.setBrush(Qt::NoBrush); Q_FOREACH (KoShape *shape, canvas()->shapeManager()->selection()->selectedShapes()) { if (shape->shapeId() == "ArtisticText" || shape->shapeId() == "TextShapeID") { KoShapePaintingContext paintContext(canvas(), false); gc.save(); gc.setTransform(shape->absoluteTransformation(m_d->viewConverter) * gc.transform()); canvas()->shapeManager()->paintShape(shape, gc, *m_d->viewConverter, paintContext); gc.restore(); } } gc.restore(); } gc.restore(); // ask the decorations to paint themselves Q_FOREACH (KisCanvasDecorationSP deco, m_d->decorations) { deco->paint(gc, m_d->coordinatesConverter->widgetToDocument(updateWidgetRect), m_d->coordinatesConverter,m_d->canvas); } gc.setTransform(transform); // - some tools do not restore gc, but that is not important here // - we need to disable clipping to draw handles properly gc.setClipping(false); toolProxy()->paint(gc, *m_d->viewConverter); gc.restore(); } void KisCanvasWidgetBase::addDecoration(KisCanvasDecorationSP deco) { m_d->decorations.push_back(deco); std::stable_sort(m_d->decorations.begin(), m_d->decorations.end(), KisCanvasDecoration::comparePriority); } void KisCanvasWidgetBase::removeDecoration(const QString &id) { for (auto it = m_d->decorations.begin(); it != m_d->decorations.end(); ++it) { if ((*it)->id() == id) { it = m_d->decorations.erase(it); break; } } } KisCanvasDecorationSP KisCanvasWidgetBase::decoration(const QString& id) const { Q_FOREACH (KisCanvasDecorationSP deco, m_d->decorations) { if (deco->id() == id) { return deco; } } return 0; } void KisCanvasWidgetBase::setDecorations(const QList &decorations) { m_d->decorations=decorations; std::stable_sort(m_d->decorations.begin(), m_d->decorations.end(), KisCanvasDecoration::comparePriority); } QList KisCanvasWidgetBase::decorations() const { return m_d->decorations; } void KisCanvasWidgetBase::setWrapAroundViewingMode(bool value) { Q_UNUSED(value); } QImage KisCanvasWidgetBase::createCheckersImage(qint32 checkSize) { KisConfig cfg(true); if(checkSize < 0) checkSize = cfg.checkSize(); QColor checkColor1 = cfg.checkersColor1(); QColor checkColor2 = cfg.checkersColor2(); QImage tile(checkSize * 2, checkSize * 2, QImage::Format_RGB32); QPainter pt(&tile); pt.fillRect(tile.rect(), checkColor2); pt.fillRect(0, 0, checkSize, checkSize, checkColor1); pt.fillRect(checkSize, checkSize, checkSize, checkSize, checkColor1); pt.end(); return tile; } void KisCanvasWidgetBase::notifyConfigChanged() { KisConfig cfg(true); m_d->borderColor = cfg.canvasBorderColor(); } QColor KisCanvasWidgetBase::borderColor() const { return m_d->borderColor; } KisCanvas2 *KisCanvasWidgetBase::canvas() const { return m_d->canvas; } KisCoordinatesConverter* KisCanvasWidgetBase::coordinatesConverter() const { return m_d->coordinatesConverter; } QVector KisCanvasWidgetBase::updateCanvasProjection(const QVector &infoObjects) { QVector dirtyViewRects; Q_FOREACH (KisUpdateInfoSP info, infoObjects) { dirtyViewRects << this->updateCanvasProjection(info); } return dirtyViewRects; } KoToolProxy *KisCanvasWidgetBase::toolProxy() const { return m_d->toolProxy; } QVariant KisCanvasWidgetBase::processInputMethodQuery(Qt::InputMethodQuery query) const { if (query == Qt::ImMicroFocus) { QRectF rect = m_d->toolProxy->inputMethodQuery(query, *m_d->viewConverter).toRectF(); return m_d->coordinatesConverter->flakeToWidget(rect); } return m_d->toolProxy->inputMethodQuery(query, *m_d->viewConverter); } void KisCanvasWidgetBase::processInputMethodEvent(QInputMethodEvent *event) { m_d->toolProxy->inputMethodEvent(event); } diff --git a/libs/ui/input/wintab/kis_tablet_support_win_p.h b/libs/ui/input/wintab/kis_tablet_support_win_p.h index 64a8da3ea1..303948b3ac 100644 --- a/libs/ui/input/wintab/kis_tablet_support_win_p.h +++ b/libs/ui/input/wintab/kis_tablet_support_win_p.h @@ -1,146 +1,146 @@ /* * Copyright (C) 2015 The Qt Company Ltd. - * Contact: http://www.qt.io/licensing/ + * Contact: https://www.qt.io/licensing/ * Copyright (C) 2015 Michael Abrahms * * 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 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_TABLET_SUPPORT_WIN_P_H #define KIS_TABLET_SUPPORT_WIN_P_H #include #include #include #include #include "wintab.h" QT_BEGIN_NAMESPACE class QDebug; class QWindow; class QRect; class QWidget; struct QWindowsWinTab32DLL { QWindowsWinTab32DLL() : wTOpen(0), wTClose(0), wTInfo(0), wTEnable(0), wTOverlap(0), wTPacketsGet(0), wTGet(0), wTQueueSizeGet(0), wTQueueSizeSet(0) {} bool init(); typedef HCTX (API *PtrWTOpen)(HWND, LPLOGCONTEXT, BOOL); typedef BOOL (API *PtrWTClose)(HCTX); typedef UINT (API *PtrWTInfo)(UINT, UINT, LPVOID); typedef BOOL (API *PtrWTEnable)(HCTX, BOOL); typedef BOOL (API *PtrWTOverlap)(HCTX, BOOL); typedef int (API *PtrWTPacketsGet)(HCTX, int, LPVOID); typedef BOOL (API *PtrWTGet)(HCTX, LPLOGCONTEXT); typedef int (API *PtrWTQueueSizeGet)(HCTX); typedef BOOL (API *PtrWTQueueSizeSet)(HCTX, int); PtrWTOpen wTOpen; PtrWTClose wTClose; PtrWTInfo wTInfo; PtrWTEnable wTEnable; PtrWTOverlap wTOverlap; PtrWTPacketsGet wTPacketsGet; PtrWTGet wTGet; PtrWTQueueSizeGet wTQueueSizeGet; PtrWTQueueSizeSet wTQueueSizeSet; }; struct QWindowsTabletDeviceData { QWindowsTabletDeviceData() : minPressure(0), maxPressure(0), minTanPressure(0), maxTanPressure(0), minX(0), maxX(0), minY(0), maxY(0), minZ(0), maxZ(0), uniqueId(0), currentDevice(0), currentPointerType(0) {} QPointF scaleCoordinates(int coordX, int coordY,const QRect &targetArea) const; qreal scalePressure(qreal p) const { return p / qreal(maxPressure - minPressure); } qreal scaleTangentialPressure(qreal p) const { return p / qreal(maxTanPressure - minTanPressure); } int minPressure; int maxPressure; int minTanPressure; int maxTanPressure; int minX, maxX, minY, maxY, minZ, maxZ; qint64 uniqueId; int currentDevice; int currentPointerType; QRect virtualDesktopArea; // Added by Krita QMap buttonsMap; }; QDebug operator<<(QDebug d, const QWindowsTabletDeviceData &t); class QWindowsTabletSupport { Q_DISABLE_COPY(QWindowsTabletSupport) explicit QWindowsTabletSupport(HWND window, HCTX context); public: ~QWindowsTabletSupport(); static QWindowsTabletSupport *create(); void notifyActivate(); QString description() const; bool translateTabletProximityEvent(WPARAM wParam, LPARAM lParam); bool translateTabletPacketEvent(); int absoluteRange() const { return m_absoluteRange; } void setAbsoluteRange(int a) { m_absoluteRange = a; } void tabletUpdateCursor(const int pkCursor); static QWindowsWinTab32DLL m_winTab32DLL; private: unsigned options() const; QWindowsTabletDeviceData tabletInit(const quint64 uniqueId, const UINT cursorType) const; const HWND m_window; const HCTX m_context; int m_absoluteRange; bool m_tiltSupport; QVector m_devices; int m_currentDevice; QWidget *targetWidget{0}; /** * This is an inelegant solution to record pen / eraser switches. * On the Surface Pro 3 we are only notified of cursor changes at the last minute. * The recommended way to handle switches is WT_CSRCHANGE, but that doesn't work * unless we save packet ID information, and we cannot change the structure of the * PACKETDATA due to Qt restrictions. * * Furthermore, WT_CSRCHANGE only ever appears *after* we receive the packet. */ UINT currentPkCursor{0}; bool isSurfacePro3{false}; //< Only enable this on SP3 or other devices with the same issue. }; QT_END_NAMESPACE #endif // KIS_TABLET_SUPPORT_WIN_P_H diff --git a/libs/ui/kis_config.cc b/libs/ui/kis_config.cc index 4c7711b248..7b81c4e4aa 100644 --- a/libs/ui/kis_config.cc +++ b/libs/ui/kis_config.cc @@ -1,2209 +1,2209 @@ /* * Copyright (c) 2002 Patrick Julien * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_canvas_resource_provider.h" #include "kis_config_notifier.h" #include "kis_snap_config.h" #include #include #include #include #include #ifdef Q_OS_WIN #include "config_use_qt_tablet_windows.h" #endif KisConfig::KisConfig(bool readOnly) : m_cfg( KSharedConfig::openConfig()->group("")) , m_readOnly(readOnly) { if (!readOnly) { KIS_SAFE_ASSERT_RECOVER_RETURN(qApp && qApp->thread() == QThread::currentThread()); } } KisConfig::~KisConfig() { if (m_readOnly) return; if (qApp && qApp->thread() != QThread::currentThread()) { dbgKrita << "WARNING: KisConfig: requested config synchronization from nonGUI thread! Called from:" << kisBacktrace(); return; } m_cfg.sync(); } void KisConfig::logImportantSettings() const { - KisUsageLogger::write("Current Settings\n"); - KisUsageLogger::write(QString("\tCurrent Swap Location: %1").arg(KisImageConfig(true).swapDir())); - KisUsageLogger::write(QString("\tUndo Enabled: %1").arg(undoEnabled())); - KisUsageLogger::write(QString("\tUndo Stack Limit: %1").arg(undoStackLimit())); - KisUsageLogger::write(QString("\tUse OpenGL: %1").arg(useOpenGL())); - KisUsageLogger::write(QString("\tUse OpenGL Texture Buffer: %1").arg(useOpenGLTextureBuffer())); - KisUsageLogger::write(QString("\tUse AMD Vectorization Workaround: %1").arg(enableAmdVectorizationWorkaround())); - KisUsageLogger::write(QString("\tCanvas State: %1").arg(canvasState())); - KisUsageLogger::write(QString("\tAutosave Interval: %1").arg(autoSaveInterval())); - KisUsageLogger::write(QString("\tUse Backup Files: %1").arg(backupFile())); - KisUsageLogger::write(QString("\tNumber of Backups Kept: %1").arg(m_cfg.readEntry("numberofbackupfiles", 1))); - KisUsageLogger::write(QString("\tBackup File Suffix: %1").arg(m_cfg.readEntry("backupfilesuffix", "~"))); + KisUsageLogger::writeSysInfo("Current Settings\n"); + KisUsageLogger::writeSysInfo(QString(" Current Swap Location: %1").arg(KisImageConfig(true).swapDir())); + KisUsageLogger::writeSysInfo(QString(" Undo Enabled: %1").arg(undoEnabled())); + KisUsageLogger::writeSysInfo(QString(" Undo Stack Limit: %1").arg(undoStackLimit())); + KisUsageLogger::writeSysInfo(QString(" Use OpenGL: %1").arg(useOpenGL())); + KisUsageLogger::writeSysInfo(QString(" Use OpenGL Texture Buffer: %1").arg(useOpenGLTextureBuffer())); + KisUsageLogger::writeSysInfo(QString(" Use AMD Vectorization Workaround: %1").arg(enableAmdVectorizationWorkaround())); + KisUsageLogger::writeSysInfo(QString(" Canvas State: %1").arg(canvasState())); + KisUsageLogger::writeSysInfo(QString(" Autosave Interval: %1").arg(autoSaveInterval())); + KisUsageLogger::writeSysInfo(QString(" Use Backup Files: %1").arg(backupFile())); + KisUsageLogger::writeSysInfo(QString(" Number of Backups Kept: %1").arg(m_cfg.readEntry("numberofbackupfiles", 1))); + KisUsageLogger::writeSysInfo(QString(" Backup File Suffix: %1").arg(m_cfg.readEntry("backupfilesuffix", "~"))); QString backupDir; switch(m_cfg.readEntry("backupfilelocation", 0)) { case 1: backupDir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); break; case 2: backupDir = QStandardPaths::writableLocation(QStandardPaths::TempLocation); break; default: // Do nothing: the empty string is user file location backupDir = "Same Folder as the File"; } - KisUsageLogger::write(QString("\tBackup Location: %1").arg(backupDir)); + KisUsageLogger::writeSysInfo(QString(" Backup Location: %1").arg(backupDir)); - KisUsageLogger::write(QString("\tUse Win8 Pointer Input: %1").arg(useWin8PointerInput())); - KisUsageLogger::write(QString("\tUse RightMiddleTabletButton Workaround: %1").arg(useRightMiddleTabletButtonWorkaround())); - KisUsageLogger::write(QString("\tLevels of Detail Enabled: %1").arg(levelOfDetailEnabled())); - KisUsageLogger::write(QString("\tUse Zip64: %1").arg(useZip64())); + KisUsageLogger::writeSysInfo(QString(" Use Win8 Pointer Input: %1").arg(useWin8PointerInput())); + KisUsageLogger::writeSysInfo(QString(" Use RightMiddleTabletButton Workaround: %1").arg(useRightMiddleTabletButtonWorkaround())); + KisUsageLogger::writeSysInfo(QString(" Levels of Detail Enabled: %1").arg(levelOfDetailEnabled())); + KisUsageLogger::writeSysInfo(QString(" Use Zip64: %1").arg(useZip64())); - KisUsageLogger::write("\n"); + KisUsageLogger::writeSysInfo("\n"); } bool KisConfig::disableTouchOnCanvas(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("disableTouchOnCanvas", false)); } void KisConfig::setDisableTouchOnCanvas(bool value) const { m_cfg.writeEntry("disableTouchOnCanvas", value); } bool KisConfig::disableTouchRotation(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("disableTouchRotation", false)); } void KisConfig::setDisableTouchRotation(bool value) const { m_cfg.writeEntry("disableTouchRotation", value); } bool KisConfig::useProjections(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("useProjections", true)); } void KisConfig::setUseProjections(bool useProj) const { m_cfg.writeEntry("useProjections", useProj); } bool KisConfig::undoEnabled(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("undoEnabled", true)); } void KisConfig::setUndoEnabled(bool undo) const { m_cfg.writeEntry("undoEnabled", undo); } int KisConfig::undoStackLimit(bool defaultValue) const { return (defaultValue ? 30 : m_cfg.readEntry("undoStackLimit", 30)); } void KisConfig::setUndoStackLimit(int limit) const { m_cfg.writeEntry("undoStackLimit", limit); } bool KisConfig::useCumulativeUndoRedo(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useCumulativeUndoRedo",false)); } void KisConfig::setCumulativeUndoRedo(bool value) { m_cfg.writeEntry("useCumulativeUndoRedo", value); } qreal KisConfig::stackT1(bool defaultValue) const { return (defaultValue ? 5 : m_cfg.readEntry("stackT1",5)); } void KisConfig::setStackT1(int T1) { m_cfg.writeEntry("stackT1", T1); } qreal KisConfig::stackT2(bool defaultValue) const { return (defaultValue ? 1 : m_cfg.readEntry("stackT2",1)); } void KisConfig::setStackT2(int T2) { m_cfg.writeEntry("stackT2", T2); } int KisConfig::stackN(bool defaultValue) const { return (defaultValue ? 5 : m_cfg.readEntry("stackN",5)); } void KisConfig::setStackN(int N) { m_cfg.writeEntry("stackN", N); } qint32 KisConfig::defImageWidth(bool defaultValue) const { return (defaultValue ? 1600 : m_cfg.readEntry("imageWidthDef", 1600)); } qint32 KisConfig::defImageHeight(bool defaultValue) const { return (defaultValue ? 1200 : m_cfg.readEntry("imageHeightDef", 1200)); } qreal KisConfig::defImageResolution(bool defaultValue) const { return (defaultValue ? 100.0 : m_cfg.readEntry("imageResolutionDef", 100.0)) / 72.0; } QString KisConfig::defColorModel(bool defaultValue) const { return (defaultValue ? KoColorSpaceRegistry::instance()->rgb8()->colorModelId().id() : m_cfg.readEntry("colorModelDef", KoColorSpaceRegistry::instance()->rgb8()->colorModelId().id())); } void KisConfig::defColorModel(const QString & model) const { m_cfg.writeEntry("colorModelDef", model); } QString KisConfig::defaultColorDepth(bool defaultValue) const { return (defaultValue ? KoColorSpaceRegistry::instance()->rgb8()->colorDepthId().id() : m_cfg.readEntry("colorDepthDef", KoColorSpaceRegistry::instance()->rgb8()->colorDepthId().id())); } void KisConfig::setDefaultColorDepth(const QString & depth) const { m_cfg.writeEntry("colorDepthDef", depth); } QString KisConfig::defColorProfile(bool defaultValue) const { return (defaultValue ? KoColorSpaceRegistry::instance()->rgb8()->profile()->name() : m_cfg.readEntry("colorProfileDef", KoColorSpaceRegistry::instance()->rgb8()->profile()->name())); } void KisConfig::defColorProfile(const QString & profile) const { m_cfg.writeEntry("colorProfileDef", profile); } void KisConfig::defImageWidth(qint32 width) const { m_cfg.writeEntry("imageWidthDef", width); } void KisConfig::defImageHeight(qint32 height) const { m_cfg.writeEntry("imageHeightDef", height); } void KisConfig::defImageResolution(qreal res) const { m_cfg.writeEntry("imageResolutionDef", res*72.0); } int KisConfig::preferredVectorImportResolutionPPI(bool defaultValue) const { return defaultValue ? 100.0 : m_cfg.readEntry("preferredVectorImportResolution", 100.0); } void KisConfig::setPreferredVectorImportResolutionPPI(int value) const { m_cfg.writeEntry("preferredVectorImportResolution", value); } void cleanOldCursorStyleKeys(KConfigGroup &cfg) { if (cfg.hasKey("newCursorStyle") && cfg.hasKey("newOutlineStyle")) { cfg.deleteEntry("cursorStyleDef"); } } CursorStyle KisConfig::newCursorStyle(bool defaultValue) const { if (defaultValue) { return CURSOR_STYLE_NO_CURSOR; } int style = m_cfg.readEntry("newCursorStyle", int(-1)); if (style < 0) { // old style format style = m_cfg.readEntry("cursorStyleDef", int(OLD_CURSOR_STYLE_OUTLINE)); switch (style) { case OLD_CURSOR_STYLE_TOOLICON: style = CURSOR_STYLE_TOOLICON; break; case OLD_CURSOR_STYLE_CROSSHAIR: case OLD_CURSOR_STYLE_OUTLINE_CENTER_CROSS: style = CURSOR_STYLE_CROSSHAIR; break; case OLD_CURSOR_STYLE_POINTER: style = CURSOR_STYLE_POINTER; break; case OLD_CURSOR_STYLE_OUTLINE: case OLD_CURSOR_STYLE_NO_CURSOR: style = CURSOR_STYLE_NO_CURSOR; break; case OLD_CURSOR_STYLE_SMALL_ROUND: case OLD_CURSOR_STYLE_OUTLINE_CENTER_DOT: style = CURSOR_STYLE_SMALL_ROUND; break; case OLD_CURSOR_STYLE_TRIANGLE_RIGHTHANDED: case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_RIGHTHANDED: style = CURSOR_STYLE_TRIANGLE_RIGHTHANDED; break; case OLD_CURSOR_STYLE_TRIANGLE_LEFTHANDED: case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_LEFTHANDED: style = CURSOR_STYLE_TRIANGLE_LEFTHANDED; break; default: style = -1; } } cleanOldCursorStyleKeys(m_cfg); // compatibility with future versions if (style < 0 || style >= N_CURSOR_STYLE_SIZE) { style = CURSOR_STYLE_NO_CURSOR; } return (CursorStyle) style; } void KisConfig::setNewCursorStyle(CursorStyle style) { m_cfg.writeEntry("newCursorStyle", (int)style); } QColor KisConfig::getCursorMainColor(bool defaultValue) const { QColor col; col.setRgbF(0.501961, 1.0, 0.501961); return (defaultValue ? col : m_cfg.readEntry("cursorMaincColor", col)); } void KisConfig::setCursorMainColor(const QColor &v) const { m_cfg.writeEntry("cursorMaincColor", v); } OutlineStyle KisConfig::newOutlineStyle(bool defaultValue) const { if (defaultValue) { return OUTLINE_FULL; } int style = m_cfg.readEntry("newOutlineStyle", int(-1)); if (style < 0) { // old style format style = m_cfg.readEntry("cursorStyleDef", int(OLD_CURSOR_STYLE_OUTLINE)); switch (style) { case OLD_CURSOR_STYLE_TOOLICON: case OLD_CURSOR_STYLE_CROSSHAIR: case OLD_CURSOR_STYLE_POINTER: case OLD_CURSOR_STYLE_NO_CURSOR: case OLD_CURSOR_STYLE_SMALL_ROUND: case OLD_CURSOR_STYLE_TRIANGLE_RIGHTHANDED: case OLD_CURSOR_STYLE_TRIANGLE_LEFTHANDED: style = OUTLINE_NONE; break; case OLD_CURSOR_STYLE_OUTLINE: case OLD_CURSOR_STYLE_OUTLINE_CENTER_DOT: case OLD_CURSOR_STYLE_OUTLINE_CENTER_CROSS: case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_RIGHTHANDED: case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_LEFTHANDED: style = OUTLINE_FULL; break; default: style = -1; } } cleanOldCursorStyleKeys(m_cfg); // compatibility with future versions if (style < 0 || style >= N_OUTLINE_STYLE_SIZE) { style = OUTLINE_FULL; } return (OutlineStyle) style; } void KisConfig::setNewOutlineStyle(OutlineStyle style) { m_cfg.writeEntry("newOutlineStyle", (int)style); } QRect KisConfig::colorPreviewRect() const { return m_cfg.readEntry("colorPreviewRect", QVariant(QRect(32, 32, 48, 48))).toRect(); } void KisConfig::setColorPreviewRect(const QRect &rect) { m_cfg.writeEntry("colorPreviewRect", QVariant(rect)); } bool KisConfig::useDirtyPresets(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useDirtyPresets", true)); } void KisConfig::setUseDirtyPresets(bool value) { m_cfg.writeEntry("useDirtyPresets",value); KisConfigNotifier::instance()->notifyConfigChanged(); } bool KisConfig::useEraserBrushSize(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useEraserBrushSize", false)); } void KisConfig::setUseEraserBrushSize(bool value) { m_cfg.writeEntry("useEraserBrushSize",value); KisConfigNotifier::instance()->notifyConfigChanged(); } bool KisConfig::useEraserBrushOpacity(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useEraserBrushOpacity",false)); } void KisConfig::setUseEraserBrushOpacity(bool value) { m_cfg.writeEntry("useEraserBrushOpacity",value); KisConfigNotifier::instance()->notifyConfigChanged(); } QString KisConfig::getMDIBackgroundColor(bool defaultValue) const { QColor col(77, 77, 77); KoColor kol(KoColorSpaceRegistry::instance()->rgb8()); kol.fromQColor(col); QString xml = kol.toXML(); return (defaultValue ? xml : m_cfg.readEntry("mdiBackgroundColorXML", xml)); } void KisConfig::setMDIBackgroundColor(const QString &v) const { m_cfg.writeEntry("mdiBackgroundColorXML", v); } QString KisConfig::getMDIBackgroundImage(bool defaultValue) const { return (defaultValue ? "" : m_cfg.readEntry("mdiBackgroundImage", "")); } void KisConfig::setMDIBackgroundImage(const QString &filename) const { m_cfg.writeEntry("mdiBackgroundImage", filename); } QString KisConfig::monitorProfile(int screen) const { // Note: keep this in sync with the default profile for the RGB colorspaces! QString profile = m_cfg.readEntry("monitorProfile" + QString(screen == 0 ? "": QString("_%1").arg(screen)), "sRGB-elle-V2-srgbtrc.icc"); //dbgKrita << "KisConfig::monitorProfile()" << profile; return profile; } QString KisConfig::monitorForScreen(int screen, const QString &defaultMonitor, bool defaultValue) const { return (defaultValue ? defaultMonitor : m_cfg.readEntry(QString("monitor_for_screen_%1").arg(screen), defaultMonitor)); } void KisConfig::setMonitorForScreen(int screen, const QString& monitor) { m_cfg.writeEntry(QString("monitor_for_screen_%1").arg(screen), monitor); } void KisConfig::setMonitorProfile(int screen, const QString & monitorProfile, bool override) const { m_cfg.writeEntry("monitorProfile/OverrideX11", override); m_cfg.writeEntry("monitorProfile" + QString(screen == 0 ? "": QString("_%1").arg(screen)), monitorProfile); } const KoColorProfile *KisConfig::getScreenProfile(int screen) { if (screen < 0) return 0; KisConfig cfg(true); QString monitorId; if (KisColorManager::instance()->devices().size() > screen) { monitorId = cfg.monitorForScreen(screen, KisColorManager::instance()->devices()[screen]); } //dbgKrita << "getScreenProfile(). Screen" << screen << "monitor id" << monitorId; if (monitorId.isEmpty()) { return 0; } QByteArray bytes = KisColorManager::instance()->displayProfile(monitorId); //dbgKrita << "\tgetScreenProfile()" << bytes.size(); const KoColorProfile * profile = 0; if (bytes.length() > 0) { profile = KoColorSpaceRegistry::instance()->createColorProfile(RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), bytes); //dbgKrita << "\tKisConfig::getScreenProfile for screen" << screen << profile->name(); } return profile; } const KoColorProfile *KisConfig::displayProfile(int screen) const { if (screen < 0) return 0; // if the user plays with the settings, they can override the display profile, in which case // we don't want the system setting. bool override = useSystemMonitorProfile(); //dbgKrita << "KisConfig::displayProfile(). Override X11:" << override; const KoColorProfile *profile = 0; if (override) { //dbgKrita << "\tGoing to get the screen profile"; profile = KisConfig::getScreenProfile(screen); } // if it fails. check the configuration if (!profile || !profile->isSuitableForDisplay()) { //dbgKrita << "\tGoing to get the monitor profile"; QString monitorProfileName = monitorProfile(screen); //dbgKrita << "\t\tmonitorProfileName:" << monitorProfileName; if (!monitorProfileName.isEmpty()) { profile = KoColorSpaceRegistry::instance()->profileByName(monitorProfileName); } if (profile) { //dbgKrita << "\t\tsuitable for display" << profile->isSuitableForDisplay(); } else { //dbgKrita << "\t\tstill no profile"; } } // if we still don't have a profile, or the profile isn't suitable for display, // we need to get a last-resort profile. the built-in sRGB is a good choice then. if (!profile || !profile->isSuitableForDisplay()) { //dbgKrita << "\tnothing worked, going to get sRGB built-in"; profile = KoColorSpaceRegistry::instance()->profileByName("sRGB Built-in"); } if (profile) { //dbgKrita << "\tKisConfig::displayProfile for screen" << screen << "is" << profile->name(); } else { //dbgKrita << "\tCouldn't get a display profile at all"; } return profile; } QString KisConfig::workingColorSpace(bool defaultValue) const { return (defaultValue ? "RGBA" : m_cfg.readEntry("workingColorSpace", "RGBA")); } void KisConfig::setWorkingColorSpace(const QString & workingColorSpace) const { m_cfg.writeEntry("workingColorSpace", workingColorSpace); } QString KisConfig::printerColorSpace(bool /*defaultValue*/) const { //TODO currently only rgb8 is supported //return (defaultValue ? "RGBA" : m_cfg.readEntry("printerColorSpace", "RGBA")); return QString("RGBA"); } void KisConfig::setPrinterColorSpace(const QString & printerColorSpace) const { m_cfg.writeEntry("printerColorSpace", printerColorSpace); } QString KisConfig::printerProfile(bool defaultValue) const { return (defaultValue ? "" : m_cfg.readEntry("printerProfile", "")); } void KisConfig::setPrinterProfile(const QString & printerProfile) const { m_cfg.writeEntry("printerProfile", printerProfile); } bool KisConfig::useBlackPointCompensation(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("useBlackPointCompensation", true)); } void KisConfig::setUseBlackPointCompensation(bool useBlackPointCompensation) const { m_cfg.writeEntry("useBlackPointCompensation", useBlackPointCompensation); } bool KisConfig::allowLCMSOptimization(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("allowLCMSOptimization", true)); } void KisConfig::setAllowLCMSOptimization(bool allowLCMSOptimization) { m_cfg.writeEntry("allowLCMSOptimization", allowLCMSOptimization); } bool KisConfig::forcePaletteColors(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("colorsettings/forcepalettecolors", false)); } void KisConfig::setForcePaletteColors(bool forcePaletteColors) { m_cfg.writeEntry("colorsettings/forcepalettecolors", forcePaletteColors); } bool KisConfig::showRulers(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("showrulers", false)); } void KisConfig::setShowRulers(bool rulers) const { m_cfg.writeEntry("showrulers", rulers); } bool KisConfig::forceShowSaveMessages(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("forceShowSaveMessages", false)); } void KisConfig::setForceShowSaveMessages(bool value) const { m_cfg.writeEntry("forceShowSaveMessages", value); } bool KisConfig::forceShowAutosaveMessages(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("forceShowAutosaveMessages", false)); } void KisConfig::setForceShowAutosaveMessages(bool value) const { m_cfg.writeEntry("forceShowAutosaveMessages", value); } bool KisConfig::rulersTrackMouse(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("rulersTrackMouse", true)); } void KisConfig::setRulersTrackMouse(bool value) const { m_cfg.writeEntry("rulersTrackMouse", value); } qint32 KisConfig::pasteBehaviour(bool defaultValue) const { return (defaultValue ? 2 : m_cfg.readEntry("pasteBehaviour", 2)); } void KisConfig::setPasteBehaviour(qint32 renderIntent) const { m_cfg.writeEntry("pasteBehaviour", renderIntent); } qint32 KisConfig::monitorRenderIntent(bool defaultValue) const { qint32 intent = m_cfg.readEntry("renderIntent", INTENT_PERCEPTUAL); if (intent > 3) intent = 3; if (intent < 0) intent = 0; return (defaultValue ? INTENT_PERCEPTUAL : intent); } void KisConfig::setRenderIntent(qint32 renderIntent) const { if (renderIntent > 3) renderIntent = 3; if (renderIntent < 0) renderIntent = 0; m_cfg.writeEntry("renderIntent", renderIntent); } bool KisConfig::useOpenGL(bool defaultValue) const { if (defaultValue) { return true; } const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); return kritarc.value("OpenGLRenderer", "auto").toString() != "none"; } void KisConfig::disableOpenGL() const { const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); kritarc.setValue("OpenGLRenderer", "none"); } int KisConfig::openGLFilteringMode(bool defaultValue) const { return (defaultValue ? 3 : m_cfg.readEntry("OpenGLFilterMode", 3)); } void KisConfig::setOpenGLFilteringMode(int filteringMode) { m_cfg.writeEntry("OpenGLFilterMode", filteringMode); } bool KisConfig::useOpenGLTextureBuffer(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("useOpenGLTextureBuffer", true)); } void KisConfig::setUseOpenGLTextureBuffer(bool useBuffer) { m_cfg.writeEntry("useOpenGLTextureBuffer", useBuffer); } int KisConfig::openGLTextureSize(bool defaultValue) const { return (defaultValue ? 256 : m_cfg.readEntry("textureSize", 256)); } bool KisConfig::disableVSync(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("disableVSync", true)); } void KisConfig::setDisableVSync(bool disableVSync) { m_cfg.writeEntry("disableVSync", disableVSync); } bool KisConfig::showAdvancedOpenGLSettings(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("showAdvancedOpenGLSettings", false)); } bool KisConfig::forceOpenGLFenceWorkaround(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("forceOpenGLFenceWorkaround", false)); } int KisConfig::numMipmapLevels(bool defaultValue) const { return (defaultValue ? 4 : m_cfg.readEntry("numMipmapLevels", 4)); } int KisConfig::textureOverlapBorder() const { return 1 << qMax(0, numMipmapLevels()); } quint32 KisConfig::getGridMainStyle(bool defaultValue) const { int v = m_cfg.readEntry("gridmainstyle", 0); v = qBound(0, v, 2); return (defaultValue ? 0 : v); } void KisConfig::setGridMainStyle(quint32 v) const { m_cfg.writeEntry("gridmainstyle", v); } quint32 KisConfig::getGridSubdivisionStyle(bool defaultValue) const { quint32 v = m_cfg.readEntry("gridsubdivisionstyle", 1); if (v > 2) v = 2; return (defaultValue ? 1 : v); } void KisConfig::setGridSubdivisionStyle(quint32 v) const { m_cfg.writeEntry("gridsubdivisionstyle", v); } QColor KisConfig::getGridMainColor(bool defaultValue) const { QColor col(99, 99, 99); return (defaultValue ? col : m_cfg.readEntry("gridmaincolor", col)); } void KisConfig::setGridMainColor(const QColor & v) const { m_cfg.writeEntry("gridmaincolor", v); } QColor KisConfig::getGridSubdivisionColor(bool defaultValue) const { QColor col(150, 150, 150); return (defaultValue ? col : m_cfg.readEntry("gridsubdivisioncolor", col)); } void KisConfig::setGridSubdivisionColor(const QColor & v) const { m_cfg.writeEntry("gridsubdivisioncolor", v); } QColor KisConfig::getPixelGridColor(bool defaultValue) const { QColor col(255, 255, 255); return (defaultValue ? col : m_cfg.readEntry("pixelGridColor", col)); } void KisConfig::setPixelGridColor(const QColor & v) const { m_cfg.writeEntry("pixelGridColor", v); } qreal KisConfig::getPixelGridDrawingThreshold(bool defaultValue) const { qreal border = 24.0f; return (defaultValue ? border : m_cfg.readEntry("pixelGridDrawingThreshold", border)); } void KisConfig::setPixelGridDrawingThreshold(qreal v) const { m_cfg.writeEntry("pixelGridDrawingThreshold", v); } bool KisConfig::pixelGridEnabled(bool defaultValue) const { bool enabled = true; return (defaultValue ? enabled : m_cfg.readEntry("pixelGridEnabled", enabled)); } void KisConfig::enablePixelGrid(bool v) const { m_cfg.writeEntry("pixelGridEnabled", v); } quint32 KisConfig::guidesLineStyle(bool defaultValue) const { int v = m_cfg.readEntry("guidesLineStyle", 0); v = qBound(0, v, 2); return (defaultValue ? 0 : v); } void KisConfig::setGuidesLineStyle(quint32 v) const { m_cfg.writeEntry("guidesLineStyle", v); } QColor KisConfig::guidesColor(bool defaultValue) const { QColor col(99, 99, 99); return (defaultValue ? col : m_cfg.readEntry("guidesColor", col)); } void KisConfig::setGuidesColor(const QColor & v) const { m_cfg.writeEntry("guidesColor", v); } void KisConfig::loadSnapConfig(KisSnapConfig *config, bool defaultValue) const { KisSnapConfig defaultConfig(false); if (defaultValue) { *config = defaultConfig; return; } config->setOrthogonal(m_cfg.readEntry("globalSnapOrthogonal", defaultConfig.orthogonal())); config->setNode(m_cfg.readEntry("globalSnapNode", defaultConfig.node())); config->setExtension(m_cfg.readEntry("globalSnapExtension", defaultConfig.extension())); config->setIntersection(m_cfg.readEntry("globalSnapIntersection", defaultConfig.intersection())); config->setBoundingBox(m_cfg.readEntry("globalSnapBoundingBox", defaultConfig.boundingBox())); config->setImageBounds(m_cfg.readEntry("globalSnapImageBounds", defaultConfig.imageBounds())); config->setImageCenter(m_cfg.readEntry("globalSnapImageCenter", defaultConfig.imageCenter())); config->setToPixel(m_cfg.readEntry("globalSnapToPixel", defaultConfig.toPixel())); } void KisConfig::saveSnapConfig(const KisSnapConfig &config) { m_cfg.writeEntry("globalSnapOrthogonal", config.orthogonal()); m_cfg.writeEntry("globalSnapNode", config.node()); m_cfg.writeEntry("globalSnapExtension", config.extension()); m_cfg.writeEntry("globalSnapIntersection", config.intersection()); m_cfg.writeEntry("globalSnapBoundingBox", config.boundingBox()); m_cfg.writeEntry("globalSnapImageBounds", config.imageBounds()); m_cfg.writeEntry("globalSnapImageCenter", config.imageCenter()); m_cfg.writeEntry("globalSnapToPixel", config.toPixel()); } qint32 KisConfig::checkSize(bool defaultValue) const { qint32 size = (defaultValue ? 32 : m_cfg.readEntry("checksize", 32)); if (size == 0) size = 32; return size; } void KisConfig::setCheckSize(qint32 checksize) const { if (checksize == 0) { checksize = 32; } m_cfg.writeEntry("checksize", checksize); } bool KisConfig::scrollCheckers(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("scrollingcheckers", false)); } void KisConfig::setScrollingCheckers(bool sc) const { m_cfg.writeEntry("scrollingcheckers", sc); } QColor KisConfig::canvasBorderColor(bool defaultValue) const { QColor color(QColor(128,128,128)); return (defaultValue ? color : m_cfg.readEntry("canvasBorderColor", color)); } void KisConfig::setCanvasBorderColor(const QColor& color) const { m_cfg.writeEntry("canvasBorderColor", color); } bool KisConfig::hideScrollbars(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("hideScrollbars", false)); } void KisConfig::setHideScrollbars(bool value) const { m_cfg.writeEntry("hideScrollbars", value); } QColor KisConfig::checkersColor1(bool defaultValue) const { QColor col(220, 220, 220); return (defaultValue ? col : m_cfg.readEntry("checkerscolor", col)); } void KisConfig::setCheckersColor1(const QColor & v) const { m_cfg.writeEntry("checkerscolor", v); } QColor KisConfig::checkersColor2(bool defaultValue) const { return (defaultValue ? QColor(Qt::white) : m_cfg.readEntry("checkerscolor2", QColor(Qt::white))); } void KisConfig::setCheckersColor2(const QColor & v) const { m_cfg.writeEntry("checkerscolor2", v); } bool KisConfig::antialiasCurves(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("antialiascurves", true)); } void KisConfig::setAntialiasCurves(bool v) const { m_cfg.writeEntry("antialiascurves", v); } bool KisConfig::antialiasSelectionOutline(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("AntialiasSelectionOutline", false)); } void KisConfig::setAntialiasSelectionOutline(bool v) const { m_cfg.writeEntry("AntialiasSelectionOutline", v); } bool KisConfig::showRootLayer(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("ShowRootLayer", false)); } void KisConfig::setShowRootLayer(bool showRootLayer) const { m_cfg.writeEntry("ShowRootLayer", showRootLayer); } bool KisConfig::showGlobalSelection(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("ShowGlobalSelection", false)); } void KisConfig::setShowGlobalSelection(bool showGlobalSelection) const { m_cfg.writeEntry("ShowGlobalSelection", showGlobalSelection); } bool KisConfig::showOutlineWhilePainting(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("ShowOutlineWhilePainting", true)); } void KisConfig::setShowOutlineWhilePainting(bool showOutlineWhilePainting) const { m_cfg.writeEntry("ShowOutlineWhilePainting", showOutlineWhilePainting); } bool KisConfig::forceAlwaysFullSizedOutline(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("forceAlwaysFullSizedOutline", false)); } void KisConfig::setForceAlwaysFullSizedOutline(bool value) const { m_cfg.writeEntry("forceAlwaysFullSizedOutline", value); } KisConfig::SessionOnStartup KisConfig::sessionOnStartup(bool defaultValue) const { int value = defaultValue ? SOS_BlankSession : m_cfg.readEntry("sessionOnStartup", (int)SOS_BlankSession); return (KisConfig::SessionOnStartup)value; } void KisConfig::setSessionOnStartup(SessionOnStartup value) { m_cfg.writeEntry("sessionOnStartup", (int)value); } bool KisConfig::saveSessionOnQuit(bool defaultValue) const { return defaultValue ? false : m_cfg.readEntry("saveSessionOnQuit", false); } void KisConfig::setSaveSessionOnQuit(bool value) { m_cfg.writeEntry("saveSessionOnQuit", value); } qreal KisConfig::outlineSizeMinimum(bool defaultValue) const { return (defaultValue ? 1.0 : m_cfg.readEntry("OutlineSizeMinimum", 1.0)); } void KisConfig::setOutlineSizeMinimum(qreal outlineSizeMinimum) const { m_cfg.writeEntry("OutlineSizeMinimum", outlineSizeMinimum); } qreal KisConfig::selectionViewSizeMinimum(bool defaultValue) const { return (defaultValue ? 5.0 : m_cfg.readEntry("SelectionViewSizeMinimum", 5.0)); } void KisConfig::setSelectionViewSizeMinimum(qreal outlineSizeMinimum) const { m_cfg.writeEntry("SelectionViewSizeMinimum", outlineSizeMinimum); } int KisConfig::autoSaveInterval(bool defaultValue) const { return (defaultValue ? 15 * 60 : m_cfg.readEntry("AutoSaveInterval", 15 * 60)); } void KisConfig::setAutoSaveInterval(int seconds) const { return m_cfg.writeEntry("AutoSaveInterval", seconds); } bool KisConfig::backupFile(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("CreateBackupFile", true)); } void KisConfig::setBackupFile(bool backupFile) const { m_cfg.writeEntry("CreateBackupFile", backupFile); } bool KisConfig::showFilterGallery(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("showFilterGallery", false)); } void KisConfig::setShowFilterGallery(bool showFilterGallery) const { m_cfg.writeEntry("showFilterGallery", showFilterGallery); } bool KisConfig::showFilterGalleryLayerMaskDialog(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("showFilterGalleryLayerMaskDialog", true)); } void KisConfig::setShowFilterGalleryLayerMaskDialog(bool showFilterGallery) const { m_cfg.writeEntry("setShowFilterGalleryLayerMaskDialog", showFilterGallery); } QString KisConfig::canvasState(bool defaultValue) const { const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); return (defaultValue ? "OPENGL_NOT_TRIED" : kritarc.value("canvasState", "OPENGL_NOT_TRIED").toString()); } void KisConfig::setCanvasState(const QString& state) const { static QStringList acceptableStates; if (acceptableStates.isEmpty()) { acceptableStates << "OPENGL_SUCCESS" << "TRY_OPENGL" << "OPENGL_NOT_TRIED" << "OPENGL_FAILED"; } if (acceptableStates.contains(state)) { const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); kritarc.setValue("canvasState", state); } } bool KisConfig::toolOptionsPopupDetached(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("ToolOptionsPopupDetached", false)); } void KisConfig::setToolOptionsPopupDetached(bool detached) const { m_cfg.writeEntry("ToolOptionsPopupDetached", detached); } bool KisConfig::paintopPopupDetached(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("PaintopPopupDetached", false)); } void KisConfig::setPaintopPopupDetached(bool detached) const { m_cfg.writeEntry("PaintopPopupDetached", detached); } QString KisConfig::pressureTabletCurve(bool defaultValue) const { return (defaultValue ? "0,0;1,1" : m_cfg.readEntry("tabletPressureCurve","0,0;1,1;")); } void KisConfig::setPressureTabletCurve(const QString& curveString) const { m_cfg.writeEntry("tabletPressureCurve", curveString); } bool KisConfig::useWin8PointerInput(bool defaultValue) const { #ifdef Q_OS_WIN #ifdef USE_QT_TABLET_WINDOWS const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); return useWin8PointerInputNoApp(&kritarc, defaultValue); #else return (defaultValue ? false : m_cfg.readEntry("useWin8PointerInput", false)); #endif #else Q_UNUSED(defaultValue); return false; #endif } void KisConfig::setUseWin8PointerInput(bool value) { #ifdef Q_OS_WIN // Special handling: Only set value if changed // I don't want it to be set if the user hasn't touched it if (useWin8PointerInput() != value) { #ifdef USE_QT_TABLET_WINDOWS const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); setUseWin8PointerInputNoApp(&kritarc, value); #else m_cfg.writeEntry("useWin8PointerInput", value); #endif } #else Q_UNUSED(value) #endif } bool KisConfig::useWin8PointerInputNoApp(QSettings *settings, bool defaultValue) { return defaultValue ? false : settings->value("useWin8PointerInput", false).toBool(); } void KisConfig::setUseWin8PointerInputNoApp(QSettings *settings, bool value) { settings->setValue("useWin8PointerInput", value); } bool KisConfig::useRightMiddleTabletButtonWorkaround(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useRightMiddleTabletButtonWorkaround", false)); } void KisConfig::setUseRightMiddleTabletButtonWorkaround(bool value) { m_cfg.writeEntry("useRightMiddleTabletButtonWorkaround", value); } qreal KisConfig::vastScrolling(bool defaultValue) const { return (defaultValue ? 0.9 : m_cfg.readEntry("vastScrolling", 0.9)); } void KisConfig::setVastScrolling(const qreal factor) const { m_cfg.writeEntry("vastScrolling", factor); } int KisConfig::presetChooserViewMode(bool defaultValue) const { return (defaultValue ? 0 : m_cfg.readEntry("presetChooserViewMode", 0)); } void KisConfig::setPresetChooserViewMode(const int mode) const { m_cfg.writeEntry("presetChooserViewMode", mode); } int KisConfig::presetIconSize(bool defaultValue) const { return (defaultValue ? 60 : m_cfg.readEntry("presetIconSize", 60)); } void KisConfig::setPresetIconSize(const int value) const { m_cfg.writeEntry("presetIconSize", value); } bool KisConfig::firstRun(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("firstRun", true)); } void KisConfig::setFirstRun(const bool first) const { m_cfg.writeEntry("firstRun", first); } int KisConfig::horizontalSplitLines(bool defaultValue) const { return (defaultValue ? 1 : m_cfg.readEntry("horizontalSplitLines", 1)); } void KisConfig::setHorizontalSplitLines(const int numberLines) const { m_cfg.writeEntry("horizontalSplitLines", numberLines); } int KisConfig::verticalSplitLines(bool defaultValue) const { return (defaultValue ? 1 : m_cfg.readEntry("verticalSplitLines", 1)); } void KisConfig::setVerticalSplitLines(const int numberLines) const { m_cfg.writeEntry("verticalSplitLines", numberLines); } bool KisConfig::clicklessSpacePan(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("clicklessSpacePan", true)); } void KisConfig::setClicklessSpacePan(const bool toggle) const { m_cfg.writeEntry("clicklessSpacePan", toggle); } bool KisConfig::hideDockersFullscreen(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("hideDockersFullScreen", true)); } void KisConfig::setHideDockersFullscreen(const bool value) const { m_cfg.writeEntry("hideDockersFullScreen", value); } bool KisConfig::showDockers(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("showDockers", true)); } void KisConfig::setShowDockers(const bool value) const { m_cfg.writeEntry("showDockers", value); } bool KisConfig::showStatusBar(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("showStatusBar", true)); } void KisConfig::setShowStatusBar(const bool value) const { m_cfg.writeEntry("showStatusBar", value); } bool KisConfig::hideMenuFullscreen(bool defaultValue) const { return (defaultValue ? true: m_cfg.readEntry("hideMenuFullScreen", true)); } void KisConfig::setHideMenuFullscreen(const bool value) const { m_cfg.writeEntry("hideMenuFullScreen", value); } bool KisConfig::hideScrollbarsFullscreen(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("hideScrollbarsFullScreen", true)); } void KisConfig::setHideScrollbarsFullscreen(const bool value) const { m_cfg.writeEntry("hideScrollbarsFullScreen", value); } bool KisConfig::hideStatusbarFullscreen(bool defaultValue) const { return (defaultValue ? true: m_cfg.readEntry("hideStatusbarFullScreen", true)); } void KisConfig::setHideStatusbarFullscreen(const bool value) const { m_cfg.writeEntry("hideStatusbarFullScreen", value); } bool KisConfig::hideTitlebarFullscreen(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("hideTitleBarFullscreen", true)); } void KisConfig::setHideTitlebarFullscreen(const bool value) const { m_cfg.writeEntry("hideTitleBarFullscreen", value); } bool KisConfig::hideToolbarFullscreen(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("hideToolbarFullscreen", true)); } void KisConfig::setHideToolbarFullscreen(const bool value) const { m_cfg.writeEntry("hideToolbarFullscreen", value); } bool KisConfig::fullscreenMode(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("fullscreenMode", true)); } void KisConfig::setFullscreenMode(const bool value) const { m_cfg.writeEntry("fullscreenMode", value); } QStringList KisConfig::favoriteCompositeOps(bool defaultValue) const { return (defaultValue ? QStringList() : m_cfg.readEntry("favoriteCompositeOps", QString("normal,erase,multiply,burn,darken,add,dodge,screen,overlay,soft_light_svg,luminize,lighten,saturation,color,divide").split(','))); } void KisConfig::setFavoriteCompositeOps(const QStringList& compositeOps) const { m_cfg.writeEntry("favoriteCompositeOps", compositeOps); } QString KisConfig::exportConfigurationXML(const QString &filterId, bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("ExportConfiguration-" + filterId, QString())); } KisPropertiesConfigurationSP KisConfig::exportConfiguration(const QString &filterId, bool defaultValue) const { KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration(); const QString xmlData = exportConfigurationXML(filterId, defaultValue); cfg->fromXML(xmlData); return cfg; } void KisConfig::setExportConfiguration(const QString &filterId, KisPropertiesConfigurationSP properties) const { QString exportConfig = properties->toXML(); m_cfg.writeEntry("ExportConfiguration-" + filterId, exportConfig); } QString KisConfig::importConfiguration(const QString &filterId, bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("ImportConfiguration-" + filterId, QString())); } void KisConfig::setImportConfiguration(const QString &filterId, KisPropertiesConfigurationSP properties) const { QString importConfig = properties->toXML(); m_cfg.writeEntry("ImportConfiguration-" + filterId, importConfig); } bool KisConfig::useOcio(bool defaultValue) const { #ifdef HAVE_OCIO return (defaultValue ? false : m_cfg.readEntry("Krita/Ocio/UseOcio", false)); #else Q_UNUSED(defaultValue); return false; #endif } void KisConfig::setUseOcio(bool useOCIO) const { m_cfg.writeEntry("Krita/Ocio/UseOcio", useOCIO); } int KisConfig::favoritePresets(bool defaultValue) const { return (defaultValue ? 10 : m_cfg.readEntry("numFavoritePresets", 10)); } void KisConfig::setFavoritePresets(const int value) { m_cfg.writeEntry("numFavoritePresets", value); } bool KisConfig::levelOfDetailEnabled(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("levelOfDetailEnabled", false)); } void KisConfig::setLevelOfDetailEnabled(bool value) { m_cfg.writeEntry("levelOfDetailEnabled", value); } KisOcioConfiguration KisConfig::ocioConfiguration(bool defaultValue) const { KisOcioConfiguration cfg; if (!defaultValue) { cfg.mode = (KisOcioConfiguration::Mode)m_cfg.readEntry("Krita/Ocio/OcioColorManagementMode", 0); cfg.configurationPath = m_cfg.readEntry("Krita/Ocio/OcioConfigPath", QString()); cfg.lutPath = m_cfg.readEntry("Krita/Ocio/OcioLutPath", QString()); cfg.inputColorSpace = m_cfg.readEntry("Krita/Ocio/InputColorSpace", QString()); cfg.displayDevice = m_cfg.readEntry("Krita/Ocio/DisplayDevice", QString()); cfg.displayView = m_cfg.readEntry("Krita/Ocio/DisplayView", QString()); cfg.look = m_cfg.readEntry("Krita/Ocio/DisplayLook", QString()); } return cfg; } void KisConfig::setOcioConfiguration(const KisOcioConfiguration &cfg) { m_cfg.writeEntry("Krita/Ocio/OcioColorManagementMode", (int) cfg.mode); m_cfg.writeEntry("Krita/Ocio/OcioConfigPath", cfg.configurationPath); m_cfg.writeEntry("Krita/Ocio/OcioLutPath", cfg.lutPath); m_cfg.writeEntry("Krita/Ocio/InputColorSpace", cfg.inputColorSpace); m_cfg.writeEntry("Krita/Ocio/DisplayDevice", cfg.displayDevice); m_cfg.writeEntry("Krita/Ocio/DisplayView", cfg.displayView); m_cfg.writeEntry("Krita/Ocio/DisplayLook", cfg.look); } KisConfig::OcioColorManagementMode KisConfig::ocioColorManagementMode(bool defaultValue) const { // FIXME: this option duplicates ocioConfiguration(), please deprecate it return (OcioColorManagementMode)(defaultValue ? INTERNAL : m_cfg.readEntry("Krita/Ocio/OcioColorManagementMode", (int) INTERNAL)); } void KisConfig::setOcioColorManagementMode(OcioColorManagementMode mode) const { // FIXME: this option duplicates ocioConfiguration(), please deprecate it m_cfg.writeEntry("Krita/Ocio/OcioColorManagementMode", (int) mode); } int KisConfig::ocioLutEdgeSize(bool defaultValue) const { return (defaultValue ? 64 : m_cfg.readEntry("Krita/Ocio/LutEdgeSize", 64)); } void KisConfig::setOcioLutEdgeSize(int value) { m_cfg.writeEntry("Krita/Ocio/LutEdgeSize", value); } bool KisConfig::ocioLockColorVisualRepresentation(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("Krita/Ocio/OcioLockColorVisualRepresentation", false)); } void KisConfig::setOcioLockColorVisualRepresentation(bool value) { m_cfg.writeEntry("Krita/Ocio/OcioLockColorVisualRepresentation", value); } QString KisConfig::defaultPalette(bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("defaultPalette", "Default")); } void KisConfig::setDefaultPalette(const QString& name) const { m_cfg.writeEntry("defaultPalette", name); } QString KisConfig::toolbarSlider(int sliderNumber, bool defaultValue) const { QString def = "flow"; if (sliderNumber == 1) { def = "opacity"; } if (sliderNumber == 2) { def = "size"; } return (defaultValue ? def : m_cfg.readEntry(QString("toolbarslider_%1").arg(sliderNumber), def)); } void KisConfig::setToolbarSlider(int sliderNumber, const QString &slider) { m_cfg.writeEntry(QString("toolbarslider_%1").arg(sliderNumber), slider); } int KisConfig::layerThumbnailSize(bool defaultValue) const { return (defaultValue ? 20 : m_cfg.readEntry("layerThumbnailSize", 20)); } void KisConfig::setLayerThumbnailSize(int size) { m_cfg.writeEntry("layerThumbnailSize", size); } bool KisConfig::sliderLabels(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("sliderLabels", true)); } void KisConfig::setSliderLabels(bool enabled) { m_cfg.writeEntry("sliderLabels", enabled); } QString KisConfig::currentInputProfile(bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("currentInputProfile", QString())); } void KisConfig::setCurrentInputProfile(const QString& name) { m_cfg.writeEntry("currentInputProfile", name); } bool KisConfig::useSystemMonitorProfile(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("ColorManagement/UseSystemMonitorProfile", false)); } void KisConfig::setUseSystemMonitorProfile(bool _useSystemMonitorProfile) const { m_cfg.writeEntry("ColorManagement/UseSystemMonitorProfile", _useSystemMonitorProfile); } bool KisConfig::presetStripVisible(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("presetStripVisible", true)); } void KisConfig::setPresetStripVisible(bool visible) { m_cfg.writeEntry("presetStripVisible", visible); } bool KisConfig::scratchpadVisible(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("scratchpadVisible", true)); } void KisConfig::setScratchpadVisible(bool visible) { m_cfg.writeEntry("scratchpadVisible", visible); } bool KisConfig::showSingleChannelAsColor(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("showSingleChannelAsColor", false)); } void KisConfig::setShowSingleChannelAsColor(bool asColor) { m_cfg.writeEntry("showSingleChannelAsColor", asColor); } bool KisConfig::hidePopups(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("hidePopups", false)); } void KisConfig::setHidePopups(bool hidepopups) { m_cfg.writeEntry("hidePopups", hidepopups); } int KisConfig::numDefaultLayers(bool defaultValue) const { return (defaultValue ? 2 : m_cfg.readEntry("NumberOfLayersForNewImage", 2)); } void KisConfig::setNumDefaultLayers(int num) { m_cfg.writeEntry("NumberOfLayersForNewImage", num); } quint8 KisConfig::defaultBackgroundOpacity(bool defaultValue) const { return (defaultValue ? (int)OPACITY_OPAQUE_U8 : m_cfg.readEntry("BackgroundOpacityForNewImage", (int)OPACITY_OPAQUE_U8)); } void KisConfig::setDefaultBackgroundOpacity(quint8 value) { m_cfg.writeEntry("BackgroundOpacityForNewImage", (int)value); } QColor KisConfig::defaultBackgroundColor(bool defaultValue) const { return (defaultValue ? QColor(Qt::white) : m_cfg.readEntry("BackgroundColorForNewImage", QColor(Qt::white))); } void KisConfig::setDefaultBackgroundColor(const QColor &value) { m_cfg.writeEntry("BackgroundColorForNewImage", value); } KisConfig::BackgroundStyle KisConfig::defaultBackgroundStyle(bool defaultValue) const { return (KisConfig::BackgroundStyle)(defaultValue ? RASTER_LAYER : m_cfg.readEntry("BackgroundStyleForNewImage", (int)RASTER_LAYER)); } void KisConfig::setDefaultBackgroundStyle(KisConfig::BackgroundStyle value) { m_cfg.writeEntry("BackgroundStyleForNewImage", (int)value); } int KisConfig::lineSmoothingType(bool defaultValue) const { return (defaultValue ? 1 : m_cfg.readEntry("LineSmoothingType", 1)); } void KisConfig::setLineSmoothingType(int value) { m_cfg.writeEntry("LineSmoothingType", value); } qreal KisConfig::lineSmoothingDistance(bool defaultValue) const { return (defaultValue ? 50.0 : m_cfg.readEntry("LineSmoothingDistance", 50.0)); } void KisConfig::setLineSmoothingDistance(qreal value) { m_cfg.writeEntry("LineSmoothingDistance", value); } qreal KisConfig::lineSmoothingTailAggressiveness(bool defaultValue) const { return (defaultValue ? 0.15 : m_cfg.readEntry("LineSmoothingTailAggressiveness", 0.15)); } void KisConfig::setLineSmoothingTailAggressiveness(qreal value) { m_cfg.writeEntry("LineSmoothingTailAggressiveness", value); } bool KisConfig::lineSmoothingSmoothPressure(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("LineSmoothingSmoothPressure", false)); } void KisConfig::setLineSmoothingSmoothPressure(bool value) { m_cfg.writeEntry("LineSmoothingSmoothPressure", value); } bool KisConfig::lineSmoothingScalableDistance(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("LineSmoothingScalableDistance", true)); } void KisConfig::setLineSmoothingScalableDistance(bool value) { m_cfg.writeEntry("LineSmoothingScalableDistance", value); } qreal KisConfig::lineSmoothingDelayDistance(bool defaultValue) const { return (defaultValue ? 50.0 : m_cfg.readEntry("LineSmoothingDelayDistance", 50.0)); } void KisConfig::setLineSmoothingDelayDistance(qreal value) { m_cfg.writeEntry("LineSmoothingDelayDistance", value); } bool KisConfig::lineSmoothingUseDelayDistance(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("LineSmoothingUseDelayDistance", true)); } void KisConfig::setLineSmoothingUseDelayDistance(bool value) { m_cfg.writeEntry("LineSmoothingUseDelayDistance", value); } bool KisConfig::lineSmoothingFinishStabilizedCurve(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("LineSmoothingFinishStabilizedCurve", true)); } void KisConfig::setLineSmoothingFinishStabilizedCurve(bool value) { m_cfg.writeEntry("LineSmoothingFinishStabilizedCurve", value); } bool KisConfig::lineSmoothingStabilizeSensors(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("LineSmoothingStabilizeSensors", true)); } void KisConfig::setLineSmoothingStabilizeSensors(bool value) { m_cfg.writeEntry("LineSmoothingStabilizeSensors", value); } int KisConfig::tabletEventsDelay(bool defaultValue) const { return (defaultValue ? 10 : m_cfg.readEntry("tabletEventsDelay", 10)); } void KisConfig::setTabletEventsDelay(int value) { m_cfg.writeEntry("tabletEventsDelay", value); } bool KisConfig::trackTabletEventLatency(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("trackTabletEventLatency", false)); } void KisConfig::setTrackTabletEventLatency(bool value) { m_cfg.writeEntry("trackTabletEventLatency", value); } bool KisConfig::testingAcceptCompressedTabletEvents(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("testingAcceptCompressedTabletEvents", false)); } void KisConfig::setTestingAcceptCompressedTabletEvents(bool value) { m_cfg.writeEntry("testingAcceptCompressedTabletEvents", value); } bool KisConfig::shouldEatDriverShortcuts(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("shouldEatDriverShortcuts", false)); } bool KisConfig::testingCompressBrushEvents(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("testingCompressBrushEvents", false)); } void KisConfig::setTestingCompressBrushEvents(bool value) { m_cfg.writeEntry("testingCompressBrushEvents", value); } int KisConfig::workaroundX11SmoothPressureSteps(bool defaultValue) const { return (defaultValue ? 0 : m_cfg.readEntry("workaroundX11SmoothPressureSteps", 0)); } bool KisConfig::showCanvasMessages(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("showOnCanvasMessages", true)); } void KisConfig::setShowCanvasMessages(bool show) { m_cfg.writeEntry("showOnCanvasMessages", show); } bool KisConfig::compressKra(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("compressLayersInKra", false)); } void KisConfig::setCompressKra(bool compress) { m_cfg.writeEntry("compressLayersInKra", compress); } bool KisConfig::toolOptionsInDocker(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("ToolOptionsInDocker", true)); } void KisConfig::setToolOptionsInDocker(bool inDocker) { m_cfg.writeEntry("ToolOptionsInDocker", inDocker); } bool KisConfig::kineticScrollingEnabled(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("KineticScrollingEnabled", true)); } void KisConfig::setKineticScrollingEnabled(bool value) { m_cfg.writeEntry("KineticScrollingEnabled", value); } int KisConfig::kineticScrollingGesture(bool defaultValue) const { return (defaultValue ? 2 : m_cfg.readEntry("KineticScrollingGesture", 2)); } void KisConfig::setKineticScrollingGesture(int gesture) { m_cfg.writeEntry("KineticScrollingGesture", gesture); } int KisConfig::kineticScrollingSensitivity(bool defaultValue) const { return (defaultValue ? 75 : m_cfg.readEntry("KineticScrollingSensitivity", 75)); } void KisConfig::setKineticScrollingSensitivity(int sensitivity) { m_cfg.writeEntry("KineticScrollingSensitivity", sensitivity); } bool KisConfig::kineticScrollingHiddenScrollbars(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("KineticScrollingHideScrollbar", false)); } void KisConfig::setKineticScrollingHideScrollbars(bool scrollbar) { m_cfg.writeEntry("KineticScrollingHideScrollbar", scrollbar); } const KoColorSpace* KisConfig::customColorSelectorColorSpace(bool defaultValue) const { const KoColorSpace *cs = 0; KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); if (defaultValue || cfg.readEntry("useCustomColorSpace", true)) { KoColorSpaceRegistry* csr = KoColorSpaceRegistry::instance(); QString modelID = cfg.readEntry("customColorSpaceModel", "RGBA"); QString depthID = cfg.readEntry("customColorSpaceDepthID", "U8"); QString profile = cfg.readEntry("customColorSpaceProfile", "sRGB built-in - (lcms internal)"); if (profile == "default") { // qDebug() << "Falling back to default color profile."; profile = "sRGB built-in - (lcms internal)"; } cs = csr->colorSpace(modelID, depthID, profile); } return cs; } void KisConfig::setCustomColorSelectorColorSpace(const KoColorSpace *cs) { KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); cfg.writeEntry("useCustomColorSpace", bool(cs)); if(cs) { cfg.writeEntry("customColorSpaceModel", cs->colorModelId().id()); cfg.writeEntry("customColorSpaceDepthID", cs->colorDepthId().id()); cfg.writeEntry("customColorSpaceProfile", cs->profile()->name()); } KisConfigNotifier::instance()->notifyConfigChanged(); } bool KisConfig::enableOpenGLFramerateLogging(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("enableOpenGLFramerateLogging", false)); } void KisConfig::setEnableOpenGLFramerateLogging(bool value) const { m_cfg.writeEntry("enableOpenGLFramerateLogging", value); } bool KisConfig::enableBrushSpeedLogging(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("enableBrushSpeedLogging", false)); } void KisConfig::setEnableBrushSpeedLogging(bool value) const { m_cfg.writeEntry("enableBrushSpeedLogging", value); } void KisConfig::setEnableAmdVectorizationWorkaround(bool value) { m_cfg.writeEntry("amdDisableVectorWorkaround", value); } bool KisConfig::enableAmdVectorizationWorkaround(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("amdDisableVectorWorkaround", false)); } void KisConfig::setDisableAVXOptimizations(bool value) { m_cfg.writeEntry("disableAVXOptimizations", value); } bool KisConfig::disableAVXOptimizations(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("disableAVXOptimizations", false)); } void KisConfig::setAnimationDropFrames(bool value) { bool oldValue = animationDropFrames(); if (value == oldValue) return; m_cfg.writeEntry("animationDropFrames", value); KisConfigNotifier::instance()->notifyDropFramesModeChanged(); } bool KisConfig::animationDropFrames(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("animationDropFrames", true)); } int KisConfig::scrubbingUpdatesDelay(bool defaultValue) const { return (defaultValue ? 30 : m_cfg.readEntry("scrubbingUpdatesDelay", 30)); } void KisConfig::setScrubbingUpdatesDelay(int value) { m_cfg.writeEntry("scrubbingUpdatesDelay", value); } int KisConfig::scrubbingAudioUpdatesDelay(bool defaultValue) const { return (defaultValue ? -1 : m_cfg.readEntry("scrubbingAudioUpdatesDelay", -1)); } void KisConfig::setScrubbingAudioUpdatesDelay(int value) { m_cfg.writeEntry("scrubbingAudioUpdatesDelay", value); } int KisConfig::audioOffsetTolerance(bool defaultValue) const { return (defaultValue ? -1 : m_cfg.readEntry("audioOffsetTolerance", -1)); } void KisConfig::setAudioOffsetTolerance(int value) { m_cfg.writeEntry("audioOffsetTolerance", value); } bool KisConfig::switchSelectionCtrlAlt(bool defaultValue) const { return defaultValue ? false : m_cfg.readEntry("switchSelectionCtrlAlt", false); } void KisConfig::setSwitchSelectionCtrlAlt(bool value) { m_cfg.writeEntry("switchSelectionCtrlAlt", value); KisConfigNotifier::instance()->notifyConfigChanged(); } bool KisConfig::convertToImageColorspaceOnImport(bool defaultValue) const { return defaultValue ? false : m_cfg.readEntry("ConvertToImageColorSpaceOnImport", false); } void KisConfig::setConvertToImageColorspaceOnImport(bool value) { m_cfg.writeEntry("ConvertToImageColorSpaceOnImport", value); } int KisConfig::stabilizerSampleSize(bool defaultValue) const { #ifdef Q_OS_WIN const int defaultSampleSize = 50; #else const int defaultSampleSize = 15; #endif return defaultValue ? defaultSampleSize : m_cfg.readEntry("stabilizerSampleSize", defaultSampleSize); } void KisConfig::setStabilizerSampleSize(int value) { m_cfg.writeEntry("stabilizerSampleSize", value); } bool KisConfig::stabilizerDelayedPaint(bool defaultValue) const { const bool defaultEnabled = true; return defaultValue ? defaultEnabled : m_cfg.readEntry("stabilizerDelayedPaint", defaultEnabled); } void KisConfig::setStabilizerDelayedPaint(bool value) { m_cfg.writeEntry("stabilizerDelayedPaint", value); } bool KisConfig::showBrushHud(bool defaultValue) const { return defaultValue ? false : m_cfg.readEntry("showBrushHud", false); } void KisConfig::setShowBrushHud(bool value) { m_cfg.writeEntry("showBrushHud", value); } QString KisConfig::brushHudSetting(bool defaultValue) const { QString defaultDoc = "\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n"; return defaultValue ? defaultDoc : m_cfg.readEntry("brushHudSettings", defaultDoc); } void KisConfig::setBrushHudSetting(const QString &value) const { m_cfg.writeEntry("brushHudSettings", value); } bool KisConfig::calculateAnimationCacheInBackground(bool defaultValue) const { return defaultValue ? true : m_cfg.readEntry("calculateAnimationCacheInBackground", true); } void KisConfig::setCalculateAnimationCacheInBackground(bool value) { m_cfg.writeEntry("calculateAnimationCacheInBackground", value); } QColor KisConfig::defaultAssistantsColor(bool defaultValue) const { static const QColor defaultColor = QColor(176, 176, 176, 255); return defaultValue ? defaultColor : m_cfg.readEntry("defaultAssistantsColor", defaultColor); } void KisConfig::setDefaultAssistantsColor(const QColor &color) const { m_cfg.writeEntry("defaultAssistantsColor", color); } bool KisConfig::autoSmoothBezierCurves(bool defaultValue) const { return defaultValue ? false : m_cfg.readEntry("autoSmoothBezierCurves", false); } void KisConfig::setAutoSmoothBezierCurves(bool value) { m_cfg.writeEntry("autoSmoothBezierCurves", value); } bool KisConfig::activateTransformToolAfterPaste(bool defaultValue) const { return defaultValue ? false : m_cfg.readEntry("activateTransformToolAfterPaste", false); } void KisConfig::setActivateTransformToolAfterPaste(bool value) { m_cfg.writeEntry("activateTransformToolAfterPaste", value); } KisConfig::RootSurfaceFormat KisConfig::rootSurfaceFormat(bool defaultValue) const { const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); return rootSurfaceFormat(&kritarc, defaultValue); } void KisConfig::setRootSurfaceFormat(KisConfig::RootSurfaceFormat value) { const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); setRootSurfaceFormat(&kritarc, value); } KisConfig::RootSurfaceFormat KisConfig::rootSurfaceFormat(QSettings *displayrc, bool defaultValue) { QString textValue = "bt709-g22"; if (!defaultValue) { textValue = displayrc->value("rootSurfaceFormat", textValue).toString(); } return textValue == "bt709-g10" ? BT709_G10 : textValue == "bt2020-pq" ? BT2020_PQ : BT709_G22; } void KisConfig::setRootSurfaceFormat(QSettings *displayrc, KisConfig::RootSurfaceFormat value) { const QString textValue = value == BT709_G10 ? "bt709-g10" : value == BT2020_PQ ? "bt2020-pq" : "bt709-g22"; displayrc->setValue("rootSurfaceFormat", textValue); } bool KisConfig::useZip64(bool defaultValue) const { return defaultValue ? false : m_cfg.readEntry("UseZip64", false); } void KisConfig::setUseZip64(bool value) { m_cfg.writeEntry("UseZip64", value); } bool KisConfig::convertLayerColorSpaceInProperties(bool defaultValue) const { return defaultValue ? true : m_cfg.readEntry("convertLayerColorSpaceInProperties", true); } void KisConfig::setConvertLayerColorSpaceInProperties(bool value) { m_cfg.writeEntry("convertLayerColorSpaceInProperties", value); } #include #include void KisConfig::writeKoColor(const QString& name, const KoColor& color) const { QDomDocument doc = QDomDocument(name); QDomElement el = doc.createElement(name); doc.appendChild(el); color.toXML(doc, el); m_cfg.writeEntry(name, doc.toString()); } //ported from kispropertiesconfig. KoColor KisConfig::readKoColor(const QString& name, const KoColor& _color) const { QDomDocument doc; KoColor color = _color; if (!m_cfg.readEntry(name).isNull()) { doc.setContent(m_cfg.readEntry(name)); QDomElement e = doc.documentElement().firstChild().toElement(); color = KoColor::fromXML(e, Integer16BitsColorDepthID.id()); } else { QString blackColor = "\n\n \n\n"; doc.setContent(blackColor); QDomElement e = doc.documentElement().firstChild().toElement(); color = KoColor::fromXML(e, Integer16BitsColorDepthID.id()); } return color; } diff --git a/libs/ui/opengl/kis_opengl.cpp b/libs/ui/opengl/kis_opengl.cpp index bcfa3a7c9c..32171dc3c1 100644 --- a/libs/ui/opengl/kis_opengl.cpp +++ b/libs/ui/opengl/kis_opengl.cpp @@ -1,913 +1,913 @@ /* * Copyright (c) 2007 Adrian Page * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "opengl/kis_opengl.h" #include "opengl/kis_opengl_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisOpenGLModeProber.h" #include #include #include "kis_assert.h" #include #include #include #ifndef GL_RENDERER # define GL_RENDERER 0x1F01 #endif using namespace KisOpenGLPrivate; namespace { // config option, set manually by main() bool g_isDebugSynchronous = false; bool g_sanityDefaultFormatIsSet = false; boost::optional openGLCheckResult; bool g_needsFenceWorkaround = false; bool g_needsPixmapCacheWorkaround = false; QString g_surfaceFormatDetectionLog; QString g_debugText("OpenGL Info\n **OpenGL not initialized**"); QVector g_openglWarningStrings; KisOpenGL::OpenGLRenderers g_supportedRenderers; KisOpenGL::OpenGLRenderer g_rendererPreferredByQt; void overrideSupportedRenderers(KisOpenGL::OpenGLRenderers supportedRenderers, KisOpenGL::OpenGLRenderer preferredByQt) { g_supportedRenderers = supportedRenderers; g_rendererPreferredByQt = preferredByQt; } void openglOnMessageLogged(const QOpenGLDebugMessage& debugMessage) { qDebug() << "OpenGL:" << debugMessage; } KisOpenGL::OpenGLRenderer getRendererFromProbeResult(KisOpenGLModeProber::Result info) { KisOpenGL::OpenGLRenderer result = KisOpenGL::RendererDesktopGL; if (info.isOpenGLES()) { const QString rendererString = info.rendererString().toLower(); if (rendererString.contains("basic render driver") || rendererString.contains("software")) { result = KisOpenGL::RendererSoftware; } else { result = KisOpenGL::RendererOpenGLES; } } return result; } } KisOpenGLPrivate::OpenGLCheckResult::OpenGLCheckResult(QOpenGLContext &context) { if (!context.isValid()) { return; } QOpenGLFunctions *funcs = context.functions(); // funcs is ready to be used m_rendererString = QString(reinterpret_cast(funcs->glGetString(GL_RENDERER))); m_driverVersionString = QString(reinterpret_cast(funcs->glGetString(GL_VERSION))); m_glMajorVersion = context.format().majorVersion(); m_glMinorVersion = context.format().minorVersion(); m_supportsDeprecatedFunctions = (context.format().options() & QSurfaceFormat::DeprecatedFunctions); m_isOpenGLES = context.isOpenGLES(); } void KisOpenGLPrivate::appendOpenGLWarningString(KLocalizedString warning) { g_openglWarningStrings << warning; } void KisOpenGLPrivate::overrideOpenGLWarningString(QVector warnings) { g_openglWarningStrings = warnings; } void KisOpenGL::initialize() { if (openGLCheckResult) return; KIS_SAFE_ASSERT_RECOVER_NOOP(g_sanityDefaultFormatIsSet); KisOpenGL::RendererConfig config; config.format = QSurfaceFormat::defaultFormat(); openGLCheckResult = KisOpenGLModeProber::instance()->probeFormat(config, false); g_debugText.clear(); QDebug debugOut(&g_debugText); debugOut << "OpenGL Info\n"; if (openGLCheckResult) { debugOut << "\n Vendor: " << openGLCheckResult->vendorString(); debugOut << "\n Renderer: " << openGLCheckResult->rendererString(); debugOut << "\n Version: " << openGLCheckResult->driverVersionString(); debugOut << "\n Shading language: " << openGLCheckResult->shadingLanguageString(); debugOut << "\n Requested format: " << QSurfaceFormat::defaultFormat(); debugOut << "\n Current format: " << openGLCheckResult->format(); debugOut.nospace(); debugOut << "\n Version: " << openGLCheckResult->glMajorVersion() << "." << openGLCheckResult->glMinorVersion(); debugOut.resetFormat(); debugOut << "\n Supports deprecated functions" << openGLCheckResult->supportsDeprecatedFunctions(); debugOut << "\n is OpenGL ES:" << openGLCheckResult->isOpenGLES(); } debugOut << "\n\nQPA OpenGL Detection Info"; debugOut << "\n supportsDesktopGL:" << bool(g_supportedRenderers & RendererDesktopGL); #ifdef Q_OS_WIN debugOut << "\n supportsAngleD3D11:" << bool(g_supportedRenderers & RendererOpenGLES); debugOut << "\n isQtPreferAngle:" << bool(g_rendererPreferredByQt == RendererOpenGLES); #else debugOut << "\n supportsOpenGLES:" << bool(g_supportedRenderers & RendererOpenGLES); debugOut << "\n isQtPreferOpenGLES:" << bool(g_rendererPreferredByQt == RendererOpenGLES); #endif // debugOut << "\n== log ==\n"; // debugOut.noquote(); // debugOut << g_surfaceFormatDetectionLog; // debugOut.resetFormat(); // debugOut << "\n== end log =="; dbgOpenGL.noquote().nospace() << g_debugText; - KisUsageLogger::write(g_debugText); + KisUsageLogger::writeSysInfo(g_debugText); if (!openGLCheckResult) { return; } // Check if we have a bugged driver that needs fence workaround bool isOnX11 = false; #ifdef HAVE_X11 isOnX11 = true; #endif KisConfig cfg(true); if ((isOnX11 && openGLCheckResult->rendererString().startsWith("AMD")) || cfg.forceOpenGLFenceWorkaround()) { g_needsFenceWorkaround = true; } /** * NVidia + Qt's openGL don't play well together and one cannot * draw a pixmap on a widget more than once in one rendering cycle. * * It can be workarounded by drawing strictly via QPixmapCache and * only when the pixmap size in bigger than doubled size of the * display framebuffer. That is for 8-bit HD display, you should have * a cache bigger than 16 MiB. Don't ask me why. (DK) * * See bug: https://bugs.kde.org/show_bug.cgi?id=361709 * * TODO: check if this workaround is still needed after merging * Qt5+openGL3 branch. */ if (openGLCheckResult->vendorString().toUpper().contains("NVIDIA")) { g_needsPixmapCacheWorkaround = true; const QRect screenSize = QGuiApplication::primaryScreen()->availableGeometry(); const int minCacheSize = 20 * 1024; const int cacheSize = 2048 + 2 * 4 * screenSize.width() * screenSize.height() / 1024; //KiB QPixmapCache::setCacheLimit(qMax(minCacheSize, cacheSize)); } } void KisOpenGL::initializeContext(QOpenGLContext *ctx) { KisConfig cfg(true); initialize(); const bool isDebugEnabled = ctx->format().testOption(QSurfaceFormat::DebugContext); dbgUI << "OpenGL: Opening new context"; if (isDebugEnabled) { // Passing ctx for ownership management only, not specifying context. // QOpenGLDebugLogger only function on the current active context. // FIXME: Do we need to make sure ctx is the active context? QOpenGLDebugLogger* openglLogger = new QOpenGLDebugLogger(ctx); if (openglLogger->initialize()) { qDebug() << "QOpenGLDebugLogger is initialized. Check whether you get a message below."; QObject::connect(openglLogger, &QOpenGLDebugLogger::messageLogged, &openglOnMessageLogged); openglLogger->startLogging(g_isDebugSynchronous ? QOpenGLDebugLogger::SynchronousLogging : QOpenGLDebugLogger::AsynchronousLogging); openglLogger->logMessage(QOpenGLDebugMessage::createApplicationMessage(QStringLiteral("QOpenGLDebugLogger is logging."))); } else { qDebug() << "QOpenGLDebugLogger cannot be initialized."; delete openglLogger; } } // Double check we were given the version we requested QSurfaceFormat format = ctx->format(); QOpenGLFunctions *f = ctx->functions(); f->initializeOpenGLFunctions(); QFile log(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/krita-opengl.txt"); log.open(QFile::WriteOnly); QString vendor((const char*)f->glGetString(GL_VENDOR)); log.write(vendor.toLatin1()); log.write(", "); log.write(openGLCheckResult->rendererString().toLatin1()); log.write(", "); QString version((const char*)f->glGetString(GL_VERSION)); log.write(version.toLatin1()); log.close(); } const QString &KisOpenGL::getDebugText() { initialize(); return g_debugText; } QStringList KisOpenGL::getOpenGLWarnings() { QStringList strings; Q_FOREACH (const KLocalizedString &item, g_openglWarningStrings) { strings << item.toString(); } return strings; } // XXX Temporary function to allow LoD on OpenGL3 without triggering // all of the other 3.2 functionality, can be removed once we move to Qt5.7 bool KisOpenGL::supportsLoD() { initialize(); return openGLCheckResult && openGLCheckResult->supportsLoD(); } bool KisOpenGL::hasOpenGL3() { initialize(); return openGLCheckResult && openGLCheckResult->hasOpenGL3(); } bool KisOpenGL::hasOpenGLES() { initialize(); return openGLCheckResult && openGLCheckResult->isOpenGLES(); } bool KisOpenGL::supportsFenceSync() { initialize(); return openGLCheckResult && openGLCheckResult->supportsFenceSync(); } bool KisOpenGL::needsFenceWorkaround() { initialize(); return g_needsFenceWorkaround; } bool KisOpenGL::needsPixmapCacheWorkaround() { initialize(); return g_needsPixmapCacheWorkaround; } void KisOpenGL::testingInitializeDefaultSurfaceFormat() { setDefaultSurfaceConfig(selectSurfaceConfig(KisOpenGL::RendererAuto, KisConfig::BT709_G22, false)); } void KisOpenGL::setDebugSynchronous(bool value) { g_isDebugSynchronous = value; } KisOpenGL::OpenGLRenderer KisOpenGL::getCurrentOpenGLRenderer() { if (!openGLCheckResult) return RendererAuto; return getRendererFromProbeResult(*openGLCheckResult); } KisOpenGL::OpenGLRenderer KisOpenGL::getQtPreferredOpenGLRenderer() { return g_rendererPreferredByQt; } KisOpenGL::OpenGLRenderers KisOpenGL::getSupportedOpenGLRenderers() { return g_supportedRenderers; } KisOpenGL::OpenGLRenderer KisOpenGL::getUserPreferredOpenGLRendererConfig() { const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); return convertConfigToOpenGLRenderer(kritarc.value("OpenGLRenderer", "auto").toString()); } void KisOpenGL::setUserPreferredOpenGLRendererConfig(KisOpenGL::OpenGLRenderer renderer) { const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); kritarc.setValue("OpenGLRenderer", KisOpenGL::convertOpenGLRendererToConfig(renderer)); } QString KisOpenGL::convertOpenGLRendererToConfig(KisOpenGL::OpenGLRenderer renderer) { switch (renderer) { case RendererNone: return QStringLiteral("none"); case RendererSoftware: return QStringLiteral("software"); case RendererDesktopGL: return QStringLiteral("desktop"); case RendererOpenGLES: return QStringLiteral("angle"); default: return QStringLiteral("auto"); } } KisOpenGL::OpenGLRenderer KisOpenGL::convertConfigToOpenGLRenderer(QString renderer) { if (renderer == "desktop") { return RendererDesktopGL; } else if (renderer == "angle") { return RendererOpenGLES; } else if (renderer == "software") { return RendererSoftware; } else if (renderer == "none") { return RendererNone; } else { return RendererAuto; } } KisOpenGL::OpenGLRenderer KisOpenGL::RendererConfig::rendererId() const { KisOpenGL::OpenGLRenderer result = RendererAuto; if (format.renderableType() == QSurfaceFormat::OpenGLES && angleRenderer == AngleRendererD3d11Warp) { result = RendererSoftware; } else if (format.renderableType() == QSurfaceFormat::OpenGLES && angleRenderer == AngleRendererD3d11) { result = RendererOpenGLES; } else if (format.renderableType() == QSurfaceFormat::OpenGL) { result = RendererDesktopGL; } else if (format.renderableType() == QSurfaceFormat::DefaultRenderableType && angleRenderer == AngleRendererD3d11) { // noop } else { qWarning() << "WARNING: unsupported combination of OpenGL renderer" << ppVar(format.renderableType()) << ppVar(angleRenderer); } return result; } namespace { typedef std::pair RendererInfo; RendererInfo getRendererInfo(KisOpenGL::OpenGLRenderer renderer) { RendererInfo info = {QSurfaceFormat::DefaultRenderableType, KisOpenGL::AngleRendererD3d11}; switch (renderer) { case KisOpenGL::RendererNone: info = {QSurfaceFormat::DefaultRenderableType, KisOpenGL::AngleRendererDefault}; break; case KisOpenGL::RendererAuto: break; case KisOpenGL::RendererDesktopGL: info = {QSurfaceFormat::OpenGL, KisOpenGL::AngleRendererD3d11}; break; case KisOpenGL::RendererOpenGLES: info = {QSurfaceFormat::OpenGLES, KisOpenGL::AngleRendererD3d11}; break; case KisOpenGL::RendererSoftware: info = {QSurfaceFormat::OpenGLES, KisOpenGL::AngleRendererD3d11Warp}; break; } return info; } KisOpenGL::RendererConfig generateSurfaceConfig(KisOpenGL::OpenGLRenderer renderer, KisConfig::RootSurfaceFormat rootSurfaceFormat, bool debugContext) { RendererInfo info = getRendererInfo(renderer); KisOpenGL::RendererConfig config; config.angleRenderer = info.second; QSurfaceFormat &format = config.format; #ifdef Q_OS_MACOS format.setVersion(3, 2); format.setProfile(QSurfaceFormat::CoreProfile); #elif !defined(Q_OS_ANDROID) // XXX This can be removed once we move to Qt5.7 format.setVersion(3, 0); format.setProfile(QSurfaceFormat::CompatibilityProfile); format.setOptions(QSurfaceFormat::DeprecatedFunctions); #endif format.setDepthBufferSize(24); format.setStencilBufferSize(8); KisOpenGLModeProber::initSurfaceFormatFromConfig(rootSurfaceFormat, &format); format.setRenderableType(info.first); format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); format.setSwapInterval(0); // Disable vertical refresh syncing if (debugContext) { format.setOption(QSurfaceFormat::DebugContext, true); } return config; } bool isOpenGLRendererBlacklisted(const QString &rendererString, const QString &driverVersionString, QVector *warningMessage) { bool isBlacklisted = false; #ifndef Q_OS_WIN Q_UNUSED(rendererString); Q_UNUSED(driverVersionString); Q_UNUSED(warningMessage); #else // Special blacklisting of OpenGL/ANGLE is tracked on: // https://phabricator.kde.org/T7411 // HACK: Specifically detect for Intel driver build number // See https://www.intel.com/content/www/us/en/support/articles/000005654/graphics-drivers.html if (rendererString.startsWith("Intel")) { KLocalizedString knownBadIntelWarning = ki18n("The Intel graphics driver in use is known to have issues with OpenGL."); KLocalizedString grossIntelWarning = ki18n( "Intel graphics drivers tend to have issues with OpenGL so ANGLE will be used by default. " "You may manually switch to OpenGL but it is not guaranteed to work properly." ); QRegularExpression regex("\\b\\d{1,2}\\.\\d{2}\\.\\d{1,3}\\.(\\d{4})\\b"); QRegularExpressionMatch match = regex.match(driverVersionString); if (match.hasMatch()) { int driverBuild = match.captured(1).toInt(); if (driverBuild > 4636 && driverBuild < 4729) { // Make ANGLE the preferred renderer for Intel driver versions // between build 4636 and 4729 (exclusive) due to an UI offset bug. // See https://communities.intel.com/thread/116003 // (Build 4636 is known to work from some test results) qDebug() << "Detected Intel driver build between 4636 and 4729, making ANGLE the preferred renderer"; isBlacklisted = true; *warningMessage << knownBadIntelWarning; } else if (driverBuild == 4358) { // There are several reports on a bug where the canvas is not being // updated properly which has debug info pointing to this build. qDebug() << "Detected Intel driver build 4358, making ANGLE the preferred renderer"; isBlacklisted = true; *warningMessage << knownBadIntelWarning; } else { // Intel tends to randomly break OpenGL in some of their new driver // builds, therefore we just shouldn't use OpenGL by default to // reduce bug report noises. qDebug() << "Detected Intel driver, making ANGLE the preferred renderer"; isBlacklisted = true; *warningMessage << grossIntelWarning; } } else { // In case Intel changed the driver version format to something that // we don't understand, we still select ANGLE. qDebug() << "Detected Intel driver with unknown version format, making ANGLE the preferred renderer"; isBlacklisted = true; *warningMessage << grossIntelWarning; } } #endif return isBlacklisted; } boost::optional orderPreference(bool lhs, bool rhs) { if (lhs == rhs) return boost::none; if (lhs && !rhs) return true; if (!lhs && rhs) return false; return false; } #define ORDER_BY(lhs, rhs) if (auto res = orderPreference((lhs), (rhs))) { return *res; } class FormatPositionLess { public: FormatPositionLess() { } bool operator()(const KisOpenGL::RendererConfig &lhs, const KisOpenGL::RendererConfig &rhs) const { KIS_SAFE_ASSERT_RECOVER_NOOP(m_preferredColorSpace != KisSurfaceColorSpace::DefaultColorSpace); if (m_preferredRendererByUser != KisOpenGL::RendererSoftware) { ORDER_BY(!isFallbackOnly(lhs.rendererId()), !isFallbackOnly(rhs.rendererId())); } #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) ORDER_BY(isPreferredColorSpace(lhs.format.colorSpace()), isPreferredColorSpace(rhs.format.colorSpace())); #endif if (doPreferHDR()) { ORDER_BY(isHDRFormat(lhs.format), isHDRFormat(rhs.format)); } else { ORDER_BY(!isHDRFormat(lhs.format), !isHDRFormat(rhs.format)); } if (m_preferredRendererByUser != KisOpenGL::RendererAuto) { ORDER_BY(lhs.rendererId() == m_preferredRendererByUser, rhs.rendererId() == m_preferredRendererByUser); } ORDER_BY(!isBlacklisted(lhs.rendererId()), !isBlacklisted(rhs.rendererId())); if (doPreferHDR() && m_preferredRendererByHDR != KisOpenGL::RendererAuto) { ORDER_BY(lhs.rendererId() == m_preferredRendererByHDR, rhs.rendererId() == m_preferredRendererByHDR); } KIS_SAFE_ASSERT_RECOVER_NOOP(m_preferredRendererByQt != KisOpenGL::RendererAuto); ORDER_BY(lhs.rendererId() == m_preferredRendererByQt, rhs.rendererId() == m_preferredRendererByQt); return false; } public: void setPreferredColorSpace(const KisSurfaceColorSpace &preferredColorSpace) { m_preferredColorSpace = preferredColorSpace; } void setPreferredRendererByQt(const KisOpenGL::OpenGLRenderer &preferredRendererByQt) { m_preferredRendererByQt = preferredRendererByQt; } void setPreferredRendererByUser(const KisOpenGL::OpenGLRenderer &preferredRendererByUser) { m_preferredRendererByUser = preferredRendererByUser; } void setPreferredRendererByHDR(const KisOpenGL::OpenGLRenderer &preferredRendererByHDR) { m_preferredRendererByHDR = preferredRendererByHDR; } void setOpenGLBlacklisted(bool openGLBlacklisted) { m_openGLBlacklisted = openGLBlacklisted; } void setOpenGLESBlacklisted(bool openGLESBlacklisted) { m_openGLESBlacklisted = openGLESBlacklisted; } bool isOpenGLBlacklisted() const { return m_openGLBlacklisted; } bool isOpenGLESBlacklisted() const { return m_openGLESBlacklisted; } KisSurfaceColorSpace preferredColorSpace() const { return m_preferredColorSpace; } KisOpenGL::OpenGLRenderer preferredRendererByUser() const { return m_preferredRendererByUser; } private: bool isHDRFormat(const QSurfaceFormat &f) const { #ifdef HAVE_HDR return f.colorSpace() == KisSurfaceColorSpace::bt2020PQColorSpace || f.colorSpace() == KisSurfaceColorSpace::scRGBColorSpace; #else Q_UNUSED(f); return false; #endif } bool isFallbackOnly(KisOpenGL::OpenGLRenderer r) const { return r == KisOpenGL::RendererSoftware; } bool isBlacklisted(KisOpenGL::OpenGLRenderer r) const { KIS_SAFE_ASSERT_RECOVER_NOOP(r == KisOpenGL::RendererAuto || r == KisOpenGL::RendererDesktopGL || r == KisOpenGL::RendererOpenGLES || r == KisOpenGL::RendererSoftware || r == KisOpenGL::RendererNone); return (r == KisOpenGL::RendererDesktopGL && m_openGLBlacklisted) || (r == KisOpenGL::RendererOpenGLES && m_openGLESBlacklisted) || (r == KisOpenGL::RendererSoftware && m_openGLESBlacklisted); } bool doPreferHDR() const { #ifdef HAVE_HDR return m_preferredColorSpace == KisSurfaceColorSpace::bt2020PQColorSpace || m_preferredColorSpace == KisSurfaceColorSpace::scRGBColorSpace; #else return false; #endif } bool isPreferredColorSpace(const KisSurfaceColorSpace cs) const { return KisOpenGLModeProber::fuzzyCompareColorSpaces(m_preferredColorSpace, cs); return false; } private: KisSurfaceColorSpace m_preferredColorSpace = KisSurfaceColorSpace::DefaultColorSpace; KisOpenGL::OpenGLRenderer m_preferredRendererByQt = KisOpenGL::RendererDesktopGL; KisOpenGL::OpenGLRenderer m_preferredRendererByUser = KisOpenGL::RendererAuto; KisOpenGL::OpenGLRenderer m_preferredRendererByHDR = KisOpenGL::RendererAuto; bool m_openGLBlacklisted = false; bool m_openGLESBlacklisted = false; }; struct DetectionDebug : public QDebug { DetectionDebug(QString *string) : QDebug(string), m_string(string), m_originalSize(string->size()) {} ~DetectionDebug() { dbgOpenGL << m_string->right(m_string->size() - m_originalSize); *this << endl; } QString *m_string; int m_originalSize; }; } #define dbgDetection() DetectionDebug(&g_surfaceFormatDetectionLog) KisOpenGL::RendererConfig KisOpenGL::selectSurfaceConfig(KisOpenGL::OpenGLRenderer preferredRenderer, KisConfig::RootSurfaceFormat preferredRootSurfaceFormat, bool enableDebug) { QVector warningMessages; using Info = boost::optional; QHash renderersToTest; renderersToTest.insert(RendererDesktopGL, Info()); renderersToTest.insert(RendererOpenGLES, Info()); #ifdef Q_OS_WIN renderersToTest.insert(RendererSoftware, Info()); #endif #ifdef HAVE_HDR QVector formatSymbols({KisConfig::BT709_G22, KisConfig::BT709_G10, KisConfig::BT2020_PQ}); #else QVector formatSymbols({KisConfig::BT709_G22}); #endif KisOpenGL::RendererConfig defaultConfig = generateSurfaceConfig(KisOpenGL::RendererAuto, KisConfig::BT709_G22, false); Info info = KisOpenGLModeProber::instance()->probeFormat(defaultConfig); #ifdef Q_OS_WIN if (!info) { // try software rasterizer (WARP) defaultConfig = generateSurfaceConfig(KisOpenGL::RendererSoftware, KisConfig::BT709_G22, false); info = KisOpenGLModeProber::instance()->probeFormat(defaultConfig); if (!info) { renderersToTest.remove(RendererSoftware); } } #endif if (!info) return KisOpenGL::RendererConfig(); const OpenGLRenderer defaultRenderer = getRendererFromProbeResult(*info); OpenGLRenderers supportedRenderers = RendererNone; FormatPositionLess compareOp; compareOp.setPreferredRendererByQt(defaultRenderer); #ifdef HAVE_HDR compareOp.setPreferredColorSpace( preferredRootSurfaceFormat == KisConfig::BT709_G22 ? KisSurfaceColorSpace::sRGBColorSpace : preferredRootSurfaceFormat == KisConfig::BT709_G10 ? KisSurfaceColorSpace::scRGBColorSpace : KisSurfaceColorSpace::bt2020PQColorSpace); #else Q_UNUSED(preferredRootSurfaceFormat); compareOp.setPreferredColorSpace(KisSurfaceColorSpace::sRGBColorSpace); #endif #ifdef Q_OS_WIN compareOp.setPreferredRendererByHDR(KisOpenGL::RendererOpenGLES); #endif compareOp.setPreferredRendererByUser(preferredRenderer); compareOp.setOpenGLESBlacklisted(false); // We cannot blacklist ES drivers atm renderersToTest[defaultRenderer] = info; for (auto it = renderersToTest.begin(); it != renderersToTest.end(); ++it) { Info info = it.value(); if (!info) { info = KisOpenGLModeProber::instance()-> probeFormat(generateSurfaceConfig(it.key(), KisConfig::BT709_G22, false)); *it = info; } compareOp.setOpenGLBlacklisted( !info || isOpenGLRendererBlacklisted(info->rendererString(), info->driverVersionString(), &warningMessages)); if (info && info->isSupportedVersion()) { supportedRenderers |= it.key(); } } OpenGLRenderer preferredByQt = defaultRenderer; if (preferredByQt == RendererDesktopGL && supportedRenderers & RendererDesktopGL && compareOp.isOpenGLBlacklisted()) { preferredByQt = RendererOpenGLES; } else if (preferredByQt == RendererOpenGLES && supportedRenderers & RendererOpenGLES && compareOp.isOpenGLESBlacklisted()) { preferredByQt = RendererDesktopGL; } QVector preferredConfigs; for (auto it = renderersToTest.begin(); it != renderersToTest.end(); ++it) { // if default mode of the renderer doesn't work, then custom won't either if (!it.value()) continue; Q_FOREACH (const KisConfig::RootSurfaceFormat formatSymbol, formatSymbols) { preferredConfigs << generateSurfaceConfig(it.key(), formatSymbol, enableDebug); } } std::stable_sort(preferredConfigs.begin(), preferredConfigs.end(), compareOp); dbgDetection() << "Supported renderers:" << supportedRenderers; dbgDetection() << "Surface format preference list:"; Q_FOREACH (const KisOpenGL::RendererConfig &config, preferredConfigs) { dbgDetection() << "*" << config.format; dbgDetection() << " " << config.rendererId(); } KisOpenGL::RendererConfig resultConfig = defaultConfig; if (preferredRenderer != RendererNone) { Q_FOREACH (const KisOpenGL::RendererConfig &config, preferredConfigs) { #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) dbgDetection() <<"Probing format..." << config.format.colorSpace() << config.rendererId(); #else dbgDetection() <<"Probing format..." << config.rendererId(); #endif Info info = KisOpenGLModeProber::instance()->probeFormat(config); if (info && info->isSupportedVersion()) { #ifdef Q_OS_WIN // HACK: Block ANGLE with Direct3D9 // Direct3D9 does not give OpenGL ES 3.0 // Some versions of ANGLE returns OpenGL version 3.0 incorrectly if (info->isUsingAngle() && info->rendererString().contains("Direct3D9", Qt::CaseInsensitive)) { dbgDetection() << "Skipping Direct3D 9 Angle implementation, it shouldn't have happened."; continue; } #endif dbgDetection() << "Found format:" << config.format; dbgDetection() << " " << config.rendererId(); resultConfig = config; break; } } { const bool colorSpaceIsCorrect = #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) KisOpenGLModeProber::fuzzyCompareColorSpaces(compareOp.preferredColorSpace(), resultConfig.format.colorSpace()); #else true; #endif const bool rendererIsCorrect = compareOp.preferredRendererByUser() == KisOpenGL::RendererAuto || compareOp.preferredRendererByUser() == resultConfig.rendererId(); if (!rendererIsCorrect && colorSpaceIsCorrect) { warningMessages << ki18n("Preferred renderer doesn't support requested surface format. Another renderer has been selected."); } else if (!colorSpaceIsCorrect) { warningMessages << ki18n("Preferred output format is not supported by available renderers"); } } } else { resultConfig.format = QSurfaceFormat(); resultConfig.angleRenderer = AngleRendererDefault; } overrideSupportedRenderers(supportedRenderers, preferredByQt); overrideOpenGLWarningString(warningMessages); return resultConfig; } void KisOpenGL::setDefaultSurfaceConfig(const KisOpenGL::RendererConfig &config) { KIS_SAFE_ASSERT_RECOVER_NOOP(!g_sanityDefaultFormatIsSet); g_sanityDefaultFormatIsSet = true; QSurfaceFormat::setDefaultFormat(config.format); #ifdef Q_OS_WIN // Force ANGLE to use Direct3D11. D3D9 doesn't support OpenGL ES 3 and WARP // might get weird crashes atm. qputenv("QT_ANGLE_PLATFORM", KisOpenGLModeProber::angleRendererToString(config.angleRenderer).toLatin1()); #endif if (config.format.renderableType() == QSurfaceFormat::OpenGLES) { QCoreApplication::setAttribute(Qt::AA_UseOpenGLES, true); } else if (config.format.renderableType() == QSurfaceFormat::OpenGL) { QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL, true); } } bool KisOpenGL::hasOpenGL() { return openGLCheckResult->isSupportedVersion(); } diff --git a/libs/ui/osx.mm b/libs/ui/osx.mm index bf2bc26d10..ebf2206d44 100644 --- a/libs/ui/osx.mm +++ b/libs/ui/osx.mm @@ -1,32 +1,32 @@ /* * Copyright (c) 2017 Bernhard Liebl * * 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. */ -#import +#import extern "C" { bool isMouseCoalescingEnabled(); void setMouseCoalescingEnabled(bool enabled); } bool isMouseCoalescingEnabled() { return NSEvent.isMouseCoalescingEnabled; } void setMouseCoalescingEnabled(bool enabled) { NSEvent.mouseCoalescingEnabled = enabled; } diff --git a/libs/ui/qtsingleapplication/qtlocalpeer.cpp b/libs/ui/qtsingleapplication/qtlocalpeer.cpp index 76761e28dd..d6eeae213c 100644 --- a/libs/ui/qtsingleapplication/qtlocalpeer.cpp +++ b/libs/ui/qtsingleapplication/qtlocalpeer.cpp @@ -1,180 +1,180 @@ /**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and -** conditions see http://www.qt.io/licensing. For further information -** use the contact form at http://www.qt.io/contact-us. +** conditions see https://www.qt.io/licensing. For further information +** use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ #include "qtlocalpeer.h" #include #include #include #if defined(Q_OS_WIN) #include #include typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*); static PProcessIdToSessionId pProcessIdToSessionId = 0; #endif #if defined(Q_OS_UNIX) #include #include #endif static const char ack[] = "ack"; QString QtLocalPeer::appSessionId(const QString &appId) { QByteArray idc = appId.toUtf8(); quint16 idNum = qChecksum(idc.constData(), idc.size()); //### could do: two 16bit checksums over separate halves of id, for a 32bit result - improved uniqeness probability. Every-other-char split would be best. QString res = QLatin1String("qtsingleapplication-") + QString::number(idNum, 16); #if defined(Q_OS_WIN) if (!pProcessIdToSessionId) { QLibrary lib(QLatin1String("kernel32")); pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId"); } if (pProcessIdToSessionId) { DWORD sessionId = 0; pProcessIdToSessionId(GetCurrentProcessId(), &sessionId); res += QLatin1Char('-') + QString::number(sessionId, 16); } #else res += QLatin1Char('-') + QString::number(::getuid(), 16); #endif return res; } QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId) : QObject(parent), id(appId) { if (id.isEmpty()) id = QCoreApplication::applicationFilePath(); //### On win, check if this returns .../argv[0] without casefolding; .\MYAPP == .\myapp on Win socketName = appSessionId(id); server = new QLocalServer(this); QString lockName = QDir(QDir::tempPath()).absolutePath() + QLatin1Char('/') + socketName + QLatin1String("-lockfile"); lockFile.setFileName(lockName); lockFile.open(QIODevice::ReadWrite); } bool QtLocalPeer::isClient() { if (lockFile.isLocked()) return false; if (!lockFile.lock(QtLockedFile::WriteLock, false)) return true; if (!QLocalServer::removeServer(socketName)) qWarning("QtSingleCoreApplication: could not cleanup socket"); bool res = server->listen(socketName); if (!res) qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString())); QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection())); return false; } bool QtLocalPeer::sendMessage(const QByteArray &message, int timeout, bool block) { if (!isClient()) return false; QLocalSocket socket; bool connOk = false; for (int i = 0; i < 2; i++) { // Try twice, in case the other instance is just starting up socket.connectToServer(socketName); connOk = socket.waitForConnected(timeout/2); if (connOk || i) break; int ms = 250; #if defined(Q_OS_WIN) Sleep(DWORD(ms)); #else struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; nanosleep(&ts, 0); #endif } if (!connOk) return false; QByteArray uMsg(message); QDataStream ds(&socket); ds.writeBytes(uMsg.constData(), uMsg.size()); bool res = socket.waitForBytesWritten(timeout); res &= socket.waitForReadyRead(timeout); // wait for ack res &= (socket.read(qstrlen(ack)) == ack); if (block) // block until peer disconnects socket.waitForDisconnected(-1); return res; } void QtLocalPeer::receiveConnection() { QLocalSocket* socket = server->nextPendingConnection(); if (!socket) return; // Why doesn't Qt have a blocking stream that takes care of this shait??? while (socket->bytesAvailable() < static_cast(sizeof(quint32))) { if (!socket->isValid()) // stale request return; socket->waitForReadyRead(1000); } QDataStream ds(socket); QByteArray uMsg; quint32 remaining; ds >> remaining; uMsg.resize(remaining); int got = 0; char* uMsgBuf = uMsg.data(); //dbgKrita << "RCV: remaining" << remaining; do { got = ds.readRawData(uMsgBuf, remaining); remaining -= got; uMsgBuf += got; //qDebug() << "RCV: got" << got << "remaining" << remaining; } while (remaining && got >= 0 && socket->waitForReadyRead(2000)); //### error check: got<0 if (got < 0) { qWarning() << "QtLocalPeer: Message reception failed" << socket->errorString(); delete socket; return; } // ### async this socket->write(ack, qstrlen(ack)); socket->waitForBytesWritten(1000); emit messageReceived(uMsg, socket); // ##(might take a long time to return) } diff --git a/libs/ui/qtsingleapplication/qtlocalpeer.h b/libs/ui/qtsingleapplication/qtlocalpeer.h index 5a48afc37e..d103781c96 100644 --- a/libs/ui/qtsingleapplication/qtlocalpeer.h +++ b/libs/ui/qtsingleapplication/qtlocalpeer.h @@ -1,62 +1,62 @@ /**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and -** conditions see http://www.qt.io/licensing. For further information -** use the contact form at http://www.qt.io/contact-us. +** conditions see https://www.qt.io/licensing. For further information +** use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ #include #include #include #include class QtLocalPeer : public QObject { Q_OBJECT public: explicit QtLocalPeer(QObject *parent = 0, const QString &appId = QString()); bool isClient(); bool sendMessage(const QByteArray &message, int timeout, bool block); QString applicationId() const { return id; } static QString appSessionId(const QString &appId); Q_SIGNALS: void messageReceived(const QByteArray &message, QObject *socket); protected Q_SLOTS: void receiveConnection(); protected: QString id; QString socketName; QLocalServer* server; QtLockedFile lockFile; }; diff --git a/libs/ui/qtsingleapplication/qtsingleapplication.cpp b/libs/ui/qtsingleapplication/qtsingleapplication.cpp index 6345b807fb..c0be620928 100644 --- a/libs/ui/qtsingleapplication/qtsingleapplication.cpp +++ b/libs/ui/qtsingleapplication/qtsingleapplication.cpp @@ -1,192 +1,192 @@ /**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and -** conditions see http://www.qt.io/licensing. For further information -** use the contact form at http://www.qt.io/contact-us. +** conditions see https://www.qt.io/licensing. For further information +** use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ #include "qtsingleapplication.h" #include "qtlocalpeer.h" #include #include #include #include #include static const int instancesSize = 1024; static QString instancesLockFilename(const QString &appSessionId) { const QChar slash(QLatin1Char('/')); QString res = QDir::tempPath(); if (!res.endsWith(slash)) res += slash; return res + appSessionId + QLatin1String("-instances"); } QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv) : QApplication(argc, argv), firstPeer(-1), pidPeer(0) { this->appId = appId; const QString appSessionId = QtLocalPeer::appSessionId(appId); // This shared memory holds a zero-terminated array of active (or crashed) instances instances = new QSharedMemory(appSessionId, this); actWin = 0; block = false; // First instance creates the shared memory, later instances attach to it const bool created = instances->create(instancesSize); if (!created) { if (!instances->attach()) { qWarning() << "Failed to initialize instances shared memory: " << instances->errorString(); delete instances; instances = 0; return; } } // QtLockedFile is used to workaround QTBUG-10364 QtLockedFile lockfile(instancesLockFilename(appSessionId)); lockfile.open(QtLockedFile::ReadWrite); lockfile.lock(QtLockedFile::WriteLock); qint64 *pids = static_cast(instances->data()); if (!created) { // Find the first instance that it still running // The whole list needs to be iterated in order to append to it for (; *pids; ++pids) { if (firstPeer == -1 && isRunning(*pids)) firstPeer = *pids; } } // Add current pid to list and terminate it *pids++ = QCoreApplication::applicationPid(); *pids = 0; pidPeer = new QtLocalPeer(this, appId + QLatin1Char('-') + QString::number(QCoreApplication::applicationPid())); connect(pidPeer, SIGNAL(messageReceived(QByteArray,QObject*)), SIGNAL(messageReceived(QByteArray,QObject*))); pidPeer->isClient(); lockfile.unlock(); } QtSingleApplication::~QtSingleApplication() { if (!instances) return; const qint64 appPid = QCoreApplication::applicationPid(); QtLockedFile lockfile(instancesLockFilename(QtLocalPeer::appSessionId(appId))); lockfile.open(QtLockedFile::ReadWrite); lockfile.lock(QtLockedFile::WriteLock); // Rewrite array, removing current pid and previously crashed ones qint64 *pids = static_cast(instances->data()); qint64 *newpids = pids; for (; *pids; ++pids) { if (*pids != appPid && isRunning(*pids)) *newpids++ = *pids; } *newpids = 0; lockfile.unlock(); } bool QtSingleApplication::event(QEvent *event) { if (event->type() == QEvent::FileOpen) { QFileOpenEvent *foe = static_cast(event); emit fileOpenRequest(foe->file()); return true; } return QApplication::event(event); } bool QtSingleApplication::isRunning(qint64 pid) { if (pid == -1) { pid = firstPeer; if (pid == -1) { return false; } } QtLocalPeer peer(this, appId + QLatin1Char('-') + QString::number(pid, 10)); return peer.isClient(); } bool QtSingleApplication::sendMessage(const QByteArray &message, int timeout, qint64 pid) { if (pid == -1) { pid = firstPeer; if (pid == -1) return false; } QtLocalPeer peer(this, appId + QLatin1Char('-') + QString::number(pid, 10)); return peer.sendMessage(message, timeout, block); } QString QtSingleApplication::applicationId() const { return appId; } void QtSingleApplication::setBlock(bool value) { block = value; } void QtSingleApplication::setActivationWindow(QWidget *aw, bool activateOnMessage) { actWin = aw; if (!pidPeer) return; if (activateOnMessage) connect(pidPeer, SIGNAL(messageReceived(QByteArray,QObject*)), this, SLOT(activateWindow())); else disconnect(pidPeer, SIGNAL(messageReceived(QByteArray,QObject*)), this, SLOT(activateWindow())); } QWidget* QtSingleApplication::activationWindow() const { return actWin; } void QtSingleApplication::activateWindow() { if (actWin) { actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized); actWin->raise(); actWin->activateWindow(); } } diff --git a/libs/ui/qtsingleapplication/qtsingleapplication.h b/libs/ui/qtsingleapplication/qtsingleapplication.h index 54261424f0..6fb39e15d0 100644 --- a/libs/ui/qtsingleapplication/qtsingleapplication.h +++ b/libs/ui/qtsingleapplication/qtsingleapplication.h @@ -1,79 +1,79 @@ /**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and -** conditions see http://www.qt.io/licensing. For further information -** use the contact form at http://www.qt.io/contact-us. +** conditions see https://www.qt.io/licensing. For further information +** use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ #ifndef QTSINGLEAPPLICATION #define QTSINGLEAPPLICATION #include QT_FORWARD_DECLARE_CLASS(QSharedMemory) class QtLocalPeer; #include class KRITAUI_EXPORT QtSingleApplication : public QApplication { Q_OBJECT public: QtSingleApplication(const QString &id, int &argc, char **argv); ~QtSingleApplication() override; bool isRunning(qint64 pid = -1); void setActivationWindow(QWidget* aw, bool activateOnMessage = true); QWidget* activationWindow() const; bool event(QEvent *event) override; QString applicationId() const; void setBlock(bool value); public Q_SLOTS: bool sendMessage(const QByteArray &message, int timeout = 5000, qint64 pid = -1); void activateWindow(); Q_SIGNALS: void messageReceived(const QByteArray &message, QObject *socket); void fileOpenRequest(const QString &file); private: QString instancesFileName(const QString &appId); qint64 firstPeer; QSharedMemory *instances; QtLocalPeer *pidPeer; QWidget *actWin; QString appId; bool block; }; #endif diff --git a/libs/ui/thememanager.cpp b/libs/ui/thememanager.cpp index 52c7995f18..d4013c2916 100644 --- a/libs/ui/thememanager.cpp +++ b/libs/ui/thememanager.cpp @@ -1,312 +1,312 @@ /* ============================================================ * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * Date : 2004-08-02 * Description : theme manager * * Copyright (C) 2006-2011 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "thememanager.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include #include #include #include #include #include #include // Calligra #include #ifdef __APPLE__ #include #endif namespace Digikam { // --------------------------------------------------------------- class ThemeManager::ThemeManagerPriv { public: ThemeManagerPriv() : themeMenuActionGroup(0) , themeMenuAction(0) { } QString currentThemeName; QMap themeMap; // map QActionGroup* themeMenuActionGroup; KActionMenu* themeMenuAction; }; ThemeManager::ThemeManager(const QString &theme, QObject *parent) : QObject(parent) , d(new ThemeManagerPriv) { //qDebug() << "Creating theme manager with theme" << theme; d->currentThemeName = theme; populateThemeMap(); } ThemeManager::~ThemeManager() { delete d; } QString ThemeManager::currentThemeName() const { //qDebug() << "getting current themename"; QString themeName; if (d->themeMenuAction && d->themeMenuActionGroup) { QAction* action = d->themeMenuActionGroup->checkedAction(); if (action) { themeName = action->text().remove('&'); } //qDebug() << "\tthemename from action" << themeName; } else if (!d->currentThemeName.isEmpty()) { //qDebug() << "\tcurrent themename" << d->currentThemeName; themeName = d->currentThemeName; } else { //qDebug() << "\tfallback"; themeName = "Krita dark"; } //qDebug() << "\tresult" << themeName; return themeName; } void ThemeManager::setCurrentTheme(const QString& name) { //qDebug() << "setCurrentTheme();" << d->currentThemeName << "to" << name; d->currentThemeName = name; if (d->themeMenuAction && d->themeMenuActionGroup) { QList list = d->themeMenuActionGroup->actions(); Q_FOREACH (QAction* action, list) { if (action->text().remove('&') == name) { action->setChecked(true); } } } slotChangePalette(); } void ThemeManager::slotChangePalette() { //qDebug() << "slotChangePalette" << sender(); QString theme(currentThemeName()); QString filename = d->themeMap.value(theme); KSharedConfigPtr config = KSharedConfig::openConfig(filename); QPalette palette = qApp->palette(); QPalette::ColorGroup states[3] = { QPalette::Active, QPalette::Inactive, QPalette::Disabled }; // TT thinks tooltips shouldn't use active, so we use our active colors for all states KColorScheme schemeTooltip(QPalette::Active, KColorScheme::Tooltip, config); for ( int i = 0; i < 3 ; ++i ) { QPalette::ColorGroup state = states[i]; KColorScheme schemeView(state, KColorScheme::View, config); KColorScheme schemeWindow(state, KColorScheme::Window, config); KColorScheme schemeButton(state, KColorScheme::Button, config); KColorScheme schemeSelection(state, KColorScheme::Selection, config); palette.setBrush(state, QPalette::WindowText, schemeWindow.foreground()); palette.setBrush(state, QPalette::Window, schemeWindow.background()); palette.setBrush(state, QPalette::Base, schemeView.background()); palette.setBrush(state, QPalette::Text, schemeView.foreground()); palette.setBrush(state, QPalette::Button, schemeButton.background()); palette.setBrush(state, QPalette::ButtonText, schemeButton.foreground()); palette.setBrush(state, QPalette::Highlight, schemeSelection.background()); palette.setBrush(state, QPalette::HighlightedText, schemeSelection.foreground()); palette.setBrush(state, QPalette::ToolTipBase, schemeTooltip.background()); palette.setBrush(state, QPalette::ToolTipText, schemeTooltip.foreground()); palette.setColor(state, QPalette::Light, schemeWindow.shade(KColorScheme::LightShade)); palette.setColor(state, QPalette::Midlight, schemeWindow.shade(KColorScheme::MidlightShade)); palette.setColor(state, QPalette::Mid, schemeWindow.shade(KColorScheme::MidShade)); palette.setColor(state, QPalette::Dark, schemeWindow.shade(KColorScheme::DarkShade)); palette.setColor(state, QPalette::Shadow, schemeWindow.shade(KColorScheme::ShadowShade)); palette.setBrush(state, QPalette::AlternateBase, schemeView.background(KColorScheme::AlternateBackground)); palette.setBrush(state, QPalette::Link, schemeView.foreground(KColorScheme::LinkText)); palette.setBrush(state, QPalette::LinkVisited, schemeView.foreground(KColorScheme::VisitedText)); } //qDebug() << ">>>>>>>>>>>>>>>>>> going to set palette on app" << theme; // hint for the style to synchronize the color scheme with the window manager/compositor qApp->setProperty("KDE_COLOR_SCHEME_PATH", filename); qApp->setPalette(palette); #ifdef Q_OS_MACOS if (theme == "Krita bright" || theme.isEmpty()) { qApp->setStyle("Macintosh"); qApp->style()->polish(qApp); } else { qApp->setStyle("Fusion"); qApp->style()->polish(qApp); } #endif KisIconUtils::clearIconCache(); emit signalThemeChanged(); } void ThemeManager::setThemeMenuAction(KActionMenu* const action) { d->themeMenuAction = action; populateThemeMenu(); } void ThemeManager::registerThemeActions(KActionCollection *actionCollection) { if (!d->themeMenuAction) return; actionCollection->addAction("theme_menu", d->themeMenuAction); } void ThemeManager::populateThemeMenu() { if (!d->themeMenuAction) return; d->themeMenuAction->menu()->clear(); delete d->themeMenuActionGroup; d->themeMenuActionGroup = new QActionGroup(d->themeMenuAction); connect(d->themeMenuActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotChangePalette())); QAction * action; const QStringList schemeFiles = KoResourcePaths::findAllResources("data", "color-schemes/*.colors"); QMap actionMap; for (int i = 0; i < schemeFiles.size(); ++i) { const QString filename = schemeFiles.at(i); const QFileInfo info(filename); KSharedConfigPtr config = KSharedConfig::openConfig(filename); QIcon icon = createSchemePreviewIcon(config); KConfigGroup group(config, "General"); const QString name = group.readEntry("Name", info.completeBaseName()); action = new QAction(name, d->themeMenuActionGroup); action->setIcon(icon); action->setCheckable(true); actionMap.insert(name, action); } // sort the list QStringList actionMapKeys = actionMap.keys(); actionMapKeys.sort(); Q_FOREACH (const QString& name, actionMapKeys) { if ( name == currentThemeName()) { actionMap.value(name)->setChecked(true); } d->themeMenuAction->addAction(actionMap.value(name)); } } QPixmap ThemeManager::createSchemePreviewIcon(const KSharedConfigPtr& config) { // code taken from kdebase/workspace/kcontrol/colors/colorscm.cpp const uchar bits1[] = { 0xff, 0xff, 0xff, 0x2c, 0x16, 0x0b }; const uchar bits2[] = { 0x68, 0x34, 0x1a, 0xff, 0xff, 0xff }; const QSize bitsSize(24, 2); const QBitmap b1 = QBitmap::fromData(bitsSize, bits1); const QBitmap b2 = QBitmap::fromData(bitsSize, bits2); QPixmap pixmap(23, 16); pixmap.fill(Qt::black); // FIXME use some color other than black for borders? KConfigGroup group(config, "WM"); QPainter p(&pixmap); KColorScheme windowScheme(QPalette::Active, KColorScheme::Window, config); p.fillRect(1, 1, 7, 7, windowScheme.background()); p.fillRect(2, 2, 5, 2, QBrush(windowScheme.foreground().color(), b1)); KColorScheme buttonScheme(QPalette::Active, KColorScheme::Button, config); p.fillRect(8, 1, 7, 7, buttonScheme.background()); p.fillRect(9, 2, 5, 2, QBrush(buttonScheme.foreground().color(), b1)); p.fillRect(15, 1, 7, 7, group.readEntry("activeBackground", QColor(96, 148, 207))); p.fillRect(16, 2, 5, 2, QBrush(group.readEntry("activeForeground", QColor(255, 255, 255)), b1)); KColorScheme viewScheme(QPalette::Active, KColorScheme::View, config); p.fillRect(1, 8, 7, 7, viewScheme.background()); p.fillRect(2, 12, 5, 2, QBrush(viewScheme.foreground().color(), b2)); KColorScheme selectionScheme(QPalette::Active, KColorScheme::Selection, config); p.fillRect(8, 8, 7, 7, selectionScheme.background()); p.fillRect(9, 12, 5, 2, QBrush(selectionScheme.foreground().color(), b2)); p.fillRect(15, 8, 7, 7, group.readEntry("inactiveBackground", QColor(224, 223, 222))); p.fillRect(16, 12, 5, 2, QBrush(group.readEntry("inactiveForeground", QColor(20, 19, 18)), b2)); p.end(); return pixmap; } void ThemeManager::populateThemeMap() { const QStringList schemeFiles = KoResourcePaths::findAllResources("data", "color-schemes/*.colors"); for (int i = 0; i < schemeFiles.size(); ++i) { const QString filename = schemeFiles.at(i); const QFileInfo info(filename); KSharedConfigPtr config = KSharedConfig::openConfig(filename); KConfigGroup group(config, "General"); const QString name = group.readEntry("Name", info.completeBaseName()); d->themeMap.insert(name, filename); } } } // namespace Digikam diff --git a/libs/ui/thememanager.h b/libs/ui/thememanager.h index 0e2dc818e8..338c518822 100644 --- a/libs/ui/thememanager.h +++ b/libs/ui/thememanager.h @@ -1,85 +1,85 @@ /* ============================================================ * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * Date : 2004-08-02 * Description : theme manager * * Copyright (C) 2006-2011 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef THEMEMANAGER_H #define THEMEMANAGER_H // Qt includes #include #include #include // KDE includes #include class KActionCollection; class KActionMenu; namespace Digikam { class ThemeManager : public QObject { Q_OBJECT public: /** * @brief ThemeManager * @param theme the currently active theme: the palette will not be changed to this theme * @param parent */ explicit ThemeManager(const QString &theme = "", QObject *parent = 0); ~ThemeManager() override; QString currentThemeName() const; void setCurrentTheme(const QString& name); void setThemeMenuAction(KActionMenu* const action); void registerThemeActions(KActionCollection *actionCollection); Q_SIGNALS: void signalThemeChanged(); private Q_SLOTS: void slotChangePalette(); private: void populateThemeMap(); void populateThemeMenu(); QPixmap createSchemePreviewIcon(const KSharedConfigPtr& config); private: class ThemeManagerPriv; ThemeManagerPriv* const d; }; } // namespace Digikam #endif /* THEMEMANAGER_H */ diff --git a/libs/ui/tool/kis_tool_freehand_helper.cpp b/libs/ui/tool/kis_tool_freehand_helper.cpp index d4665eeb04..fe2692c67e 100644 --- a/libs/ui/tool/kis_tool_freehand_helper.cpp +++ b/libs/ui/tool/kis_tool_freehand_helper.cpp @@ -1,964 +1,964 @@ /* * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tool_freehand_helper.h" #include #include #include #include #include #include "kis_algebra_2d.h" #include "kis_distance_information.h" #include "kis_painting_information_builder.h" #include "kis_image.h" #include "kis_painter.h" #include #include #include "kis_update_time_monitor.h" #include "kis_stabilized_events_sampler.h" #include "KisStabilizerDelayedPaintHelper.h" #include "kis_config.h" #include "kis_random_source.h" #include "KisPerStrokeRandomSource.h" #include "strokes/freehand_stroke.h" #include "strokes/KisFreehandStrokeInfo.h" #include "KisAsyncronousStrokeUpdateHelper.h" #include //#define DEBUG_BEZIER_CURVES // Factor by which to scale the airbrush timer's interval, relative to the actual airbrushing rate. // Setting this less than 1 makes the timer-generated pseudo-events happen faster than the desired // airbrush rate, which can improve responsiveness. const qreal AIRBRUSH_INTERVAL_FACTOR = 0.5; // The amount of time, in milliseconds, to allow between updates of the spacing information. Only // used when spacing updates between dabs are enabled. const qreal SPACING_UPDATE_INTERVAL = 50.0; // The amount of time, in milliseconds, to allow between updates of the timing information. Only // used when airbrushing. const qreal TIMING_UPDATE_INTERVAL = 50.0; struct KisToolFreehandHelper::Private { KisPaintingInformationBuilder *infoBuilder; KisStrokesFacade *strokesFacade; KisAsyncronousStrokeUpdateHelper asyncUpdateHelper; KUndo2MagicString transactionText; bool haveTangent; QPointF previousTangent; bool hasPaintAtLeastOnce; QTime strokeTime; QTimer strokeTimeoutTimer; QVector strokeInfos; KisResourcesSnapshotSP resources; KisStrokeId strokeId; KisPaintInformation previousPaintInformation; KisPaintInformation olderPaintInformation; KisSmoothingOptionsSP smoothingOptions; // fake random sources for hovering outline *only* KisRandomSourceSP fakeDabRandomSource; KisPerStrokeRandomSourceSP fakeStrokeRandomSource; // Timer used to generate paint updates periodically even without input events. This is only // used for paintops that depend on timely updates even when the cursor is not moving, e.g. for // airbrushing effects. QTimer airbrushingTimer; QList history; QList distanceHistory; // Keeps track of past cursor positions. This is used to determine the drawing angle when // drawing the brush outline or starting a stroke. KisPaintOpUtils::PositionHistory lastCursorPos; // Stabilizer data bool usingStabilizer; QQueue stabilizerDeque; QTimer stabilizerPollTimer; KisStabilizedEventsSampler stabilizedSampler; KisStabilizerDelayedPaintHelper stabilizerDelayedPaintHelper; qreal effectiveSmoothnessDistance() const; }; KisToolFreehandHelper::KisToolFreehandHelper(KisPaintingInformationBuilder *infoBuilder, const KUndo2MagicString &transactionText, KisSmoothingOptions *smoothingOptions) : m_d(new Private()) { m_d->infoBuilder = infoBuilder; m_d->transactionText = transactionText; m_d->smoothingOptions = KisSmoothingOptionsSP( smoothingOptions ? smoothingOptions : new KisSmoothingOptions()); m_d->fakeDabRandomSource = new KisRandomSource(); m_d->fakeStrokeRandomSource = new KisPerStrokeRandomSource(); m_d->strokeTimeoutTimer.setSingleShot(true); connect(&m_d->strokeTimeoutTimer, SIGNAL(timeout()), SLOT(finishStroke())); connect(&m_d->airbrushingTimer, SIGNAL(timeout()), SLOT(doAirbrushing())); connect(&m_d->stabilizerPollTimer, SIGNAL(timeout()), SLOT(stabilizerPollAndPaint())); connect(m_d->smoothingOptions.data(), SIGNAL(sigSmoothingTypeChanged()), SLOT(slotSmoothingTypeChanged())); m_d->stabilizerDelayedPaintHelper.setPaintLineCallback( [this](const KisPaintInformation &pi1, const KisPaintInformation &pi2) { paintLine(pi1, pi2); }); m_d->stabilizerDelayedPaintHelper.setUpdateOutlineCallback( [this]() { emit requestExplicitUpdateOutline(); }); } KisToolFreehandHelper::~KisToolFreehandHelper() { delete m_d; } void KisToolFreehandHelper::setSmoothness(KisSmoothingOptionsSP smoothingOptions) { m_d->smoothingOptions = smoothingOptions; } KisSmoothingOptionsSP KisToolFreehandHelper::smoothingOptions() const { return m_d->smoothingOptions; } QPainterPath KisToolFreehandHelper::paintOpOutline(const QPointF &savedCursorPos, const KoPointerEvent *event, const KisPaintOpSettingsSP globalSettings, KisPaintOpSettings::OutlineMode mode) const { KisPaintOpSettingsSP settings = globalSettings; KisPaintInformation info = m_d->infoBuilder->hover(savedCursorPos, event); QPointF prevPoint = m_d->lastCursorPos.pushThroughHistory(savedCursorPos); qreal startAngle = KisAlgebra2D::directionBetweenPoints(prevPoint, savedCursorPos, 0); KisDistanceInformation distanceInfo(prevPoint, startAngle); if (!m_d->strokeInfos.isEmpty()) { settings = m_d->resources->currentPaintOpPreset()->settings(); if (m_d->stabilizerDelayedPaintHelper.running() && m_d->stabilizerDelayedPaintHelper.hasLastPaintInformation()) { info = m_d->stabilizerDelayedPaintHelper.lastPaintInformation(); } else { info = m_d->previousPaintInformation; } /** * When LoD mode is active it may happen that the helper has * already started a stroke, but it painted noting, because * all the work is being calculated by the scaled-down LodN * stroke. So at first we try to fetch the data from the lodN * stroke ("buddy") and then check if there is at least * something has been painted with this distance information * object. */ KisDistanceInformation *buddyDistance = m_d->strokeInfos.first()->buddyDragDistance(); if (buddyDistance) { /** * Tiny hack alert: here we fetch the distance information * directly from the LodN stroke. Ideally, we should * upscale its data, but here we just override it with our * local copy of the coordinates. */ distanceInfo = *buddyDistance; distanceInfo.overrideLastValues(prevPoint, startAngle); } else if (m_d->strokeInfos.first()->dragDistance->isStarted()) { distanceInfo = *m_d->strokeInfos.first()->dragDistance; } } KisPaintInformation::DistanceInformationRegistrar registrar = info.registerDistanceInformation(&distanceInfo); info.setRandomSource(m_d->fakeDabRandomSource); info.setPerStrokeRandomSource(m_d->fakeStrokeRandomSource); QPainterPath outline = settings->brushOutline(info, mode); if (m_d->resources && m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER && m_d->smoothingOptions->useDelayDistance()) { const qreal R = m_d->smoothingOptions->delayDistance() / m_d->resources->effectiveZoom(); outline.addEllipse(info.pos(), R, R); } return outline; } void KisToolFreehandHelper::cursorMoved(const QPointF &cursorPos) { m_d->lastCursorPos.pushThroughHistory(cursorPos); } void KisToolFreehandHelper::initPaint(KoPointerEvent *event, const QPointF &pixelCoords, KoCanvasResourceProvider *resourceManager, KisImageWSP image, KisNodeSP currentNode, KisStrokesFacade *strokesFacade, KisNodeSP overrideNode, KisDefaultBoundsBaseSP bounds) { QPointF prevPoint = m_d->lastCursorPos.pushThroughHistory(pixelCoords); m_d->strokeTime.start(); KisPaintInformation pi = m_d->infoBuilder->startStroke(event, elapsedStrokeTime(), resourceManager); qreal startAngle = KisAlgebra2D::directionBetweenPoints(prevPoint, pixelCoords, 0.0); initPaintImpl(startAngle, pi, resourceManager, image, currentNode, strokesFacade, overrideNode, bounds); } bool KisToolFreehandHelper::isRunning() const { return m_d->strokeId; } void KisToolFreehandHelper::initPaintImpl(qreal startAngle, const KisPaintInformation &pi, KoCanvasResourceProvider *resourceManager, KisImageWSP image, KisNodeSP currentNode, KisStrokesFacade *strokesFacade, KisNodeSP overrideNode, KisDefaultBoundsBaseSP bounds) { m_d->strokesFacade = strokesFacade; m_d->haveTangent = false; m_d->previousTangent = QPointF(); m_d->hasPaintAtLeastOnce = false; m_d->previousPaintInformation = pi; m_d->resources = new KisResourcesSnapshot(image, currentNode, resourceManager, bounds); if(overrideNode) { m_d->resources->setCurrentNode(overrideNode); } const bool airbrushing = m_d->resources->needsAirbrushing(); const bool useSpacingUpdates = m_d->resources->needsSpacingUpdates(); KisDistanceInitInfo startDistInfo(m_d->previousPaintInformation.pos(), startAngle, useSpacingUpdates ? SPACING_UPDATE_INTERVAL : LONG_TIME, airbrushing ? TIMING_UPDATE_INTERVAL : LONG_TIME, 0); KisDistanceInformation startDist = startDistInfo.makeDistInfo(); createPainters(m_d->strokeInfos, startDist); KisStrokeStrategy *stroke = new FreehandStrokeStrategy(m_d->resources, m_d->strokeInfos, m_d->transactionText); m_d->strokeId = m_d->strokesFacade->startStroke(stroke); m_d->history.clear(); m_d->distanceHistory.clear(); if (airbrushing) { m_d->airbrushingTimer.setInterval(computeAirbrushTimerInterval()); m_d->airbrushingTimer.start(); } else if (m_d->resources->presetNeedsAsynchronousUpdates()) { m_d->asyncUpdateHelper.startUpdateStream(m_d->strokesFacade, m_d->strokeId); } if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) { stabilizerStart(m_d->previousPaintInformation); } // If airbrushing, paint an initial dab immediately. This is a workaround for an issue where // some paintops (Dyna, Particle, Sketch) might never initialize their spacing/timing // information until paintAt is called. if (airbrushing) { paintAt(pi); } } void KisToolFreehandHelper::paintBezierSegment(KisPaintInformation pi1, KisPaintInformation pi2, QPointF tangent1, QPointF tangent2) { if (tangent1.isNull() || tangent2.isNull()) return; const qreal maxSanePoint = 1e6; QPointF controlTarget1; QPointF controlTarget2; // Shows the direction in which control points go QPointF controlDirection1 = pi1.pos() + tangent1; QPointF controlDirection2 = pi2.pos() - tangent2; // Lines in the direction of the control points QLineF line1(pi1.pos(), controlDirection1); QLineF line2(pi2.pos(), controlDirection2); // Lines to check whether the control points lay on the opposite // side of the line QLineF line3(controlDirection1, controlDirection2); QLineF line4(pi1.pos(), pi2.pos()); QPointF intersection; if (line3.intersect(line4, &intersection) == QLineF::BoundedIntersection) { qreal controlLength = line4.length() / 2; line1.setLength(controlLength); line2.setLength(controlLength); controlTarget1 = line1.p2(); controlTarget2 = line2.p2(); } else { QLineF::IntersectType type = line1.intersect(line2, &intersection); if (type == QLineF::NoIntersection || intersection.manhattanLength() > maxSanePoint) { intersection = 0.5 * (pi1.pos() + pi2.pos()); // dbgKrita << "WARNING: there is no intersection point " // << "in the basic smoothing algorithms"; } controlTarget1 = intersection; controlTarget2 = intersection; } // shows how near to the controlTarget the value raises qreal coeff = 0.8; qreal velocity1 = QLineF(QPointF(), tangent1).length(); qreal velocity2 = QLineF(QPointF(), tangent2).length(); if (velocity1 == 0.0 || velocity2 == 0.0) { velocity1 = 1e-6; velocity2 = 1e-6; warnKrita << "WARNING: Basic Smoothing: Velocity is Zero! Please report a bug:" << ppVar(velocity1) << ppVar(velocity2); } qreal similarity = qMin(velocity1/velocity2, velocity2/velocity1); // the controls should not differ more than 50% similarity = qMax(similarity, qreal(0.5)); // when the controls are symmetric, their size should be smaller // to avoid corner-like curves coeff *= 1 - qMax(qreal(0.0), similarity - qreal(0.8)); Q_ASSERT(coeff > 0); QPointF control1; QPointF control2; if (velocity1 > velocity2) { control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1; coeff *= similarity; control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2; } else { control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2; coeff *= similarity; control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1; } paintBezierCurve(pi1, control1, control2, pi2); } qreal KisToolFreehandHelper::Private::effectiveSmoothnessDistance() const { const qreal effectiveSmoothnessDistance = !smoothingOptions->useScalableDistance() ? smoothingOptions->smoothnessDistance() : smoothingOptions->smoothnessDistance() / resources->effectiveZoom(); return effectiveSmoothnessDistance; } void KisToolFreehandHelper::paintEvent(KoPointerEvent *event) { KisPaintInformation info = m_d->infoBuilder->continueStroke(event, elapsedStrokeTime()); KisUpdateTimeMonitor::instance()->reportMouseMove(info.pos()); paint(info); } void KisToolFreehandHelper::paint(KisPaintInformation &info) { /** * Smooth the coordinates out using the history and the * distance. This is a heavily modified version of an algo used in * Gimp and described in https://bugs.kde.org/show_bug.cgi?id=281267 and - * http://www24.atwiki.jp/sigetch_2007/pages/17.html. The main + * https://w.atwiki.jp/sigetch_2007/pages/17.html. The main * differences are: * * 1) It uses 'distance' instead of 'velocity', since time * measurements are too unstable in realworld environment * * 2) There is no 'Quality' parameter, since the number of samples * is calculated automatically * * 3) 'Tail Aggressiveness' is used for controlling the end of the * stroke * * 4) The formila is a little bit different: 'Distance' parameter * stands for $3 \Sigma$ */ if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::WEIGHTED_SMOOTHING && m_d->smoothingOptions->smoothnessDistance() > 0.0) { { // initialize current distance QPointF prevPos; if (!m_d->history.isEmpty()) { const KisPaintInformation &prevPi = m_d->history.last(); prevPos = prevPi.pos(); } else { prevPos = m_d->previousPaintInformation.pos(); } qreal currentDistance = QVector2D(info.pos() - prevPos).length(); m_d->distanceHistory.append(currentDistance); } m_d->history.append(info); qreal x = 0.0; qreal y = 0.0; if (m_d->history.size() > 3) { const qreal sigma = m_d->effectiveSmoothnessDistance() / 3.0; // '3.0' for (3 * sigma) range qreal gaussianWeight = 1 / (sqrt(2 * M_PI) * sigma); qreal gaussianWeight2 = sigma * sigma; qreal distanceSum = 0.0; qreal scaleSum = 0.0; qreal pressure = 0.0; qreal baseRate = 0.0; Q_ASSERT(m_d->history.size() == m_d->distanceHistory.size()); for (int i = m_d->history.size() - 1; i >= 0; i--) { qreal rate = 0.0; const KisPaintInformation nextInfo = m_d->history.at(i); double distance = m_d->distanceHistory.at(i); Q_ASSERT(distance >= 0.0); qreal pressureGrad = 0.0; if (i < m_d->history.size() - 1) { pressureGrad = nextInfo.pressure() - m_d->history.at(i + 1).pressure(); const qreal tailAgressiveness = 40.0 * m_d->smoothingOptions->tailAggressiveness(); if (pressureGrad > 0.0 ) { pressureGrad *= tailAgressiveness * (1.0 - nextInfo.pressure()); distance += pressureGrad * 3.0 * sigma; // (3 * sigma) --- holds > 90% of the region } } if (gaussianWeight2 != 0.0) { distanceSum += distance; rate = gaussianWeight * exp(-distanceSum * distanceSum / (2 * gaussianWeight2)); } if (m_d->history.size() - i == 1) { baseRate = rate; } else if (baseRate / rate > 100) { break; } scaleSum += rate; x += rate * nextInfo.pos().x(); y += rate * nextInfo.pos().y(); if (m_d->smoothingOptions->smoothPressure()) { pressure += rate * nextInfo.pressure(); } } if (scaleSum != 0.0) { x /= scaleSum; y /= scaleSum; if (m_d->smoothingOptions->smoothPressure()) { pressure /= scaleSum; } } if ((x != 0.0 && y != 0.0) || (x == info.pos().x() && y == info.pos().y())) { info.setPos(QPointF(x, y)); if (m_d->smoothingOptions->smoothPressure()) { info.setPressure(pressure); } m_d->history.last() = info; } } } if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::SIMPLE_SMOOTHING || m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::WEIGHTED_SMOOTHING) { // Now paint between the coordinates, using the bezier curve interpolation if (!m_d->haveTangent) { m_d->haveTangent = true; m_d->previousTangent = (info.pos() - m_d->previousPaintInformation.pos()) / qMax(qreal(1.0), info.currentTime() - m_d->previousPaintInformation.currentTime()); } else { QPointF newTangent = (info.pos() - m_d->olderPaintInformation.pos()) / qMax(qreal(1.0), info.currentTime() - m_d->olderPaintInformation.currentTime()); if (newTangent.isNull() || m_d->previousTangent.isNull()) { paintLine(m_d->previousPaintInformation, info); } else { paintBezierSegment(m_d->olderPaintInformation, m_d->previousPaintInformation, m_d->previousTangent, newTangent); } m_d->previousTangent = newTangent; } m_d->olderPaintInformation = m_d->previousPaintInformation; // Enable stroke timeout only when not airbrushing. if (!m_d->airbrushingTimer.isActive()) { m_d->strokeTimeoutTimer.start(100); } } else if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::NO_SMOOTHING){ paintLine(m_d->previousPaintInformation, info); } if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) { m_d->stabilizedSampler.addEvent(info); if (m_d->stabilizerDelayedPaintHelper.running()) { // Paint here so we don't have to rely on the timer // This is just a tricky source for a relatively stable 7ms "timer" m_d->stabilizerDelayedPaintHelper.paintSome(); } } else { m_d->previousPaintInformation = info; } if(m_d->airbrushingTimer.isActive()) { m_d->airbrushingTimer.start(); } } void KisToolFreehandHelper::endPaint() { if (!m_d->hasPaintAtLeastOnce) { paintAt(m_d->previousPaintInformation); } else if (m_d->smoothingOptions->smoothingType() != KisSmoothingOptions::NO_SMOOTHING) { finishStroke(); } m_d->strokeTimeoutTimer.stop(); if(m_d->airbrushingTimer.isActive()) { m_d->airbrushingTimer.stop(); } if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) { stabilizerEnd(); } if (m_d->asyncUpdateHelper.isActive()) { m_d->asyncUpdateHelper.endUpdateStream(); } /** * There might be some timer events still pending, so * we should cancel them. Use this flag for the purpose. * Please note that we are not in MT here, so no mutex * is needed */ m_d->strokeInfos.clear(); m_d->strokesFacade->endStroke(m_d->strokeId); m_d->strokeId.clear(); } void KisToolFreehandHelper::cancelPaint() { if (!m_d->strokeId) return; m_d->strokeTimeoutTimer.stop(); if (m_d->airbrushingTimer.isActive()) { m_d->airbrushingTimer.stop(); } if (m_d->asyncUpdateHelper.isActive()) { m_d->asyncUpdateHelper.cancelUpdateStream(); } if (m_d->stabilizerPollTimer.isActive()) { m_d->stabilizerPollTimer.stop(); } if (m_d->stabilizerDelayedPaintHelper.running()) { m_d->stabilizerDelayedPaintHelper.cancel(); } // see a comment in endPaint() m_d->strokeInfos.clear(); m_d->strokesFacade->cancelStroke(m_d->strokeId); m_d->strokeId.clear(); } int KisToolFreehandHelper::elapsedStrokeTime() const { return m_d->strokeTime.elapsed(); } void KisToolFreehandHelper::stabilizerStart(KisPaintInformation firstPaintInfo) { m_d->usingStabilizer = true; // FIXME: Ugly hack, this is no a "distance" in any way int sampleSize = qRound(m_d->effectiveSmoothnessDistance()); sampleSize = qMax(3, sampleSize); // Fill the deque with the current value repeated until filling the sample m_d->stabilizerDeque.clear(); for (int i = sampleSize; i > 0; i--) { m_d->stabilizerDeque.enqueue(firstPaintInfo); } // Poll and draw regularly KisConfig cfg(true); int stabilizerSampleSize = cfg.stabilizerSampleSize(); m_d->stabilizerPollTimer.setInterval(stabilizerSampleSize); m_d->stabilizerPollTimer.start(); bool delayedPaintEnabled = cfg.stabilizerDelayedPaint(); if (delayedPaintEnabled) { m_d->stabilizerDelayedPaintHelper.start(firstPaintInfo); } m_d->stabilizedSampler.clear(); m_d->stabilizedSampler.addEvent(firstPaintInfo); } KisPaintInformation KisToolFreehandHelper::getStabilizedPaintInfo(const QQueue &queue, const KisPaintInformation &lastPaintInfo) { KisPaintInformation result(lastPaintInfo.pos(), lastPaintInfo.pressure(), lastPaintInfo.xTilt(), lastPaintInfo.yTilt(), lastPaintInfo.rotation(), lastPaintInfo.tangentialPressure(), lastPaintInfo.perspective(), elapsedStrokeTime(), lastPaintInfo.drawingSpeed()); if (queue.size() > 1) { QQueue::const_iterator it = queue.constBegin(); QQueue::const_iterator end = queue.constEnd(); /** * The first point is going to be overridden by lastPaintInfo, skip it. */ it++; int i = 2; if (m_d->smoothingOptions->stabilizeSensors()) { while (it != end) { qreal k = qreal(i - 1) / i; // coeff for uniform averaging result.KisPaintInformation::mixOtherWithoutTime(k, *it); it++; i++; } } else{ while (it != end) { qreal k = qreal(i - 1) / i; // coeff for uniform averaging result.KisPaintInformation::mixOtherOnlyPosition(k, *it); it++; i++; } } } return result; } void KisToolFreehandHelper::stabilizerPollAndPaint() { KisStabilizedEventsSampler::iterator it; KisStabilizedEventsSampler::iterator end; std::tie(it, end) = m_d->stabilizedSampler.range(); QVector delayedPaintTodoItems; for (; it != end; ++it) { KisPaintInformation sampledInfo = *it; bool canPaint = true; if (m_d->smoothingOptions->useDelayDistance()) { const qreal R = m_d->smoothingOptions->delayDistance() / m_d->resources->effectiveZoom(); QPointF diff = sampledInfo.pos() - m_d->previousPaintInformation.pos(); qreal dx = sqrt(pow2(diff.x()) + pow2(diff.y())); if (!(dx > R)) { if (m_d->resources->needsAirbrushing()) { sampledInfo.setPos(m_d->previousPaintInformation.pos()); } else { canPaint = false; } } } if (canPaint) { KisPaintInformation newInfo = getStabilizedPaintInfo(m_d->stabilizerDeque, sampledInfo); if (m_d->stabilizerDelayedPaintHelper.running()) { delayedPaintTodoItems.append(newInfo); } else { paintLine(m_d->previousPaintInformation, newInfo); } m_d->previousPaintInformation = newInfo; // Push the new entry through the queue m_d->stabilizerDeque.dequeue(); m_d->stabilizerDeque.enqueue(sampledInfo); } else if (m_d->stabilizerDeque.head().pos() != m_d->previousPaintInformation.pos()) { QQueue::iterator it = m_d->stabilizerDeque.begin(); QQueue::iterator end = m_d->stabilizerDeque.end(); while (it != end) { *it = m_d->previousPaintInformation; ++it; } } } m_d->stabilizedSampler.clear(); if (m_d->stabilizerDelayedPaintHelper.running()) { m_d->stabilizerDelayedPaintHelper.update(delayedPaintTodoItems); } else { emit requestExplicitUpdateOutline(); } } void KisToolFreehandHelper::stabilizerEnd() { // Stop the timer m_d->stabilizerPollTimer.stop(); // Finish the line if (m_d->smoothingOptions->finishStabilizedCurve()) { // Process all the existing events first stabilizerPollAndPaint(); // Draw the finish line with pending events and a time override m_d->stabilizedSampler.addFinishingEvent(m_d->stabilizerDeque.size()); stabilizerPollAndPaint(); } if (m_d->stabilizerDelayedPaintHelper.running()) { m_d->stabilizerDelayedPaintHelper.end(); } m_d->usingStabilizer = false; } void KisToolFreehandHelper::slotSmoothingTypeChanged() { if (!isRunning()) { return; } KisSmoothingOptions::SmoothingType currentSmoothingType = m_d->smoothingOptions->smoothingType(); if (m_d->usingStabilizer && (currentSmoothingType != KisSmoothingOptions::STABILIZER)) { stabilizerEnd(); } else if (!m_d->usingStabilizer && (currentSmoothingType == KisSmoothingOptions::STABILIZER)) { stabilizerStart(m_d->previousPaintInformation); } } void KisToolFreehandHelper::finishStroke() { if (m_d->haveTangent) { m_d->haveTangent = false; QPointF newTangent = (m_d->previousPaintInformation.pos() - m_d->olderPaintInformation.pos()) / (m_d->previousPaintInformation.currentTime() - m_d->olderPaintInformation.currentTime()); paintBezierSegment(m_d->olderPaintInformation, m_d->previousPaintInformation, m_d->previousTangent, newTangent); } } void KisToolFreehandHelper::doAirbrushing() { // Check that the stroke hasn't ended. if (!m_d->strokeInfos.isEmpty()) { // Add a new painting update at a point identical to the previous one, except for the time // and speed information. const KisPaintInformation &prevPaint = m_d->previousPaintInformation; KisPaintInformation nextPaint(prevPaint.pos(), prevPaint.pressure(), prevPaint.xTilt(), prevPaint.yTilt(), prevPaint.rotation(), prevPaint.tangentialPressure(), prevPaint.perspective(), elapsedStrokeTime(), 0.0); paint(nextPaint); } } int KisToolFreehandHelper::computeAirbrushTimerInterval() const { qreal realInterval = m_d->resources->airbrushingInterval() * AIRBRUSH_INTERVAL_FACTOR; return qMax(1, qFloor(realInterval)); } void KisToolFreehandHelper::paintAt(int strokeInfoId, const KisPaintInformation &pi) { m_d->hasPaintAtLeastOnce = true; m_d->strokesFacade->addJob(m_d->strokeId, new FreehandStrokeStrategy::Data(strokeInfoId, pi)); } void KisToolFreehandHelper::paintLine(int strokeInfoId, const KisPaintInformation &pi1, const KisPaintInformation &pi2) { m_d->hasPaintAtLeastOnce = true; m_d->strokesFacade->addJob(m_d->strokeId, new FreehandStrokeStrategy::Data(strokeInfoId, pi1, pi2)); } void KisToolFreehandHelper::paintBezierCurve(int strokeInfoId, const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2) { #ifdef DEBUG_BEZIER_CURVES KisPaintInformation tpi1; KisPaintInformation tpi2; tpi1 = pi1; tpi2 = pi2; tpi1.setPressure(0.3); tpi2.setPressure(0.3); paintLine(tpi1, tpi2); tpi1.setPressure(0.6); tpi2.setPressure(0.3); tpi1.setPos(pi1.pos()); tpi2.setPos(control1); paintLine(tpi1, tpi2); tpi1.setPos(pi2.pos()); tpi2.setPos(control2); paintLine(tpi1, tpi2); #endif m_d->hasPaintAtLeastOnce = true; m_d->strokesFacade->addJob(m_d->strokeId, new FreehandStrokeStrategy::Data(strokeInfoId, pi1, control1, control2, pi2)); } void KisToolFreehandHelper::createPainters(QVector &strokeInfos, const KisDistanceInformation &startDist) { strokeInfos << new KisFreehandStrokeInfo(startDist); } void KisToolFreehandHelper::paintAt(const KisPaintInformation &pi) { paintAt(0, pi); } void KisToolFreehandHelper::paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2) { paintLine(0, pi1, pi2); } void KisToolFreehandHelper::paintBezierCurve(const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2) { paintBezierCurve(0, pi1, control1, control2, pi2); } diff --git a/libs/ui/utils/KisClipboardUtil.cpp b/libs/ui/utils/KisClipboardUtil.cpp index 49bc209a08..9f1039cc14 100644 --- a/libs/ui/utils/KisClipboardUtil.cpp +++ b/libs/ui/utils/KisClipboardUtil.cpp @@ -1,76 +1,76 @@ /* * Copyright (c) 2019 Dmitrii Utkin * * 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 "KisClipboardUtil.h" #include #include #include #include #include #include #include namespace KisClipboardUtil { struct ClipboardImageFormat { QSet mimeTypes; QString format; }; QImage getImageFromClipboard() { static const QList supportedFormats = { {{"image/png"}, "PNG"}, {{"image/tiff"}, "TIFF"}, {{"image/bmp", "image/x-bmp", "image/x-MS-bmp", "image/x-win-bitmap"}, "BMP"}, {{"image/jpeg"}, "JPG"} }; QClipboard *clipboard = QApplication::clipboard(); QImage image; const QSet &clipboardMimeTypes = clipboard->mimeData()->formats().toSet(); Q_FOREACH (const ClipboardImageFormat &item, supportedFormats) { const QSet &intersection = item.mimeTypes & clipboardMimeTypes; if (intersection.isEmpty()) { continue; } const QString &format = *intersection.constBegin(); const QByteArray &imageData = clipboard->mimeData()->data(format); if (imageData.isEmpty()) { continue; } if (image.loadFromData(imageData, item.format.toLatin1())) { break; } } if (image.isNull()) { image = clipboard->image(); } return image; } -} \ No newline at end of file +} diff --git a/libs/ui/widgets/kis_advanced_color_space_selector.cc b/libs/ui/widgets/kis_advanced_color_space_selector.cc index 2550e89840..3fbc2b1ada 100644 --- a/libs/ui/widgets/kis_advanced_color_space_selector.cc +++ b/libs/ui/widgets/kis_advanced_color_space_selector.cc @@ -1,795 +1,795 @@ /* * Copyright (C) 2007 Cyrille Berger * Copyright (C) 2011 Boudewijn Rempt * Copyright (C) 2011 Srikanth Tiyyagura * Copyright (C) 2015 Wolthera van Hövell tot Westerflier * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_advanced_color_space_selector.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ui_wdgcolorspaceselectoradvanced.h" #include struct KisAdvancedColorSpaceSelector::Private { Ui_WdgColorSpaceSelectorAdvanced* colorSpaceSelector; QString knsrcFile; }; KisAdvancedColorSpaceSelector::KisAdvancedColorSpaceSelector(QWidget* parent, const QString &caption) : QDialog(parent) , d(new Private) { setWindowTitle(caption); d->colorSpaceSelector = new Ui_WdgColorSpaceSelectorAdvanced; d->colorSpaceSelector->setupUi(this); d->colorSpaceSelector->cmbColorModels->setIDList(KoColorSpaceRegistry::instance()->colorModelsList(KoColorSpaceRegistry::OnlyUserVisible)); fillCmbDepths(d->colorSpaceSelector->cmbColorModels->currentItem()); d->colorSpaceSelector->bnInstallProfile->setIcon(KisIconUtils::loadIcon("document-open")); d->colorSpaceSelector->bnInstallProfile->setToolTip( i18n("Open Color Profile") ); connect(d->colorSpaceSelector->cmbColorModels, SIGNAL(activated(KoID)), this, SLOT(fillCmbDepths(KoID))); connect(d->colorSpaceSelector->cmbColorDepth, SIGNAL(activated(KoID)), this, SLOT(fillLstProfiles())); connect(d->colorSpaceSelector->cmbColorModels, SIGNAL(activated(KoID)), this, SLOT(fillLstProfiles())); connect(d->colorSpaceSelector->lstProfile, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(colorSpaceChanged())); connect(this, SIGNAL(selectionChanged(bool)), this, SLOT(fillDescription())); connect(this, SIGNAL(selectionChanged(bool)), d->colorSpaceSelector->TongueWidget, SLOT(repaint())); connect(this, SIGNAL(selectionChanged(bool)), d->colorSpaceSelector->TRCwidget, SLOT(repaint())); connect(d->colorSpaceSelector->bnInstallProfile, SIGNAL(clicked()), this, SLOT(installProfile())); connect(d->colorSpaceSelector->bnOK, SIGNAL(accepted()), this, SLOT(accept())); connect(d->colorSpaceSelector->bnOK, SIGNAL(rejected()), this, SLOT(reject())); fillLstProfiles(); } KisAdvancedColorSpaceSelector::~KisAdvancedColorSpaceSelector() { delete d->colorSpaceSelector; delete d; } void KisAdvancedColorSpaceSelector::fillLstProfiles() { d->colorSpaceSelector->lstProfile->blockSignals(true); const QString colorSpaceId = KoColorSpaceRegistry::instance()->colorSpaceId(d->colorSpaceSelector->cmbColorModels->currentItem(), d->colorSpaceSelector->cmbColorDepth->currentItem()); const QString defaultProfileName = KoColorSpaceRegistry::instance()->defaultProfileForColorSpace(colorSpaceId); d->colorSpaceSelector->lstProfile->clear(); QList profileList = KoColorSpaceRegistry::instance()->profilesFor(colorSpaceId); QStringList profileNames; Q_FOREACH (const KoColorProfile *profile, profileList) { profileNames.append(profile->name()); } std::sort(profileNames.begin(), profileNames.end()); QListWidgetItem *defaultProfile = new QListWidgetItem; defaultProfile->setText(defaultProfileName + " " + i18nc("This is appended to the color profile which is the default for the given colorspace and bit-depth","(Default)")); Q_FOREACH (QString stringName, profileNames) { if (stringName == defaultProfileName) { d->colorSpaceSelector->lstProfile->addItem(defaultProfile); } else { d->colorSpaceSelector->lstProfile->addItem(stringName); } } d->colorSpaceSelector->lstProfile->setCurrentItem(defaultProfile); d->colorSpaceSelector->lstProfile->blockSignals(false); colorSpaceChanged(); } void KisAdvancedColorSpaceSelector::fillCmbDepths(const KoID& id) { KoID activeDepth = d->colorSpaceSelector->cmbColorDepth->currentItem(); d->colorSpaceSelector->cmbColorDepth->clear(); QList depths = KoColorSpaceRegistry::instance()->colorDepthList(id, KoColorSpaceRegistry::OnlyUserVisible); QList sortedDepths; if (depths.contains(Integer8BitsColorDepthID)) { sortedDepths << Integer8BitsColorDepthID; } if (depths.contains(Integer16BitsColorDepthID)) { sortedDepths << Integer16BitsColorDepthID; } if (depths.contains(Float16BitsColorDepthID)) { sortedDepths << Float16BitsColorDepthID; } if (depths.contains(Float32BitsColorDepthID)) { sortedDepths << Float32BitsColorDepthID; } if (depths.contains(Float64BitsColorDepthID)) { sortedDepths << Float64BitsColorDepthID; } d->colorSpaceSelector->cmbColorDepth->setIDList(sortedDepths); if (sortedDepths.contains(activeDepth)) { d->colorSpaceSelector->cmbColorDepth->setCurrent(activeDepth); } } void KisAdvancedColorSpaceSelector::fillDescription() { QString notApplicable = i18nc("Not Applicable, used where there's no colorants or gamma curve found","N/A"); QString notApplicableTooltip = i18nc("@info:tooltip","This profile has no colorants."); QString profileName = i18nc("Shows up instead of the name when there's no profile","No Profile Found"); QString whatIsColorant = i18n("Colorant in d50-adapted xyY."); //set colorants const QString colorSpaceId = KoColorSpaceRegistry::instance()->colorSpaceId(d->colorSpaceSelector->cmbColorModels->currentItem(), d->colorSpaceSelector->cmbColorDepth->currentItem()); QList profileList = KoColorSpaceRegistry::instance()->profilesFor(colorSpaceId); if (!profileList.isEmpty()) { profileName = currentColorSpace()->profile()->name(); if (currentColorSpace()->profile()->hasColorants()){ QVector colorants = currentColorSpace()->profile()->getColorantsxyY(); QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); //QString text = currentColorSpace()->profile()->info() + " =" + d->colorSpaceSelector->lblXYZ_W->setText(nameWhitePoint(whitepoint)); d->colorSpaceSelector->lblXYZ_W->setToolTip(QString::number(whitepoint[0], 'f', 4) + ", " + QString::number(whitepoint[1], 'f', 4) + ", " + QString::number(whitepoint[2], 'f', 4)); d->colorSpaceSelector->TongueWidget->setToolTip("
"+i18nc("@info:tooltip","This profile has the following xyY colorants:")+"
"+ i18n("Red:") +""+QString::number(colorants[0], 'f', 4) + "" + QString::number(colorants[1], 'f', 4) + "" + QString::number(colorants[2], 'f', 4)+"
"+ i18n("Green:")+""+QString::number(colorants[3], 'f', 4) + "" + QString::number(colorants[4], 'f', 4) + "" + QString::number(colorants[5], 'f', 4)+"
"+ i18n("Blue:") +""+QString::number(colorants[6], 'f', 4) + "" + QString::number(colorants[7], 'f', 4) + "" + QString::number(colorants[8], 'f', 4)+"
"); } else { QVector whitepoint2 = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->lblXYZ_W->setText(nameWhitePoint(whitepoint2)); d->colorSpaceSelector->lblXYZ_W->setToolTip(QString::number(whitepoint2[0], 'f', 4) + ", " + QString::number(whitepoint2[1], 'f', 4) + ", " + QString::number(whitepoint2[2], 'f', 4)); d->colorSpaceSelector->TongueWidget->setToolTip(notApplicableTooltip); } } else { d->colorSpaceSelector->lblXYZ_W->setText(notApplicable); d->colorSpaceSelector->lblXYZ_W->setToolTip(notApplicableTooltip); d->colorSpaceSelector->TongueWidget->setToolTip(notApplicableTooltip); } //set TRC QVector estimatedTRC(3); QString estimatedGamma = i18nc("Estimated Gamma indicates how the TRC (Tone Response Curve or Tone Reproduction Curve) is bent. A Gamma of 1.0 means linear.", "Estimated Gamma: "); QString estimatedsRGB = i18nc("This is for special Gamma types that LCMS cannot differentiate between", "Estimated Gamma: sRGB, L* or rec709 TRC"); QString whatissRGB = i18nc("@info:tooltip","The Tone Response Curve of this color space is either sRGB, L* or rec709 TRC."); QString currentModelStr = d->colorSpaceSelector->cmbColorModels->currentItem().id(); if (profileList.isEmpty()) { d->colorSpaceSelector->TongueWidget->setProfileDataAvailable(false); d->colorSpaceSelector->TRCwidget->setProfileDataAvailable(false); } else if (currentModelStr == "RGBA") { QVector colorants = currentColorSpace()->profile()->getColorantsxyY(); QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); if (currentColorSpace()->profile()->hasColorants()){ d->colorSpaceSelector->TongueWidget->setRGBData(whitepoint, colorants); } else { colorants.fill(0.0); d->colorSpaceSelector->TongueWidget->setRGBData(whitepoint, colorants); } d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); estimatedTRC = currentColorSpace()->profile()->getEstimatedTRC(); QString estimatedCurve = " Estimated curve: "; QPolygonF redcurve; QPolygonF greencurve; QPolygonF bluecurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); redcurve<colorSpaceSelector->TRCwidget->setRGBCurve(redcurve, greencurve, bluecurve); } else { QPolygonF curve = currentColorSpace()->estimatedTRCXYY(); redcurve << curve.at(0) << curve.at(1) << curve.at(2) << curve.at(3) << curve.at(4); greencurve << curve.at(5) << curve.at(6) << curve.at(7) << curve.at(8) << curve.at(9); bluecurve << curve.at(10) << curve.at(11) << curve.at(12) << curve.at(13) << curve.at(14); d->colorSpaceSelector->TRCwidget->setRGBCurve(redcurve, greencurve, bluecurve); } if (estimatedTRC[0] == -1) { d->colorSpaceSelector->TRCwidget->setToolTip(""+whatissRGB+"
"+estimatedCurve+""); } else { d->colorSpaceSelector->TRCwidget->setToolTip(""+estimatedGamma + QString::number(estimatedTRC[0]) + "," + QString::number(estimatedTRC[1]) + "," + QString::number(estimatedTRC[2])+"
"+estimatedCurve+""); } } else if (currentModelStr == "GRAYA") { QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->TongueWidget->setGrayData(whitepoint); d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); estimatedTRC = currentColorSpace()->profile()->getEstimatedTRC(); QString estimatedCurve = " Estimated curve: "; QPolygonF tonecurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); tonecurve<colorSpaceSelector->TRCwidget->setProfileDataAvailable(false); } d->colorSpaceSelector->TRCwidget->setGreyscaleCurve(tonecurve); if (estimatedTRC[0] == -1) { d->colorSpaceSelector->TRCwidget->setToolTip(""+whatissRGB+"
"+estimatedCurve+""); } else { d->colorSpaceSelector->TRCwidget->setToolTip(""+estimatedGamma + QString::number(estimatedTRC[0])+"
"+estimatedCurve+""); } } else if (currentModelStr == "CMYKA") { QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->TongueWidget->setCMYKData(whitepoint); d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); QString estimatedCurve = " Estimated curve: "; QPolygonF tonecurve; QPolygonF cyancurve; QPolygonF magentacurve; QPolygonF yellowcurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); tonecurve<colorSpaceSelector->TRCwidget->setGreyscaleCurve(tonecurve); } else { QPolygonF curve = currentColorSpace()->estimatedTRCXYY(); cyancurve << curve.at(0) << curve.at(1) << curve.at(2) << curve.at(3) << curve.at(4); magentacurve << curve.at(5) << curve.at(6) << curve.at(7) << curve.at(8) << curve.at(9); yellowcurve << curve.at(10) << curve.at(11) << curve.at(12) << curve.at(13) << curve.at(14); tonecurve << curve.at(15) << curve.at(16) << curve.at(17) << curve.at(18) << curve.at(19); d->colorSpaceSelector->TRCwidget->setCMYKCurve(cyancurve, magentacurve, yellowcurve, tonecurve); } d->colorSpaceSelector->TRCwidget->setToolTip(i18nc("@info:tooltip","Estimated Gamma cannot be retrieved for CMYK.")); } else if (currentModelStr == "XYZA") { QString estimatedCurve = " Estimated curve: "; estimatedTRC = currentColorSpace()->profile()->getEstimatedTRC(); QPolygonF tonecurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); tonecurve<colorSpaceSelector->TRCwidget->setGreyscaleCurve(tonecurve); } else { d->colorSpaceSelector->TRCwidget->setProfileDataAvailable(false); } QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->TongueWidget->setXYZData(whitepoint); d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); d->colorSpaceSelector->TRCwidget->setToolTip(""+estimatedGamma + QString::number(estimatedTRC[0])+"< br />"+estimatedCurve+""); } else if (currentModelStr == "LABA") { estimatedTRC = currentColorSpace()->profile()->getEstimatedTRC(); QString estimatedCurve = " Estimated curve: "; QPolygonF tonecurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); tonecurve<colorSpaceSelector->TRCwidget->setGreyscaleCurve(tonecurve); } else { d->colorSpaceSelector->TRCwidget->setProfileDataAvailable(false); } QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->TongueWidget->setLABData(whitepoint); d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); d->colorSpaceSelector->TRCwidget->setToolTip(""+i18nc("@info:tooltip","This is assumed to be the L * TRC. ")+"
"+estimatedCurve+""); } else if (currentModelStr == "YCbCrA") { QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->TongueWidget->setYCbCrData(whitepoint); QString estimatedCurve = " Estimated curve: "; QPolygonF tonecurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); tonecurve<colorSpaceSelector->TRCwidget->setGreyscaleCurve(tonecurve); } else { d->colorSpaceSelector->TRCwidget->setProfileDataAvailable(false); } d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); d->colorSpaceSelector->TRCwidget->setToolTip(i18nc("@info:tooltip","Estimated Gamma cannot be retrieved for YCrCb.")); } d->colorSpaceSelector->textProfileDescription->clear(); if (profileList.isEmpty()==false) { d->colorSpaceSelector->textProfileDescription->append("

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

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

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

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

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

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

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

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

" + profileName + "

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Extra notes on profiles by Elle Stone:

" "

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

")); + "Elle Stone. This is a summary. Please check " + "the full documentation as well.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

" "The ProPhotoRGB primaries are " "hard-coded into Adobe products such as Lightroom and the Dng-DCP camera 'profiles'. However, " "other than being large enough to hold a lot of colors, ProPhotoRGB has no particular merit " "as an RGB working space. Personally I recommend the Rec.2020 or ACEScg profiles over " "ProPhotoRGB. But if you have an already well-established workflow using ProPhotoRGB, you " "might find a shift to another RGB working space a little odd, at least at first, and so you " "have to weight the pros and cons of changing your workflow.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

")); } } d->colorSpaceSelector->textProfileDescription->moveCursor(QTextCursor::Start); } QString KisAdvancedColorSpaceSelector::nameWhitePoint(QVector whitePoint) { QString name=(QString::number(whitePoint[0]) + ", " + QString::number(whitePoint[1], 'f', 4)); //A (0.451170, 0.40594) (2856K)(tungsten) if ((whitePoint[0]>0.451170-0.005 && whitePoint[0]<0.451170 + 0.005) && (whitePoint[1]>0.40594-0.005 && whitePoint[1]<0.40594 + 0.005)){ name="A"; return name; } //B (0.34980, 0.35270) (4874K) (Direct Sunlight at noon)(obsolete) //C (0.31039, 0.31905) (6774K) (average/north sky daylight)(obsolete) //D50 (0.34773, 0.35952) (5003K) (Horizon Light, default color of white paper, ICC profile standard illuminant) if ((whitePoint[0]>0.34773-0.005 && whitePoint[0]<0.34773 + 0.005) && (whitePoint[1]>0.35952-0.005 && whitePoint[1]<0.35952 + 0.005)){ name="D50"; return name; } //D55 (0.33411, 0.34877) (5503K) (Mid-morning / Mid-afternoon Daylight) if ((whitePoint[0]>0.33411-0.001 && whitePoint[0]<0.33411 + 0.001) && (whitePoint[1]>0.34877-0.005 && whitePoint[1]<0.34877 + 0.005)){ name="D55"; return name; } //D60 (0.3217, 0.3378) (~6000K) (ACES colorspace default) if ((whitePoint[0]>0.3217-0.001 && whitePoint[0]<0.3217 + 0.001) && (whitePoint[1]>0.3378-0.005 && whitePoint[1]<0.3378 + 0.005)){ name="D60"; return name; } //D65 (0.31382, 0.33100) (6504K) (Noon Daylight, default for computer and tv screens, sRGB default) //Elle's are old school with 0.3127 and 0.3289 if ((whitePoint[0]>0.31382-0.002 && whitePoint[0]<0.31382 + 0.002) && (whitePoint[1]>0.33100-0.005 && whitePoint[1]<0.33100 + 0.002)){ name="D65"; return name; } //D75 (0.29968, 0.31740) (7504K) (North sky Daylight) if ((whitePoint[0]>0.29968-0.001 && whitePoint[0]<0.29968 + 0.001) && (whitePoint[1]>0.31740-0.005 && whitePoint[1]<0.31740 + 0.005)){ name="D75"; return name; } //E (1/3, 1/3) (5454K) (Equal Energy. CIERGB default) if ((whitePoint[0]>(1.0/3.0)-0.001 && whitePoint[0]<(1.0/3.0) + 0.001) && (whitePoint[1]>(1.0/3.0)-0.001 && whitePoint[1]<(1.0/3.0) + 0.001)){ name="E"; return name; } //The F series seems to sorta overlap with the D series, so I'll just leave them in comment here.// //F1 (0.31811, 0.33559) (6430K) (Daylight Fluorescent) //F2 (0.37925, 0.36733) (4230K) (Cool White Fluorescent) //F3 (0.41761, 0.38324) (3450K) (White Fluorescent) //F4 (0.44920, 0.39074) (2940K) (Warm White Fluorescent) //F5 (0.31975, 0.34246) (6350K) (Daylight Fluorescent) //F6 (0.38660, 0.37847) (4150K) (Lite White Fluorescent) //F7 (0.31569, 0.32960) (6500K) (D65 simulator, Daylight simulator) //F8 (0.34902, 0.35939) (5000K) (D50 simulator) //F9 (0.37829, 0.37045) (4150K) (Cool White Deluxe Fluorescent) //F10 (0.35090, 0.35444) (5000K) (Philips TL85, Ultralume 50) //F11 (0.38541, 0.37123) (4000K) (Philips TL84, Ultralume 40) //F12 (0.44256, 0.39717) (3000K) (Philips TL83, Ultralume 30) return name; } const KoColorSpace* KisAdvancedColorSpaceSelector::currentColorSpace() { QString check = ""; if (d->colorSpaceSelector->lstProfile->currentItem()) { check = d->colorSpaceSelector->lstProfile->currentItem()->text(); } else if (d->colorSpaceSelector->lstProfile->item(0)) { check = d->colorSpaceSelector->lstProfile->item(0)->text(); } return KoColorSpaceRegistry::instance()->colorSpace(d->colorSpaceSelector->cmbColorModels->currentItem().id(), d->colorSpaceSelector->cmbColorDepth->currentItem().id(), check); } void KisAdvancedColorSpaceSelector::setCurrentColorModel(const KoID& id) { d->colorSpaceSelector->cmbColorModels->setCurrent(id); fillLstProfiles(); fillCmbDepths(id); } void KisAdvancedColorSpaceSelector::setCurrentColorDepth(const KoID& id) { d->colorSpaceSelector->cmbColorDepth->setCurrent(id); fillLstProfiles(); } void KisAdvancedColorSpaceSelector::setCurrentProfile(const QString& name) { QList Items= d->colorSpaceSelector->lstProfile->findItems(name, Qt::MatchStartsWith); d->colorSpaceSelector->lstProfile->setCurrentItem(Items.at(0)); } void KisAdvancedColorSpaceSelector::setCurrentColorSpace(const KoColorSpace* colorSpace) { if (!colorSpace) { return; } setCurrentColorModel(colorSpace->colorModelId()); setCurrentColorDepth(colorSpace->colorDepthId()); setCurrentProfile(colorSpace->profile()->name()); } void KisAdvancedColorSpaceSelector::colorSpaceChanged() { bool valid = d->colorSpaceSelector->lstProfile->count() != 0; emit(selectionChanged(valid)); if (valid) { emit colorSpaceChanged(currentColorSpace()); } } void KisAdvancedColorSpaceSelector::installProfile() { KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocumentICC"); dialog.setCaption(i18n("Install Color Profiles")); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); dialog.setMimeTypeFilters(QStringList() << "application/vnd.iccprofile", "application/vnd.iccprofile"); QStringList profileNames = dialog.filenames(); KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); Q_ASSERT(iccEngine); QString saveLocation = KoResourcePaths::saveLocation("icc_profiles"); Q_FOREACH (const QString &profileName, profileNames) { QUrl file(profileName); if (!QFile::copy(profileName, saveLocation + file.fileName())) { dbgKrita << "Could not install profile!"; return; } iccEngine->addProfile(saveLocation + file.fileName()); } fillLstProfiles(); } diff --git a/libs/ui/widgets/kis_cmb_gradient.cpp b/libs/ui/widgets/kis_cmb_gradient.cpp index f46b3f39a1..e98cb40be0 100644 --- a/libs/ui/widgets/kis_cmb_gradient.cpp +++ b/libs/ui/widgets/kis_cmb_gradient.cpp @@ -1,78 +1,77 @@ /* * Copyright (c) 2015 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_cmb_gradient.h" #include #include #include #include #include #include #include #include "kis_gradient_chooser.h" KisCmbGradient::KisCmbGradient(QWidget *parent) : KisPopupButton(parent) , m_gradientChooser(new KisGradientChooser(this)) { connect(m_gradientChooser, SIGNAL(resourceSelected(KoResource*)), SLOT(gradientSelected(KoResource*))); setPopupWidget(m_gradientChooser); } void KisCmbGradient::setGradient(KoAbstractGradient *gradient) { m_gradientChooser->setCurrentResource(gradient); } KoAbstractGradient *KisCmbGradient::gradient() const { return dynamic_cast(m_gradientChooser->currentResource()); } void KisCmbGradient::gradientSelected(KoResource *resource) { KoAbstractGradient *gradient = dynamic_cast(resource); if (!gradient) return; QImage pm = gradient->generatePreview(iconSize().width(), iconSize().height()); setIcon(QIcon(QPixmap::fromImage(pm))); emit gradientChanged(gradient); } QSize KisCmbGradient::sizeHint() const { ensurePolished(); QFontMetrics fm = fontMetrics(); int maxW = 7 * fm.width(QChar('x')) + 18; int maxH = qMax(fm.lineSpacing(), 14) + 2; QStyleOptionComboBox options; options.initFrom(this); - return style()->sizeFromContents(QStyle::CT_ComboBox, &options, - QSize(maxW, maxH), this).expandedTo(QApplication::globalStrut()); + return style()->sizeFromContents(QStyle::CT_ComboBox, &options, QSize(maxW, maxH), this); } void KisCmbGradient::resizeEvent(QResizeEvent *event) { setIconSize(QSize(event->size().width() - 30, event->size().height() - 4)); KisPopupButton::resizeEvent(event); } diff --git a/libs/ui/widgets/kis_color_filter_combo.cpp b/libs/ui/widgets/kis_color_filter_combo.cpp index 518d2e8eb7..224054391f 100644 --- a/libs/ui/widgets/kis_color_filter_combo.cpp +++ b/libs/ui/widgets/kis_color_filter_combo.cpp @@ -1,348 +1,371 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_color_filter_combo.h" #include #include #include #include +#include #include #include #include #include #include #include "kis_node_view_color_scheme.h" #include "kis_debug.h" #include "kis_icon_utils.h" #include "krita_utils.h" #include "kis_node.h" enum AdditionalRoles { OriginalLabelIndex = Qt::UserRole + 1000 }; struct LabelFilteringModel : public QSortFilterProxyModel { LabelFilteringModel(QObject *parent) : QSortFilterProxyModel(parent) {} bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override { const QModelIndex index = sourceModel()->index(source_row, 0, source_parent); const int labelIndex = index.data(OriginalLabelIndex).toInt(); return labelIndex < 0 || m_acceptedLabels.contains(labelIndex); } void setAcceptedLabels(const QSet &value) { m_acceptedLabels = value; invalidateFilter(); } private: QSet m_acceptedLabels; }; class ComboEventFilter : public QObject { public: ComboEventFilter(KisColorFilterCombo *parent) : m_parent(parent), m_buttonPressed(false) {} protected: bool eventFilter(QObject *obj, QEvent *event) override { if (event->type() == QEvent::Leave) { m_buttonPressed = false; } else if (event->type() == QEvent::MouseButtonPress) { QMouseEvent *mevent = static_cast(event); m_buttonPressed = mevent->button() == Qt::LeftButton; } else if (event->type() == QEvent::MouseButtonRelease) { QMouseEvent *mevent = static_cast(event); QModelIndex index = m_parent->view()->indexAt(mevent->pos()); if (!index.isValid()) return false; /** * We should eat the first event that arrives exactly when * the drop down appears on screen. */ if (!m_buttonPressed) return true; const bool toUncheckedState = index.data(Qt::CheckStateRole) == Qt::Checked; if (toUncheckedState) { m_parent->model()->setData(index, Qt::Unchecked, Qt::CheckStateRole); } else { m_parent->model()->setData(index, Qt::Checked, Qt::CheckStateRole); } if (index.data(OriginalLabelIndex).toInt() == -1) { for (int i = 0; i < m_parent->model()->rowCount(); i++) { const QModelIndex &other = m_parent->model()->index(i, 0); if (other.data(OriginalLabelIndex) != -1) { m_parent->model()->setData(other, toUncheckedState ? Qt::Unchecked : Qt::Checked, Qt::CheckStateRole); } } } else { bool prevChecked = false; bool checkedVaries = false; QModelIndex allLabelsIndex; for (int i = 0; i < m_parent->model()->rowCount(); i++) { const QModelIndex &other = m_parent->model()->index(i, 0); if (other.data(OriginalLabelIndex) != -1) { const bool currentChecked = other.data(Qt::CheckStateRole) == Qt::Checked; if (i == 0) { prevChecked = currentChecked; } else { if (prevChecked != currentChecked) { checkedVaries = true; break; } } } else { allLabelsIndex = other; } } const bool allLabelsIndexShouldBeChecked = prevChecked && !checkedVaries; if (allLabelsIndexShouldBeChecked != (allLabelsIndex.data(Qt::CheckStateRole) == Qt::Checked)) { m_parent->model()->setData(allLabelsIndex, allLabelsIndexShouldBeChecked ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole); } } emit m_parent->selectedColorsChanged(); m_buttonPressed = false; return true; } return QObject::eventFilter(obj, event); } private: KisColorFilterCombo *m_parent; bool m_buttonPressed; }; class FullSizedListView : public QListView { public: QSize sizeHint() const override { return contentsSize(); } }; +class PopupComboBoxStyle : public QProxyStyle +{ +public: + PopupComboBoxStyle(QStyle *baseStyle = nullptr) : QProxyStyle(baseStyle) {} + + int styleHint(QStyle::StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returnData) const override + { + // This flag makes ComboBox popup float ontop of its parent ComboBox, like in Fusion style. + // Only when this hint is set will Qt respect combobox popup size hints, otherwise the popup + // can never exceed the width of its parent ComboBox, like in Breeze style. + if (hint == QStyle::SH_ComboBox_Popup) { + return true; + } + + return QProxyStyle::styleHint(hint, option, widget, returnData); + } +}; + struct KisColorFilterCombo::Private { LabelFilteringModel *filteringModel; }; KisColorFilterCombo::KisColorFilterCombo(QWidget *parent) : QComboBox(parent), m_d(new Private) { QStandardItemModel *newModel = new QStandardItemModel(this); setModel(newModel); + PopupComboBoxStyle *proxyStyle = new PopupComboBoxStyle(style()); + proxyStyle->setParent(this); + setStyle(proxyStyle); + setView(new FullSizedListView); m_eventFilters.append(new ComboEventFilter(this)); m_eventFilters.append(new ComboEventFilter(this)); view()->installEventFilter(m_eventFilters[0]); view()->viewport()->installEventFilter(m_eventFilters[1]); KisNodeViewColorScheme scm; QStandardItem* item = new QStandardItem(i18nc("combo box: show all layers", "All")); item->setCheckable(true); item->setCheckState(Qt::Unchecked); item->setData(QColor(Qt::transparent), Qt::BackgroundColorRole); item->setData(int(-1), OriginalLabelIndex); item->setData(QSize(30, scm.rowHeight()), Qt::SizeHintRole); newModel->appendRow(item); int labelIndex = 0; foreach (const QColor &color, scm.allColorLabels()) { const QString title = color.alpha() > 0 ? "" : i18nc("combo box: select all layers without a label", "No Label"); QStandardItem* item = new QStandardItem(title); item->setCheckable(true); item->setCheckState(Qt::Unchecked); item->setData(color, Qt::BackgroundColorRole); item->setData(labelIndex, OriginalLabelIndex); item->setData(QSize(30, scm.rowHeight()), Qt::SizeHintRole); newModel->appendRow(item); labelIndex++; } m_d->filteringModel = new LabelFilteringModel(this); QAbstractItemModel *originalModel = model(); originalModel->setParent(m_d->filteringModel); m_d->filteringModel->setSourceModel(originalModel); setModel(m_d->filteringModel); } KisColorFilterCombo::~KisColorFilterCombo() { qDeleteAll(m_eventFilters); } void collectAvailableLabels(KisNodeSP root, QSet *labels) { labels->insert(root->colorLabelIndex()); KisNodeSP node = root->firstChild(); while (node) { collectAvailableLabels(node, labels); node = node->nextSibling(); } } void KisColorFilterCombo::updateAvailableLabels(KisNodeSP rootNode) { QSet labels; collectAvailableLabels(rootNode, &labels); updateAvailableLabels(labels); } void KisColorFilterCombo::updateAvailableLabels(const QSet &labels) { m_d->filteringModel->setAcceptedLabels(labels); } QList KisColorFilterCombo::selectedColors() const { QList colors; for (int i = 0; i < model()->rowCount(); i++) { const QModelIndex &other = model()->index(i, 0); const int label = other.data(OriginalLabelIndex).toInt(); if (label != -1 && other.data(Qt::CheckStateRole) == Qt::Checked) { colors << label; } } return colors; } void KisColorFilterCombo::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QStylePainter painter(this); painter.setPen(palette().color(QPalette::Text)); // draw the combobox frame, focusrect and selected etc. QStyleOptionComboBox opt; initStyleOption(&opt); painter.drawComplexControl(QStyle::CC_ComboBox, opt); { KisNodeViewColorScheme scm; const QRect editRect = style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this); const int size = qMin(editRect.width(), editRect.height()); const QList selectedColors = this->selectedColors(); if (selectedColors.size() == 0 || selectedColors.size() == model()->rowCount() - 1) { QIcon icon = KisIconUtils::loadIcon("view-filter"); QPixmap pixmap = icon.pixmap(QSize(size, size), !isEnabled() ? QIcon::Disabled : QIcon::Normal); painter.drawPixmap(editRect.right() - size, editRect.top(), pixmap); } else if (selectedColors.size() == 1) { const int currentLabel = selectedColors.first(); QColor currentColor = scm.colorLabel(currentLabel); if (currentColor.alpha() > 0) { painter.fillRect(editRect, currentColor); } else if (currentLabel == 0) { QPen oldPen = painter.pen(); const int border = 4; QRect crossRect(0, 0, size - 2 * border, size - 2 * border); crossRect.moveCenter(editRect.center()); QColor shade = opt.palette.dark().color(); painter.setPen(QPen(shade, 2)); painter.drawLine(crossRect.topLeft(), crossRect.bottomRight()); painter.drawLine(crossRect.bottomLeft(), crossRect.topRight()); } } else { const int border = 0; QRect pieRect(0, 0, size - 2 * border, size - 2 * border); pieRect.moveCenter(editRect.center()); const int numColors = selectedColors.size(); const int step = 16 * 360 / numColors; int currentAngle = 0; //painter.save(); // optimize out! painter.setRenderHint(QPainter::Antialiasing); for (int i = 0; i < numColors; i++) { QColor color = scm.colorLabel(selectedColors[i]); QBrush brush = color.alpha() > 0 ? QBrush(color) : QBrush(Qt::black, Qt::Dense4Pattern); painter.setPen(color); painter.setBrush(brush); painter.drawPie(pieRect, currentAngle, step); currentAngle += step; } //painter.restore(); // optimize out! } } // draw the icon and text //painter.drawControl(QStyle::CE_ComboBoxLabel, opt); } QSize KisColorFilterCombo::minimumSizeHint() const { return sizeHint(); } QSize KisColorFilterCombo::sizeHint() const { QStyleOptionComboBox opt; initStyleOption(&opt); const QStyleOption *baseOption = qstyleoption_cast(&opt); const int arrowSize = style()->pixelMetric(QStyle::PM_ScrollBarExtent, baseOption, this); const QSize originalHint = QComboBox::sizeHint(); QSize sh(3 * arrowSize, originalHint.height()); - return sh.expandedTo(QApplication::globalStrut()); + return sh; } diff --git a/libs/ui/widgets/kis_curve_widget.h b/libs/ui/widgets/kis_curve_widget.h index 5ffca8bdf8..5e0c644434 100644 --- a/libs/ui/widgets/kis_curve_widget.h +++ b/libs/ui/widgets/kis_curve_widget.h @@ -1,178 +1,178 @@ /* * Copyright (c) 2005 C. Boemann * Copyright (c) 2009 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_CURVE_WIDGET_H #define KIS_CURVE_WIDGET_H // Qt includes. #include #include #include #include #include #include #include #include #include #include class QSpinBox; class KisCubicCurve; /** * KisCurveWidget is a widget that shows a single curve that can be edited * by the user. The user can grab the curve and move it; this creates * a new control point. Control points can be deleted by selecting a point * and pressing the delete key. * - * (From: http://techbase.kde.org/Projects/Widgets_and_Classes#KisCurveWidget) + * (From: https://techbase.kde.org/Projects/Widgets_and_Classes#KisCurveWidget) * KisCurveWidget allows editing of spline based y=f(x) curves. Handy for cases * where you want the user to control such things as tablet pressure * response, color transformations, acceleration by time, aeroplane lift *by angle of attack. */ class KRITAUI_EXPORT KisCurveWidget : public QWidget { Q_OBJECT Q_PROPERTY(bool pointSelected READ pointSelected NOTIFY pointSelectedChanged); public: friend class CurveEditorItem; /** * Create a new curve widget with a default curve, that is a straight * line from bottom-left to top-right. */ KisCurveWidget(QWidget *parent = 0, Qt::WindowFlags f = 0); ~KisCurveWidget() override; /** * Reset the curve to the default shape */ void reset(void); /** * Enable the guide and set the guide color to the specified color. * * XXX: it seems that the guide feature isn't actually implemented yet? */ void setCurveGuide(const QColor & color); /** * Set a background pixmap. The background pixmap will be drawn under * the grid and the curve. * * XXX: or is the pixmap what is drawn to the left and bottom of the curve * itself? */ void setPixmap(const QPixmap & pix); QPixmap getPixmap(); void setBasePixmap(const QPixmap & pix); QPixmap getBasePixmap(); /** * Whether or not there is a point selected * This does NOT include the first and last points */ bool pointSelected() const; Q_SIGNALS: /** * Emitted whenever a control point has changed position. */ void modified(void); /** * Emitted whenever the status of whether a control point is selected or not changes */ void pointSelectedChanged(); /** * Emitted to notify that the start() function in compressor can be activated. * Thanks to that, blocking signals in curve widget blocks "sending signals" * (calling start() function) *to* the signal compressor. * It effectively makes signals work nearly the same way they worked before * adding the signal compressor in between. */ void compressorShouldEmitModified(); protected Q_SLOTS: void inOutChanged(int); void notifyModified(); /** * This function is called when compressorShouldEmitModified() is emitted. * For why it's needed, \see compressorShouldEmitModified() */ void slotCompressorShouldEmitModified(); protected: void keyPressEvent(QKeyEvent *) override; void paintEvent(QPaintEvent *) override; void mousePressEvent(QMouseEvent * e) override; void mouseReleaseEvent(QMouseEvent * e) override; void mouseMoveEvent(QMouseEvent * e) override; void leaveEvent(QEvent *) override; void resizeEvent(QResizeEvent *e) override; public: /** * @return get a list with all defined points. If you want to know what the * y value for a given x is on the curve defined by these points, use getCurveValue(). * @see getCurveValue */ KisCubicCurve curve(); /** * Replace the current curve with a curve specified by the curve defined by the control * points in @p inlist. */ void setCurve(KisCubicCurve inlist); /** * Connect/disconnect external spinboxes to the curve * @p inMin / @p inMax - is the range for input values * @p outMin / @p outMax - is the range for output values */ void setupInOutControls(QSpinBox *in, QSpinBox *out, int inMin, int inMax, int outMin, int outMax); void dropInOutControls(); /** * Handy function that creates new point in the middle * of the curve and sets focus on the @p m_intIn field, * so the user can move this point anywhere in a moment */ void addPointInTheMiddle(); private: class Private; Private * const d; }; #endif /* KIS_CURVE_WIDGET_H */ diff --git a/libs/ui/widgets/kis_slider_spin_box.cpp b/libs/ui/widgets/kis_slider_spin_box.cpp index 8db1984887..5e36564366 100644 --- a/libs/ui/widgets/kis_slider_spin_box.cpp +++ b/libs/ui/widgets/kis_slider_spin_box.cpp @@ -1,1052 +1,1052 @@ /* This file is part of the KDE project * Copyright (c) 2010 Justin Noel * Copyright (c) 2010 Cyrille Berger * Copyright (c) 2015 Moritz Molch * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_slider_spin_box.h" #include #include #include #include #include #include #include #include #include #include #include #include "kis_cursor.h" #include "KisPart.h" #include "input/kis_input_manager.h" #include "kis_num_parser.h" class KisAbstractSliderSpinBoxPrivate { public: enum Style { STYLE_NOQUIRK, STYLE_PLASTIQUE, STYLE_BREEZE, STYLE_FUSION, }; QLineEdit* edit; QDoubleValidator* validator; bool upButtonDown; bool downButtonDown; int factor; int fastSliderStep; qreal slowFactor; qreal shiftPercent; bool shiftMode; QString prefix; QString suffix; qreal exponentRatio; int value; int maximum; int minimum; int singleStep; QSpinBox* dummySpinBox; Style style; bool blockUpdateSignalOnDrag; bool isDragging; bool parseInt; }; KisAbstractSliderSpinBox::KisAbstractSliderSpinBox(QWidget* parent, KisAbstractSliderSpinBoxPrivate* _d) : QWidget(parent) , d_ptr(_d) { Q_D(KisAbstractSliderSpinBox); QEvent e(QEvent::StyleChange); changeEvent(&e); d->upButtonDown = false; d->downButtonDown = false; d->edit = new QLineEdit(this); d->edit->setFrame(false); d->edit->setAlignment(Qt::AlignCenter); d->edit->hide(); d->edit->setContentsMargins(0,0,0,0); d->edit->installEventFilter(this); //Make edit transparent d->edit->setAutoFillBackground(false); QPalette pal = d->edit->palette(); pal.setColor(QPalette::Base, Qt::transparent); d->edit->setPalette(pal); d->edit->setContextMenuPolicy(Qt::PreventContextMenu); connect(d->edit, SIGNAL(editingFinished()), this, SLOT(editLostFocus())); d->validator = new QDoubleValidator(d->edit); d->value = 0; d->minimum = 0; d->maximum = 100; d->factor = 1.0; d->singleStep = 1; d->fastSliderStep = 5; d->slowFactor = 0.1; d->shiftMode = false; d->blockUpdateSignalOnDrag = false; d->isDragging = false; d->parseInt = false; setExponentRatio(1.0); setCursor(KisCursor::splitHCursor()); //Set sane defaults setFocusPolicy(Qt::StrongFocus); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); //dummy needed to fix a bug in the polyester theme d->dummySpinBox = new QSpinBox(this); d->dummySpinBox->hide(); } KisAbstractSliderSpinBox::~KisAbstractSliderSpinBox() { Q_D(KisAbstractSliderSpinBox); delete d; } void KisAbstractSliderSpinBox::showEdit() { Q_D(KisAbstractSliderSpinBox); if (d->edit->isVisible()) return; if (d->style == KisAbstractSliderSpinBoxPrivate::STYLE_PLASTIQUE) { d->edit->setGeometry(progressRect(spinBoxOptions()).adjusted(0,0,-2,0)); } else { d->edit->setGeometry(progressRect(spinBoxOptions())); } d->edit->setText(valueString()); d->edit->selectAll(); d->edit->show(); d->edit->setFocus(Qt::OtherFocusReason); update(); } void KisAbstractSliderSpinBox::hideEdit() { Q_D(KisAbstractSliderSpinBox); d->edit->hide(); update(); } void KisAbstractSliderSpinBox::paintEvent(QPaintEvent* e) { Q_D(KisAbstractSliderSpinBox); Q_UNUSED(e) QPainter painter(this); switch (d->style) { case KisAbstractSliderSpinBoxPrivate::STYLE_FUSION: paintFusion(painter); break; case KisAbstractSliderSpinBoxPrivate::STYLE_PLASTIQUE: paintPlastique(painter); break; case KisAbstractSliderSpinBoxPrivate::STYLE_BREEZE: paintBreeze(painter); break; default: paint(painter); break; } painter.end(); } void KisAbstractSliderSpinBox::paint(QPainter &painter) { Q_D(KisAbstractSliderSpinBox); //Create options to draw spin box parts QStyleOptionSpinBox spinOpts = spinBoxOptions(); spinOpts.rect.adjust(0, 2, 0, -2); //Draw "SpinBox".Clip off the area of the lineEdit to avoid double //borders being drawn painter.save(); painter.setClipping(true); QRect eraseRect(QPoint(rect().x(), rect().y()), QPoint(progressRect(spinOpts).right(), rect().bottom())); painter.setClipRegion(QRegion(rect()).subtracted(eraseRect)); style()->drawComplexControl(QStyle::CC_SpinBox, &spinOpts, &painter, d->dummySpinBox); painter.setClipping(false); painter.restore(); QStyleOptionProgressBar progressOpts = progressBarOptions(); progressOpts.rect.adjust(0, 2, 0, -2); style()->drawControl(QStyle::CE_ProgressBar, &progressOpts, &painter, 0); //Draw focus if necessary if (hasFocus() && d->edit->hasFocus()) { QStyleOptionFocusRect focusOpts; focusOpts.initFrom(this); focusOpts.rect = progressOpts.rect; focusOpts.backgroundColor = palette().color(QPalette::Window); style()->drawPrimitive(QStyle::PE_FrameFocusRect, &focusOpts, &painter, this); } } void KisAbstractSliderSpinBox::paintFusion(QPainter &painter) { Q_D(KisAbstractSliderSpinBox); QStyleOptionSpinBox spinOpts = spinBoxOptions(); spinOpts.frame = true; spinOpts.rect.adjust(0, -1, 0, 1); //spinOpts.palette().setBrush(QPalette::Base, palette().highlight()); QStyleOptionProgressBar progressOpts = progressBarOptions(); style()->drawComplexControl(QStyle::CC_SpinBox, &spinOpts, &painter, d->dummySpinBox); painter.save(); QRect rect = progressOpts.rect.adjusted(1,2,-4,-2); QRect leftRect; int progressIndicatorPos = (progressOpts.progress - qreal(progressOpts.minimum)) / qMax(qreal(1.0), qreal(progressOpts.maximum) - progressOpts.minimum) * rect.width(); if (progressIndicatorPos >= 0 && progressIndicatorPos <= rect.width()) { leftRect = QRect(rect.left(), rect.top(), progressIndicatorPos, rect.height()); } else if (progressIndicatorPos > rect.width()) { painter.setPen(palette().highlightedText().color()); } else { painter.setPen(palette().buttonText().color()); } QRegion rightRect = rect; rightRect = rightRect.subtracted(leftRect); QTextOption textOption(Qt::AlignAbsolute | Qt::AlignHCenter | Qt::AlignVCenter); textOption.setWrapMode(QTextOption::NoWrap); if (!(d->edit && d->edit->isVisible())) { painter.setClipRegion(rightRect); painter.setClipping(true); painter.drawText(rect.adjusted(-2,0,2,0), progressOpts.text, textOption); painter.setClipping(false); } if (!leftRect.isNull()) { painter.setClipRect(leftRect.adjusted(0, -1, 1, 1)); painter.setPen(palette().highlight().color()); painter.setBrush(palette().highlight()); spinOpts.palette.setBrush(QPalette::Base, palette().highlight()); style()->drawComplexControl(QStyle::CC_SpinBox, &spinOpts, &painter, d->dummySpinBox); if (!(d->edit && d->edit->isVisible())) { painter.setPen(palette().highlightedText().color()); painter.setClipping(true); painter.drawText(rect.adjusted(-2,0,2,0), progressOpts.text, textOption); } painter.setClipping(false); } painter.restore(); } void KisAbstractSliderSpinBox::paintPlastique(QPainter &painter) { Q_D(KisAbstractSliderSpinBox); QStyleOptionSpinBox spinOpts = spinBoxOptions(); QStyleOptionProgressBar progressOpts = progressBarOptions(); style()->drawComplexControl(QStyle::CC_SpinBox, &spinOpts, &painter, d->dummySpinBox); painter.save(); QRect rect = progressOpts.rect.adjusted(2,0,-2,0); QRect leftRect; int progressIndicatorPos = (progressOpts.progress - qreal(progressOpts.minimum)) / qMax(qreal(1.0), qreal(progressOpts.maximum) - progressOpts.minimum) * rect.width(); if (progressIndicatorPos >= 0 && progressIndicatorPos <= rect.width()) { leftRect = QRect(rect.left(), rect.top(), progressIndicatorPos, rect.height()); } else if (progressIndicatorPos > rect.width()) { painter.setPen(palette().highlightedText().color()); } else { painter.setPen(palette().buttonText().color()); } QRegion rightRect = rect; rightRect = rightRect.subtracted(leftRect); QTextOption textOption(Qt::AlignAbsolute | Qt::AlignHCenter | Qt::AlignVCenter); textOption.setWrapMode(QTextOption::NoWrap); if (!(d->edit && d->edit->isVisible())) { painter.setClipRegion(rightRect); painter.setClipping(true); painter.drawText(rect.adjusted(-2,0,2,0), progressOpts.text, textOption); painter.setClipping(false); } if (!leftRect.isNull()) { painter.setPen(palette().highlight().color()); painter.setBrush(palette().highlight()); painter.drawRect(leftRect.adjusted(0,0,0,-1)); if (!(d->edit && d->edit->isVisible())) { painter.setPen(palette().highlightedText().color()); painter.setClipRect(leftRect.adjusted(0,0,1,0)); painter.setClipping(true); painter.drawText(rect.adjusted(-2,0,2,0), progressOpts.text, textOption); painter.setClipping(false); } } painter.restore(); } void KisAbstractSliderSpinBox::paintBreeze(QPainter &painter) { Q_D(KisAbstractSliderSpinBox); QStyleOptionSpinBox spinOpts = spinBoxOptions(); QStyleOptionProgressBar progressOpts = progressBarOptions(); QString valueText = progressOpts.text; progressOpts.text = ""; progressOpts.rect.adjust(0, 1, 0, -1); style()->drawComplexControl(QStyle::CC_SpinBox, &spinOpts, &painter, this); style()->drawControl(QStyle::CE_ProgressBarGroove, &progressOpts, &painter, this); painter.save(); QRect leftRect; int progressIndicatorPos = (progressOpts.progress - qreal(progressOpts.minimum)) / qMax(qreal(1.0), qreal(progressOpts.maximum) - progressOpts.minimum) * progressOpts.rect.width(); if (progressIndicatorPos >= 0 && progressIndicatorPos <= progressOpts.rect.width()) { leftRect = QRect(progressOpts.rect.left(), progressOpts.rect.top(), progressIndicatorPos, progressOpts.rect.height()); } else if (progressIndicatorPos > progressOpts.rect.width()) { painter.setPen(palette().highlightedText().color()); } else { painter.setPen(palette().buttonText().color()); } QRegion rightRect = progressOpts.rect; rightRect = rightRect.subtracted(leftRect); painter.setClipRegion(rightRect); QTextOption textOption(Qt::AlignAbsolute | Qt::AlignHCenter | Qt::AlignVCenter); textOption.setWrapMode(QTextOption::NoWrap); if (!(d->edit && d->edit->isVisible())) { painter.drawText(progressOpts.rect, valueText, textOption); } if (!leftRect.isNull()) { painter.setPen(palette().highlightedText().color()); painter.setClipRect(leftRect); style()->drawControl(QStyle::CE_ProgressBarContents, &progressOpts, &painter, this); if (!(d->edit && d->edit->isVisible())) { painter.drawText(progressOpts.rect, valueText, textOption); } } painter.restore(); } void KisAbstractSliderSpinBox::mousePressEvent(QMouseEvent* e) { Q_D(KisAbstractSliderSpinBox); QStyleOptionSpinBox spinOpts = spinBoxOptions(); //Depress buttons or highlight slider //Also used to emulate mouse grab... if (e->buttons() & Qt::LeftButton) { if (upButtonRect(spinOpts).contains(e->pos())) { d->upButtonDown = true; } else if (downButtonRect(spinOpts).contains(e->pos())) { d->downButtonDown = true; } } else if (e->buttons() & Qt::RightButton) { showEdit(); } update(); } void KisAbstractSliderSpinBox::mouseReleaseEvent(QMouseEvent* e) { Q_D(KisAbstractSliderSpinBox); QStyleOptionSpinBox spinOpts = spinBoxOptions(); d->isDragging = false; //Step up/down for buttons //Emualting mouse grab too if (upButtonRect(spinOpts).contains(e->pos()) && d->upButtonDown) { setInternalValue(d->value + d->singleStep); } else if (downButtonRect(spinOpts).contains(e->pos()) && d->downButtonDown) { setInternalValue(d->value - d->singleStep); } else if (progressRect(spinOpts).contains(e->pos()) && !(d->edit->isVisible()) && !(d->upButtonDown || d->downButtonDown)) { //Snap to percentage for progress area setInternalValue(valueForX(e->pos().x(),e->modifiers())); } else { // Confirm the last known value, since we might be ignoring move events setInternalValue(d->value); } d->upButtonDown = false; d->downButtonDown = false; update(); } void KisAbstractSliderSpinBox::mouseMoveEvent(QMouseEvent* e) { Q_D(KisAbstractSliderSpinBox); if( e->modifiers() & Qt::ShiftModifier ) { if( !d->shiftMode ) { d->shiftPercent = pow( qreal(d->value - d->minimum)/qreal(d->maximum - d->minimum), 1/qreal(d->exponentRatio) ); d->shiftMode = true; } } else { d->shiftMode = false; } //Respect emulated mouse grab. if (e->buttons() & Qt::LeftButton && !(d->downButtonDown || d->upButtonDown)) { d->isDragging = true; setInternalValue(valueForX(e->pos().x(),e->modifiers()), d->blockUpdateSignalOnDrag); update(); } } void KisAbstractSliderSpinBox::keyPressEvent(QKeyEvent* e) { Q_D(KisAbstractSliderSpinBox); switch (e->key()) { case Qt::Key_Up: case Qt::Key_Right: setInternalValue(d->value + d->singleStep); break; case Qt::Key_Down: case Qt::Key_Left: setInternalValue(d->value - d->singleStep); break; case Qt::Key_Shift: d->shiftPercent = pow( qreal(d->value - d->minimum)/qreal(d->maximum - d->minimum), 1/qreal(d->exponentRatio) ); d->shiftMode = true; break; case Qt::Key_Enter: //Line edit isn't "accepting" key strokes... case Qt::Key_Return: case Qt::Key_Escape: case Qt::Key_Control: case Qt::Key_Alt: case Qt::Key_AltGr: case Qt::Key_Super_L: case Qt::Key_Super_R: break; default: showEdit(); d->edit->event(e); break; } } void KisAbstractSliderSpinBox::wheelEvent(QWheelEvent *e) { Q_D(KisAbstractSliderSpinBox); if ( e->delta() > 0) { setInternalValue(d->value + d->singleStep); } else { setInternalValue(d->value - d->singleStep); } update(); e->accept(); } bool KisAbstractSliderSpinBox::event(QEvent *event) { if (event->type() == QEvent::ShortcutOverride){ QKeyEvent* key = static_cast(event); if (key->modifiers() == Qt::NoModifier){ switch(key->key()){ case Qt::Key_Up: case Qt::Key_Right: case Qt::Key_Down: case Qt::Key_Left: event->accept(); return true; default: break; } } } return QWidget::event(event); } void KisAbstractSliderSpinBox::commitEnteredValue() { Q_D(KisAbstractSliderSpinBox); //QLocale locale; bool ok = false; //qreal value = locale.toDouble(d->edit->text(), &ok) * d->factor; qreal value; if (d->parseInt) { value = KisNumericParser::parseIntegerMathExpr(d->edit->text(), &ok) * d->factor; } else { value = KisNumericParser::parseSimpleMathExpr(d->edit->text(), &ok) * d->factor; } if (ok) { setInternalValue(value); } } bool KisAbstractSliderSpinBox::eventFilter(QObject* recv, QEvent* e) { Q_D(KisAbstractSliderSpinBox); if (recv == static_cast(d->edit) && e->type() == QEvent::KeyRelease) { QKeyEvent* keyEvent = static_cast(e); switch (keyEvent->key()) { case Qt::Key_Enter: case Qt::Key_Return: { commitEnteredValue(); hideEdit(); return true; } case Qt::Key_Escape: d->edit->setText(valueString()); hideEdit(); return true; default: break; } } return false; } QSize KisAbstractSliderSpinBox::sizeHint() const { const Q_D(KisAbstractSliderSpinBox); QStyleOptionSpinBox spinOpts = spinBoxOptions(); QFont ft(font()); if (d->style == KisAbstractSliderSpinBoxPrivate::STYLE_NOQUIRK) { // Some styles use bold font in progressbars // unfortunately there is no reliable way to check for that ft.setBold(true); } QFontMetrics fm(ft); QSize hint(fm.boundingRect(d->prefix + QString::number(d->maximum) + d->suffix).size()); hint += QSize(0, 2); switch (d->style) { case KisAbstractSliderSpinBoxPrivate::STYLE_FUSION: hint += QSize(8, 8); break; case KisAbstractSliderSpinBoxPrivate::STYLE_PLASTIQUE: hint += QSize(8, 0); break; case KisAbstractSliderSpinBoxPrivate::STYLE_BREEZE: hint += QSize(2, 0); break; case KisAbstractSliderSpinBoxPrivate::STYLE_NOQUIRK: // almost all "modern" styles have a margin around controls hint += QSize(6, 6); break; default: break; } //Getting the size of the buttons is a pain as the calcs require a rect //that is "big enough". We run the calc twice to get the "smallest" buttons //This code was inspired by QAbstractSpinBox QSize extra(1000, 0); spinOpts.rect.setSize(hint + extra); extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &spinOpts, QStyle::SC_SpinBoxEditField, this).size(); spinOpts.rect.setSize(hint + extra); extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &spinOpts, QStyle::SC_SpinBoxEditField, this).size(); hint += extra; spinOpts.rect.setSize(hint); return style()->sizeFromContents(QStyle::CT_SpinBox, &spinOpts, hint) .expandedTo(QApplication::globalStrut()); } QSize KisAbstractSliderSpinBox::minimumSizeHint() const { return sizeHint(); } QSize KisAbstractSliderSpinBox::minimumSize() const { - return QWidget::minimumSize().expandedTo(minimumSizeHint()); + return QWidget::minimumSize(); } QStyleOptionSpinBox KisAbstractSliderSpinBox::spinBoxOptions() const { const Q_D(KisAbstractSliderSpinBox); QStyleOptionSpinBox opts; opts.initFrom(this); opts.frame = false; opts.buttonSymbols = QAbstractSpinBox::UpDownArrows; opts.subControls = QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown; //Disable non-logical buttons if (d->value == d->minimum) { opts.stepEnabled = QAbstractSpinBox::StepUpEnabled; } else if (d->value == d->maximum) { opts.stepEnabled = QAbstractSpinBox::StepDownEnabled; } else { opts.stepEnabled = QAbstractSpinBox::StepUpEnabled | QAbstractSpinBox::StepDownEnabled; } //Deal with depressed buttons if (d->upButtonDown) { opts.activeSubControls = QStyle::SC_SpinBoxUp; } else if (d->downButtonDown) { opts.activeSubControls = QStyle::SC_SpinBoxDown; } else { opts.activeSubControls = 0; } return opts; } QStyleOptionProgressBar KisAbstractSliderSpinBox::progressBarOptions() const { const Q_D(KisAbstractSliderSpinBox); QStyleOptionSpinBox spinOpts = spinBoxOptions(); //Create opts for drawing the progress portion QStyleOptionProgressBar progressOpts; progressOpts.initFrom(this); progressOpts.maximum = d->maximum; progressOpts.minimum = d->minimum; qreal minDbl = d->minimum; qreal dValues = (d->maximum - minDbl); progressOpts.progress = dValues * pow((d->value - minDbl) / dValues, 1.0 / d->exponentRatio) + minDbl; progressOpts.text = d->prefix + valueString() + d->suffix; progressOpts.textAlignment = Qt::AlignCenter; progressOpts.textVisible = !(d->edit->isVisible()); //Change opts rect to be only the ComboBox's text area progressOpts.rect = progressRect(spinOpts); return progressOpts; } QRect KisAbstractSliderSpinBox::progressRect(const QStyleOptionSpinBox& spinBoxOptions) const { const Q_D(KisAbstractSliderSpinBox); QRect ret = style()->subControlRect(QStyle::CC_SpinBox, &spinBoxOptions, QStyle::SC_SpinBoxEditField); switch (d->style) { case KisAbstractSliderSpinBoxPrivate::STYLE_PLASTIQUE: ret.adjust(-2, 0, 1, 0); break; case KisAbstractSliderSpinBoxPrivate::STYLE_BREEZE: ret.adjust(1, 0, 0, 0); break; default: break; } return ret; } QRect KisAbstractSliderSpinBox::upButtonRect(const QStyleOptionSpinBox& spinBoxOptions) const { return style()->subControlRect(QStyle::CC_SpinBox, &spinBoxOptions, QStyle::SC_SpinBoxUp); } QRect KisAbstractSliderSpinBox::downButtonRect(const QStyleOptionSpinBox& spinBoxOptions) const { return style()->subControlRect(QStyle::CC_SpinBox, &spinBoxOptions, QStyle::SC_SpinBoxDown); } int KisAbstractSliderSpinBox::valueForX(int x, Qt::KeyboardModifiers modifiers) const { const Q_D(KisAbstractSliderSpinBox); QStyleOptionSpinBox spinOpts = spinBoxOptions(); QRect correctedProgRect; if (d->style == KisAbstractSliderSpinBoxPrivate::STYLE_FUSION) { correctedProgRect = progressRect(spinOpts).adjusted(2, 0, -2, 0); } else if (d->style == KisAbstractSliderSpinBoxPrivate::STYLE_BREEZE) { correctedProgRect = progressRect(spinOpts); } else { //Adjust for magic number in style code (margins) correctedProgRect = progressRect(spinOpts).adjusted(2, 2, -2, -2); } //Compute the distance of the progress bar, in pixel qreal leftDbl = correctedProgRect.left(); qreal xDbl = x - leftDbl; //Compute the ration of the progress bar used, linearly (ignoring the exponent) qreal rightDbl = correctedProgRect.right(); qreal minDbl = d->minimum; qreal maxDbl = d->maximum; qreal dValues = (maxDbl - minDbl); qreal percent = (xDbl / (rightDbl - leftDbl)); //If SHIFT is pressed, movement should be slowed. if( modifiers & Qt::ShiftModifier ) { percent = d->shiftPercent + ( percent - d->shiftPercent ) * d->slowFactor; } //Final value qreal exp_percent = pow(percent, d->exponentRatio); qreal realvalue = ((dValues * (percent * exp_percent >= 0 ? exp_percent : -exp_percent)) + minDbl); //If key CTRL is pressed, round to the closest step. if( modifiers & Qt::ControlModifier ) { qreal fstep = d->fastSliderStep; if( modifiers & Qt::ShiftModifier ) { fstep*=d->slowFactor; } realvalue = floor( (realvalue+fstep/2) / fstep ) * fstep; } //Return the value return int(realvalue); } void KisAbstractSliderSpinBox::setPrefix(const QString& prefix) { Q_D(KisAbstractSliderSpinBox); d->prefix = prefix; } void KisAbstractSliderSpinBox::setSuffix(const QString& suffix) { Q_D(KisAbstractSliderSpinBox); d->suffix = suffix; } void KisAbstractSliderSpinBox::setExponentRatio(qreal dbl) { Q_D(KisAbstractSliderSpinBox); Q_ASSERT(dbl > 0); d->exponentRatio = dbl; } void KisAbstractSliderSpinBox::setBlockUpdateSignalOnDrag(bool blockUpdateSignal) { Q_D(KisAbstractSliderSpinBox); d->blockUpdateSignalOnDrag = blockUpdateSignal; } void KisAbstractSliderSpinBox::contextMenuEvent(QContextMenuEvent* event) { event->accept(); } void KisAbstractSliderSpinBox::editLostFocus() { Q_D(KisAbstractSliderSpinBox); if (!d->edit->hasFocus()) { commitEnteredValue(); hideEdit(); } } void KisAbstractSliderSpinBox::setInternalValue(int value) { setInternalValue(value, false); } bool KisAbstractSliderSpinBox::isDragging() const { Q_D(const KisAbstractSliderSpinBox); return d->isDragging; } void KisAbstractSliderSpinBox::setPrivateValue(int value) { Q_D(KisAbstractSliderSpinBox); d->value = qBound(d->minimum, value, d->maximum); } class KisSliderSpinBoxPrivate : public KisAbstractSliderSpinBoxPrivate { }; KisSliderSpinBox::KisSliderSpinBox(QWidget* parent) : KisAbstractSliderSpinBox(parent, new KisSliderSpinBoxPrivate) { Q_D(KisSliderSpinBox); d->parseInt = true; setRange(0,99); } KisSliderSpinBox::~KisSliderSpinBox() { } void KisSliderSpinBox::setRange(int minimum, int maximum) { Q_D(KisSliderSpinBox); d->minimum = minimum; d->maximum = maximum; d->fastSliderStep = (maximum-minimum+1)/20; d->validator->setRange(minimum, maximum, 0); update(); } int KisSliderSpinBox::minimum() const { const Q_D(KisSliderSpinBox); return d->minimum; } void KisSliderSpinBox::setMinimum(int minimum) { Q_D(KisSliderSpinBox); setRange(minimum, d->maximum); } int KisSliderSpinBox::maximum() const { const Q_D(KisSliderSpinBox); return d->maximum; } void KisSliderSpinBox::setMaximum(int maximum) { Q_D(KisSliderSpinBox); setRange(d->minimum, maximum); } int KisSliderSpinBox::fastSliderStep() const { const Q_D(KisSliderSpinBox); return d->fastSliderStep; } void KisSliderSpinBox::setFastSliderStep(int step) { Q_D(KisSliderSpinBox); d->fastSliderStep = step; } int KisSliderSpinBox::value() { Q_D(KisSliderSpinBox); return d->value; } void KisSliderSpinBox::setValue(int value) { setInternalValue(value, false); update(); } QString KisSliderSpinBox::valueString() const { const Q_D(KisSliderSpinBox); QLocale locale; return locale.toString((qreal)d->value, 'f', d->validator->decimals()); } void KisSliderSpinBox::setSingleStep(int value) { Q_D(KisSliderSpinBox); d->singleStep = value; } void KisSliderSpinBox::setPageStep(int value) { Q_UNUSED(value); } void KisSliderSpinBox::setInternalValue(int _value, bool blockUpdateSignal) { Q_D(KisAbstractSliderSpinBox); d->value = qBound(d->minimum, _value, d->maximum); if(!blockUpdateSignal) { emit(valueChanged(value())); } } class KisDoubleSliderSpinBoxPrivate : public KisAbstractSliderSpinBoxPrivate { }; KisDoubleSliderSpinBox::KisDoubleSliderSpinBox(QWidget* parent) : KisAbstractSliderSpinBox(parent, new KisDoubleSliderSpinBoxPrivate) { Q_D(KisDoubleSliderSpinBox); d->parseInt = false; } KisDoubleSliderSpinBox::~KisDoubleSliderSpinBox() { } void KisDoubleSliderSpinBox::setRange(qreal minimum, qreal maximum, int decimals) { Q_D(KisDoubleSliderSpinBox); d->factor = pow(10.0, decimals); d->minimum = minimum * d->factor; d->maximum = maximum * d->factor; //This code auto-compute a new step when pressing control. //A flag defaulting to "do not change the fast step" should be added, but it implies changing every call if(maximum - minimum >= 2.0 || decimals <= 0) { //Quick step on integers d->fastSliderStep = int(pow(10.0, decimals)); } else if(decimals == 1) { d->fastSliderStep = (maximum-minimum)*d->factor/10; } else { d->fastSliderStep = (maximum-minimum)*d->factor/20; } d->validator->setRange(minimum, maximum, decimals); update(); setValue(value()); } qreal KisDoubleSliderSpinBox::minimum() const { const Q_D(KisAbstractSliderSpinBox); return d->minimum / d->factor; } void KisDoubleSliderSpinBox::setMinimum(qreal minimum) { Q_D(KisAbstractSliderSpinBox); setRange(minimum, d->maximum); } qreal KisDoubleSliderSpinBox::maximum() const { const Q_D(KisAbstractSliderSpinBox); return d->maximum / d->factor; } void KisDoubleSliderSpinBox::setMaximum(qreal maximum) { Q_D(KisAbstractSliderSpinBox); setRange(d->minimum, maximum); } qreal KisDoubleSliderSpinBox::fastSliderStep() const { const Q_D(KisAbstractSliderSpinBox); return d->fastSliderStep; } void KisDoubleSliderSpinBox::setFastSliderStep(qreal step) { Q_D(KisAbstractSliderSpinBox); d->fastSliderStep = step; } qreal KisDoubleSliderSpinBox::value() { Q_D(KisAbstractSliderSpinBox); return (qreal)d->value / d->factor; } void KisDoubleSliderSpinBox::setValue(qreal value) { Q_D(KisAbstractSliderSpinBox); setInternalValue(d->value = qRound(value * d->factor), false); update(); } void KisDoubleSliderSpinBox::setSingleStep(qreal value) { Q_D(KisAbstractSliderSpinBox); d->singleStep = value * d->factor; } QString KisDoubleSliderSpinBox::valueString() const { const Q_D(KisAbstractSliderSpinBox); QLocale locale; return locale.toString((qreal)d->value / d->factor, 'f', d->validator->decimals()); } void KisDoubleSliderSpinBox::setInternalValue(int _value, bool blockUpdateSignal) { Q_D(KisAbstractSliderSpinBox); d->value = qBound(d->minimum, _value, d->maximum); if(!blockUpdateSignal) { emit(valueChanged(value())); } } void KisAbstractSliderSpinBox::changeEvent(QEvent *e) { Q_D(KisAbstractSliderSpinBox); QWidget::changeEvent(e); switch (e->type()) { case QEvent::StyleChange: if (style()->objectName() == "fusion") { d->style = KisAbstractSliderSpinBoxPrivate::STYLE_FUSION; } else if (style()->objectName() == "plastique") { d->style = KisAbstractSliderSpinBoxPrivate::STYLE_PLASTIQUE; } else if (style()->objectName() == "breeze") { d->style = KisAbstractSliderSpinBoxPrivate::STYLE_BREEZE; } else { d->style = KisAbstractSliderSpinBoxPrivate::STYLE_NOQUIRK; } break; default: break; } } diff --git a/libs/ui/widgets/kis_stopgradient_slider_widget.cpp b/libs/ui/widgets/kis_stopgradient_slider_widget.cpp index df43af3e4b..210486ab72 100644 --- a/libs/ui/widgets/kis_stopgradient_slider_widget.cpp +++ b/libs/ui/widgets/kis_stopgradient_slider_widget.cpp @@ -1,368 +1,367 @@ /* * Copyright (c) 2004 Cyrille Berger * 2016 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 "widgets/kis_stopgradient_slider_widget.h" #include #include #include #include #include #include #include #include #include #include #include "kis_global.h" #include "kis_debug.h" #include "krita_utils.h" KisStopGradientSliderWidget::KisStopGradientSliderWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) , m_selectedStop(0) , m_drag(0) { QLinearGradient defaultGradient; m_defaultGradient.reset(KoStopGradient::fromQGradient(&defaultGradient)); setGradientResource(0); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); setMouseTracking(true); QWindow *window = this->window()->windowHandle(); if (window) { connect(window, SIGNAL(screenChanged(QScreen*)), SLOT(updateHandleSize())); } updateHandleSize(); } void KisStopGradientSliderWidget::updateHandleSize() { QFontMetrics fm(font()); const int h = fm.height(); m_handleSize = QSize(0.34 * h, h); } int KisStopGradientSliderWidget::handleClickTolerance() const { // the size of the default text! return m_handleSize.width(); } void KisStopGradientSliderWidget::setGradientResource(KoStopGradient* gradient) { m_gradient = gradient ? gradient : m_defaultGradient.data(); if (m_gradient && m_selectedStop >= 0) { m_selectedStop = qBound(0, m_selectedStop, m_gradient->stops().size() - 1); emit sigSelectedStop(m_selectedStop); } else { m_selectedStop = -1; } } void KisStopGradientSliderWidget::paintHandle(qreal position, const QColor &color, bool isSelected, QPainter *painter) { const QRect handlesRect = this->handlesStipeRect(); const int handleCenter = handlesRect.left() + position * handlesRect.width(); const int handlesHalfWidth = handlesRect.height() * 0.26; // = 1.0 / 0.66 * 0.34 / 2.0 <-- golden ratio QPolygon triangle(3); triangle[0] = QPoint(handleCenter, handlesRect.top()); triangle[1] = QPoint(handleCenter - handlesHalfWidth, handlesRect.bottom()); triangle[2] = QPoint(handleCenter + handlesHalfWidth, handlesRect.bottom()); const qreal lineWidth = 1.0; if (!isSelected) { painter->setPen(QPen(palette().text(), lineWidth)); painter->setBrush(QBrush(color)); painter->setRenderHint(QPainter::Antialiasing); painter->drawPolygon(triangle); } else { painter->setPen(QPen(palette().highlight(), 1.5 * lineWidth)); painter->setBrush(QBrush(color)); painter->setRenderHint(QPainter::Antialiasing); painter->drawPolygon(triangle); } } void KisStopGradientSliderWidget::paintEvent(QPaintEvent* pe) { QWidget::paintEvent(pe); QPainter painter(this); painter.setPen(Qt::black); const QRect previewRect = gradientStripeRect(); KritaUtils::renderExactRect(&painter, kisGrowRect(previewRect, 1)); painter.drawRect(previewRect); if (m_gradient) { QImage image = m_gradient->generatePreview(previewRect.width(), previewRect.height()); if (!image.isNull()) { painter.drawImage(previewRect.topLeft(), image); } QList handlePositions = m_gradient->stops(); for (int i = 0; i < handlePositions.count(); i++) { if (i == m_selectedStop) continue; paintHandle(handlePositions[i].first, handlePositions[i].second.toQColor(), false, &painter); } if (m_selectedStop >= 0) { paintHandle(handlePositions[m_selectedStop].first, handlePositions[m_selectedStop].second.toQColor(), true, &painter); } } } int findNearestHandle(qreal t, const qreal tolerance, const QList &stops) { int result = -1; qreal minDistance = tolerance; for (int i = 0; i < stops.size(); i++) { const KoGradientStop &stop = stops[i]; const qreal distance = qAbs(t - stop.first); if (distance < minDistance) { minDistance = distance; result = i; } } return result; } void KisStopGradientSliderWidget::mousePressEvent(QMouseEvent * e) { if (!allowedClickRegion(handleClickTolerance()).contains(e->pos())) { QWidget::mousePressEvent(e); return; } if (e->buttons() != Qt::LeftButton ) { QWidget::mousePressEvent(e); return; } const QRect handlesRect = this->handlesStipeRect(); const qreal t = (qreal(e->x()) - handlesRect.x()) / handlesRect.width(); const QList stops = m_gradient->stops(); const int clickedStop = findNearestHandle(t, qreal(handleClickTolerance()) / handlesRect.width(), stops); if (clickedStop >= 0) { if (m_selectedStop != clickedStop) { m_selectedStop = clickedStop; emit sigSelectedStop(m_selectedStop); } m_drag = true; } else { insertStop(qBound(0.0, t, 1.0)); m_drag = true; } update(); updateCursor(e->pos()); } void KisStopGradientSliderWidget::mouseReleaseEvent(QMouseEvent * e) { Q_UNUSED(e); m_drag = false; updateCursor(e->pos()); } int getNewInsertPosition(const KoGradientStop &stop, const QList &stops) { int result = 0; for (int i = 0; i < stops.size(); i++) { if (stop.first <= stops[i].first) break; result = i + 1; } return result; } void KisStopGradientSliderWidget::mouseMoveEvent(QMouseEvent * e) { updateCursor(e->pos()); if (m_drag) { const QRect handlesRect = this->handlesStipeRect(); double t = (qreal(e->x()) - handlesRect.x()) / handlesRect.width(); QList stops = m_gradient->stops(); if (t < -0.1 || t > 1.1) { if (stops.size() > 2 && m_selectedStop >= 0) { m_removedStop = stops[m_selectedStop]; stops.removeAt(m_selectedStop); m_selectedStop = -1; } } else { if (m_selectedStop < 0) { m_removedStop.first = qBound(0.0, t, 1.0); const int newPos = getNewInsertPosition(m_removedStop, stops); stops.insert(newPos, m_removedStop); m_selectedStop = newPos; } else { KoGradientStop draggedStop = stops[m_selectedStop]; draggedStop.first = qBound(0.0, t, 1.0); stops.removeAt(m_selectedStop); const int newPos = getNewInsertPosition(draggedStop, stops); stops.insert(newPos, draggedStop); m_selectedStop = newPos; } } m_gradient->setStops(stops); emit sigSelectedStop(m_selectedStop); update(); } else { QWidget::mouseMoveEvent(e); } } void KisStopGradientSliderWidget::updateCursor(const QPoint &pos) { const bool isInAllowedRegion = allowedClickRegion(handleClickTolerance()).contains(pos); QCursor currentCursor; if (isInAllowedRegion) { const QRect handlesRect = this->handlesStipeRect(); const qreal t = (qreal(pos.x()) - handlesRect.x()) / handlesRect.width(); const QList stops = m_gradient->stops(); const int clickedStop = findNearestHandle(t, qreal(handleClickTolerance()) / handlesRect.width(), stops); if (clickedStop >= 0) { currentCursor = m_drag ? Qt::ClosedHandCursor : Qt::OpenHandCursor; } } if (currentCursor.shape() != Qt::ArrowCursor) { setCursor(currentCursor); } else { unsetCursor(); } } void KisStopGradientSliderWidget::insertStop(double t) { KIS_ASSERT_RECOVER(t >= 0 && t <= 1.0 ) { t = qBound(0.0, t, 1.0); } QList stops = m_gradient->stops(); KoColor color; m_gradient->colorAt(color, t); const KoGradientStop stop(t, color); const int newPos = getNewInsertPosition(stop, stops); stops.insert(newPos, stop); m_gradient->setStops(stops); m_selectedStop = newPos; emit sigSelectedStop(m_selectedStop); } QRect KisStopGradientSliderWidget::sliderRect() const { return QRect(QPoint(), size()).adjusted(m_handleSize.width(), 1, -m_handleSize.width(), -1); } QRect KisStopGradientSliderWidget::gradientStripeRect() const { const QRect rc = sliderRect(); return rc.adjusted(0, 0, 0, -m_handleSize.height()); } QRect KisStopGradientSliderWidget::handlesStipeRect() const { const QRect rc = sliderRect(); return rc.adjusted(0, rc.height() - m_handleSize.height(), 0, 0); } QRegion KisStopGradientSliderWidget::allowedClickRegion(int tolerance) const { QRegion result; result += sliderRect(); result += handlesStipeRect().adjusted(-tolerance, 0, tolerance, 0); return result; } int KisStopGradientSliderWidget::selectedStop() { return m_selectedStop; } void KisStopGradientSliderWidget::setSelectedStop(int selected) { m_selectedStop = selected; emit sigSelectedStop(m_selectedStop); update(); } int KisStopGradientSliderWidget::minimalHeight() const { QFontMetrics fm(font()); const int h = fm.height(); QStyleOptionToolButton opt; - QSize sz = (style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(h, h), this). - expandedTo(QApplication::globalStrut())); + QSize sz = style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(h, h), this); return sz.height() + m_handleSize.height(); } QSize KisStopGradientSliderWidget::sizeHint() const { const int h = minimalHeight(); return QSize(2 * h, h); } QSize KisStopGradientSliderWidget::minimumSizeHint() const { const int h = minimalHeight(); return QSize(h, h); } diff --git a/libs/widgets/KisPaletteModel.cpp b/libs/widgets/KisPaletteModel.cpp index f1c4e71f8b..43948f4aeb 100644 --- a/libs/widgets/KisPaletteModel.cpp +++ b/libs/widgets/KisPaletteModel.cpp @@ -1,508 +1,508 @@ /* * Copyright (c) 2013 Sven Langkamp * Copyright (c) 2018 Michael Zhou * * 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 "KisPaletteModel.h" #include #include #include #include #include #include #include #include #include KisPaletteModel::KisPaletteModel(QObject* parent) : QAbstractTableModel(parent) , m_colorSet(0) , m_displayRenderer(KoDumbColorDisplayRenderer::instance()) { connect(this, SIGNAL(sigPaletteModified()), SLOT(slotPaletteModified())); } KisPaletteModel::~KisPaletteModel() { } QVariant KisPaletteModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } bool groupNameRow = m_rowGroupNameMap.contains(index.row()); if (role == IsGroupNameRole) { return groupNameRow; } if (groupNameRow) { return dataForGroupNameRow(index, role); } else { return dataForSwatch(index, role); } } int KisPaletteModel::rowCount(const QModelIndex& /*parent*/) const { if (!m_colorSet) return 0; return m_colorSet->rowCount() // count of color rows + m_rowGroupNameMap.size() // rows for names - 1; // global doesn't have a name } int KisPaletteModel::columnCount(const QModelIndex& /*parent*/) const { if (m_colorSet && m_colorSet->columnCount() > 0) { return m_colorSet->columnCount(); } if (!m_colorSet) { return 0; } return 16; } Qt::ItemFlags KisPaletteModel::flags(const QModelIndex& index) const { if (index.isValid()) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; } return Qt::ItemIsDropEnabled; } QModelIndex KisPaletteModel::index(int row, int column, const QModelIndex& parent) const { - Q_UNUSED(parent); + Q_UNUSED(parent) Q_ASSERT(m_colorSet); int groupNameRow = groupNameRowForRow(row); KisSwatchGroup *group = m_colorSet->getGroup(m_rowGroupNameMap[groupNameRow]); - Q_ASSERT(group); + KIS_ASSERT_RECOVER_RETURN_VALUE(group,QModelIndex()); return createIndex(row, column, group); } void KisPaletteModel::resetGroupNameRows() { m_rowGroupNameMap.clear(); int row = -1; for (const QString &groupName : m_colorSet->getGroupNames()) { m_rowGroupNameMap[row] = groupName; row += m_colorSet->getGroup(groupName)->rowCount(); row += 1; // row for group name } } void KisPaletteModel::setPalette(KoColorSet* palette) { beginResetModel(); m_colorSet = palette; if (palette) { resetGroupNameRows(); } endResetModel(); emit sigPaletteChanged(); } KoColorSet* KisPaletteModel::colorSet() const { return m_colorSet; } int KisPaletteModel::rowNumberInGroup(int rowInModel) const { if (m_rowGroupNameMap.contains(rowInModel)) { return -1; } QList rowNumberList = m_rowGroupNameMap.keys(); for (auto it = rowNumberList.rbegin(); it != rowNumberList.rend(); it++) { if (*it < rowInModel) { return rowInModel - *it - 1; } } return rowInModel; } int KisPaletteModel::groupNameRowForName(const QString &groupName) { for (auto it = m_rowGroupNameMap.begin(); it != m_rowGroupNameMap.end(); it++) { if (it.value() == groupName) { return it.key(); } } return -1; } bool KisPaletteModel::addEntry(const KisSwatch &entry, const QString &groupName) { beginInsertRows(QModelIndex(), rowCount(), rowCount() + 1); m_colorSet->add(entry, groupName); endInsertRows(); if (m_colorSet->isGlobal()) { m_colorSet->save(); } emit sigPaletteModified(); return true; } bool KisPaletteModel::removeEntry(const QModelIndex &index, bool keepColors) { if (!qvariant_cast(data(index, IsGroupNameRole))) { static_cast(index.internalPointer())->removeEntry(index.column(), rowNumberInGroup(index.row())); emit dataChanged(index, index); } else { int groupNameRow = groupNameRowForRow(index.row()); QString groupName = m_rowGroupNameMap[groupNameRow]; removeGroup(groupName, keepColors); } emit sigPaletteModified(); return true; } void KisPaletteModel::removeGroup(const QString &groupName, bool keepColors) { int removeStart = groupNameRowForName(groupName); int removedRowCount = m_colorSet->getGroup(groupName)->rowCount(); int insertStart = m_colorSet->getGlobalGroup()->rowCount(); beginRemoveRows(QModelIndex(), removeStart, removeStart + removedRowCount); m_colorSet->removeGroup(groupName, keepColors); resetGroupNameRows(); endRemoveRows(); beginInsertRows(QModelIndex(), insertStart, m_colorSet->getGlobalGroup()->rowCount()); endInsertRows(); emit sigPaletteModified(); } bool KisPaletteModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { Q_UNUSED(row); Q_UNUSED(column); if (!data->hasFormat("krita/x-colorsetentry") && !data->hasFormat("krita/x-colorsetgroup")) { return false; } if (action == Qt::IgnoreAction) { return false; } QModelIndex finalIndex = parent; if (!finalIndex.isValid()) { return false; } if (data->hasFormat("krita/x-colorsetgroup")) { // dragging group not supported for now QByteArray encodedData = data->data("krita/x-colorsetgroup"); QDataStream stream(&encodedData, QIODevice::ReadOnly); while (!stream.atEnd()) { QString groupNameDroppedOn = qvariant_cast(finalIndex.data(GroupNameRole)); if (groupNameDroppedOn == KoColorSet::GLOBAL_GROUP_NAME) { return false; } QString groupNameDragged; stream >> groupNameDragged; KisSwatchGroup *groupDragged = m_colorSet->getGroup(groupNameDragged); int start = groupNameRowForName(groupNameDragged); int end = start + groupDragged->rowCount(); if (!beginMoveRows(QModelIndex(), start, end, QModelIndex(), groupNameRowForName(groupNameDroppedOn))) { return false; } m_colorSet->moveGroup(groupNameDragged, groupNameDroppedOn); resetGroupNameRows(); endMoveRows(); emit sigPaletteModified(); if (m_colorSet->isGlobal()) { m_colorSet->save(); } } return true; } if (qvariant_cast(finalIndex.data(KisPaletteModel::IsGroupNameRole))) { return true; } QByteArray encodedData = data->data("krita/x-colorsetentry"); QDataStream stream(&encodedData, QIODevice::ReadOnly); while (!stream.atEnd()) { KisSwatch entry; QString name, id; bool spotColor; QString oldGroupName; int oriRow; int oriColumn; QString colorXml; stream >> name >> id >> spotColor >> oriRow >> oriColumn >> oldGroupName >> colorXml; entry.setName(name); entry.setId(id); entry.setSpotColor(spotColor); QDomDocument doc; doc.setContent(colorXml); QDomElement e = doc.documentElement(); QDomElement c = e.firstChildElement(); if (!c.isNull()) { QString colorDepthId = c.attribute("bitdepth", Integer8BitsColorDepthID.id()); entry.setColor(KoColor::fromXML(c, colorDepthId)); } if (action == Qt::MoveAction){ KisSwatchGroup *g = m_colorSet->getGroup(oldGroupName); if (g) { if (qvariant_cast(finalIndex.data(KisPaletteModel::CheckSlotRole))) { g->setEntry(getEntry(finalIndex), oriColumn, oriRow); } else { g->removeEntry(oriColumn, oriRow); } } setEntry(entry, finalIndex); emit sigPaletteModified(); if (m_colorSet->isGlobal()) { m_colorSet->save(); } } } return true; } QMimeData *KisPaletteModel::mimeData(const QModelIndexList &indexes) const { QMimeData *mimeData = new QMimeData(); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); QModelIndex index = indexes.last(); if (index.isValid() && qvariant_cast(index.data(CheckSlotRole))) { QString mimeTypeName = "krita/x-colorsetentry"; if (qvariant_cast(index.data(IsGroupNameRole))==false) { KisSwatch entry = getEntry(index); QDomDocument doc; QDomElement root = doc.createElement("Color"); root.setAttribute("bitdepth", entry.color().colorSpace()->colorDepthId().id()); doc.appendChild(root); entry.color().toXML(doc, root); stream << entry.name() << entry.id() << entry.spotColor() << rowNumberInGroup(index.row()) << index.column() << qvariant_cast(index.data(GroupNameRole)) << doc.toString(); } else { mimeTypeName = "krita/x-colorsetgroup"; QString groupName = qvariant_cast(index.data(GroupNameRole)); stream << groupName; } mimeData->setData(mimeTypeName, encodedData); } return mimeData; } QStringList KisPaletteModel::mimeTypes() const { return QStringList() << "krita/x-colorsetentry" << "krita/x-colorsetgroup"; } Qt::DropActions KisPaletteModel::supportedDropActions() const { return Qt::MoveAction; } void KisPaletteModel::setEntry(const KisSwatch &entry, const QModelIndex &index) { KisSwatchGroup *group = static_cast(index.internalPointer()); Q_ASSERT(group); group->setEntry(entry, index.column(), rowNumberInGroup(index.row())); emit sigPaletteModified(); emit dataChanged(index, index); if (m_colorSet->isGlobal()) { m_colorSet->save(); } } bool KisPaletteModel::renameGroup(const QString &groupName, const QString &newName) { beginResetModel(); bool success = m_colorSet->changeGroupName(groupName, newName); for (auto it = m_rowGroupNameMap.begin(); it != m_rowGroupNameMap.end(); it++) { if (it.value() == groupName) { m_rowGroupNameMap[it.key()] = newName; break; } } endResetModel(); emit sigPaletteModified(); return success; } void KisPaletteModel::addGroup(const KisSwatchGroup &group) { beginInsertRows(QModelIndex(), rowCount(), rowCount() + group.rowCount()); m_colorSet->addGroup(group.name()); *m_colorSet->getGroup(group.name()) = group; endInsertColumns(); emit sigPaletteModified(); } void KisPaletteModel::setRowNumber(const QString &groupName, int rowCount) { beginResetModel(); KisSwatchGroup *g = m_colorSet->getGroup(groupName); if (g) { g->setRowCount(rowCount); } endResetModel(); } void KisPaletteModel::clear() { beginResetModel(); m_colorSet->clear(); endResetModel(); } QVariant KisPaletteModel::dataForGroupNameRow(const QModelIndex &idx, int role) const { KisSwatchGroup *group = static_cast(idx.internalPointer()); Q_ASSERT(group); QString groupName = group->name(); switch (role) { case Qt::ToolTipRole: case Qt::DisplayRole: { return groupName; } case GroupNameRole: { return groupName; } case CheckSlotRole: { return true; } case RowInGroupRole: { return -1; } default: { return QVariant(); } } } QVariant KisPaletteModel::dataForSwatch(const QModelIndex &idx, int role) const { KisSwatchGroup *group = static_cast(idx.internalPointer()); Q_ASSERT(group); int rowInGroup = rowNumberInGroup(idx.row()); bool entryPresent = group->checkEntry(idx.column(), rowInGroup); KisSwatch entry; if (entryPresent) { entry = group->getEntry(idx.column(), rowInGroup); } switch (role) { case Qt::ToolTipRole: case Qt::DisplayRole: { return entryPresent ? entry.name() : i18n("Empty slot"); } case Qt::BackgroundRole: { QColor color(0, 0, 0, 0); if (entryPresent) { color = m_displayRenderer->toQColor(entry.color()); } return QBrush(color); } case GroupNameRole: { return group->name(); } case CheckSlotRole: { return entryPresent; } case RowInGroupRole: { return rowInGroup; } default: { return QVariant(); } } } void KisPaletteModel::setDisplayRenderer(const KoColorDisplayRendererInterface *displayRenderer) { if (displayRenderer) { if (m_displayRenderer) { disconnect(m_displayRenderer, 0, this, 0); } m_displayRenderer = displayRenderer; connect(m_displayRenderer, SIGNAL(displayConfigurationChanged()), SLOT(slotDisplayConfigurationChanged()), Qt::UniqueConnection); } else { m_displayRenderer = KoDumbColorDisplayRenderer::instance(); } } void KisPaletteModel::slotDisplayConfigurationChanged() { beginResetModel(); endResetModel(); } void KisPaletteModel::slotPaletteModified() { m_colorSet->setPaletteType(KoColorSet::KPL); } QModelIndex KisPaletteModel::indexForClosest(const KoColor &compare) { KisSwatchGroup::SwatchInfo info = colorSet()->getClosestColorInfo(compare); return createIndex(indexRowForInfo(info), info.column, colorSet()->getGroup(info.group)); } int KisPaletteModel::indexRowForInfo(const KisSwatchGroup::SwatchInfo &info) { for (auto it = m_rowGroupNameMap.begin(); it != m_rowGroupNameMap.end(); it++) { if (it.value() == info.group) { return it.key() + info.row + 1; } } return info.row; } KisSwatch KisPaletteModel::getEntry(const QModelIndex &index) const { KisSwatchGroup *group = static_cast(index.internalPointer()); if (!group || !group->checkEntry(index.column(), rowNumberInGroup(index.row()))) { return KisSwatch(); } return group->getEntry(index.column(), rowNumberInGroup(index.row())); } int KisPaletteModel::groupNameRowForRow(int rowInModel) const { return rowInModel - rowNumberInGroup(rowInModel) - 1; } diff --git a/libs/widgets/KoFontComboBox.h b/libs/widgets/KoFontComboBox.h deleted file mode 100644 index 5f7d607e1a..0000000000 --- a/libs/widgets/KoFontComboBox.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2014 Dmitry Kazakov - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ -#ifndef _KO_FONT_COMBO_BOX_H_ -#define _KO_FONT_COMBO_BOX_H_ - -#include -typedef QFontComboBox KoFontComboBox; - -#endif // _KO_FONT_COMBO_BOX_H_ diff --git a/libs/widgets/KoSliderCombo.cpp b/libs/widgets/KoSliderCombo.cpp index f3d78735b3..15493157b9 100644 --- a/libs/widgets/KoSliderCombo.cpp +++ b/libs/widgets/KoSliderCombo.cpp @@ -1,291 +1,291 @@ /* This file is part of the KDE project Copyright (c) 2007 C. Boemann 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 "KoSliderCombo.h" #include "KoSliderCombo_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include KoSliderCombo::KoSliderCombo(QWidget *parent) : QComboBox(parent) ,d(new KoSliderComboPrivate()) { d->thePublic = this; d->minimum = 0.0; d->maximum = 100.0; d->decimals = 2; d->container = new KoSliderComboContainer(this); d->container->setAttribute(Qt::WA_WindowPropagation); QStyleOptionComboBox opt; opt.initFrom(this); // d->container->setFrameStyle(style()->styleHint(QStyle::SH_ComboBox_PopupFrameStyle, &opt, this)); d->slider = new QSlider(Qt::Horizontal); d->slider->setMinimum(0); d->slider->setMaximum(256); d->slider->setPageStep(10); d->slider->setValue(0); // When set to true, causes flicker on Qt 4.6. Any reason to keep it? d->firstShowOfSlider = false; //true; QHBoxLayout * l = new QHBoxLayout(); l->setMargin(2); l->setSpacing(2); l->addWidget(d->slider); d->container->setLayout(l); d->container->resize(200, 30); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); setEditable(true); setEditText(QLocale().toString(0.0, d->decimals)); connect(d->slider, SIGNAL(valueChanged(int)), SLOT(sliderValueChanged(int))); connect(d->slider, SIGNAL(sliderReleased()), SLOT(sliderReleased())); connect(lineEdit(), SIGNAL(editingFinished()), SLOT(lineEditFinished())); } KoSliderCombo::~KoSliderCombo() { delete d; } QSize KoSliderCombo::sizeHint() const { return minimumSizeHint(); } QSize KoSliderCombo::minimumSizeHint() const { QSize sh; const QFontMetrics &fm = fontMetrics(); sh.setWidth(5 * fm.width(QLatin1Char('8'))); sh.setHeight(qMax(fm.lineSpacing(), 14) + 2); // add style and strut values QStyleOptionComboBox opt; opt.initFrom(this); opt.subControls = QStyle::SC_All; opt.editable = true; sh = style()->sizeFromContents(QStyle::CT_ComboBox, &opt, sh, this); - return sh.expandedTo(QApplication::globalStrut()); + return sh; } void KoSliderCombo::KoSliderComboPrivate::showPopup() { if(firstShowOfSlider) { container->show(); //show container a bit early so the slider can be layout'ed firstShowOfSlider = false; } QStyleOptionSlider opt; opt.initFrom(slider); opt.maximum=256; opt.sliderPosition = opt.sliderValue = slider->value(); int hdlPos = thePublic->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle).center().x(); QStyleOptionComboBox optThis; optThis.initFrom(thePublic); optThis.subControls = QStyle::SC_All; optThis.editable = true; int arrowPos = thePublic->style()->subControlRect(QStyle::CC_ComboBox, &optThis, QStyle::SC_ComboBoxArrow).center().x(); QSize popSize = container->size(); QRect popupRect(thePublic->mapToGlobal(QPoint(arrowPos - hdlPos - slider->x(), thePublic->size().height())), popSize); // Make sure the popup is not drawn outside the screen area QRect screenRect = QApplication::desktop()->availableGeometry(thePublic); if (popupRect.right() > screenRect.right()) popupRect.translate(screenRect.right() - popupRect.right(), 0); if (popupRect.left() < screenRect.left()) popupRect.translate(screenRect.left() - popupRect.left(), 0); if (popupRect.bottom() > screenRect.bottom()) popupRect.translate(0, -(thePublic->height() + container->height())); container->setGeometry(popupRect); container->raise(); container->show(); slider->setFocus(); } void KoSliderCombo::KoSliderComboPrivate::hidePopup() { container->hide(); } void KoSliderCombo::hideEvent(QHideEvent *) { d->hidePopup(); } void KoSliderCombo::changeEvent(QEvent *e) { switch (e->type()) { case QEvent::EnabledChange: if (!isEnabled()) d->hidePopup(); break; case QEvent::PaletteChange: d->container->setPalette(palette()); break; default: break; } QComboBox::changeEvent(e); } void KoSliderCombo::paintEvent(QPaintEvent *) { QStylePainter gc(this); gc.setPen(palette().color(QPalette::Text)); QStyleOptionComboBox opt; opt.initFrom(this); opt.subControls = QStyle::SC_All; opt.editable = true; gc.drawComplexControl(QStyle::CC_ComboBox, opt); gc.drawControl(QStyle::CE_ComboBoxLabel, opt); } void KoSliderCombo::mousePressEvent(QMouseEvent *e) { QStyleOptionComboBox opt; opt.initFrom(this); opt.subControls = QStyle::SC_All; opt.editable = true; QStyle::SubControl sc = style()->hitTestComplexControl(QStyle::CC_ComboBox, &opt, e->pos(), this); if (sc == QStyle::SC_ComboBoxArrow && !d->container->isVisible()) { d->showPopup(); } else QComboBox::mousePressEvent(e); } void KoSliderCombo::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Up) setValue(value() + d->slider->singleStep() * (maximum() - minimum()) / 256 + 0.5); else if (e->key() == Qt::Key_Down) setValue(value() - d->slider->singleStep() * (maximum() - minimum()) / 256 - 0.5); else QComboBox::keyPressEvent(e); } void KoSliderCombo::wheelEvent(QWheelEvent *e) { if (e->delta() > 0) setValue(value() + d->slider->singleStep() * (maximum() - minimum()) / 256 + 0.5); else setValue(value() - d->slider->singleStep() * (maximum() - minimum()) / 256 - 0.5); } void KoSliderCombo::KoSliderComboPrivate::lineEditFinished() { qreal value = QLocale().toDouble(thePublic->currentText()); slider->blockSignals(true); slider->setValue(int((value - minimum) * 256 / (maximum - minimum) + 0.5)); slider->blockSignals(false); emit thePublic->valueChanged(value, true); } void KoSliderCombo::KoSliderComboPrivate::sliderValueChanged(int slidervalue) { thePublic->setEditText(QLocale().toString(minimum + (maximum - minimum)*slidervalue/256, decimals)); qreal value = QLocale().toDouble(thePublic->currentText()); emit thePublic->valueChanged(value, false); } void KoSliderCombo::KoSliderComboPrivate::sliderReleased() { qreal value = QLocale().toDouble(thePublic->currentText()); emit thePublic->valueChanged(value, true); } qreal KoSliderCombo::maximum() const { return d->maximum; } qreal KoSliderCombo::minimum() const { return d->minimum; } qreal KoSliderCombo::decimals() const { return d->decimals; } qreal KoSliderCombo::value() const { return QLocale().toDouble(currentText()); } void KoSliderCombo::setDecimals(int dec) { d->decimals = dec; if (dec == 0) lineEdit()->setValidator(new QIntValidator(this)); else lineEdit()->setValidator(new QDoubleValidator(this)); } void KoSliderCombo::setMinimum(qreal min) { d->minimum = min; } void KoSliderCombo::setMaximum(qreal max) { d->maximum = max; } void KoSliderCombo::setValue(qreal value) { if(value < d->minimum) value = d->minimum; if(value > d->maximum) value = d->maximum; setEditText(QLocale().toString(value, d->decimals)); d->slider->blockSignals(true); d->slider->setValue(int((value - d->minimum) * 256 / (d->maximum - d->minimum) + 0.5)); d->slider->blockSignals(false); emit valueChanged(value, true); } //have to include this because of Q_PRIVATE_SLOT #include diff --git a/libs/widgets/kis_color_button.cpp b/libs/widgets/kis_color_button.cpp index 2e60eb5e47..49ba3729ce 100644 --- a/libs/widgets/kis_color_button.cpp +++ b/libs/widgets/kis_color_button.cpp @@ -1,387 +1,385 @@ /* This file is part of the KDE libraries Copyright (C) 1997 Martin Jones (mjones@kde.org) Copyright (C) 1999 Cristian Tibirna (ctibirna@kde.org) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_color_button.h" #include #include #include #include #include #include #include #include #include #include #include #include #include class KisColorButton::KisColorButtonPrivate { public: KisColorButtonPrivate(KisColorButton *q); ~KisColorButtonPrivate() { if (dialogPtr) { dialogPtr.data()->close(); } } void _k_chooseColor(); void _k_colorChosen(); KisColorButton *q; KoColor m_defaultColor; bool m_bdefaultColor : 1; bool m_alphaChannel : 1; bool m_palette : 1; KoColor col; QPoint mPos; #ifndef Q_OS_MACOS QPointer dialogPtr; #else QPointer dialogPtr; #endif void initStyleOption(QStyleOptionButton *opt) const; }; ///////////////////////////////////////////////////////////////////// // Functions duplicated from KColorMimeData // Should be kept in sync void _k_populateMimeData(QMimeData *mimeData, const KoColor &color) { mimeData->setColorData(color.toQColor()); mimeData->setText(color.toQColor().name()); } bool _k_canDecode(const QMimeData *mimeData) { if (mimeData->hasColor()) { return true; } if (mimeData->hasText()) { const QString colorName = mimeData->text(); if ((colorName.length() >= 4) && (colorName[0] == QLatin1Char('#'))) { return true; } } return false; } QColor _k_fromMimeData(const QMimeData *mimeData) { if (mimeData->hasColor()) { return mimeData->colorData().value(); } if (_k_canDecode(mimeData)) { return QColor(mimeData->text()); } return QColor(); } QDrag *_k_createDrag(const KoColor &color, QObject *dragsource) { QDrag *drag = new QDrag(dragsource); QMimeData *mime = new QMimeData; _k_populateMimeData(mime, color); drag->setMimeData(mime); QPixmap colorpix(25, 20); colorpix.fill(color.toQColor()); QPainter p(&colorpix); p.setPen(Qt::black); p.drawRect(0, 0, 24, 19); p.end(); drag->setPixmap(colorpix); drag->setHotSpot(QPoint(-5, -7)); return drag; } ///////////////////////////////////////////////////////////////////// KisColorButton::KisColorButtonPrivate::KisColorButtonPrivate(KisColorButton *q) : q(q) { m_bdefaultColor = false; m_alphaChannel = false; m_palette = true; q->setAcceptDrops(true); connect(q, SIGNAL(clicked()), q, SLOT(_k_chooseColor())); } KisColorButton::KisColorButton(QWidget *parent) : QPushButton(parent) , d(new KisColorButtonPrivate(this)) { } KisColorButton::KisColorButton(const KoColor &c, QWidget *parent) : QPushButton(parent) , d(new KisColorButtonPrivate(this)) { d->col = c; } KisColorButton::KisColorButton(const KoColor &c, const KoColor &defaultColor, QWidget *parent) : QPushButton(parent) , d(new KisColorButtonPrivate(this)) { d->col = c; setDefaultColor(defaultColor); } KisColorButton::~KisColorButton() { delete d; } KoColor KisColorButton::color() const { return d->col; } void KisColorButton::setColor(const KoColor &c) { d->col = c; update(); emit changed(d->col); } void KisColorButton::setAlphaChannelEnabled(bool alpha) { d->m_alphaChannel = alpha; } bool KisColorButton::isAlphaChannelEnabled() const { return d->m_alphaChannel; } void KisColorButton::setPaletteViewEnabled(bool enable) { d->m_palette = enable; } bool KisColorButton::paletteViewEnabled() const { return d->m_palette; } KoColor KisColorButton::defaultColor() const { return d->m_defaultColor; } void KisColorButton::setDefaultColor(const KoColor &c) { d->m_bdefaultColor = true; d->m_defaultColor = c; } void KisColorButton::KisColorButtonPrivate::initStyleOption(QStyleOptionButton *opt) const { opt->initFrom(q); opt->state |= q->isDown() ? QStyle::State_Sunken : QStyle::State_Raised; opt->features = QStyleOptionButton::None; if (q->isDefault()) { opt->features |= QStyleOptionButton::DefaultButton; } opt->text.clear(); opt->icon = QIcon(); } void KisColorButton::paintEvent(QPaintEvent *) { QPainter painter(this); QStyle *style = QWidget::style(); //First, we need to draw the bevel. QStyleOptionButton butOpt; d->initStyleOption(&butOpt); style->drawControl(QStyle::CE_PushButtonBevel, &butOpt, &painter, this); //OK, now we can muck around with drawing out pretty little color box //First, sort out where it goes QRect labelRect = style->subElementRect(QStyle::SE_PushButtonContents, &butOpt, this); int shift = style->pixelMetric(QStyle::PM_ButtonMargin, &butOpt, this) / 2; labelRect.adjust(shift, shift, -shift, -shift); int x, y, w, h; labelRect.getRect(&x, &y, &w, &h); if (isChecked() || isDown()) { x += style->pixelMetric(QStyle::PM_ButtonShiftHorizontal, &butOpt, this); y += style->pixelMetric(QStyle::PM_ButtonShiftVertical, &butOpt, this); } QColor fillCol = isEnabled() ? d->col.toQColor() : palette().color(backgroundRole()); qDrawShadePanel(&painter, x, y, w, h, palette(), true, 1, NULL); if (fillCol.isValid()) { const QRect rect(x + 1, y + 1, w - 2, h - 2); if (fillCol.alpha() < 255) { QPixmap chessboardPattern(16, 16); QPainter patternPainter(&chessboardPattern); patternPainter.fillRect(0, 0, 8, 8, Qt::black); patternPainter.fillRect(8, 8, 8, 8, Qt::black); patternPainter.fillRect(0, 8, 8, 8, Qt::white); patternPainter.fillRect(8, 0, 8, 8, Qt::white); patternPainter.end(); painter.fillRect(rect, QBrush(chessboardPattern)); } painter.fillRect(rect, fillCol); } if (hasFocus()) { QRect focusRect = style->subElementRect(QStyle::SE_PushButtonFocusRect, &butOpt, this); QStyleOptionFocusRect focusOpt; focusOpt.init(this); focusOpt.rect = focusRect; focusOpt.backgroundColor = palette().window().color(); style->drawPrimitive(QStyle::PE_FrameFocusRect, &focusOpt, &painter, this); } } QSize KisColorButton::sizeHint() const { QStyleOptionButton opt; d->initStyleOption(&opt); - return style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(40, 15), this). - expandedTo(QApplication::globalStrut()); + return style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(40, 15), this); } QSize KisColorButton::minimumSizeHint() const { QStyleOptionButton opt; d->initStyleOption(&opt); - return style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(3, 3), this). - expandedTo(QApplication::globalStrut()); + return style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(3, 3), this); } void KisColorButton::dragEnterEvent(QDragEnterEvent *event) { event->setAccepted(_k_canDecode(event->mimeData()) && isEnabled()); } void KisColorButton::dropEvent(QDropEvent *event) { QColor c = _k_fromMimeData(event->mimeData()); if (c.isValid()) { KoColor col; col.fromQColor(c); setColor(col); } } void KisColorButton::keyPressEvent(QKeyEvent *e) { int key = e->key() | e->modifiers(); if (QKeySequence::keyBindings(QKeySequence::Copy).contains(key)) { QMimeData *mime = new QMimeData; _k_populateMimeData(mime, color()); QApplication::clipboard()->setMimeData(mime, QClipboard::Clipboard); } else if (QKeySequence::keyBindings(QKeySequence::Paste).contains(key)) { QColor color = _k_fromMimeData(QApplication::clipboard()->mimeData(QClipboard::Clipboard)); KoColor col; col.fromQColor(color); setColor(col); } else { QPushButton::keyPressEvent(e); } } void KisColorButton::mousePressEvent(QMouseEvent *e) { d->mPos = e->pos(); QPushButton::mousePressEvent(e); } void KisColorButton::mouseMoveEvent(QMouseEvent *e) { if ((e->buttons() & Qt::LeftButton) && (e->pos() - d->mPos).manhattanLength() > QApplication::startDragDistance()) { _k_createDrag(color(), this)->exec(); setDown(false); } } void KisColorButton::KisColorButtonPrivate::_k_chooseColor() { #ifndef Q_OS_MACOS KisDlgInternalColorSelector *dialog = dialogPtr.data(); #else QColorDialog *dialog = dialogPtr.data(); #endif if (dialog) { #ifndef Q_OS_MACOS dialog->setPreviousColor(q->color()); #else dialog->setCurrentColor(q->color().toQColor()); #endif dialog->show(); dialog->raise(); dialog->activateWindow(); return; } KisDlgInternalColorSelector::Config cfg; cfg.paletteBox = q->paletteViewEnabled(); #ifndef Q_OS_MACOS dialog = new KisDlgInternalColorSelector(q, q->color(), cfg, i18n("Choose a color")); #else dialog = new QColorDialog(q); dialog->setOption(QColorDialog::ShowAlphaChannel, m_alphaChannel); #endif dialog->setAttribute(Qt::WA_DeleteOnClose); connect(dialog, SIGNAL(accepted()), q, SLOT(_k_colorChosen())); dialogPtr = dialog; #ifndef Q_OS_MACOS dialog->setPreviousColor(q->color()); #else dialog->setCurrentColor(q->color().toQColor()); #endif dialog->show(); } void KisColorButton::KisColorButtonPrivate::_k_colorChosen() { #ifndef Q_OS_MACOS KisDlgInternalColorSelector *dialog = dialogPtr.data(); #else QColorDialog *dialog = dialogPtr.data(); #endif if (!dialog) { return; } #ifndef Q_OS_MACOS q->setColor(dialog->getCurrentColor()); #else KoColor c; c.fromQColor(dialog->currentColor()); q->setColor(c); #endif } #include "moc_kis_color_button.cpp" diff --git a/libs/widgets/kis_color_input.cpp b/libs/widgets/kis_color_input.cpp index 01b7b0dd74..8687176162 100644 --- a/libs/widgets/kis_color_input.cpp +++ b/libs/widgets/kis_color_input.cpp @@ -1,436 +1,439 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2011 Sven Langkamp * Copyright (c) 2015 Moritz Molch * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_color_input.h" #include #ifdef HAVE_OPENEXR #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_double_parse_spin_box.h" #include "kis_int_parse_spin_box.h" KisColorInput::KisColorInput(QWidget* parent, const KoChannelInfo* channelInfo, KoColor* color, KoColorDisplayRendererInterface *displayRenderer, bool usePercentage) : QWidget(parent), m_channelInfo(channelInfo), m_color(color), m_displayRenderer(displayRenderer), m_usePercentage(usePercentage) { } void KisColorInput::init() { QHBoxLayout* m_layout = new QHBoxLayout(this); m_layout->setContentsMargins(0,0,0,0); m_layout->setSpacing(1); QLabel* m_label = new QLabel(i18n("%1:", m_channelInfo->name()), this); m_layout->addWidget(m_label); m_colorSlider = new KoColorSlider(Qt::Horizontal, this, m_displayRenderer); m_colorSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); m_layout->addWidget(m_colorSlider); QWidget* m_input = createInput(); m_colorSlider->setFixedHeight(m_input->sizeHint().height()); m_layout->addWidget(m_input); } KisIntegerColorInput::KisIntegerColorInput(QWidget* parent, const KoChannelInfo* channelInfo, KoColor* color, KoColorDisplayRendererInterface *displayRenderer, bool usePercentage) : KisColorInput(parent, channelInfo, color, displayRenderer, usePercentage) { init(); } void KisIntegerColorInput::setValue(int v) { quint8* data = m_color->data() + m_channelInfo->pos(); switch (m_channelInfo->channelValueType()) { case KoChannelInfo::UINT8: *(reinterpret_cast(data)) = v; break; case KoChannelInfo::UINT16: *(reinterpret_cast(data)) = v; break; case KoChannelInfo::UINT32: *(reinterpret_cast(data)) = v; break; default: Q_ASSERT(false); } emit(updated()); } void KisIntegerColorInput::update() { KoColor min = *m_color; KoColor max = *m_color; quint8* data = m_color->data() + m_channelInfo->pos(); quint8* dataMin = min.data() + m_channelInfo->pos(); quint8* dataMax = max.data() + m_channelInfo->pos(); m_intNumInput->blockSignals(true); m_colorSlider->blockSignals(true); switch (m_channelInfo->channelValueType()) { case KoChannelInfo::UINT8: if (m_usePercentage) { m_intNumInput->setMaximum(100); m_intNumInput->setValue(round(*(reinterpret_cast(data))*1.0 / 255.0 * 100.0)); } else { m_intNumInput->setMaximum(0xFF); m_intNumInput->setValue(*(reinterpret_cast(data))); } m_colorSlider->setValue(*(reinterpret_cast(data))); *(reinterpret_cast(dataMin)) = 0x0; *(reinterpret_cast(dataMax)) = 0xFF; break; case KoChannelInfo::UINT16: if (m_usePercentage) { m_intNumInput->setMaximum(100); m_intNumInput->setValue(round(*(reinterpret_cast(data))*1.0 / 65535.0 * 100.0)); } else { m_intNumInput->setMaximum(0xFFFF); m_intNumInput->setValue(*(reinterpret_cast(data))); } m_colorSlider->setValue(*(reinterpret_cast(data))); *(reinterpret_cast(dataMin)) = 0x0; *(reinterpret_cast(dataMax)) = 0xFFFF; break; case KoChannelInfo::UINT32: if (m_usePercentage) { m_intNumInput->setMaximum(100); m_intNumInput->setValue(round(*(reinterpret_cast(data))*1.0 / 4294967295.0 * 100.0)); } else { m_intNumInput->setMaximum(0xFFFF); m_intNumInput->setValue(*(reinterpret_cast(data))); } m_colorSlider->setValue(*(reinterpret_cast(data))); *(reinterpret_cast(dataMin)) = 0x0; *(reinterpret_cast(dataMax)) = 0xFFFFFFFF; break; default: Q_ASSERT(false); } m_colorSlider->setColors(min, max); m_intNumInput->blockSignals(false); m_colorSlider->blockSignals(false); } QWidget* KisIntegerColorInput::createInput() { m_intNumInput = new KisIntParseSpinBox(this); m_intNumInput->setMinimum(0); m_colorSlider->setMinimum(0); if (m_usePercentage) { m_intNumInput->setSuffix(i18n("%")); } else { m_intNumInput->setSuffix(""); } switch (m_channelInfo->channelValueType()) { case KoChannelInfo::UINT8: if (m_usePercentage) { m_intNumInput->setMaximum(100); } else { m_intNumInput->setMaximum(0xFF); } m_colorSlider->setMaximum(0xFF); break; case KoChannelInfo::UINT16: if (m_usePercentage) { m_intNumInput->setMaximum(100); } else { m_intNumInput->setMaximum(0xFFFF); } m_colorSlider->setMaximum(0xFFFF); break; case KoChannelInfo::UINT32: if (m_usePercentage) { m_intNumInput->setMaximum(100); } else { m_intNumInput->setMaximum(0xFFFFFFFF); } m_colorSlider->setMaximum(0xFFFFFFFF); break; default: Q_ASSERT(false); } connect(m_colorSlider, SIGNAL(valueChanged(int)), this, SLOT(onColorSliderChanged(int))); connect(m_intNumInput, SIGNAL(valueChanged(int)), this, SLOT(onNumInputChanged(int))); return m_intNumInput; } void KisIntegerColorInput::setPercentageWise(bool val) { m_usePercentage = val; if (m_usePercentage) { m_intNumInput->setSuffix(i18n("%")); } else { m_intNumInput->setSuffix(""); } } void KisIntegerColorInput::onColorSliderChanged(int val) { m_intNumInput->blockSignals(true); if (m_usePercentage) { switch (m_channelInfo->channelValueType()) { case KoChannelInfo::UINT8: m_intNumInput->setValue(round((val*1.0) / 255.0 * 100.0)); break; case KoChannelInfo::UINT16: m_intNumInput->setValue(round((val*1.0) / 65535.0 * 100.0)); break; case KoChannelInfo::UINT32: m_intNumInput->setValue(round((val*1.0) / 4294967295.0 * 100.0)); break; default: Q_ASSERT(false); } } else { m_intNumInput->setValue(val); } m_intNumInput->blockSignals(false); setValue(val); } void KisIntegerColorInput::onNumInputChanged(int val) { m_colorSlider->blockSignals(true); if (m_usePercentage) { switch (m_channelInfo->channelValueType()) { case KoChannelInfo::UINT8: m_colorSlider->setValue((val*1.0)/100.0 * 255.0); m_colorSlider->blockSignals(false); setValue((val*1.0)/100.0 * 255.0); break; case KoChannelInfo::UINT16: m_colorSlider->setValue((val*1.0)/100.0 * 65535.0); m_colorSlider->blockSignals(false); setValue((val*1.0)/100.0 * 65535.0); break; case KoChannelInfo::UINT32: m_colorSlider->setValue((val*1.0)/100.0 * 4294967295.0); m_colorSlider->blockSignals(false); setValue((val*1.0)/100.0 * 4294967295.0); break; default: Q_ASSERT(false); } } else { m_colorSlider->setValue(val); m_colorSlider->blockSignals(false); setValue(val); } } KisFloatColorInput::KisFloatColorInput(QWidget* parent, const KoChannelInfo* channelInfo, KoColor* color, KoColorDisplayRendererInterface *displayRenderer, bool usePercentage) : KisColorInput(parent, channelInfo, color, displayRenderer, usePercentage) { init(); } void KisFloatColorInput::setValue(double v) { quint8* data = m_color->data() + m_channelInfo->pos(); switch (m_channelInfo->channelValueType()) { #ifdef HAVE_OPENEXR case KoChannelInfo::FLOAT16: *(reinterpret_cast(data)) = v; break; #endif case KoChannelInfo::FLOAT32: *(reinterpret_cast(data)) = v; break; default: Q_ASSERT(false); } emit(updated()); } QWidget* KisFloatColorInput::createInput() { m_dblNumInput = new KisDoubleParseSpinBox(this); m_dblNumInput->setMinimum(0); m_dblNumInput->setMaximum(1.0); connect(m_colorSlider, SIGNAL(valueChanged(int)), this, SLOT(sliderChanged(int))); connect(m_dblNumInput, SIGNAL(valueChanged(double)), this, SLOT(setValue(double))); m_dblNumInput->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); m_dblNumInput->setMinimumWidth(60); m_dblNumInput->setMaximumWidth(60); quint8* data = m_color->data() + m_channelInfo->pos(); qreal value = 1.0; switch (m_channelInfo->channelValueType()) { #ifdef HAVE_OPENEXR case KoChannelInfo::FLOAT16: value = *(reinterpret_cast(data)); break; #endif case KoChannelInfo::FLOAT32: value = *(reinterpret_cast(data)); break; default: Q_ASSERT(false); } m_dblNumInput->setValue(value); return m_dblNumInput; } void KisFloatColorInput::sliderChanged(int i) { const qreal floatRange = m_maxValue - m_minValue; m_dblNumInput->setValue(m_minValue + (i / 255.0) * floatRange); } void KisFloatColorInput::update() { KoColor min = *m_color; KoColor max = *m_color; quint8* data = m_color->data() + m_channelInfo->pos(); quint8* dataMin = min.data() + m_channelInfo->pos(); quint8* dataMax = max.data() + m_channelInfo->pos(); qreal value = 1.0; m_minValue = m_displayRenderer->minVisibleFloatValue(m_channelInfo); m_maxValue = m_displayRenderer->maxVisibleFloatValue(m_channelInfo); + m_dblNumInput->blockSignals(true); m_colorSlider->blockSignals(true); switch (m_channelInfo->channelValueType()) { #ifdef HAVE_OPENEXR case KoChannelInfo::FLOAT16: value = *(reinterpret_cast(data)); m_minValue = qMin(value, m_minValue); m_maxValue = qMax(value, m_maxValue); *(reinterpret_cast(dataMin)) = m_minValue; *(reinterpret_cast(dataMax)) = m_maxValue; break; #endif case KoChannelInfo::FLOAT32: value = *(reinterpret_cast(data)); m_minValue = qMin(value, m_minValue); m_maxValue = qMax(value, m_maxValue); *(reinterpret_cast(dataMin)) = m_minValue; *(reinterpret_cast(dataMax)) = m_maxValue; break; default: Q_ASSERT(false); } m_dblNumInput->setMinimum(m_minValue); m_dblNumInput->setMaximum(m_maxValue); // ensure at least 3 significant digits are always shown int newPrecision = 2 + qMax(qreal(0.0), std::ceil(-std::log10(m_maxValue))); if (newPrecision != m_dblNumInput->decimals()) { m_dblNumInput->setDecimals(newPrecision); m_dblNumInput->updateGeometry(); } + m_dblNumInput->setValue(value); m_colorSlider->setColors(min, max); const qreal floatRange = m_maxValue - m_minValue; m_colorSlider->setValue((value - m_minValue) / floatRange * 255); + m_dblNumInput->blockSignals(false); m_colorSlider->blockSignals(false); } KisHexColorInput::KisHexColorInput(QWidget* parent, KoColor* color, KoColorDisplayRendererInterface *displayRenderer, bool usePercentage) : KisColorInput(parent, 0, color, displayRenderer, usePercentage) { QHBoxLayout* m_layout = new QHBoxLayout(this); m_layout->setContentsMargins(0,0,0,0); m_layout->setSpacing(1); QLabel* m_label = new QLabel(i18n("Color name:"), this); m_label->setMinimumWidth(50); m_layout->addWidget(m_label); QWidget* m_input = createInput(); m_input->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); m_layout->addWidget(m_input); } void KisHexColorInput::setValue() { QString valueString = m_hexInput->text(); valueString.remove(QChar('#')); QList channels = m_color->colorSpace()->channels(); channels = KoChannelInfo::displayOrderSorted(channels); Q_FOREACH (KoChannelInfo* channel, channels) { if (channel->channelType() == KoChannelInfo::COLOR) { Q_ASSERT(channel->channelValueType() == KoChannelInfo::UINT8); quint8* data = m_color->data() + channel->pos(); int value = valueString.left(2).toInt(0, 16); *(reinterpret_cast(data)) = value; valueString.remove(0, 2); } } emit(updated()); } void KisHexColorInput::update() { QString hexString("#"); QList channels = m_color->colorSpace()->channels(); channels = KoChannelInfo::displayOrderSorted(channels); Q_FOREACH (KoChannelInfo* channel, channels) { if (channel->channelType() == KoChannelInfo::COLOR) { Q_ASSERT(channel->channelValueType() == KoChannelInfo::UINT8); quint8* data = m_color->data() + channel->pos(); hexString.append(QString("%1").arg(*(reinterpret_cast(data)), 2, 16, QChar('0'))); } } m_hexInput->setText(hexString); } QWidget* KisHexColorInput::createInput() { m_hexInput = new QLineEdit(this); m_hexInput->setAlignment(Qt::AlignRight); int digits = 2*m_color->colorSpace()->colorChannelCount(); QString pattern = QString("#?[a-fA-F0-9]{%1,%2}").arg(digits).arg(digits); m_hexInput->setValidator(new QRegExpValidator(QRegExp(pattern), this)); connect(m_hexInput, SIGNAL(editingFinished()), this, SLOT(setValue())); return m_hexInput; } diff --git a/libs/widgetutils/KisSqueezedComboBox.cpp b/libs/widgetutils/KisSqueezedComboBox.cpp index 6e5d35baec..afd5de755d 100644 --- a/libs/widgetutils/KisSqueezedComboBox.cpp +++ b/libs/widgetutils/KisSqueezedComboBox.cpp @@ -1,179 +1,178 @@ /* ============================================================ * Author: Tom Albers * Date : 2005-01-01 * Description : * * Copyright 2005 by Tom Albers * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option)* any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "KisSqueezedComboBox.h" /** @file KisSqueezedComboBox.cpp */ // Qt includes. #include #include #include #include #include #include KisSqueezedComboBox::KisSqueezedComboBox(QWidget *parent, const char *name) : QComboBox(parent) { setObjectName(name); setMinimumWidth(100); m_timer = new QTimer(this); m_timer->setSingleShot(true); connect(m_timer, SIGNAL(timeout()), SLOT(slotTimeOut())); } KisSqueezedComboBox::~KisSqueezedComboBox() { delete m_timer; } bool KisSqueezedComboBox::contains(const QString& _text) const { if (_text.isEmpty()) return false; for (QMap::const_iterator it = m_originalItems.begin() ; it != m_originalItems.end(); ++it) { if (it.value() == _text) { return true; } } return false; } qint32 KisSqueezedComboBox::findOriginalText(const QString& text) const { for (int i = 0; i < m_originalItems.size(); i++) { if(m_originalItems.value(i) == text) { return i; } } return -1; } QStringList KisSqueezedComboBox::originalTexts() const { return m_originalItems.values(); } void KisSqueezedComboBox::resetOriginalTexts(const QStringList &texts) { if (texts == m_originalItems.values()) return; clear(); m_originalItems.clear(); Q_FOREACH (const QString &item, texts) { addSqueezedItem(item); } } QSize KisSqueezedComboBox::sizeHint() const { ensurePolished(); QFontMetrics fm = fontMetrics(); int maxW = count() ? 18 : 7 * fm.boundingRect(QChar('x')).width() + 18; int maxH = qMax(fm.lineSpacing(), 14) + 2; QStyleOptionComboBox options; options.initFrom(this); - return style()->sizeFromContents(QStyle::CT_ComboBox, &options, - QSize(maxW, maxH), this).expandedTo(QApplication::globalStrut()); + return style()->sizeFromContents(QStyle::CT_ComboBox, &options, QSize(maxW, maxH), this); } void KisSqueezedComboBox::insertSqueezedItem(const QString& newItem, int index, QVariant userData) { m_originalItems[index] = newItem; QComboBox::insertItem(index, squeezeText(newItem, this), userData); } void KisSqueezedComboBox::insertSqueezedItem(const QIcon &icon, const QString &newItem, int index, QVariant userData) { m_originalItems[index] = newItem; QComboBox::insertItem(index, icon, squeezeText(newItem, this), userData); } void KisSqueezedComboBox::addSqueezedItem(const QString& newItem, QVariant userData) { insertSqueezedItem(newItem, count(), userData); } void KisSqueezedComboBox::addSqueezedItem(const QIcon &icon, const QString &newItem, QVariant userData) { insertSqueezedItem(icon, newItem, count(), userData); } void KisSqueezedComboBox::setCurrent(const QString& itemText) { qint32 itemIndex = findOriginalText(itemText); if (itemIndex >= 0) { setCurrentIndex(itemIndex); } } void KisSqueezedComboBox::resizeEvent(QResizeEvent *) { m_timer->start(200); } void KisSqueezedComboBox::slotTimeOut() { for (QMap::iterator it = m_originalItems.begin() ; it != m_originalItems.end(); ++it) { setItemText(it.key(), squeezeText(it.value(), this)); } } QString KisSqueezedComboBox::squeezeText(const QString& original, const QWidget *widget) { // not the complete widgetSize is usable. Need to compensate for that. int widgetSize = widget->width() - 30; QFontMetrics fm(widget->fontMetrics()); // If we can fit the full text, return that. if (fm.boundingRect(original).width() < widgetSize) return(original); // We need to squeeze. QString sqItem = original; // prevent empty return value; widgetSize = widgetSize - fm.boundingRect("...").width(); for (int i = 0 ; i != original.length(); ++i) { if ((int)fm.boundingRect(original.right(i)).width() > widgetSize) { sqItem = QString("..." + original.right(--i)); break; } } return sqItem; } QString KisSqueezedComboBox::currentUnsqueezedText() { int curItem = currentIndex(); return m_originalItems[curItem]; } void KisSqueezedComboBox::removeSqueezedItem(int index) { removeItem(index); m_originalItems.remove(index); } diff --git a/libs/widgetutils/config/kstandardaction.h b/libs/widgetutils/config/kstandardaction.h index 9055e1614b..729bcb9d6c 100644 --- a/libs/widgetutils/config/kstandardaction.h +++ b/libs/widgetutils/config/kstandardaction.h @@ -1,598 +1,597 @@ /* This file is part of the KDE libraries Copyright (C) 1999,2000 Kurt Granroth Copyright (C) 2001,2002 Ellis Whitehead This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 KSTANDARDACTION_H #define KSTANDARDACTION_H #include #include #include class QObject; class QStringList; class QWidget; class QAction; class KRecentFilesAction; class KDualAction; class KToggleAction; class KToggleFullScreenAction; /** * Convenience methods to access all standard KDE actions. * * These actions should be used instead of hardcoding menubar and * toolbar items. Using these actions helps your application easily - * conform to the KDE UI Style Guide - * @see http://developer.kde.org/documentation/standards/kde/style/basics/index.html . + * conform to the KDE UI Style Guide. * * All of the documentation for QAction holds for KStandardAction * also. When in doubt on how things work, check the QAction * documentation first. * Please note that calling any of these methods automatically adds the action * to the actionCollection() of the QObject given by the 'parent' parameter. * * Simple Example:\n * * In general, using standard actions should be a drop in replacement * for regular actions. For example, if you previously had: * * \code * QAction *newAct = new QAction(i18n("&New"), KisIconUtils::loadIcon("document-new"), * KStandardShortcut::shortcut(KStandardShortcut::New), this, * SLOT(fileNew()), actionCollection()); * \endcode * * You could drop that and replace it with: * * \code * QAction *newAct = KStandardAction::openNew(this, SLOT(fileNew()), * actionCollection()); * \endcode * * Non-standard Usages\n * * It is possible to use the standard actions in various * non-recommended ways. Say, for instance, you wanted to have a * standard action (with the associated correct text and icon and * accelerator, etc) but you didn't want it to go in the standard * place (this is not recommended, by the way). One way to do this is * to simply not use the XML UI framework and plug it into wherever * you want. If you do want to use the XML UI framework (good!), then * it is still possible. * * Basically, the XML building code matches names in the XML code with * the internal names of the actions. You can find out the internal * names of each of the standard actions by using the name * method like so: KStandardAction::name(KStandardAction::Cut) would return * 'edit_cut'. The XML building code will match 'edit_cut' to the * attribute in the global XML file and place your action there. * * However, you can change the internal name. In this example, just * do something like: * * \code * (void)KStandardAction::cut(this, SLOT(editCut()), actionCollection(), "my_cut"); * \endcode * * Now, in your local XML resource file (e.g., yourappui.rc), simply * put 'my_cut' where you want it to go. * * Another non-standard usage concerns getting a pointer to an * existing action if, say, you want to enable or disable the action. * You could do it the recommended way and just grab a pointer when * you instantiate it as in the 'openNew' example above... or you * could do it the hard way: * * \code * QAction *cut = actionCollection()->action(KStandardAction::name(KStandardAction::Cut)); * \endcode * * Another non-standard usage concerns instantiating the action in the * first place. Usually, you would use the member functions as * shown above (e.g., KStandardAction::cut(this, SLOT, parent)). You * may, however, do this using the enums provided. This author can't * think of a reason why you would want to, but, hey, if you do, * here's how: * * \code * (void)KStandardAction::action(KStandardAction::New, this, SLOT(fileNew()), actionCollection()); * (void)KStandardAction::action(KStandardAction::Cut, this, SLOT(editCut()), actionCollection()); * \endcode * * @author Kurt Granroth */ namespace KStandardAction { /** * The standard menubar and toolbar actions. */ enum StandardAction { ActionNone, // File Menu New, Open, OpenRecent, Save, SaveAs, Revert, Close, Print, PrintPreview, Mail, Quit, // Edit Menu Undo, Redo, Cut, Copy, Paste, SelectAll, Deselect, Find, FindNext, FindPrev, Replace, // View Menu ActualSize, FitToPage, FitToWidth, FitToHeight, ZoomIn, ZoomOut, Zoom, Redisplay, // Go Menu Up, Back, Forward, Home /*Home page*/, Prior, Next, Goto, GotoPage, GotoLine, FirstPage, LastPage, DocumentBack, DocumentForward, // Bookmarks Menu AddBookmark, EditBookmarks, // Tools Menu Spelling, // Settings Menu ShowMenubar, ShowToolbar, ShowStatusbar, SaveOptions, KeyBindings, Preferences, ConfigureToolbars, // Help Menu Help, HelpContents, WhatsThis, ReportBug, AboutApp, AboutKDE, TipofDay, // Other standard actions ConfigureNotifications, FullScreen, Clear, PasteText, SwitchApplicationLanguage }; /** * Creates an action corresponding to one of the * KStandardAction::StandardAction actions, which is connected to the given * object and @p slot, and is owned by @p parent. * * The signal that is connected to @p slot is triggered(bool), except for the case of * OpenRecent standard action, which uses the urlSelected(const QUrl &) signal of * KRecentFilesAction. * * @param id The StandardAction identifier to create a QAction for. * @param recvr The QObject to receive the signal, or 0 if no notification * is needed. * @param slot The slot to connect the signal to (remember to use the SLOT() macro). * @param parent The QObject that should own the created QAction, or 0 if no parent will * own the QAction returned (ensure you delete it manually in this case). */ KRITAWIDGETUTILS_EXPORT QAction *create(StandardAction id, const QObject *recvr, const char *slot, QObject *parent); /** * This will return the internal name of a given standard action. */ KRITAWIDGETUTILS_EXPORT const char *name(StandardAction id); /// @deprecated use name() #ifndef KDE_NO_DEPRECATED inline KRITAWIDGETUTILS_DEPRECATED const char *stdName(StandardAction act_enum) { return name(act_enum); } #endif /** * Returns a list of all standard names. Used by KAccelManager * to give those higher weight. */ KRITAWIDGETUTILS_EXPORT QStringList stdNames(); /** * Returns a list of all actionIds. * * @since 4.2 */ KRITAWIDGETUTILS_EXPORT QList actionIds(); /** * Returns the standardshortcut associated with @a actionId. * * @param id The actionId whose associated shortcut is wanted. * * @since 4.2 */ KRITAWIDGETUTILS_EXPORT KStandardShortcut::StandardShortcut shortcutForActionId(StandardAction id); /** * Create a new document or window. */ KRITAWIDGETUTILS_EXPORT QAction *openNew(const QObject *recvr, const char *slot, QObject *parent); /** * Open an existing file. */ KRITAWIDGETUTILS_EXPORT QAction *open(const QObject *recvr, const char *slot, QObject *parent); /** * Open a recently used document. The signature of the slot being called * is of the form slotURLSelected( const QUrl & ). * @param recvr object to receive slot * @param slot The SLOT to invoke when a URL is selected. The slot's * signature is slotURLSelected( const QUrl & ). * @param parent parent widget */ KRITAWIDGETUTILS_EXPORT KRecentFilesAction *openRecent(const QObject *recvr, const char *slot, QObject *parent); /** * Save the current document. */ KRITAWIDGETUTILS_EXPORT QAction *save(const QObject *recvr, const char *slot, QObject *parent); /** * Save the current document under a different name. */ KRITAWIDGETUTILS_EXPORT QAction *saveAs(const QObject *recvr, const char *slot, QObject *parent); /** * Revert the current document to the last saved version * (essentially will undo all changes). */ KRITAWIDGETUTILS_EXPORT QAction *revert(const QObject *recvr, const char *slot, QObject *parent); /** * Close the current document. */ KRITAWIDGETUTILS_EXPORT QAction *close(const QObject *recvr, const char *slot, QObject *parent); /** * Print the current document. */ KRITAWIDGETUTILS_EXPORT QAction *print(const QObject *recvr, const char *slot, QObject *parent); /** * Show a print preview of the current document. */ KRITAWIDGETUTILS_EXPORT QAction *printPreview(const QObject *recvr, const char *slot, QObject *parent); /** * Mail this document. */ KRITAWIDGETUTILS_EXPORT QAction *mail(const QObject *recvr, const char *slot, QObject *parent); /** * Quit the program. * * Note that you probably want to connect this action to either QWidget::close() * or QApplication::closeAllWindows(), but not QApplication::quit(), so that * KMainWindow::queryClose() is called on any open window (to warn the user * about unsaved changes for example). */ KRITAWIDGETUTILS_EXPORT QAction *quit(const QObject *recvr, const char *slot, QObject *parent); /** * Undo the last operation. */ KRITAWIDGETUTILS_EXPORT QAction *undo(const QObject *recvr, const char *slot, QObject *parent); /** * Redo the last operation. */ KRITAWIDGETUTILS_EXPORT QAction *redo(const QObject *recvr, const char *slot, QObject *parent); /** * Cut selected area and store it in the clipboard. * Calls cut() on the widget with the current focus. */ KRITAWIDGETUTILS_EXPORT QAction *cut(QObject *parent); /** * Copy selected area and store it in the clipboard. * Calls copy() on the widget with the current focus. */ KRITAWIDGETUTILS_EXPORT QAction *copy(QObject *parent); /** * Paste the contents of clipboard at the current mouse or cursor * Calls paste() on the widget with the current focus. */ KRITAWIDGETUTILS_EXPORT QAction *paste(QObject *parent); /** * Clear selected area. Calls clear() on the widget with the current focus. * Note that for some widgets, this may not provide the intended behavior. For * example if you make use of the code above and a K3ListView has the focus, clear() * will clear all of the items in the list. If this is not the intened behavior * and you want to make use of this slot, you can subclass K3ListView and reimplement * this slot. For example the following code would implement a K3ListView without this * behavior: * * \code * class MyListView : public K3ListView { * Q_OBJECT * public: * MyListView( QWidget * parent = 0, const char * name = 0, WFlags f = 0 ) : K3ListView( parent, name, f ) {} * virtual ~MyListView() {} * public Q_SLOTS: * virtual void clear() {} * }; * \endcode */ KRITAWIDGETUTILS_EXPORT QAction *clear(QObject *parent); /** * Calls selectAll() on the widget with the current focus. */ KRITAWIDGETUTILS_EXPORT QAction *selectAll(QObject *parent); /** * Cut selected area and store it in the clipboard. */ KRITAWIDGETUTILS_EXPORT QAction *cut(const QObject *recvr, const char *slot, QObject *parent); /** * Copy the selected area into the clipboard. */ KRITAWIDGETUTILS_EXPORT QAction *copy(const QObject *recvr, const char *slot, QObject *parent); /** * Paste the contents of clipboard at the current mouse or cursor * position. */ KRITAWIDGETUTILS_EXPORT QAction *paste(const QObject *recvr, const char *slot, QObject *parent); /** * Paste the contents of clipboard at the current mouse or cursor * position. Provide a button on the toolbar with the clipboard history * menu if Klipper is running. */ KRITAWIDGETUTILS_EXPORT QAction *pasteText(const QObject *recvr, const char *slot, QObject *parent); /** * Clear the content of the focus widget */ KRITAWIDGETUTILS_EXPORT QAction *clear(const QObject *recvr, const char *slot, QObject *parent); /** * Select all elements in the current document. */ KRITAWIDGETUTILS_EXPORT QAction *selectAll(const QObject *recvr, const char *slot, QObject *parent); /** * Deselect any selected elements in the current document. */ KRITAWIDGETUTILS_EXPORT QAction *deselect(const QObject *recvr, const char *slot, QObject *parent); /** * Initiate a 'find' request in the current document. */ KRITAWIDGETUTILS_EXPORT QAction *find(const QObject *recvr, const char *slot, QObject *parent); /** * Find the next instance of a stored 'find'. */ KRITAWIDGETUTILS_EXPORT QAction *findNext(const QObject *recvr, const char *slot, QObject *parent); /** * Find a previous instance of a stored 'find'. */ KRITAWIDGETUTILS_EXPORT QAction *findPrev(const QObject *recvr, const char *slot, QObject *parent); /** * Find and replace matches. */ KRITAWIDGETUTILS_EXPORT QAction *replace(const QObject *recvr, const char *slot, QObject *parent); /** * View the document at its actual size. */ KRITAWIDGETUTILS_EXPORT QAction *actualSize(const QObject *recvr, const char *slot, QObject *parent); /** * Fit the document view to the size of the current window. */ KRITAWIDGETUTILS_EXPORT QAction *fitToPage(const QObject *recvr, const char *slot, QObject *parent); /** * Fit the document view to the width of the current window. */ KRITAWIDGETUTILS_EXPORT QAction *fitToWidth(const QObject *recvr, const char *slot, QObject *parent); /** * Fit the document view to the height of the current window. */ KRITAWIDGETUTILS_EXPORT QAction *fitToHeight(const QObject *recvr, const char *slot, QObject *parent); /** * Zoom in. */ KRITAWIDGETUTILS_EXPORT QAction *zoomIn(const QObject *recvr, const char *slot, QObject *parent); /** * Zoom out. */ KRITAWIDGETUTILS_EXPORT QAction *zoomOut(const QObject *recvr, const char *slot, QObject *parent); /** * Popup a zoom dialog. */ KRITAWIDGETUTILS_EXPORT QAction *zoom(const QObject *recvr, const char *slot, QObject *parent); /** * Redisplay or redraw the document. */ KRITAWIDGETUTILS_EXPORT QAction *redisplay(const QObject *recvr, const char *slot, QObject *parent); /** * Move up (web style menu). */ KRITAWIDGETUTILS_EXPORT QAction *up(const QObject *recvr, const char *slot, QObject *parent); /** * Move back (web style menu). */ KRITAWIDGETUTILS_EXPORT QAction *back(const QObject *recvr, const char *slot, QObject *parent); /** * Move forward (web style menu). */ KRITAWIDGETUTILS_EXPORT QAction *forward(const QObject *recvr, const char *slot, QObject *parent); /** * Go to the "Home" position or document. */ KRITAWIDGETUTILS_EXPORT QAction *home(const QObject *recvr, const char *slot, QObject *parent); /** * Scroll up one page. */ KRITAWIDGETUTILS_EXPORT QAction *prior(const QObject *recvr, const char *slot, QObject *parent); /** * Scroll down one page. */ KRITAWIDGETUTILS_EXPORT QAction *next(const QObject *recvr, const char *slot, QObject *parent); /** * Go to somewhere in general. */ KRITAWIDGETUTILS_EXPORT QAction *goTo(const QObject *recvr, const char *slot, QObject *parent); /** * Go to a specific page (dialog). */ KRITAWIDGETUTILS_EXPORT QAction *gotoPage(const QObject *recvr, const char *slot, QObject *parent); /** * Go to a specific line (dialog). */ KRITAWIDGETUTILS_EXPORT QAction *gotoLine(const QObject *recvr, const char *slot, QObject *parent); /** * Jump to the first page. */ KRITAWIDGETUTILS_EXPORT QAction *firstPage(const QObject *recvr, const char *slot, QObject *parent); /** * Jump to the last page. */ KRITAWIDGETUTILS_EXPORT QAction *lastPage(const QObject *recvr, const char *slot, QObject *parent); /** * Move back (document style menu). */ KRITAWIDGETUTILS_EXPORT QAction *documentBack(const QObject *recvr, const char *slot, QObject *parent); /** * Move forward (document style menu). */ KRITAWIDGETUTILS_EXPORT QAction *documentForward(const QObject *recvr, const char *slot, QObject *parent); /** * Add the current page to the bookmarks tree. */ KRITAWIDGETUTILS_EXPORT QAction *addBookmark(const QObject *recvr, const char *slot, QObject *parent); /** * Edit the application bookmarks. */ KRITAWIDGETUTILS_EXPORT QAction *editBookmarks(const QObject *recvr, const char *slot, QObject *parent); /** * Pop up the spell checker. */ KRITAWIDGETUTILS_EXPORT QAction *spelling(const QObject *recvr, const char *slot, QObject *parent); /** * Show/Hide the menubar. */ KRITAWIDGETUTILS_EXPORT KToggleAction *showMenubar(const QObject *recvr, const char *slot, QObject *parent); /** * Show/Hide the statusbar. */ KRITAWIDGETUTILS_EXPORT KToggleAction *showStatusbar(const QObject *recvr, const char *slot, QObject *parent); /** * Switch to/from full screen mode */ KRITAWIDGETUTILS_EXPORT KToggleFullScreenAction *fullScreen(const QObject *recvr, const char *slot, QWidget *window, QObject *parent); /** * Display the save options dialog. */ KRITAWIDGETUTILS_EXPORT QAction *saveOptions(const QObject *recvr, const char *slot, QObject *parent); /** * Display the configure key bindings dialog. * * Note that you might be able to use the pre-built KXMLGUIFactory's function: * KStandardAction::keyBindings(guiFactory(), SLOT(configureShortcuts()), actionCollection()); */ KRITAWIDGETUTILS_EXPORT QAction *keyBindings(const QObject *recvr, const char *slot, QObject *parent); /** * Display the preferences/options dialog. */ KRITAWIDGETUTILS_EXPORT QAction *preferences(const QObject *recvr, const char *slot, QObject *parent); /** * The Customize Toolbar dialog. */ KRITAWIDGETUTILS_EXPORT QAction *configureToolbars(const QObject *recvr, const char *slot, QObject *parent); /** * The Configure Notifications dialog. */ KRITAWIDGETUTILS_EXPORT QAction *configureNotifications(const QObject *recvr, const char *slot, QObject *parent); /** * Display the help. */ KRITAWIDGETUTILS_EXPORT QAction *help(const QObject *recvr, const char *slot, QObject *parent); /** * Display the help contents. */ KRITAWIDGETUTILS_EXPORT QAction *helpContents(const QObject *recvr, const char *slot, QObject *parent); /** * Trigger the What's This cursor. */ KRITAWIDGETUTILS_EXPORT QAction *whatsThis(const QObject *recvr, const char *slot, QObject *parent); /** * Display "Tip of the Day" */ KRITAWIDGETUTILS_EXPORT QAction *tipOfDay(const QObject *recvr, const char *slot, QObject *parent); /** * Open up the Report Bug dialog. */ KRITAWIDGETUTILS_EXPORT QAction *reportBug(const QObject *recvr, const char *slot, QObject *parent); /** * Display the application's About box. */ KRITAWIDGETUTILS_EXPORT QAction *aboutApp(const QObject *recvr, const char *slot, QObject *parent); /** * Display the About KDE dialog. */ KRITAWIDGETUTILS_EXPORT QAction *aboutKDE(const QObject *recvr, const char *slot, QObject *parent); } #endif // KSTDACTION_H diff --git a/libs/widgetutils/kis_double_parse_spin_box.cpp b/libs/widgetutils/kis_double_parse_spin_box.cpp index 50005053dd..47c4ead442 100644 --- a/libs/widgetutils/kis_double_parse_spin_box.cpp +++ b/libs/widgetutils/kis_double_parse_spin_box.cpp @@ -1,236 +1,237 @@ /* * Copyright (c) 2016 Laurent Valentin Jospin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_double_parse_spin_box.h" #include "kis_num_parser.h" #include #include #include #include #include #include // for qIsNaN KisDoubleParseSpinBox::KisDoubleParseSpinBox(QWidget *parent) : QDoubleSpinBox(parent), boolLastValid(true), lastExprParsed(QStringLiteral("0.0")) { setAlignment(Qt::AlignRight); connect(this, SIGNAL(noMoreParsingError()), this, SLOT(clearErrorStyle())); //hack to let the clearError be called, even if the value changed method is the one from QDoubleSpinBox. connect(this, SIGNAL(valueChanged(double)), this, SLOT(clearError())); connect(this, SIGNAL(errorWhileParsing(QString)), this, SLOT(setErrorStyle())); oldValue = value(); warningIcon = new QLabel(this); if (QFile(":/./16_light_warning.svg").exists()) { warningIcon->setPixmap(QIcon(":/./16_light_warning.svg").pixmap(16, 16)); } else { warningIcon->setText("!"); } warningIcon->setStyleSheet("background:transparent;"); warningIcon->move(1, 1); warningIcon->setVisible(false); isOldPaletteSaved = false; areOldMarginsSaved = false; } KisDoubleParseSpinBox::~KisDoubleParseSpinBox() { } double KisDoubleParseSpinBox::valueFromText(const QString & text) const { lastExprParsed = text; bool ok; double ret; if ( (suffix().isEmpty() || !text.endsWith(suffix())) && (prefix().isEmpty() || !text.startsWith(prefix())) ) { ret = KisNumericParser::parseSimpleMathExpr(text, &ok); } else { QString expr = text; if (text.endsWith(suffix())) { expr.remove(text.size()-suffix().size(), suffix().size()); } if(text.startsWith(prefix())){ expr.remove(0, prefix().size()); } lastExprParsed = expr; ret = KisNumericParser::parseSimpleMathExpr(expr, &ok); } if(qIsNaN(ret) || qIsInf(ret)){ ok = false; } if (!ok) { if (boolLastValid) { oldValue = value(); } boolLastValid = false; ret = oldValue; //in case of error set to minimum. } else { if (!boolLastValid) { oldValue = ret; } boolLastValid = true; } return ret; } QString KisDoubleParseSpinBox::textFromValue(double val) const { if (!boolLastValid) { emit errorWhileParsing(lastExprParsed); return lastExprParsed; } emit noMoreParsingError(); return QDoubleSpinBox::textFromValue(val); } QString KisDoubleParseSpinBox::veryCleanText() const { return cleanText(); } QValidator::State KisDoubleParseSpinBox::validate ( QString & input, int & pos ) const { Q_UNUSED(input); Q_UNUSED(pos); return QValidator::Acceptable; } void KisDoubleParseSpinBox::stepBy(int steps) { boolLastValid = true; //reset to valid state so we can use the up and down buttons. emit noMoreParsingError(); QDoubleSpinBox::stepBy(steps); } void KisDoubleParseSpinBox::setValue(double value) { - if(value == oldValue && hasFocus()){ //avoid to reset the button when it set the value of something that will recall this slot. + // Avoid to reset the button when it set the val of something that will recall this slot. + if(hasFocus() && QString::number( value, 'f', this->decimals()) == QString::number( oldValue, 'f', this->decimals())){ return; } QDoubleSpinBox::setValue(value); if (!hasFocus()) { clearError(); } } void KisDoubleParseSpinBox::setErrorStyle() { if (!boolLastValid) { //setStyleSheet(_oldStyleSheet + "Background: red; color: white; padding-left: 18px;"); if (!isOldPaletteSaved) { oldPalette = palette(); } isOldPaletteSaved = true; QPalette nP = oldPalette; nP.setColor(QPalette::Background, Qt::red); nP.setColor(QPalette::Base, Qt::red); nP.setColor(QPalette::Text, Qt::white); setPalette(nP); if (!areOldMarginsSaved) { oldMargins = lineEdit()->textMargins(); } areOldMarginsSaved = true; if (width() - height() >= 3*height()) { //if we have twice as much place as needed by the warning icon then display it. QMargins newMargins = oldMargins; newMargins.setLeft( newMargins.left() + height() - 4 ); lineEdit()->setTextMargins(newMargins); int h = warningIcon->height(); int hp = height()-2; if (h != hp) { warningIcon->resize(hp, hp); if (QFile(":/./16_light_warning.svg").exists()) { warningIcon->setPixmap(QIcon(":/./16_light_warning.svg").pixmap(hp-7, hp-7)); } } warningIcon->move(oldMargins.left()+4, 1); warningIcon->setVisible(true); } } } void KisDoubleParseSpinBox::clearErrorStyle() { if (boolLastValid) { warningIcon->setVisible(false); //setStyleSheet(QString()); setPalette(oldPalette); isOldPaletteSaved = false; lineEdit()->setTextMargins(oldMargins); areOldMarginsSaved = false; } } void KisDoubleParseSpinBox::clearError() { boolLastValid = true; emit noMoreParsingError(); oldValue = value(); clearErrorStyle(); } diff --git a/libs/widgetutils/xmlgui/kaboutkdedialog_p.cpp b/libs/widgetutils/xmlgui/kaboutkdedialog_p.cpp index cdc1a27ac6..316f0da8a7 100644 --- a/libs/widgetutils/xmlgui/kaboutkdedialog_p.cpp +++ b/libs/widgetutils/xmlgui/kaboutkdedialog_p.cpp @@ -1,159 +1,159 @@ /* This file is part of the KDE libraries Copyright (C) 2007 Urs Wolfer Parts of this class have been take from the KAboutKDE class, which was Copyright (C) 2000 Espen Sand This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 "kaboutkdedialog_p.h" #include #include #include #include #include #include #include #include #include namespace KDEPrivate { KAboutKdeDialog::KAboutKdeDialog(QWidget *parent) : QDialog(parent), d(0) { setWindowTitle(i18n("About KDE")); KTitleWidget *titleWidget = new KTitleWidget(this); titleWidget->setText(i18n("KDE - Be Free!")); titleWidget->setPixmap(KisIconUtils::loadIcon(QStringLiteral("kde")).pixmap(48), KTitleWidget::ImageLeft); QLabel *about = new QLabel; about->setMargin(10); about->setAlignment(Qt::AlignTop); about->setWordWrap(true); about->setOpenExternalLinks(true); about->setTextInteractionFlags(Qt::TextBrowserInteraction); about->setText(i18n("" "KDE is a world-wide network of software engineers, artists, writers, translators and facilitators " "who are committed to Free Software development. " "This community has created hundreds of Free Software applications as part of the KDE " "frameworks, workspaces and applications.

" "KDE is a cooperative enterprise in which no single entity controls the " "efforts or products of KDE to the exclusion of others. Everyone is welcome to join and " "contribute to KDE, including you.

" "Visit %2 for " "more information about the KDE community and the software we produce.", - QStringLiteral("http://www.gnu.org/philosophy/free-sw.html"), - QStringLiteral("http://www.kde.org/"))); + QStringLiteral("https://www.gnu.org/philosophy/free-sw.html"), + QStringLiteral("https://www.kde.org/"))); QLabel *report = new QLabel; report->setMargin(10); report->setAlignment(Qt::AlignTop); report->setWordWrap(true); report->setOpenExternalLinks(true); report->setTextInteractionFlags(Qt::TextBrowserInteraction); report->setText(i18n("" "Software can always be improved, and the KDE team is ready to " "do so. However, you - the user - must tell us when " "something does not work as expected or could be done better.

" "KDE has a bug tracking system. Visit " "%1 or " "use the \"Report Bug...\" dialog from the \"Help\" menu to report bugs.

" "If you have a suggestion for improvement then you are welcome to use " "the bug tracking system to register your wish. Make sure you use the " "severity called \"Wishlist\".", QStringLiteral("https://bugs.kde.org/"))); QLabel *join = new QLabel; join->setMargin(10); join->setAlignment(Qt::AlignTop); join->setWordWrap(true); join->setOpenExternalLinks(true); join->setTextInteractionFlags(Qt::TextBrowserInteraction); join->setText(i18n("" "You do not have to be a software developer to be a member of the " "KDE team. You can join the national teams that translate " "program interfaces. You can provide graphics, themes, sounds, and " "improved documentation. You decide!" "

" "Visit " "%1 " "for information on some projects in which you can participate." "

" "If you need more information or documentation, then a visit to " "%2 " "will provide you with what you need.", - QStringLiteral("http://www.kde.org/community/getinvolved/"), - QStringLiteral("http://techbase.kde.org/"))); + QStringLiteral("https://community.kde.org/Get_Involved"), + QStringLiteral("https://techbase.kde.org/"))); QLabel *support = new QLabel; support->setMargin(10); support->setAlignment(Qt::AlignTop); support->setWordWrap(true); support->setOpenExternalLinks(true); support->setTextInteractionFlags(Qt::TextBrowserInteraction); support->setText(i18n("" "KDE software is and will always be available free of charge, however creating it is not free.

" "To support development the KDE community has formed the KDE e.V., a non-profit organization " "legally founded in Germany. KDE e.V. represents the KDE community in legal and financial matters. " "See %1" " for information on KDE e.V.

" "KDE benefits from many kinds of contributions, including financial. " "We use the funds to reimburse members and others for expenses " "they incur when contributing. Further funds are used for legal " "support and organizing conferences and meetings.

" "We would like to encourage you to support our efforts with a " "financial donation, using one of the ways described at " "%2." "

Thank you very much in advance for your support.", - QStringLiteral("http://ev.kde.org/"), - QStringLiteral("http://www.kde.org/community/donations/")) + QLatin1String("

")); // FIXME: ugly
at the end... + QStringLiteral("https://ev.kde.org/"), + QStringLiteral("https://www.kde.org/community/donations/")) + QLatin1String("

")); // FIXME: ugly
at the end... QTabWidget *tabWidget = new QTabWidget; tabWidget->setUsesScrollButtons(false); tabWidget->addTab(about, i18nc("About KDE", "&About")); tabWidget->addTab(report, i18n("&Report Bugs or Wishes")); tabWidget->addTab(join, i18n("&Join KDE")); tabWidget->addTab(support, i18n("&Support KDE")); QLabel *image = new QLabel; image->setPixmap(QStringLiteral(":/kxmlgui5/aboutkde.png")); QHBoxLayout *midLayout = new QHBoxLayout; midLayout->addWidget(image); midLayout->addWidget(tabWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox; buttonBox->setStandardButtons(QDialogButtonBox::Close); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(titleWidget); mainLayout->addLayout(midLayout); mainLayout->addWidget(buttonBox); setLayout(mainLayout); } } diff --git a/libs/widgetutils/xmlgui/kbugreport.cpp b/libs/widgetutils/xmlgui/kbugreport.cpp index 9d2cdebbfc..3ba99a225b 100644 --- a/libs/widgetutils/xmlgui/kbugreport.cpp +++ b/libs/widgetutils/xmlgui/kbugreport.cpp @@ -1,233 +1,233 @@ /* This file is part of the KDE project Copyright (C) 1999 David Faure 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 "kbugreport.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "systeminformation_p.h" #include "config-xmlgui.h" #include class KBugReportPrivate { public: KBugReportPrivate(KBugReport *q): q(q), m_aboutData(KAboutData::applicationData()) {} void _k_updateUrl(); KBugReport *q; QProcess *m_process; KAboutData m_aboutData; QTextEdit *m_lineedit; QLineEdit *m_subject; QLabel *m_version; QString m_strVersion; QGroupBox *m_bgSeverity; QComboBox *appcombo; QString lastError; QString appname; QString os; QUrl url; QList severityButtons; int currentSeverity() { for (int i = 0; i < severityButtons.count(); i++) if (severityButtons[i]->isChecked()) { return i; } return -1; } }; KBugReport::KBugReport(const KAboutData &aboutData, QWidget *_parent) : QDialog(_parent), d(new KBugReportPrivate(this)) { setWindowTitle(i18n("Submit Bug Report")); QDialogButtonBox *buttonBox = new QDialogButtonBox(this); buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); d->m_aboutData = aboutData; d->m_process = 0; KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::close()); QLabel *tmpLabel; QVBoxLayout *lay = new QVBoxLayout; setLayout(lay); KTitleWidget *title = new KTitleWidget(this); title->setText(i18n("Submit Bug Report")); title->setPixmap(KisIconUtils::loadIcon(QStringLiteral("tools-report-bug")).pixmap(32)); lay->addWidget(title); QGridLayout *glay = new QGridLayout(); lay->addLayout(glay); int row = 0; // Program name QString qwtstr = i18n("The application for which you wish to submit a bug report - if incorrect, please use the Report Bug menu item of the correct application"); tmpLabel = new QLabel(i18n("Application: "), this); glay->addWidget(tmpLabel, row, 0); tmpLabel->setWhatsThis(qwtstr); d->appcombo = new QComboBox(this); d->appcombo->setWhatsThis(qwtstr); QStringList packageList = QStringList() << "krita"; d->appcombo->addItems(packageList); connect(d->appcombo, SIGNAL(activated(int)), SLOT(_k_appChanged(int))); d->appname = d->m_aboutData.productName(); glay->addWidget(d->appcombo, row, 1); int index = 0; for (; index < d->appcombo->count(); index++) { if (d->appcombo->itemText(index) == d->appname) { break; } } if (index == d->appcombo->count()) { // not present d->appcombo->addItem(d->appname); } d->appcombo->setCurrentIndex(index); tmpLabel->setWhatsThis(qwtstr); // Version qwtstr = i18n("The version of this application - please make sure that no newer version is available before sending a bug report"); tmpLabel = new QLabel(i18n("Version:"), this); glay->addWidget(tmpLabel, ++row, 0); tmpLabel->setWhatsThis(qwtstr); d->m_strVersion = d->m_aboutData.version(); if (d->m_strVersion.isEmpty()) { d->m_strVersion = i18n("no version set (programmer error)"); } d->m_version = new QLabel(d->m_strVersion, this); d->m_version->setTextInteractionFlags(Qt::TextBrowserInteraction); //glay->addWidget( d->m_version, row, 1 ); glay->addWidget(d->m_version, row, 1, 1, 2); d->m_version->setWhatsThis(qwtstr); tmpLabel = new QLabel(i18n("OS:"), this); glay->addWidget(tmpLabel, ++row, 0); d->os = SystemInformation::operatingSystemVersion(); tmpLabel = new QLabel(d->os, this); tmpLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); glay->addWidget(tmpLabel, row, 1, 1, 2); tmpLabel = new QLabel(i18n("Compiler:"), this); glay->addWidget(tmpLabel, ++row, 0); tmpLabel = new QLabel(QString::fromLatin1(XMLGUI_COMPILER_VERSION), this); tmpLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); glay->addWidget(tmpLabel, row, 1, 1, 2); // Point to the web form lay->addSpacing(10); QString text = i18n("" "

Please read this guide for reporting bugs first!

" "

To submit a bug report, click on the button below. This will open a web browser " - "window on http://bugs.kde.org where you will find " + "window on https://bugs.kde.org where you will find " "a form to fill in. The information displayed above will be transferred to that server.

"); QLabel *label = new QLabel(text, this); label->setOpenExternalLinks(true); label->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); label->setWordWrap(true); lay->addWidget(label); lay->addSpacing(10); d->appcombo->setFocus(); d->_k_updateUrl(); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setText(i18n("&Submit Bug Report")); okButton->setIcon(KisIconUtils::loadIcon(QStringLiteral("tools-report-bug"))); lay->addWidget(buttonBox); setMinimumHeight(sizeHint().height() + 20); // WORKAROUND: prevent "cropped" qcombobox } KBugReport::~KBugReport() { delete d; } void KBugReportPrivate::_k_updateUrl() { url = QUrl(QStringLiteral("https://bugs.kde.org/enter_bug.cgi")); QUrlQuery query; query.addQueryItem(QStringLiteral("format"), QLatin1String("guided")); // use the guided form // the string format is product/component, where component is optional QStringList list = appcombo->currentText().split(QLatin1Char('/')); query.addQueryItem(QStringLiteral("product"), list[0]); if (list.size() == 2) { query.addQueryItem(QStringLiteral("component"), list[1]); } query.addQueryItem(QStringLiteral("version"), m_strVersion); // TODO: guess and fill OS(sys_os) and Platform(rep_platform) fields #ifdef Q_OS_WIN query.addQueryItem(QStringLiteral("op_sys"), QStringLiteral("MS Windows")); query.addQueryItem(QStringLiteral("rep_platform"), QStringLiteral("MS Windows")); #endif url.setQuery(query); } void KBugReport::accept() { QDesktopServices::openUrl(d->url); } #include "moc_kbugreport.cpp" diff --git a/packaging/linux/flatpak/org.kde.krita-nightly.json b/packaging/linux/flatpak/org.kde.krita-nightly.json deleted file mode 100644 index 304487b6c1..0000000000 --- a/packaging/linux/flatpak/org.kde.krita-nightly.json +++ /dev/null @@ -1,355 +0,0 @@ -{ - "app-id": "org.kde.krita", - "branch": "master", - "runtime": "org.kde.Platform", - "runtime-version": "5.9", - "sdk": "org.kde.Sdk", - "command": "krita", - "rename-icon": "krita", - "tags": [ - "nightly" - ], - "desktop-file-name-prefix": "(Nightly) ", - "finish-args": [ - "--share=ipc", - "--socket=x11", - "--share=network", - "--device=dri", - "--filesystem=home", - "--env=PYTHONPATH=/app/lib/python3/dist-packages" - ], - "cleanup": [ - "/include", - "/lib/pkgconfig", - "/lib/cmake", - "/share/aclocal", - "/share/pkgconfig", - "*.la", - "*.cmake" - ], - "modules": [ - { - "name": "boost", - "buildsystem": "simple", - "build-commands": [ - "./bootstrap.sh --prefix=/app --with-libraries=system", - "./b2 -j `nproc` install" - ], - "cleanup": [ - "*.a" - ], - "sources": [ - { - "type": "archive", - "url": "https://sourceforge.net/projects/boost/files/boost/1.63.0/boost_1_63_0.tar.bz2", - "sha256": "beae2529f759f6b3bf3f4969a19c2e9d6f0c503edcb2de4a61d1428519fcb3b0" - } - ] - }, - { - "name": "eigen", - "buildsystem": "cmake-ninja", - "builddir": true, - "config-opts": [ - "-DCMAKE_BUILD_TYPE=Release" - ], - "cleanup": [ - "/share" - ], - "sources": [ - { - "type": "archive", - "url": "https://bitbucket.org/eigen/eigen/get/3.3.4.tar.bz2", - "sha256": "dd254beb0bafc695d0f62ae1a222ff85b52dbaa3a16f76e781dce22d0d20a4a6" - } - ] - }, - { - "name": "exiv2", - "buildsystem": "cmake-ninja", - "builddir": true, - "config-opts": [ - "-DCMAKE_BUILD_TYPE=Release" - ], - "cleanup": [ - "/bin", - "*.a", - "/share/man" - ], - "sources": [ - { - "type": "archive", - "url": "http://www.exiv2.org/builds/exiv2-0.26-trunk.tar.gz", - "sha256": "c75e3c4a0811bf700d92c82319373b7a825a2331c12b8b37d41eb58e4f18eafb" - } - ] - }, - { - "name": "ilmbase", - "config-opts": [ - "--disable-static" - ], - "sources": [ - { - "type": "archive", - "url": "https://download.savannah.nongnu.org/releases/openexr/ilmbase-2.2.1.tar.gz", - "sha256": "cac206e63be68136ef556c2b555df659f45098c159ce24804e9d5e9e0286609e" - } - ] - }, - { - "name": "openexr", - "config-opts": [ - "--disable-static" - ], - "cleanup": [ - "/bin", - "/share/doc" - ], - "sources": [ - { - "type": "archive", - "url": "https://download.savannah.nongnu.org/releases/openexr/openexr-2.2.1.tar.gz", - "sha256": "8f9a5af6131583404261931d9a5c83de0a425cb4b8b25ddab2b169fbf113aecd" - } - ] - }, - { - "name": "libraw", - "config-opts": [ - "--disable-static" - ], - "cleanup": [ - "/bin", - "/share/doc" - ], - "sources": [ - { - "type": "archive", - "url": "https://www.libraw.org/data/LibRaw-0.18.6.tar.gz", - "sha256": "e5b8acca558aa457bc9214802004320c5610d1434c2adb1f3ea367f026afa53b" - } - ] - }, - { - "name": "fftw", - "config-opts": [ - "--disable-static", - "--enable-shared", - "--disable-doc", - "--enable-threads" - ], - "cleanup": [ - "/bin", - "/share/man" - ], - "sources": [ - { - "type": "archive", - "url": "http://www.fftw.org/fftw-3.3.7.tar.gz", - "sha256": "3b609b7feba5230e8f6dd8d245ddbefac324c5a6ae4186947670d9ac2cd25573" - } - ] - }, - { - "name": "opencolorio", - "buildsystem": "cmake", - "builddir": true, - "build-options": { - "arch": { - "arm": { - "config-opts": [ - "-DOCIO_USE_SSE=OFF" - ] - }, - "aarch64": { - "config-opts": [ - "-DOCIO_USE_SSE=OFF" - ] - } - } - }, - "config-opts": [ - "-DCMAKE_BUILD_TYPE=Release", - "-DOCIO_BUILD_STATIC=OFF" - ], - "cleanup": [ - "/bin" - ], - "sources": [ - { - "type": "archive", - "url": "https://github.com/imageworks/OpenColorIO/archive/v1.0.9.tar.gz", - "sha256": "27c81e691c15753cd2b560c2ca4bd5679a60c2350eedd43c99d44ca25d65ea7f" - } - ] - }, - { - "name": "vc", - "skip-arches": [ - "aarch64", - "arm" - ], - "buildsystem": "cmake-ninja", - "builddir": true, - "config-opts": [ - "-DCMAKE_BUILD_TYPE=Release" - ], - "cleanup": [ - "*.a" - ], - "sources": [ - { - "type": "archive", - "url": "https://github.com/VcDevel/Vc/releases/download/1.3.3/Vc-1.3.3.tar.gz", - "sha256": "08c629d2e14bfb8e4f1a10f09535e4a3c755292503c971ab46637d2986bdb4fe" - }, - { - "type": "shell", - "commands": [ - "sed -i 's/x86|/x86|i686|/' CMakeLists.txt" - ] - } - ] - }, - { - "name": "poppler-data", - "buildsystem": "cmake-ninja", - "builddir": true, - "config-opts": [ - "-DCMAKE_BUILD_TYPE=Release" - ], - "sources": [ - { - "type": "archive", - "url": "https://poppler.freedesktop.org/poppler-data-0.4.8.tar.gz", - "sha256": "1096a18161f263cccdc6d8a2eb5548c41ff8fcf9a3609243f1b6296abdf72872" - } - ] - }, - { - "name": "poppler", - "buildsystem": "cmake-ninja", - "builddir": true, - "config-opts": [ - "-DCMAKE_BUILD_TYPE=Release", - "-DBUILD_GTK_TESTS=OFF", - "-DBUILD_QT5_TESTS=OFF", - "-DBUILD_CPP_TESTS=OFF", - "-DENABLE_UTILS=OFF", - "-DENABLE_CPP=OFF", - "-DENABLE_GLIB=OFF" - ], - "sources": [ - { - "type": "archive", - "url": "https://poppler.freedesktop.org/poppler-0.62.0.tar.xz", - "sha256": "5b9a73dfd4d6f61d165ada1e4f0abd2d420494bf9d0b1c15d0db3f7b83a729c6" - } - ] - }, - { - "name": "gsl", - "config-opts": [ - "--disable-static" - ], - "cleanup": [ - "/bin", - "/share/info", - "/share/man" - ], - "sources": [ - { - "type": "archive", - "url": "https://ftpmirror.gnu.org/gnu/gsl/gsl-2.4.tar.gz", - "sha256": "4d46d07b946e7b31c19bbf33dda6204d7bedc2f5462a1bae1d4013426cd1ce9b" - } - ] - }, - { - "name": "gmic-qt", - "buildsystem": "cmake-ninja", - "builddir": true, - "config-opts": [ - "-DCMAKE_BUILD_TYPE=Release", - "-DGMIC_QT_HOST=krita", - "-DGMIC_PATH=./gmic/src" - ], - "sources": [ - { - "type": "archive", - "url": "https://github.com/c-koi/gmic-qt/archive/v.218.tar.gz", - "sha256": "697861e5a1e024a3ccf9c96e513ec9f60f65d6e3c438ba355afcd10839b06f39" - }, - { - "type": "file", - "url": "https://gmic.eu/files/source/gmic_2.1.8.tar.gz", - "sha256": "f22783f14cb202dec4a840733f2028f6e2c464fdd2f0166fc38943702cea6bde", - "dest-filename": "gmic.tar.gz" - }, - { - "type": "shell", - "commands": [ - "tar xf gmic.tar.gz", - "mv gmic-* gmic" - ] - } - ], - "post-install": [ - "install -Dm755 gmic_krita_qt /app/bin/gmic_krita_qt" - ] - }, - { - "name": "sip", - "buildsystem": "simple", - "build-commands": [ - "python3 configure.py --bindir=/app/bin --destdir=/app/lib/python3/dist-packages --incdir=/app/include/python3 --sipdir=/app/share/sip --stubsdir=/app/lib/python3/dist-packages", - "make -j `nproc`", - "make install" - ], - "sources": [ - { - "type": "archive", - "url": "https://sourceforge.net/projects/pyqt/files/sip/sip-4.19.6/sip-4.19.6.tar.gz", - "sha256": "9dda27ae181bea782ebc8768d29f22f85ab6e5128ee3ab21f491febad707925a" - } - ] - }, - { - "name": "pyqt", - "buildsystem": "simple", - "build-commands": [ - "python3 configure.py --confirm-license --sip-incdir=/app/include/python3 --bindir=/app/bin --destdir=/app/lib/python3/dist-packages --designer-plugindir=/app/lib/plugins/designer --qml-plugindir=/app/lib/plugins/PyQt5 --sipdir=/app/share/sip --stubsdir=/app/lib/python3/dist-packages/PyQt5", - "make -j `nproc`", - "make install" - ], - "sources": [ - { - "type": "archive", - "url": "https://sourceforge.net/projects/pyqt/files/PyQt5/PyQt-5.9.2/PyQt5_gpl-5.9.2.tar.gz", - "sha256": "c190dac598c97b0113ca5e7a37c71c623f02d1d713088addfacac4acfa4b8394" - } - ] - }, - { - "name": "krita", - "buildsystem": "cmake-ninja", - "builddir": true, - "build-options": { - "env": { - "PYTHONPATH": "/app/lib/python3/dist-packages" - } - }, - "config-opts": [ - "-DCMAKE_BUILD_TYPE=RelWithDebInfo" - ], - "sources": [ - { - "type": "git", - "url": "git://anongit.kde.org/krita.git", - "branch": "master" - } - ] - } - ] -} diff --git a/packaging/linux/snap/build_in_container.sh b/packaging/linux/snap/build_in_container.sh index 0edfffe8c5..afc3d0f20d 100755 --- a/packaging/linux/snap/build_in_container.sh +++ b/packaging/linux/snap/build_in_container.sh @@ -1,19 +1,19 @@ #!/bin/bash set -ex cd /workspace/snap ping -c1 networkcheck.kde.org apt-key adv --keyserver keyserver.ubuntu.com --recv E6D4736255751E5D echo 'deb http://archive.neon.kde.org/unstable bionic main' > /etc/apt/sources.list.d/neon.list apt update -snap install --edge --classic snapcraft +snap install --classic snapcraft snapcraft --version snapcraft --destructive-mode mkdir -p /workspace/result mv *.snap /workspace/result/ diff --git a/packaging/linux/snap/snapcraft.yaml b/packaging/linux/snap/snapcraft.yaml index 6a71aa7a57..6165c1163f 100644 --- a/packaging/linux/snap/snapcraft.yaml +++ b/packaging/linux/snap/snapcraft.yaml @@ -1,179 +1,173 @@ name: krita -version: 4.2.7.1 -summary: Krita is the digital painting studio for artists -description: Krita is a creative application for raster images. Whether you want to create - from scratch or work with existing images, Krita is for you. You can work with - photos or scanned images, or start with a blank slate. Krita supports most - graphics tablets out of the box. +version: 4.2.8.2 +adopt-info: krita + base: core18 +confinement: strict apps: krita: + common-id: org.kde.krita command: usr/bin/krita command-chain: - bin/qt5-launch plugs: [x11, unity7, home, opengl, network, network-bind, removable-media, desktop, desktop-legacy] - desktop: usr/share/applications/org.kde.krita.desktop layout: /usr/bin/ffmpeg: bind-file: $SNAP/usr/bin/ffmpeg parts: krita: plugin: cmake configflags: - "-DCMAKE_INSTALL_PREFIX=/usr" - "-DCMAKE_BUILD_TYPE=Release" - "-DENABLE_TESTING=OFF" - "-DBUILD_TESTING=OFF" - "-DHIDE_SAFE_ASSERTS=OFF" - "-DKDE_SKIP_TEST_SETTINGS=ON" - source: https://download.kde.org/stable/krita/4.2.7.1/krita-4.2.7.1.tar.xz -# Use these instead to build from the git source -# source: https://anongit.kde.org/krita.git -# source-type: git -# source-branch: master - override-stage: | - # Stage the part - snapcraftctl stage - # Modify the .desktop file in the stage to point to where the icon will be in the installed snap - sed -i 's|Icon=\(.*\)|Icon=${SNAP}/usr/share/icons/hicolor/1024x1024/apps/\1.png|' usr/share/applications/org.kde.krita.desktop + source: https://download.kde.org/stable/krita/4.2.8/krita-$SNAPCRAFT_PROJECT_VERSION.tar.xz + # Use these instead to build from the git source + # source: https://anongit.kde.org/krita.git + # source-type: git + # source-branch: master + parse-info: ["usr/share/metainfo/org.kde.krita.appdata.xml"] build-packages: - gettext - build-essential - cmake - libboost-dev - libboost-system-dev - libeigen3-dev - libexiv2-dev - libfftw3-dev - libfontconfig1-dev - libfreetype6-dev - libgl1-mesa-dev - libglew-dev - libglib2.0-dev - libglu1-mesa-dev - libgsf-1-dev - libgsl-dev - libjpeg-dev - liblcms2-dev - libopenexr-dev - libpng-dev - libpoppler-qt5-dev - - libopenjpeg-dev + - libopenjp2-7-dev - libtiff5-dev - libvc-dev - libopencolorio-dev - libx11-dev - libxml2-dev - libxslt1-dev - libxi-dev - pkg-config - vc-dev - zlib1g-dev - libkf5kdcraw-dev - shared-mime-info - libopenimageio-dev - extra-cmake-modules - libkf5archive-dev - libkf5coreaddons-dev - libkf5guiaddons-dev - libkf5itemmodels-dev - libkf5itemviews-dev - libkf5widgetsaddons-dev - libkf5i18n-dev - libkf5windowsystem-dev - libkf5completion-dev - libkf5iconthemes-dev - libkf5kiocore5 - libqt5svg5-dev - libqt5x11extras5-dev - libqt5opengl5-dev - libquazip5-dev - qtmultimedia5-dev - qtdeclarative5-dev - libkf5crash-dev runtime: plugin: nil stage-packages: - libexiv2-27 - libfftw3-double3 - libgomp1 - libgsl23 - libilmbase12 - libjpeg8 - liblcms2-2 - libopencolorio1v5 - libopenexr22 - libpng16-16 - libstdc++6 - libtiff5 - libx11-6 - libxcb1 - libxi6 - zlib1g - libpoppler-qt5-1 - shared-mime-info - libboost-system1.65.1 - librtmp1 - libqt5quickwidgets5 - libkf5archive5 - libkf5completion5 - libkf5configcore5 - libkf5configgui5 - libkf5coreaddons5 - libkf5guiaddons5 - libkf5i18n5 - libkf5itemviews5 - libkf5widgetsaddons5 - libkf5windowsystem5 - libkf5crash5 - libqt5concurrent5 - libqt5core5a - libqt5dbus5 - libqt5gui5 - libqt5network5 - libqt5printsupport5 - libqt5svg5 - libqt5widgets5 - libqt5x11extras5 - libqt5xml5 - locales - libc-bin - libquazip5-1 - libqt5quick5 - libqt5quickparticles5 - libqt5quickshapes5 - qt5-qmltooling-plugins - libqt5qml5 - libqt5multimedia5 - libqt5multimediagsttools5 - libqt5multimediaquick5 - libqt5multimediawidgets5 # Required for rendering animations - ffmpeg - libglu1-mesa - libslang2 prime: - "-usr/share/wallpapers/*" - "-usr/share/fonts/*" plasma-integration: plugin: nil stage-packages: - plasma-integration - kde-style-breeze - breeze-icon-theme - kio # runtime slaves for kio prime: - "-usr/share/wallpapers/*" - "-usr/share/fonts/*" launcher: plugin: dump source: scripts organize: qt5-launch: bin/qt5-launch kf5-locale-gen: bin/kf5-locale-gen diff --git a/plugins/color/colorspaceextensions/kis_desaturate_adjustment.cpp b/plugins/color/colorspaceextensions/kis_desaturate_adjustment.cpp index 7091e446c5..d870724128 100644 --- a/plugins/color/colorspaceextensions/kis_desaturate_adjustment.cpp +++ b/plugins/color/colorspaceextensions/kis_desaturate_adjustment.cpp @@ -1,193 +1,193 @@ /* * Copyright (c) 2013 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; version 2 * of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_desaturate_adjustment.h" #include #include #include #include #include #include #include #include #include #define SCALE_TO_FLOAT( v ) KoColorSpaceMaths< _channel_type_, float>::scaleToA( v ) #define SCALE_FROM_FLOAT( v ) KoColorSpaceMaths< float, _channel_type_>::scaleToA( v ) template class KisDesaturateAdjustment : public KoColorTransformation { typedef traits RGBTrait; typedef typename RGBTrait::Pixel RGBPixel; public: KisDesaturateAdjustment() { } public: void transform(const quint8 *srcU8, quint8 *dstU8, qint32 nPixels) const override { const RGBPixel* src = reinterpret_cast(srcU8); RGBPixel* dst = reinterpret_cast(dstU8); float r, g, b, gray; while (nPixels > 0) { r = SCALE_TO_FLOAT(src->red); g = SCALE_TO_FLOAT(src->green); b = SCALE_TO_FLOAT(src->blue); - // http://www.tannerhelland.com/3643/grayscale-image-algorithm-vb6/ + // https://www.tannerhelland.com/3643/grayscale-image-algorithm-vb6/ switch(m_type) { case 0: // lightness { gray = (qMax(qMax(r, g), b) + qMin(qMin(r, g), b)) / 2; break; } case 1: // luminosity BT 709 { gray = r * 0.2126 + g * 0.7152 + b * 0.0722; break; } case 2: // luminosity BT 601 { gray = r * 0.299 + g * 0.587 + b * 0.114; break; } case 3: // average { gray = (r + g + b) / 3; break; } case 4: // min { gray = qMin(qMin(r, g), b); break; } case 5: // min { gray = qMax(qMax(r, g), b); break; } default: gray = 0; } dst->red = SCALE_FROM_FLOAT(gray); dst->green = SCALE_FROM_FLOAT(gray); dst->blue = SCALE_FROM_FLOAT(gray); dst->alpha = src->alpha; --nPixels; ++src; ++dst; } } QList parameters() const override { QList list; list << "type"; return list; } int parameterId(const QString& name) const override { if (name == "type") { return 0; } return -1; } /** * name - "type": * 0: lightness * 1: luminosity * 2: average */ void setParameter(int id, const QVariant& parameter) override { switch(id) { case 0: m_type = parameter.toDouble(); break; default: ; } } private: int m_type; }; KisDesaturateAdjustmentFactory::KisDesaturateAdjustmentFactory() : KoColorTransformationFactory("desaturate_adjustment") { } QList< QPair< KoID, KoID > > KisDesaturateAdjustmentFactory::supportedModels() const { QList< QPair< KoID, KoID > > l; l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer8BitsColorDepthID)); l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer16BitsColorDepthID)); l.append(QPair< KoID, KoID >(RGBAColorModelID , Float16BitsColorDepthID)); l.append(QPair< KoID, KoID >(RGBAColorModelID , Float32BitsColorDepthID)); return l; } KoColorTransformation* KisDesaturateAdjustmentFactory::createTransformation(const KoColorSpace* colorSpace, QHash parameters) const { KoColorTransformation * adj; if (colorSpace->colorModelId() != RGBAColorModelID) { dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisDesaturateAdjustmentFactory::createTransformation"; return 0; } if (colorSpace->colorDepthId() == Integer8BitsColorDepthID) { adj = new KisDesaturateAdjustment< quint8, KoBgrTraits < quint8 > >(); } else if (colorSpace->colorDepthId() == Integer16BitsColorDepthID) { adj = new KisDesaturateAdjustment< quint16, KoBgrTraits < quint16 > >(); } #ifdef HAVE_OPENEXR else if (colorSpace->colorDepthId() == Float16BitsColorDepthID) { adj = new KisDesaturateAdjustment< half, KoRgbTraits < half > >(); } #endif else if (colorSpace->colorDepthId() == Float32BitsColorDepthID) { adj = new KisDesaturateAdjustment< float, KoRgbTraits < float > >(); } else { dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisDesaturateAdjustmentFactory::createTransformation"; return 0; } adj->setParameters(parameters); return adj; } diff --git a/plugins/dockers/layerdocker/NodeView.h b/plugins/dockers/layerdocker/NodeView.h index abcd06d9af..489c2c1c85 100644 --- a/plugins/dockers/layerdocker/NodeView.h +++ b/plugins/dockers/layerdocker/NodeView.h @@ -1,187 +1,187 @@ /* Copyright (c) 2006 Gábor Lehel 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_DOCUMENT_SECTION_VIEW_H #define KIS_DOCUMENT_SECTION_VIEW_H #include #include class QStyleOptionViewItem; class KisNodeModel; /** * A widget displaying the Krita nodes (layers, masks, local selections, etc.) * * The widget can show the document sections as big thumbnails, * in a listview with two rows of informative text and icons, * or as single rows of text and property icons. * * This class is designed as a Qt model-view widget. * * The Qt documentation explains the design and terminology for these classes: - * http://doc.qt.io/qt-5/model-view-programming.html + * https://doc.qt.io/qt-5/model-view-programming.html * * This widget should work correctly in your Qt designer .ui file. */ class NodeView: public QTreeView { Q_OBJECT Q_SIGNALS: /** * Emitted whenever the user clicks with the secondary mouse * button on an item. It is up to the application to design the * contents of the context menu and show it. */ void contextMenuRequested(const QPoint &globalPos, const QModelIndex &index); void selectionChanged(const QModelIndexList &); public: /** * Create a new NodeView. */ explicit NodeView(QWidget *parent = 0); ~NodeView() override; /// how items should be displayed enum DisplayMode { /// large fit-to-width thumbnails, with only titles or page numbers ThumbnailMode, /// smaller thumbnails, with titles and property icons in two rows DetailedMode, /// no thumbnails, with titles and property icons in a single row MinimalMode }; void resizeEvent(QResizeEvent * event) override; void paintEvent (QPaintEvent *event) override; void drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const override; void dropEvent(QDropEvent *ev) override; void dragEnterEvent(QDragEnterEvent *e) override; void dragMoveEvent(QDragMoveEvent *ev) override; void dragLeaveEvent(QDragLeaveEvent *e) override; /** * Set the display mode of the view to one of the options. * * @param mode The NodeView::DisplayMode mode */ void setDisplayMode(DisplayMode mode); /** * @return the currently active display mode */ DisplayMode displayMode() const; /** * Add toggle actions for all the properties associated with the * current document section associated with the model index to the * specified menu. * * For instance, if a document section can be locked and visible, * the menu will be expanded with locked and visible toggle * actions. * * For instance @code NodeView * nodeView; QModelIndex index = getCurrentNode(); QMenu menu; if (index.isValid()) { sectionView->addPropertyActions(&menu, index); } else { menu.addAction(...); // Something to create a new document section, for example. } @endcode * * @param menu A pointer to the menu that will be expanded with * the toglge actions * @param index The model index associated with the document * section that may or may not provide a number of toggle actions. */ void addPropertyActions(QMenu *menu, const QModelIndex &index); void updateNode(const QModelIndex &index); QRect originalVisualRect(const QModelIndex &index) const; protected: QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex &index, const QEvent *event) const override; QRect visualRect(const QModelIndex &index) const override; QModelIndex indexAt(const QPoint &point) const override; bool viewportEvent(QEvent *event) override; void contextMenuEvent(QContextMenuEvent *event) override; virtual void showContextMenu(const QPoint &globalPos, const QModelIndex &index); void startDrag (Qt::DropActions supportedActions) override; QPixmap createDragPixmap() const; /** * Calculates the index of the nearest item to the cursor position */ int cursorPageIndex() const; public Q_SLOTS: /// called with a theme change to refresh icon colors void slotUpdateIcons(); void slotScrollerStateChanged(QScroller::State state); protected Q_SLOTS: void currentChanged(const QModelIndex ¤t, const QModelIndex &previous) override; void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles = QVector()) override; void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override; private Q_SLOTS: void slotActionToggled(bool on, const QPersistentModelIndex &index, int property); private: /** * Permit to know if a slide is dragging * * @return boolean */ bool isDragging() const; /** * Setter for the dragging flag * * @param flag boolean */ void setDraggingFlag(bool flag = true); bool m_draggingFlag; QStyleOptionViewItem optionForIndex(const QModelIndex &index) const; typedef KisNodeModel Model; class PropertyAction; class Private; Private* const d; }; #endif diff --git a/plugins/dockers/lut/lutdocker_dock.cpp b/plugins/dockers/lut/lutdocker_dock.cpp index ccd665216d..c7e68dce06 100644 --- a/plugins/dockers/lut/lutdocker_dock.cpp +++ b/plugins/dockers/lut/lutdocker_dock.cpp @@ -1,685 +1,685 @@ /* * Copyright (c) 2004 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "lutdocker_dock.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_icon_utils.h" #include #include #include #include #include #include #include #include #include #include "kis_signals_blocker.h" #include "krita_utils.h" #include "ocio_display_filter.h" #include "black_white_point_chooser.h" #include "KisOcioConfiguration.h" #include OCIO::ConstConfigRcPtr defaultRawProfile() { /** * Copied from OCIO, just a noop profile */ const char * INTERNAL_RAW_PROFILE = "ocio_profile_version: 1\n" "strictparsing: false\n" "roles:\n" " default: raw\n" "displays:\n" " sRGB:\n" " - ! {name: Raw, colorspace: raw}\n" "colorspaces:\n" " - !\n" " name: raw\n" " family: raw\n" " equalitygroup:\n" " bitdepth: 32f\n" " isdata: true\n" " allocation: uniform\n" " description: 'A raw color space. Conversions to and from this space are no-ops.'\n"; std::istringstream istream; istream.str(INTERNAL_RAW_PROFILE); return OCIO::Config::CreateFromStream(istream); } LutDockerDock::LutDockerDock() : QDockWidget(i18n("LUT Management")) , m_canvas(0) , m_draggingSlider(false) { using namespace std::placeholders; // For _1 m_exposureCompressor.reset( new KisSignalCompressorWithParam(40, std::bind(&LutDockerDock::setCurrentExposureImpl, this, _1))); m_gammaCompressor.reset( new KisSignalCompressorWithParam(40, std::bind(&LutDockerDock::setCurrentGammaImpl, this, _1))); m_page = new QWidget(this); setupUi(m_page); setWidget(m_page); KisConfig cfg(true); m_chkUseOcio->setChecked(cfg.useOcio()); connect(m_chkUseOcio, SIGNAL(toggled(bool)), SLOT(updateDisplaySettings())); connect(m_colorManagement, SIGNAL(currentIndexChanged(int)), SLOT(slotColorManagementModeChanged())); m_bnSelectConfigurationFile->setToolTip(i18n("Select custom configuration file.")); connect(m_bnSelectConfigurationFile,SIGNAL(clicked()), SLOT(selectOcioConfiguration())); KisOcioConfiguration ocioOptions = cfg.ocioConfiguration(); m_txtConfigurationPath->setText(ocioOptions.configurationPath); m_txtLut->setText(ocioOptions.lutPath); m_bnSelectLut->setToolTip(i18n("Select LUT file")); connect(m_bnSelectLut, SIGNAL(clicked()), SLOT(selectLut())); connect(m_bnClearLut, SIGNAL(clicked()), SLOT(clearLut())); - // See http://groups.google.com/group/ocio-dev/browse_thread/thread/ec95c5f54a74af65 -- maybe need to be reinstated + // See https://groups.google.com/group/ocio-dev/browse_thread/thread/ec95c5f54a74af65 -- maybe need to be reinstated // when people ask for it. m_lblLut->hide(); m_txtLut->hide(); m_bnSelectLut->hide(); m_bnClearLut->hide(); connect(m_cmbDisplayDevice, SIGNAL(currentIndexChanged(int)), SLOT(refillViewCombobox())); m_exposureDoubleWidget->setToolTip(i18n("Select the exposure (stops) for HDR images.")); m_exposureDoubleWidget->setRange(-10, 10); m_exposureDoubleWidget->setPrecision(1); m_exposureDoubleWidget->setValue(0.0); m_exposureDoubleWidget->setSingleStep(0.25); m_exposureDoubleWidget->setPageStep(1); connect(m_exposureDoubleWidget, SIGNAL(valueChanged(double)), SLOT(exposureValueChanged(double))); connect(m_exposureDoubleWidget, SIGNAL(sliderPressed()), SLOT(exposureSliderPressed())); connect(m_exposureDoubleWidget, SIGNAL(sliderReleased()), SLOT(exposureSliderReleased())); // Gamma needs to be exponential (gamma *= 1.1f, gamma /= 1.1f as steps) m_gammaDoubleWidget->setToolTip(i18n("Select the amount of gamma modification for display. This does not affect the pixels of your image.")); m_gammaDoubleWidget->setRange(0.1, 5); m_gammaDoubleWidget->setPrecision(2); m_gammaDoubleWidget->setValue(1.0); m_gammaDoubleWidget->setSingleStep(0.1); m_gammaDoubleWidget->setPageStep(1); connect(m_gammaDoubleWidget, SIGNAL(valueChanged(double)), SLOT(gammaValueChanged(double))); connect(m_gammaDoubleWidget, SIGNAL(sliderPressed()), SLOT(gammaSliderPressed())); connect(m_gammaDoubleWidget, SIGNAL(sliderReleased()), SLOT(gammaSliderReleased())); m_bwPointChooser = new BlackWhitePointChooser(this); connect(m_bwPointChooser, SIGNAL(sigBlackPointChanged(qreal)), SLOT(updateDisplaySettings())); connect(m_bwPointChooser, SIGNAL(sigWhitePointChanged(qreal)), SLOT(updateDisplaySettings())); connect(m_btnConvertCurrentColor, SIGNAL(toggled(bool)), SLOT(updateDisplaySettings())); connect(m_btmShowBWConfiguration, SIGNAL(clicked()), SLOT(slotShowBWConfiguration())); slotUpdateIcons(); connect(m_cmbInputColorSpace, SIGNAL(currentIndexChanged(int)), SLOT(updateDisplaySettings())); connect(m_cmbDisplayDevice, SIGNAL(currentIndexChanged(int)), SLOT(updateDisplaySettings())); connect(m_cmbView, SIGNAL(currentIndexChanged(int)), SLOT(updateDisplaySettings())); connect(m_cmbLook, SIGNAL(currentIndexChanged(int)), SLOT(updateDisplaySettings())); connect(m_cmbComponents, SIGNAL(currentIndexChanged(int)), SLOT(updateDisplaySettings())); m_draggingSlider = false; connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(resetOcioConfiguration())); resetOcioConfiguration(); } LutDockerDock::~LutDockerDock() { } void LutDockerDock::setCanvas(KoCanvasBase* _canvas) { if (m_canvas) { m_canvas->disconnect(this); } setEnabled(_canvas != 0); if (KisCanvas2* canvas = dynamic_cast(_canvas)) { m_canvas = canvas; if (m_canvas) { if (!m_canvas->displayFilter()) { resetOcioConfiguration(); updateDisplaySettings(); } else { m_displayFilter = m_canvas->displayFilter(); OcioDisplayFilter *displayFilter = qobject_cast(m_displayFilter.data()); Q_ASSERT(displayFilter); m_ocioConfig = displayFilter->config; KisSignalsBlocker exposureBlocker(m_exposureDoubleWidget); m_exposureDoubleWidget->setValue(displayFilter->exposure); KisSignalsBlocker gammaBlocker(m_gammaDoubleWidget); m_gammaDoubleWidget->setValue(displayFilter->gamma); KisSignalsBlocker componentsBlocker(m_cmbComponents); m_cmbComponents->setCurrentIndex((int)displayFilter->swizzle); KisSignalsBlocker bwBlocker(m_bwPointChooser); m_bwPointChooser->setBlackPoint(displayFilter->blackPoint); m_bwPointChooser->setWhitePoint(displayFilter->whitePoint); } connect(m_canvas->image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), SLOT(slotImageColorSpaceChanged()), Qt::UniqueConnection); connect(m_canvas->viewManager()->mainWindow(), SIGNAL(themeChanged()), SLOT(slotUpdateIcons()), Qt::UniqueConnection); } } } void LutDockerDock::unsetCanvas() { m_canvas = 0; setEnabled(false); m_displayFilter = QSharedPointer(0); } void LutDockerDock::slotUpdateIcons() { m_btnConvertCurrentColor->setIcon(KisIconUtils::loadIcon("krita_tool_freehand")); m_btmShowBWConfiguration->setIcon(KisIconUtils::loadIcon("properties")); } void LutDockerDock::slotShowBWConfiguration() { m_bwPointChooser->showPopup(m_btmShowBWConfiguration->mapToGlobal(QPoint())); } bool LutDockerDock::canChangeExposureAndGamma() const { if (!m_chkUseOcio->isChecked() || !m_ocioConfig) return false; const bool externalColorManagementEnabled = m_colorManagement->currentIndex() != (int)KisOcioConfiguration::INTERNAL; #ifdef HAVE_HDR #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) KisSurfaceColorSpace currentColorSpace = KisOpenGLModeProber::instance()->surfaceformatInUse().colorSpace(); #else KisSurfaceColorSpace currentColorSpace = KisSurfaceColorSpace::DefaultColorSpace; #endif #endif const bool exposureManagementEnabled = externalColorManagementEnabled #ifdef HAVE_HDR || currentColorSpace == KisSurfaceColorSpace::scRGBColorSpace #endif ; return exposureManagementEnabled; } qreal LutDockerDock::currentExposure() const { if (!m_displayFilter) return 0.0; OcioDisplayFilter *displayFilter = qobject_cast(m_displayFilter.data()); return canChangeExposureAndGamma() ? displayFilter->exposure : 0.0; } void LutDockerDock::setCurrentExposure(qreal value) { if (!canChangeExposureAndGamma()) return; m_exposureCompressor->start(value); } qreal LutDockerDock::currentGamma() const { if (!m_displayFilter) return 1.0; OcioDisplayFilter *displayFilter = qobject_cast(m_displayFilter.data()); return canChangeExposureAndGamma() ? displayFilter->gamma : 1.0; } void LutDockerDock::setCurrentGamma(qreal value) { if (!canChangeExposureAndGamma()) return; m_gammaCompressor->start(value); } void LutDockerDock::setCurrentExposureImpl(qreal value) { m_exposureDoubleWidget->setValue(value); if (!m_canvas) return; m_canvas->viewManager()->showFloatingMessage( i18nc("floating message about exposure", "Exposure: %1", KritaUtils::prettyFormatReal(m_exposureDoubleWidget->value())), QIcon(), 500, KisFloatingMessage::Low); } void LutDockerDock::setCurrentGammaImpl(qreal value) { m_gammaDoubleWidget->setValue(value); if (!m_canvas) return; m_canvas->viewManager()->showFloatingMessage( i18nc("floating message about gamma", "Gamma: %1", KritaUtils::prettyFormatReal(m_gammaDoubleWidget->value())), QIcon(), 500, KisFloatingMessage::Low); } void LutDockerDock::slotImageColorSpaceChanged() { enableControls(); writeControls(); resetOcioConfiguration(); } void LutDockerDock::exposureValueChanged(double exposure) { if (m_canvas && !m_draggingSlider) { m_canvas->viewManager()->canvasResourceProvider()->setHDRExposure(exposure); updateDisplaySettings(); } } void LutDockerDock::exposureSliderPressed() { m_draggingSlider = true; } void LutDockerDock::exposureSliderReleased() { m_draggingSlider = false; exposureValueChanged(m_exposureDoubleWidget->value()); } void LutDockerDock::gammaValueChanged(double gamma) { if (m_canvas && !m_draggingSlider) { m_canvas->viewManager()->canvasResourceProvider()->setHDRGamma(gamma); updateDisplaySettings(); } } void LutDockerDock::gammaSliderPressed() { m_draggingSlider = true; } void LutDockerDock::gammaSliderReleased() { m_draggingSlider = false; gammaValueChanged(m_gammaDoubleWidget->value()); } void LutDockerDock::enableControls() { bool canDoExternalColorCorrection = false; if (m_canvas) { KisImageSP image = m_canvas->viewManager()->image(); canDoExternalColorCorrection = image->colorSpace()->colorModelId() == RGBAColorModelID; } if (!canDoExternalColorCorrection) { KisSignalsBlocker colorManagementBlocker(m_colorManagement); Q_UNUSED(colorManagementBlocker); m_colorManagement->setCurrentIndex((int) KisOcioConfiguration::INTERNAL); } const bool ocioEnabled = m_chkUseOcio->isChecked(); m_colorManagement->setEnabled(ocioEnabled && canDoExternalColorCorrection); const bool externalColorManagementEnabled = m_colorManagement->currentIndex() != (int)KisOcioConfiguration::INTERNAL; m_lblInputColorSpace->setEnabled(ocioEnabled && externalColorManagementEnabled); m_cmbInputColorSpace->setEnabled(ocioEnabled && externalColorManagementEnabled); m_lblDisplayDevice->setEnabled(ocioEnabled && externalColorManagementEnabled); m_cmbDisplayDevice->setEnabled(ocioEnabled && externalColorManagementEnabled); m_lblView->setEnabled(ocioEnabled && externalColorManagementEnabled); m_cmbView->setEnabled(ocioEnabled && externalColorManagementEnabled); m_lblLook->setEnabled(ocioEnabled && externalColorManagementEnabled); m_cmbLook->setEnabled(ocioEnabled && externalColorManagementEnabled); const bool exposureManagementEnabled = canChangeExposureAndGamma(); m_exposureDoubleWidget->setEnabled(exposureManagementEnabled); m_gammaDoubleWidget->setEnabled(exposureManagementEnabled); m_lblExposure->setEnabled(exposureManagementEnabled); m_lblGamma->setEnabled(exposureManagementEnabled); QString exposureToolTip; if (!exposureManagementEnabled) { exposureToolTip = i18nc("@info:tooltip", "Exposure and Gamma corrections are disabled in Internal mode. Switch to OCIO mode to use them"); } m_exposureDoubleWidget->setToolTip(exposureToolTip); m_gammaDoubleWidget->setToolTip(exposureToolTip); m_lblExposure->setToolTip(exposureToolTip); m_lblGamma->setToolTip(exposureToolTip); bool enableConfigPath = m_colorManagement->currentIndex() == (int) KisOcioConfiguration::OCIO_CONFIG; lblConfig->setEnabled(ocioEnabled && enableConfigPath); m_txtConfigurationPath->setEnabled(ocioEnabled && enableConfigPath); m_bnSelectConfigurationFile->setEnabled(ocioEnabled && enableConfigPath); } void LutDockerDock::updateDisplaySettings() { if (!m_canvas || !m_canvas->viewManager() || !m_canvas->viewManager()->image()) { return; } enableControls(); writeControls(); if (m_chkUseOcio->isChecked() && m_ocioConfig) { KIS_SAFE_ASSERT_RECOVER_NOOP(!m_canvas->displayFilter() || m_canvas->displayFilter() == m_displayFilter); if (!m_displayFilter) { m_displayFilter = m_canvas->displayFilter() ? m_canvas->displayFilter() : QSharedPointer(new OcioDisplayFilter(this)); } OcioDisplayFilter *displayFilter = qobject_cast(m_displayFilter.data()); displayFilter->config = m_ocioConfig; displayFilter->inputColorSpaceName = m_ocioConfig->getColorSpaceNameByIndex(m_cmbInputColorSpace->currentIndex()); displayFilter->displayDevice = m_ocioConfig->getDisplay(m_cmbDisplayDevice->currentIndex()); displayFilter->view = m_ocioConfig->getView(displayFilter->displayDevice, m_cmbView->currentIndex()); displayFilter->look = m_ocioConfig->getLookNameByIndex(m_cmbLook->currentIndex()); displayFilter->gamma = m_gammaDoubleWidget->isEnabled() ? m_gammaDoubleWidget->value() : 1.0; displayFilter->exposure = m_exposureDoubleWidget->isEnabled() ? m_exposureDoubleWidget->value() : 0.0; displayFilter->swizzle = (OCIO_CHANNEL_SWIZZLE)m_cmbComponents->currentIndex(); displayFilter->blackPoint = m_bwPointChooser->blackPoint(); displayFilter->whitePoint = m_bwPointChooser->whitePoint(); displayFilter->forceInternalColorManagement = m_colorManagement->currentIndex() == (int)KisOcioConfiguration::INTERNAL; displayFilter->setLockCurrentColorVisualRepresentation(m_btnConvertCurrentColor->isChecked()); displayFilter->updateProcessor(); m_canvas->setDisplayFilter(m_displayFilter); } else { m_canvas->setDisplayFilter(QSharedPointer(0)); } m_canvas->updateCanvas(); } void LutDockerDock::writeControls() { KisOcioConfiguration ocioOptions; ocioOptions.mode = (KisOcioConfiguration::Mode)m_colorManagement->currentIndex(); ocioOptions.configurationPath = m_txtConfigurationPath->text(); ocioOptions.lutPath = m_txtLut->text(); ocioOptions.inputColorSpace = m_cmbInputColorSpace->currentUnsqueezedText(); ocioOptions.displayDevice = m_cmbDisplayDevice->currentUnsqueezedText(); ocioOptions.displayView = m_cmbView->currentUnsqueezedText(); ocioOptions.look = m_cmbLook->currentUnsqueezedText(); KisConfig cfg(false); cfg.setUseOcio(m_chkUseOcio->isChecked()); cfg.setOcioConfiguration(ocioOptions); cfg.setOcioLockColorVisualRepresentation(m_btnConvertCurrentColor->isChecked()); } void LutDockerDock::slotColorManagementModeChanged() { enableControls(); writeControls(); resetOcioConfiguration(); } void LutDockerDock::selectOcioConfiguration() { QString filename = m_txtConfigurationPath->text(); KoFileDialog dialog(this, KoFileDialog::OpenFile, "lutdocker"); dialog.setCaption(i18n("Select OpenColorIO Configuration")); dialog.setDefaultDir(QDir::cleanPath(filename.isEmpty() ? QDir::homePath() : filename)); dialog.setMimeTypeFilters(QStringList() << "application/x-opencolorio-configuration"); filename = dialog.filename(); QFile f(filename); if (f.exists()) { m_txtConfigurationPath->setText(filename); writeControls(); resetOcioConfiguration(); } } void LutDockerDock::resetOcioConfiguration() { KisConfig cfg(true); KisOcioConfiguration ocioOptions = cfg.ocioConfiguration(); m_ocioConfig.reset(); try { if (ocioOptions.mode == KisOcioConfiguration::INTERNAL) { m_ocioConfig = defaultRawProfile(); } else if (ocioOptions.mode == KisOcioConfiguration::OCIO_ENVIRONMENT) { m_ocioConfig = OCIO::Config::CreateFromEnv(); } else if (ocioOptions.mode == KisOcioConfiguration::OCIO_CONFIG) { QString configFile = ocioOptions.configurationPath; if (QFile::exists(configFile)) { m_ocioConfig = OCIO::Config::CreateFromFile(configFile.toUtf8()); } else { m_ocioConfig = defaultRawProfile(); } } if (m_ocioConfig) { OCIO::SetCurrentConfig(m_ocioConfig); } } catch (OCIO::Exception &exception) { dbgKrita << "OpenColorIO Error:" << exception.what() << "Cannot create the LUT docker"; } if (m_ocioConfig) { refillControls(); } } void LutDockerDock::refillControls() { if (!m_canvas) return; if (!m_canvas->viewManager()) return; if (!m_canvas->viewManager()->canvasResourceProvider()) return; if (!m_canvas->viewManager()->image()) return; KIS_ASSERT_RECOVER_RETURN(m_ocioConfig); KisConfig cfg(true); KisOcioConfiguration ocioOptions = cfg.ocioConfiguration(); { // Color Management Mode KisSignalsBlocker modeBlocker(m_colorManagement); m_colorManagement->setCurrentIndex((int) ocioOptions.mode); } { // Exposure KisSignalsBlocker exposureBlocker(m_exposureDoubleWidget); m_exposureDoubleWidget->setValue(m_canvas->viewManager()->canvasResourceProvider()->HDRExposure()); } { // Gamma KisSignalsBlocker gammaBlocker(m_gammaDoubleWidget); m_gammaDoubleWidget->setValue(m_canvas->viewManager()->canvasResourceProvider()->HDRGamma()); } { // Components const KoColorSpace *cs = m_canvas->viewManager()->image()->colorSpace(); QStringList itemsList; itemsList << i18n("Luminance"); itemsList << i18n("All Channels"); Q_FOREACH (KoChannelInfo *channel, KoChannelInfo::displayOrderSorted(cs->channels())) { itemsList << channel->name(); } if (m_cmbComponents->originalTexts() != itemsList) { KisSignalsBlocker componentsBlocker(m_cmbComponents); m_cmbComponents->resetOriginalTexts(itemsList); m_cmbComponents->setCurrentIndex(1); // All Channels... } } { // Input Color Space QStringList itemsList; int numOcioColorSpaces = m_ocioConfig->getNumColorSpaces(); for(int i = 0; i < numOcioColorSpaces; ++i) { const char *cs = m_ocioConfig->getColorSpaceNameByIndex(i); OCIO::ConstColorSpaceRcPtr colorSpace = m_ocioConfig->getColorSpace(cs); itemsList << QString::fromUtf8(colorSpace->getName()); } KisSignalsBlocker inputCSBlocker(m_cmbInputColorSpace); if (itemsList != m_cmbInputColorSpace->originalTexts()) { m_cmbInputColorSpace->resetOriginalTexts(itemsList); } m_cmbInputColorSpace->setCurrent(ocioOptions.inputColorSpace); } { // Display Device QStringList itemsList; int numDisplays = m_ocioConfig->getNumDisplays(); for (int i = 0; i < numDisplays; ++i) { itemsList << QString::fromUtf8(m_ocioConfig->getDisplay(i)); } KisSignalsBlocker displayDeviceLocker(m_cmbDisplayDevice); if (itemsList != m_cmbDisplayDevice->originalTexts()) { m_cmbDisplayDevice->resetOriginalTexts(itemsList); } m_cmbDisplayDevice->setCurrent(ocioOptions.displayDevice); } { // Lock Current Color KisSignalsBlocker locker(m_btnConvertCurrentColor); m_btnConvertCurrentColor->setChecked(cfg.ocioLockColorVisualRepresentation()); } refillViewCombobox(); { QStringList itemsList; int numLooks = m_ocioConfig->getNumLooks(); for (int k = 0; k < numLooks; k++) { itemsList << QString::fromUtf8(m_ocioConfig->getLookNameByIndex(k)); } itemsList << i18nc("Item to indicate no look transform being selected","None"); KisSignalsBlocker LookComboLocker(m_cmbLook); if (itemsList != m_cmbLook->originalTexts()) { m_cmbLook->resetOriginalTexts(itemsList); } m_cmbLook->setCurrent(ocioOptions.look); } updateDisplaySettings(); } void LutDockerDock::refillViewCombobox() { KisSignalsBlocker viewComboLocker(m_cmbView); m_cmbView->clear(); if (!m_canvas || !m_ocioConfig) return; const char *display = m_ocioConfig->getDisplay(m_cmbDisplayDevice->currentIndex()); int numViews = m_ocioConfig->getNumViews(display); for (int j = 0; j < numViews; ++j) { m_cmbView->addSqueezedItem(QString::fromUtf8(m_ocioConfig->getView(display, j))); } KisConfig cfg(true); KisOcioConfiguration ocioOptions = cfg.ocioConfiguration(); m_cmbView->setCurrent(ocioOptions.displayView); } void LutDockerDock::selectLut() { QString filename = m_txtLut->text(); KoFileDialog dialog(this, KoFileDialog::OpenFile, "lutdocker"); dialog.setCaption(i18n("Select LUT file")); dialog.setDefaultDir(QDir::cleanPath(filename)); dialog.setMimeTypeFilters(QStringList() << "application/octet-stream", "application/octet-stream"); filename = dialog.filename(); QFile f(filename); if (f.exists() && filename != m_txtLut->text()) { m_txtLut->setText(filename); writeControls(); updateDisplaySettings(); } } void LutDockerDock::clearLut() { m_txtLut->clear(); updateDisplaySettings(); } diff --git a/plugins/extensions/buginfo/dlg_buginfo.cpp b/plugins/extensions/buginfo/dlg_buginfo.cpp index 2a097360a2..1a65fa89c6 100644 --- a/plugins/extensions/buginfo/dlg_buginfo.cpp +++ b/plugins/extensions/buginfo/dlg_buginfo.cpp @@ -1,118 +1,126 @@ /* * Copyright (c) 2017 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 "dlg_buginfo.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_document_aware_spin_box_unit_manager.h" #include DlgBugInfo::DlgBugInfo(QWidget *parent) : KoDialog(parent) { setCaption(i18n("Please paste this information in your bug report")); setButtons(User1 | Ok); setButtonText(User1, i18n("Copy to clipboard")); setDefaultButton(Ok); m_page = new WdgBugInfo(this); Q_CHECK_PTR(m_page); setMainWidget(m_page); QString info; const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); if (!kritarc.value("LogUsage", true).toBool() || !QFileInfo(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/krita.log").exists()) { // NOTE: This is intentionally not translated! // Krita version info info.append("Krita"); info.append("\n Version: ").append(KritaVersionWrapper::versionString(true)); info.append("\n\n"); info.append("Qt"); info.append("\n Version (compiled): ").append(QT_VERSION_STR); info.append("\n Version (loaded): ").append(qVersion()); info.append("\n\n"); // OS information info.append("OS Information"); info.append("\n Build ABI: ").append(QSysInfo::buildAbi()); info.append("\n Build CPU: ").append(QSysInfo::buildCpuArchitecture()); info.append("\n CPU: ").append(QSysInfo::currentCpuArchitecture()); info.append("\n Kernel Type: ").append(QSysInfo::kernelType()); info.append("\n Kernel Version: ").append(QSysInfo::kernelVersion()); info.append("\n Pretty Productname: ").append(QSysInfo::prettyProductName()); info.append("\n Product Type: ").append(QSysInfo::productType()); info.append("\n Product Version: ").append(QSysInfo::productVersion()); info.append("\n\n"); // OpenGL information info.append("\n").append(KisOpenGL::getDebugText()); info.append("\n\n"); // Hardware information info.append("Hardware Information"); info.append(QString("\n Memory: %1").arg(KisImageConfig(true).totalRAM() / 1024)).append(" Gb"); info.append(QString("\n Cores: %1").arg(QThread::idealThreadCount())); info.append("\n Swap: ").append(KisImageConfig(true).swapDir()); } else { - QFile f(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/krita.log"); - f.open(QFile::ReadOnly | QFile::Text); - info = QString::fromUtf8(f.readAll()); - f.close(); + + QFile sysinfo(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/krita-sysinfo.log"); + sysinfo.open(QFile::ReadOnly | QFile::Text); + info = QString::fromUtf8(sysinfo.readAll()); + sysinfo.close(); + + info += "\n\n"; + + QFile log(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/krita.log"); + log.open(QFile::ReadOnly | QFile::Text); + info += QString::fromUtf8(log.readAll()); + log.close(); } // calculate a default height for the widget int wheight = m_page->sizeHint().height(); m_page->txtBugInfo->setText(info); QFontMetrics fm = m_page->txtBugInfo->fontMetrics(); int target_height = fm.height() * info.split('\n').size() + wheight; QRect screen_rect = QGuiApplication::primaryScreen()->availableGeometry(); resize(m_page->size().width(), target_height > screen_rect.height() ? screen_rect.height() : target_height); connect(this, &KoDialog::user1Clicked, this, [this](){ QGuiApplication::clipboard()->setText(m_page->txtBugInfo->toPlainText()); m_page->txtBugInfo->selectAll(); // feedback }); } DlgBugInfo::~DlgBugInfo() { delete m_page; } diff --git a/plugins/extensions/offsetimage/wdg_offsetimage.ui b/plugins/extensions/offsetimage/wdg_offsetimage.ui index 8a1eedac33..6f64221f09 100644 --- a/plugins/extensions/offsetimage/wdg_offsetimage.ui +++ b/plugins/extensions/offsetimage/wdg_offsetimage.ui @@ -1,132 +1,138 @@ WdgOffsetImage 0 0 214 157 Rotate Image Offset 0 0 0 0 X: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 + + 100000.000000000000000 + Y: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 + + 100000.000000000000000 + 0 0 Offset by x/2, y/2 Qt::Vertical QSizePolicy::Minimum 0 0 KisDoubleParseUnitSpinBox QDoubleSpinBox
kis_double_parse_unit_spin_box.h
diff --git a/plugins/extensions/pykrita/plugin/PythonPluginManager.cpp b/plugins/extensions/pykrita/plugin/PythonPluginManager.cpp index 6db844de55..268c3f9880 100644 --- a/plugins/extensions/pykrita/plugin/PythonPluginManager.cpp +++ b/plugins/extensions/pykrita/plugin/PythonPluginManager.cpp @@ -1,418 +1,418 @@ /* * This file is part of PyKrita, Krita' Python scripting plugin. * * Copyright (C) 2013 Alex Turbov * Copyright (C) 2014-2016 Boudewijn Rempt * Copyright (C) 2017 Jouni Pentikäinen (joupent@gmail.com) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "PythonPluginManager.h" #include #include #include #include #include #include #include #include #include "config.h" #include "version_checker.h" PythonPluginManager* instance = 0; // PythonPlugin implementation QString PythonPlugin::moduleFilePathPart() const { QString filePath = m_moduleName; return filePath.replace(".", "/"); } bool PythonPlugin::isValid() const { dbgScript << "Got Krita/PythonPlugin: " << name() << ", module-path=" << moduleName() ; // Make sure mandatory properties are here if (m_name.isEmpty()) { dbgScript << "Ignore desktop file w/o a name"; return false; } if (m_moduleName.isEmpty()) { dbgScript << "Ignore desktop file w/o a module to import"; return false; } #if PY_MAJOR_VERSION == 2 // Check if the plug-in is compatible with Python 2 or not. if (m_properties["X-Python-2-Compatible"].toBool() != true) { dbgScript << "Ignoring plug-in. It is marked incompatible with Python 2."; return false; } #endif return true; } // PythonPluginManager implementation PythonPluginManager::PythonPluginManager() : QObject(0) , m_model(0, this) {} const QList& PythonPluginManager::plugins() const { return m_plugins; } PythonPlugin * PythonPluginManager::plugin(int index) { if (index >= 0 && index < m_plugins.count()) { return &m_plugins[index]; } return nullptr; } PythonPluginsModel * PythonPluginManager::model() { return &m_model; } void PythonPluginManager::unloadAllModules() { Q_FOREACH(PythonPlugin plugin, m_plugins) { if (plugin.m_loaded) { unloadModule(plugin); } } } bool PythonPluginManager::verifyModuleExists(PythonPlugin &plugin) { // Find the module: // 0) try to locate directory based plugin first QString rel_path = plugin.moduleFilePathPart(); rel_path = rel_path + "/" + "__init__.py"; dbgScript << "Finding Python module with rel_path:" << rel_path; QString module_path = KoResourcePaths::findResource("pythonscripts", rel_path); dbgScript << "module_path:" << module_path; if (module_path.isEmpty()) { // 1) Nothing found, then try file based plugin rel_path = plugin.moduleFilePathPart() + ".py"; dbgScript << "Finding Python module with rel_path:" << rel_path; module_path = KoResourcePaths::findResource("pythonscripts", rel_path); dbgScript << "module_path:" << module_path; } // Is anything found at all? if (module_path.isEmpty()) { plugin.m_broken = true; plugin.m_errorReason = i18nc( "@info:tooltip" , "Unable to find the module specified %1" , plugin.moduleName() ); dbgScript << "Cannot load module:" << plugin.m_errorReason; return false; } dbgScript << "Found module path:" << module_path; return true; } QPair PythonPluginManager::parseDependency(const QString& d) { // Check if dependency has package info attached const int pnfo = d.indexOf('('); if (pnfo != -1) { QString dependency = d.mid(0, pnfo); QString version_str = d.mid(pnfo + 1, d.size() - pnfo - 2).trimmed(); dbgScript << "Desired version spec [" << dependency << "]:" << version_str; PyKrita::version_checker checker = PyKrita::version_checker::fromString(version_str); if (!(checker.isValid() && d.endsWith(')'))) { dbgScript << "Invalid version spec " << d; QString reason = i18nc( "@info:tooltip" , "

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

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

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

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

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

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

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

Failure on module load %1:

%2
" , dependency , py.lastTraceback() ); } } if (plugin.isBroken() || plugin.isUnstable()) { plugin.m_errorReason = reason; } } void PythonPluginManager::scanPlugins() { m_plugins.clear(); KConfigGroup pluginSettings(KSharedConfig::openConfig(), "python"); QStringList desktopFiles = KoResourcePaths::findAllResources("data", "pykrita/*desktop"); Q_FOREACH(const QString &desktopFile, desktopFiles) { const KDesktopFile df(desktopFile); const KConfigGroup dg = df.desktopGroup(); if (dg.readEntry("ServiceTypes") == "Krita/PythonPlugin") { PythonPlugin plugin; plugin.m_comment = df.readComment(); plugin.m_name = df.readName(); plugin.m_moduleName = dg.readEntry("X-KDE-Library"); plugin.m_properties["X-Python-2-Compatible"] = dg.readEntry("X-Python-2-Compatible", false); QString manual = dg.readEntry("X-Krita-Manual"); if (!manual.isEmpty()) { QFile f(QFileInfo(desktopFile).path() + "/" + plugin.m_moduleName + "/" + manual); if (f.exists()) { f.open(QFile::ReadOnly); QByteArray ba = f.readAll(); f.close(); plugin.m_manual = QString::fromUtf8(ba); } } if (!plugin.isValid()) { dbgScript << plugin.name() << "is not usable"; continue; } if (!verifyModuleExists(plugin)) { dbgScript << "Cannot load" << plugin.name() << ": broken" << plugin.isBroken() << "because:" << plugin.errorReason(); continue; } verifyDependenciesSetStatus(plugin); plugin.m_enabled = pluginSettings.readEntry(QString("enable_") + plugin.moduleName(), false); m_plugins.append(plugin); } } } void PythonPluginManager::tryLoadEnabledPlugins() { for (PythonPlugin &plugin : m_plugins) { dbgScript << "Trying to load plugin" << plugin.moduleName() << ". Enabled:" << plugin.isEnabled() << ". Broken: " << plugin.isBroken(); if (plugin.m_enabled && !plugin.isBroken()) { loadModule(plugin); } } } void PythonPluginManager::loadModule(PythonPlugin &plugin) { KIS_SAFE_ASSERT_RECOVER_RETURN(plugin.isEnabled() && !plugin.isBroken()); QString module_name = plugin.moduleName(); dbgScript << "Loading module: " << module_name; PyKrita::Python py = PyKrita::Python(); // Get 'plugins' key from 'pykrita' module dictionary. // Every entry has a module name as a key and 2 elements tuple as a value PyObject* plugins = py.itemString("plugins"); KIS_SAFE_ASSERT_RECOVER_RETURN(plugins); PyObject* module = py.moduleImport(PQ(module_name)); if (module) { // Move just loaded module to the dict const int ins_result = PyDict_SetItemString(plugins, PQ(module_name), module); KIS_SAFE_ASSERT_RECOVER_NOOP(ins_result == 0); Py_DECREF(module); // Handle failure in release mode. if (ins_result == 0) { // Initialize the module from Python's side PyObject* const args = Py_BuildValue("(s)", PQ(module_name)); PyObject* result = py.functionCall("_pluginLoaded", PyKrita::Python::PYKRITA_ENGINE, args); Py_DECREF(args); if (result) { dbgScript << "\t" << "success!"; plugin.m_loaded = true; return; } } plugin.m_errorReason = i18nc("@info:tooltip", "Internal engine failure"); } else { plugin.m_errorReason = i18nc( "@info:tooltip" , "Module not loaded:
%1" , py.lastTraceback().replace("\n", "
") ); } plugin.m_broken = true; warnScript << "Error loading plugin" << module_name; } void PythonPluginManager::unloadModule(PythonPlugin &plugin) { KIS_SAFE_ASSERT_RECOVER_RETURN(plugin.m_loaded); KIS_SAFE_ASSERT_RECOVER_RETURN(!plugin.isBroken()); dbgScript << "Unloading module: " << plugin.moduleName(); PyKrita::Python py = PyKrita::Python(); // Get 'plugins' key from 'pykrita' module dictionary PyObject* plugins = py.itemString("plugins"); KIS_SAFE_ASSERT_RECOVER_RETURN(plugins); PyObject* const args = Py_BuildValue("(s)", PQ(plugin.moduleName())); py.functionCall("_pluginUnloading", PyKrita::Python::PYKRITA_ENGINE, args); Py_DECREF(args); // This will just decrement a reference count for module instance PyDict_DelItemString(plugins, PQ(plugin.moduleName())); // Remove the module also from 'sys.modules' dict to really unload it, // so if reloaded all @init actions will work again! PyObject* sys_modules = py.itemString("modules", "sys"); KIS_SAFE_ASSERT_RECOVER_RETURN(sys_modules); PyDict_DelItemString(sys_modules, PQ(plugin.moduleName())); plugin.m_loaded = false; } void PythonPluginManager::setPluginEnabled(PythonPlugin &plugin, bool enabled) { bool wasEnabled = plugin.isEnabled(); if (wasEnabled && !enabled) { unloadModule(plugin); } plugin.m_enabled = enabled; KConfigGroup pluginSettings(KSharedConfig::openConfig(), "python"); pluginSettings.writeEntry(QString("enable_") + plugin.moduleName(), enabled); if (!wasEnabled && enabled) { loadModule(plugin); } } diff --git a/plugins/extensions/pykrita/plugin/utilities.cpp b/plugins/extensions/pykrita/plugin/utilities.cpp index de5c6c9123..dd599c1b44 100644 --- a/plugins/extensions/pykrita/plugin/utilities.cpp +++ b/plugins/extensions/pykrita/plugin/utilities.cpp @@ -1,706 +1,706 @@ // This file is part of PyKrita, Krita' Python scripting plugin. // // Copyright (C) 2006 Paul Giannaros // Copyright (C) 2012, 2013 Shaheed Haque // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) version 3, or any // later version accepted by the membership of KDE e.V. (or its // successor approved by the membership of KDE e.V.), which shall // act as a proxy defined in Section 6 of version 3 of the license. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library. If not, see . // // config.h defines PYKRITA_PYTHON_LIBRARY, the path to libpython.so // on the build system #include "config.h" #include "utilities.h" #include "PythonPluginManager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "PykritaModule.h" #define THREADED 1 namespace PyKrita { static InitResult initStatus = INIT_UNINITIALIZED; static QScopedPointer pluginManagerInstance; InitResult initialize() { // Already initialized? if (initStatus == INIT_OK) return INIT_OK; dbgScript << "Initializing Python plugin for Python" << PY_MAJOR_VERSION << "," << PY_MINOR_VERSION; if (!Python::libraryLoad()) { return INIT_CANNOT_LOAD_PYTHON_LIBRARY; } // Update PYTHONPATH // 0) custom plugin directories (prefer local dir over systems') // 1) shipped krita module's dir QStringList pluginDirectories = KoResourcePaths::findDirs("pythonscripts"); dbgScript << "Plugin Directories: " << pluginDirectories; if (!Python::setPath(pluginDirectories)) { initStatus = INIT_CANNOT_SET_PYTHON_PATHS; return initStatus; } #if defined(IS_PY3K) if (0 != PyImport_AppendInittab(Python::PYKRITA_ENGINE, PyInit_pykrita)) { #else if (0 != PyImport_AppendInittab(Python::PYKRITA_ENGINE, initpykrita)) { #endif initStatus = INIT_CANNOT_LOAD_PYKRITA_MODULE; return initStatus; } Python::ensureInitialized(); Python py = Python(); // NOTE: This code is not needed on Python 3. // This might also fail if private sip module for PyQt5 is used, // as required by newer PyQt5. It's fine since any exceptions // in this script will be ignored. PyRun_SimpleString( "import sip\n" "sip.setapi('QDate', 2)\n" "sip.setapi('QTime', 2)\n" "sip.setapi('QDateTime', 2)\n" "sip.setapi('QUrl', 2)\n" "sip.setapi('QTextStream', 2)\n" "sip.setapi('QString', 2)\n" "sip.setapi('QVariant', 2)\n" ); // Initialize 'plugins' dict of module 'pykrita' PyObject* plugins = PyDict_New(); py.itemStringSet("plugins", plugins); pluginManagerInstance.reset(new PythonPluginManager()); #if defined(IS_PY3K) // Initialize our built-in module. auto pykritaModule = PyInit_pykrita(); if (!pykritaModule) { initStatus = INIT_CANNOT_LOAD_PYKRITA_MODULE; return initStatus; //return i18nc("@info:tooltip ", "No pykrita built-in module"); } #else initpykrita(); #endif initStatus = INIT_OK; return initStatus; } PythonPluginManager *pluginManager() { auto pluginManager = pluginManagerInstance.data(); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(pluginManager, nullptr); return pluginManager; } void finalize() { dbgScript << "Going to destroy the Python engine"; if (pluginManagerInstance) { pluginManagerInstance->unloadAllModules(); PyKrita::Python::maybeFinalize(); PyKrita::Python::libraryUnload(); pluginManagerInstance.reset(); initStatus = INIT_UNINITIALIZED; } } namespace { #ifndef Q_OS_WIN QLibrary* s_pythonLibrary = 0; #endif PyThreadState* s_pythonThreadState = 0; bool isPythonPathSet = false; } // anonymous namespace const char* Python::PYKRITA_ENGINE = "pykrita"; Python::Python() { #if THREADED m_state = PyGILState_Ensure(); #endif } Python::~Python() { #if THREADED PyGILState_Release(m_state); #endif } bool Python::prependStringToList(PyObject* const list, const QString& value) { PyObject* const u = unicode(value); bool result = !PyList_Insert(list, 0, u); Py_DECREF(u); if (!result) traceback(QString("Failed to prepend %1").arg(value)); return result; } bool Python::functionCall(const char* const functionName, const char* const moduleName) { PyObject* const result = functionCall(functionName, moduleName, PyTuple_New(0)); if (result) Py_DECREF(result); return bool(result); } PyObject* Python::functionCall( const char* const functionName , const char* const moduleName , PyObject* const arguments ) { if (!arguments) { errScript << "Missing arguments for" << moduleName << functionName; return 0; } PyObject* const func = itemString(functionName, moduleName); if (!func) { errScript << "Failed to resolve" << moduleName << functionName; return 0; } if (!PyCallable_Check(func)) { traceback(QString("Not callable %1.%2").arg(moduleName).arg(functionName)); return 0; } PyObject* const result = PyObject_CallObject(func, arguments); Py_DECREF(arguments); if (!result) traceback(QString("No result from %1.%2").arg(moduleName).arg(functionName)); return result; } bool Python::itemStringDel(const char* const item, const char* const moduleName) { PyObject* const dict = moduleDict(moduleName); const bool result = dict && PyDict_DelItemString(dict, item); if (!result) traceback(QString("Could not delete item string %1.%2").arg(moduleName).arg(item)); return result; } PyObject* Python::itemString(const char* const item, const char* const moduleName) { if (PyObject* const value = itemString(item, moduleDict(moduleName))) return value; errScript << "Could not get item string" << moduleName << item; return 0; } PyObject* Python::itemString(const char* item, PyObject* dict) { if (dict) if (PyObject* const value = PyDict_GetItemString(dict, item)) return value; traceback(QString("Could not get item string %1").arg(item)); return 0; } bool Python::itemStringSet(const char* const item, PyObject* const value, const char* const moduleName) { PyObject* const dict = moduleDict(moduleName); const bool result = dict && !PyDict_SetItemString(dict, item, value); if (!result) traceback(QString("Could not set item string %1.%2").arg(moduleName).arg(item)); return result; } PyObject* Python::kritaHandler(const char* const moduleName, const char* const handler) { if (PyObject* const module = moduleImport(moduleName)) return functionCall(handler, "krita", Py_BuildValue("(O)", module)); return 0; } QString Python::lastTraceback() const { QString result; result.swap(m_traceback); return result; } bool Python::libraryLoad() { // no-op on Windows #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) if (!s_pythonLibrary) { QFileInfo fi(PYKRITA_PYTHON_LIBRARY); // get the filename of the configured Python library, without the .so suffix const QString libraryName = fi.completeBaseName(); // 1.0 is the SONAME of the shared Python library s_pythonLibrary = new QLibrary(libraryName, "1.0"); s_pythonLibrary->setLoadHints(QLibrary::ExportExternalSymbolsHint); if (!s_pythonLibrary->load()) { dbgScript << QString("Could not load %1 -- Reason: %2").arg(s_pythonLibrary->fileName()).arg(s_pythonLibrary->errorString()); delete s_pythonLibrary; s_pythonLibrary = 0; return false; } dbgScript << QString("Loaded %1").arg(s_pythonLibrary->fileName()); } #endif return true; } namespace { QString findKritaPythonLibsPath(const QString &libdir) { QDir rootDir(KoResourcePaths::getApplicationRoot()); QFileInfoList candidates = rootDir.entryInfoList(QStringList() << "lib*", QDir::Dirs | QDir::NoDotAndDotDot) + rootDir.entryInfoList(QStringList() << "Frameworks", QDir::Dirs | QDir::NoDotAndDotDot); Q_FOREACH (const QFileInfo &entry, candidates) { QDir libDir(entry.absoluteFilePath()); if (libDir.cd(libdir)) { return libDir.absolutePath(); } else { // Handle cases like Linux where libs are placed in a sub-dir // with the ABI name Q_FOREACH (const QFileInfo &subEntry, libDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) { QDir subDir(subEntry.absoluteFilePath()); if (subDir.cd(libdir)) { return subDir.absolutePath(); } } } } return QString(); } } // namespace bool Python::setPath(const QStringList& scriptPaths) { KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!Py_IsInitialized(), false); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!isPythonPathSet, false); // qDebug() << ">>>>>>>>>>>" << qgetenv("APPDIR") // << KoResourcePaths::getApplicationRoot() // << (!qgetenv("APPDIR").isNull() && KoResourcePaths::getApplicationRoot().contains(qgetenv("APPDIR"))); bool runningInBundle = ((!qgetenv("APPDIR").isNull() && KoResourcePaths::getApplicationRoot().contains(qgetenv("APPDIR"))) || KoResourcePaths::getApplicationRoot().toLower().contains("krita.app")); dbgScript << "Python::setPath. Script paths:" << scriptPaths << runningInBundle; #ifdef Q_OS_WIN constexpr char pathSeparator = ';'; #else constexpr char pathSeparator = ':'; #endif QString originalPath; // Start with the script paths QStringList paths(scriptPaths); // Append the Krita libraries path QString pythonLibsPath = findKritaPythonLibsPath("krita-python-libs"); dbgScript << "pythonLibsPath (krita-python-libs)" << pythonLibsPath; if (pythonLibsPath.isEmpty()) { dbgScript << "Cannot find krita-python-libs"; return false; } dbgScript << "Found krita-python-libs at" << pythonLibsPath; paths.append(pythonLibsPath); #ifndef Q_OS_WIN // Append the sip libraries path pythonLibsPath = findKritaPythonLibsPath("sip"); dbgScript << "pythonLibsPath (sip)" << pythonLibsPath; if (!pythonLibsPath.isEmpty()) { dbgScript << "Found sip at" << pythonLibsPath; paths.append(pythonLibsPath); } #endif #ifdef Q_OS_WIN // Find embeddable Python at /python QDir pythonDir(KoResourcePaths::getApplicationRoot()); if (pythonDir.cd("python")) { dbgScript << "Found bundled Python at" << pythonDir.absolutePath(); // The default paths for Windows embeddable Python is ./python36.zip;./ // HACK: Assuming bundled Python is version 3.6.* // FIXME: Should we read python36._pth for the paths or use Py_GetPath? paths.append(pythonDir.absoluteFilePath("python36.zip")); paths.append(pythonDir.absolutePath()); } else { errScript << "Bundled Python not found, cannot set Python library paths"; return false; } #else // If using a system Python install, respect the current PYTHONPATH if (runningInBundle) { // We're running from an appimage, so we need our local python QString p = QFileInfo(PYKRITA_PYTHON_LIBRARY).fileName(); #ifdef Q_OS_MAC QString p2 = p.remove("lib").remove("m.dy"); #else QString p2 = p.remove("lib").remove("m.so"); #endif dbgScript << "\t" << p << p2; originalPath = findKritaPythonLibsPath(p); #ifdef Q_OS_MAC // Are we running with a system Python library instead? if (originalPath.isEmpty()) { // Keep the original Python search path. originalPath = QString::fromWCharArray(Py_GetPath()); QString d = QFileInfo(PYKRITA_PYTHON_LIBRARY).absolutePath(); paths.append(d + "/" + p2 + "/site-packages"); paths.append(d + "/" + p2 + "/site-packages/PyQt5"); } else { #endif paths.append(originalPath + "/lib-dynload"); paths.append(originalPath + "/site-packages"); paths.append(originalPath + "/site-packages/PyQt5"); #ifdef Q_OS_MAC } #endif } else { // Use the system path originalPath = QString::fromLocal8Bit(qgetenv("PYTHONPATH")); } #endif QString joinedPaths = paths.join(pathSeparator); if (!originalPath.isEmpty()) { joinedPaths = joinedPaths + pathSeparator + originalPath; } dbgScript << "Setting python paths:" << joinedPaths; #ifdef Q_OS_WIN QVector joinedPathsWChars(joinedPaths.size() + 1, 0); joinedPaths.toWCharArray(joinedPathsWChars.data()); Py_SetPath(joinedPathsWChars.data()); #else if (runningInBundle) { QVector joinedPathsWChars(joinedPaths.size() + 1, 0); joinedPaths.toWCharArray(joinedPathsWChars.data()); Py_SetPath(joinedPathsWChars.data()); } else { qputenv("PYTHONPATH", joinedPaths.toLocal8Bit()); } #endif isPythonPathSet = true; return true; } void Python::ensureInitialized() { if (Py_IsInitialized()) { warnScript << "Python interpreter is already initialized, not initializing again"; } else { dbgScript << "Initializing Python interpreter"; Py_InitializeEx(0); if (!Py_IsInitialized()) { errScript << "Could not initialize Python interpreter"; } #if THREADED PyEval_InitThreads(); s_pythonThreadState = PyGILState_GetThisThreadState(); PyEval_ReleaseThread(s_pythonThreadState); #endif } } void Python::maybeFinalize() { if (!Py_IsInitialized()) { warnScript << "Python interpreter not initialized, no need to finalize"; } else { #if THREADED PyEval_AcquireThread(s_pythonThreadState); #endif Py_Finalize(); } } void Python::libraryUnload() { // no-op on Windows #ifndef Q_OS_WIN if (s_pythonLibrary) { // Shut the interpreter down if it has been started. if (s_pythonLibrary->isLoaded()) { s_pythonLibrary->unload(); } delete s_pythonLibrary; s_pythonLibrary = 0; } #endif } PyObject* Python::moduleActions(const char* moduleName) { return kritaHandler(moduleName, "moduleGetActions"); } PyObject* Python::moduleConfigPages(const char* const moduleName) { return kritaHandler(moduleName, "moduleGetConfigPages"); } QString Python::moduleHelp(const char* moduleName) { QString r; PyObject* const result = kritaHandler(moduleName, "moduleGetHelp"); if (result) { r = unicode(result); Py_DECREF(result); } return r; } PyObject* Python::moduleDict(const char* const moduleName) { PyObject* const module = moduleImport(moduleName); if (module) if (PyObject* const dictionary = PyModule_GetDict(module)) return dictionary; traceback(QString("Could not get dict %1").arg(moduleName)); return 0; } PyObject* Python::moduleImport(const char* const moduleName) { PyObject* const module = PyImport_ImportModule(moduleName); if (module) return module; traceback(QString("Could not import %1").arg(moduleName)); return 0; } -// Inspired by http://www.gossamer-threads.com/lists/python/python/150924. +// Inspired by https://lists.gt.net/python/python/150924. void Python::traceback(const QString& description) { m_traceback.clear(); if (!PyErr_Occurred()) // Return an empty string on no error. // NOTE "Return a string?" really?? return; PyObject* exc_typ; PyObject* exc_val; PyObject* exc_tb; PyErr_Fetch(&exc_typ, &exc_val, &exc_tb); PyErr_NormalizeException(&exc_typ, &exc_val, &exc_tb); // Include the traceback. if (exc_tb) { m_traceback = "Traceback (most recent call last):\n"; PyObject* const arguments = PyTuple_New(1); PyTuple_SetItem(arguments, 0, exc_tb); PyObject* const result = functionCall("format_tb", "traceback", arguments); if (result) { for (int i = 0, j = PyList_Size(result); i < j; i++) { PyObject* const tt = PyList_GetItem(result, i); PyObject* const t = Py_BuildValue("(O)", tt); char* buffer; if (!PyArg_ParseTuple(t, "s", &buffer)) break; m_traceback += buffer; } Py_DECREF(result); } Py_DECREF(exc_tb); } // Include the exception type and value. if (exc_typ) { PyObject* const temp = PyObject_GetAttrString(exc_typ, "__name__"); if (temp) { m_traceback += unicode(temp); m_traceback += ": "; } Py_DECREF(exc_typ); } if (exc_val) { PyObject* const temp = PyObject_Str(exc_val); if (temp) { m_traceback += unicode(temp); m_traceback += "\n"; } Py_DECREF(exc_val); } m_traceback += description; QStringList l = m_traceback.split("\n"); Q_FOREACH(const QString &s, l) { errScript << s; } /// \todo How about to show it somewhere else than "console output"? } PyObject* Python::unicode(const QString& string) { #if PY_MAJOR_VERSION < 3 - /* Python 2.x. http://docs.python.org/2/c-api/unicode.html */ + /* Python 2.x. https://docs.python.org/2/c-api/unicode.html */ PyObject* s = PyString_FromString(PQ(string)); PyObject* u = PyUnicode_FromEncodedObject(s, "utf-8", "strict"); Py_DECREF(s); return u; #elif PY_MINOR_VERSION < 3 - /* Python 3.2 or less. http://docs.python.org/3.2/c-api/unicode.html#unicode-objects */ + /* Python 3.2 or less. https://docs.python.org/3.2/c-api/unicode.html#unicode-objects */ # ifdef Py_UNICODE_WIDE return PyUnicode_DecodeUTF16((const char*)string.constData(), string.length() * 2, 0, 0); # else return PyUnicode_FromUnicode(string.constData(), string.length()); # endif -#else /* Python 3.3 or greater. http://docs.python.org/3.3/c-api/unicode.html#unicode-objects */ +#else /* Python 3.3 or greater. https://docs.python.org/3.3/c-api/unicode.html#unicode-objects */ return PyUnicode_FromKindAndData(PyUnicode_2BYTE_KIND, string.constData(), string.length()); #endif } QString Python::unicode(PyObject* const string) { #if PY_MAJOR_VERSION < 3 - /* Python 2.x. http://docs.python.org/2/c-api/unicode.html */ + /* Python 2.x. https://docs.python.org/2/c-api/unicode.html */ if (PyString_Check(string)) return QString(PyString_AsString(string)); else if (PyUnicode_Check(string)) { const int unichars = PyUnicode_GetSize(string); # ifdef HAVE_USABLE_WCHAR_T return QString::fromWCharArray(PyUnicode_AsUnicode(string), unichars); # else # ifdef Py_UNICODE_WIDE return QString::fromUcs4((const unsigned int*)PyUnicode_AsUnicode(string), unichars); # else return QString::fromUtf16(PyUnicode_AsUnicode(string), unichars); # endif # endif } else return QString(); #elif PY_MINOR_VERSION < 3 - /* Python 3.2 or less. http://docs.python.org/3.2/c-api/unicode.html#unicode-objects */ + /* Python 3.2 or less. https://docs.python.org/3.2/c-api/unicode.html#unicode-objects */ if (!PyUnicode_Check(string)) return QString(); const int unichars = PyUnicode_GetSize(string); # ifdef HAVE_USABLE_WCHAR_T return QString::fromWCharArray(PyUnicode_AsUnicode(string), unichars); # else # ifdef Py_UNICODE_WIDE return QString::fromUcs4(PyUnicode_AsUnicode(string), unichars); # else return QString::fromUtf16(PyUnicode_AsUnicode(string), unichars); # endif # endif -#else /* Python 3.3 or greater. http://docs.python.org/3.3/c-api/unicode.html#unicode-objects */ +#else /* Python 3.3 or greater. https://docs.python.org/3.3/c-api/unicode.html#unicode-objects */ if (!PyUnicode_Check(string)) return QString(); const int unichars = PyUnicode_GetLength(string); if (0 != PyUnicode_READY(string)) return QString(); switch (PyUnicode_KIND(string)) { case PyUnicode_1BYTE_KIND: return QString::fromLatin1((const char*)PyUnicode_1BYTE_DATA(string), unichars); case PyUnicode_2BYTE_KIND: return QString::fromUtf16(PyUnicode_2BYTE_DATA(string), unichars); case PyUnicode_4BYTE_KIND: return QString::fromUcs4(PyUnicode_4BYTE_DATA(string), unichars); default: break; } return QString(); #endif } bool Python::isUnicode(PyObject* const string) { #if PY_MAJOR_VERSION < 3 return PyString_Check(string) || PyUnicode_Check(string); #else return PyUnicode_Check(string); #endif } bool Python::prependPythonPaths(const QString& path) { PyObject* sys_path = itemString("path", "sys"); return bool(sys_path) && prependPythonPaths(path, sys_path); } bool Python::prependPythonPaths(const QStringList& paths) { PyObject* sys_path = itemString("path", "sys"); if (!sys_path) return false; /// \todo Heh, boosts' range adaptors would be good here! QStringList reversed_paths; std::reverse_copy( paths.begin() , paths.end() , std::back_inserter(reversed_paths) ); Q_FOREACH(const QString & path, reversed_paths) if (!prependPythonPaths(path, sys_path)) return false; return true; } bool Python::prependPythonPaths(const QString& path, PyObject* sys_path) { Q_ASSERT("Dir entry expected to be valid" && sys_path); return bool(prependStringToList(sys_path, path)); } } // namespace PyKrita // krita: indent-width 4; diff --git a/plugins/extensions/qmic/WdgQMicSettings.ui b/plugins/extensions/qmic/WdgQMicSettings.ui index dc1678f3a8..1d70a449b0 100644 --- a/plugins/extensions/qmic/WdgQMicSettings.ui +++ b/plugins/extensions/qmic/WdgQMicSettings.ui @@ -1,79 +1,79 @@ WdgQMicSettings 0 0 400 300 - <html><head/><body><p>Select the location of the G'Mic-Qt plugin. You can download the plugin from the <a href="http://gmic.eu/"><span style=" text-decoration: underline; color:#2980b9;">G'Mic website</span></a>. Make sure you download the special version for Krita, not the standalone or the GIMP version.</p></body></html> + <html><head/><body><p>Select the location of the G'Mic-Qt plugin. You can download the plugin from the <a href="https://gmic.eu/"><span style=" text-decoration: underline; color:#2980b9;">G'Mic website</span></a>. Make sure you download the special version for Krita, not the standalone or the GIMP version.</p></body></html> Qt::RichText true true Qt::TextBrowserInteraction Plugin: 1 0 Qt::Vertical 20 40 KisFileNameRequester QWidget
kis_file_name_requester.h
1
diff --git a/plugins/flake/artistictextshape/ArtisticTextShapeConfigWidget.ui b/plugins/flake/artistictextshape/ArtisticTextShapeConfigWidget.ui index b081e19102..53c0b931bc 100644 --- a/plugins/flake/artistictextshape/ArtisticTextShapeConfigWidget.ui +++ b/plugins/flake/artistictextshape/ArtisticTextShapeConfigWidget.ui @@ -1,161 +1,156 @@ ArtisticTextShapeConfigWidget 0 0 259 73 0 0 0 0 0 0 - + 0 0 0 0 Qt::Vertical Qt::Vertical Qt::Horizontal 40 20 Qt::Vertical 20 40 KisIntParseSpinBox QSpinBox
kis_int_parse_spin_box.h
- - KoFontComboBox - QComboBox -
KoFontComboBox.h
-
diff --git a/plugins/flake/textshape/FontFamilyAction.cpp b/plugins/flake/textshape/FontFamilyAction.cpp index 09f424e073..58f267ceba 100644 --- a/plugins/flake/textshape/FontFamilyAction.cpp +++ b/plugins/flake/textshape/FontFamilyAction.cpp @@ -1,194 +1,194 @@ /* This file is part of the KDE libraries Copyright (C) 1999 Reginald Stadlbauer (C) 1999 Simon Hausmann (C) 2000 Nicolas Hadacek (C) 2000 Kurt Granroth (C) 2000 Michael Koch (C) 2001 Holger Freyther (C) 2002 Ellis Whitehead (C) 2002 Joseph Wenninger (C) 2003 Andras Mantia (C) 2005-2006 Hamish Rodda (C) 2007 Clarence Dang (C) 2014 Dan Leinir Turthra Jensen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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. */ // This is a minorly modified version of the KFontAction class. It exists // entirely because there's a hang bug on windows at the moment. #include "FontFamilyAction.h" #include #include #include #include #include -#include +#include class KoFontFamilyAction::KoFontFamilyActionPrivate { public: KoFontFamilyActionPrivate(KoFontFamilyAction *parent) : q(parent) , settingFont(0) { } void _ko_slotFontChanged(const QFont &font) { - qDebug() << "KoFontComboBox - slotFontChanged(" + qDebug() << "QFontComboBox - slotFontChanged(" << font.family() << ") settingFont=" << settingFont; if (settingFont) { return; } q->setFont(font.family()); q->triggered(font.family()); qDebug() << "\tslotFontChanged done"; } KoFontFamilyAction *q; int settingFont; }; KoFontFamilyAction::KoFontFamilyAction(uint fontListCriteria, QObject *parent) : KSelectAction(parent) , d(new KoFontFamilyActionPrivate(this)) { QStringList list; KFontChooser::getFontList(list, fontListCriteria); KSelectAction::setItems(list); setEditable(true); } KoFontFamilyAction::KoFontFamilyAction(QObject *parent) : KSelectAction(parent) , d(new KoFontFamilyActionPrivate(this)) { QStringList list; KFontChooser::getFontList(list, 0); KSelectAction::setItems(list); setEditable(true); } KoFontFamilyAction::KoFontFamilyAction(const QString &text, QObject *parent) : KSelectAction(text, parent) , d(new KoFontFamilyActionPrivate(this)) { QStringList list; KFontChooser::getFontList(list, 0); KSelectAction::setItems(list); setEditable(true); } KoFontFamilyAction::KoFontFamilyAction(const QIcon &icon, const QString &text, QObject *parent) : KSelectAction(icon, text, parent) , d(new KoFontFamilyActionPrivate(this)) { QStringList list; KFontChooser::getFontList(list, 0); KSelectAction::setItems(list); setEditable(true); } KoFontFamilyAction::~KoFontFamilyAction() { delete d; } QString KoFontFamilyAction::font() const { return currentText(); } QWidget *KoFontFamilyAction::createWidget(QWidget *parent) { qDebug() << "KoFontFamilyAction::createWidget()"; // silence unclear warning from original kfontaction.acpp // #ifdef __GNUC__ // #warning FIXME: items need to be converted // #endif // This is the visual element on the screen. This method overrides // the KSelectAction one, preventing KSelectAction from creating its // regular KComboBox. - KoFontComboBox *cb = new KoFontComboBox(parent); + QFontComboBox *cb = new QFontComboBox(parent); //cb->setFontList(items()); qDebug() << "\tset=" << font(); // Do this before connecting the signal so that nothing will fire. cb->setCurrentFont(QFont(font().toLower())); qDebug() << "\tspit back=" << cb->currentFont().family(); connect(cb, SIGNAL(currentFontChanged(QFont)), SLOT(_ko_slotFontChanged(QFont))); cb->setMinimumWidth(cb->sizeHint().width()); return cb; } /* * Maintenance note: Keep in sync with KFontComboBox::setCurrentFont() */ void KoFontFamilyAction::setFont(const QString &family) { qDebug() << "KoFontFamilyAction::setFont(" << family << ")"; // Suppress triggered(QString) signal and prevent recursive call to ourself. d->settingFont++; Q_FOREACH (QWidget *w, createdWidgets()) { - KoFontComboBox *cb = qobject_cast(w); + QFontComboBox *cb = qobject_cast(w); qDebug() << "\tw=" << w << "cb=" << cb; if (!cb) { continue; } cb->setCurrentFont(QFont(family.toLower())); qDebug() << "\t\tw spit back=" << cb->currentFont().family(); } d->settingFont--; qDebug() << "\tcalling setCurrentAction()"; QString lowerName = family.toLower(); if (setCurrentAction(lowerName, Qt::CaseInsensitive)) { return; } int i = lowerName.indexOf(" ["); if (i > -1) { lowerName = lowerName.left(i); i = 0; if (setCurrentAction(lowerName, Qt::CaseInsensitive)) { return; } } lowerName += " ["; if (setCurrentAction(lowerName, Qt::CaseInsensitive)) { return; } // TODO: Inconsistent state if KFontComboBox::setCurrentFont() succeeded // but setCurrentAction() did not and vice-versa. qDebug() << "Font not found " << family.toLower(); } // have to include this because of Q_PRIVATE_SLOT #include "moc_FontFamilyAction.cpp" diff --git a/plugins/flake/textshape/kotext/KoTextBlockData.cpp b/plugins/flake/textshape/kotext/KoTextBlockData.cpp index 608322aac3..1fbdbf8a81 100644 --- a/plugins/flake/textshape/kotext/KoTextBlockData.cpp +++ b/plugins/flake/textshape/kotext/KoTextBlockData.cpp @@ -1,302 +1,302 @@ /* This file is part of the KDE project * Copyright (C) 2006 Thomas Zander * Copyright (C) 2010 C. Boemann * Copyright (C) 2011 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoTextBlockData.h" #include "KoTextBlockBorderData.h" #include "KoTextBlockPaintStrategyBase.h" class Q_DECL_HIDDEN KoTextBlockData::Private : public QTextBlockUserData { public: Private() : counterWidth(-1.0) , counterSpacing(0) , counterIsImage(false) , counterIndex(1) , border(0) , paintStrategy(0) { layoutedMarkupRanges[KoTextBlockData::Misspell] = false; layoutedMarkupRanges[KoTextBlockData::Grammar] = false; } ~Private() override { if (border && !border->deref()) delete border; delete paintStrategy; } qreal counterWidth; qreal counterSpacing; QString counterPrefix; QString counterPlainText; QString counterSuffix; QString partialCounterText; bool counterIsImage; int counterIndex; QPointF counterPos; QTextCharFormat labelFormat; KoTextBlockBorderData *border; KoTextBlockPaintStrategyBase *paintStrategy; QMap > markupRangesMap; QMap layoutedMarkupRanges; }; KoTextBlockData::KoTextBlockData(QTextBlock &block) : d(block.userData() ? dynamic_cast(block.userData()) : new Private()) { block.setUserData(d); } KoTextBlockData::KoTextBlockData(QTextBlockUserData *userData) : d(dynamic_cast(userData)) { Q_ASSERT(d); } KoTextBlockData::~KoTextBlockData() { // explicitly do not delete the d-pointer here } void KoTextBlockData::appendMarkup(MarkupType type, int firstChar, int lastChar) { Q_ASSERT(d->markupRangesMap[type].isEmpty() || d->markupRangesMap[type].last().lastChar < firstChar); MarkupRange range; range.firstChar = firstChar; range.lastChar = lastChar; d->layoutedMarkupRanges[type] = false; d->markupRangesMap[type].append(range); } void KoTextBlockData::clearMarkups(MarkupType type) { d->markupRangesMap[type].clear(); d->layoutedMarkupRanges[type] = false; } KoTextBlockData::MarkupRange KoTextBlockData::findMarkup(MarkupType type, int positionWithin) const { foreach (const MarkupRange &range, d->markupRangesMap[type]) { if (positionWithin <= range.lastChar) { // possible hit if (positionWithin >= range.firstChar) { return range; } else { return MarkupRange(); // we have passed it without finding } } } return MarkupRange(); // either no ranges or not in last either } void KoTextBlockData::rebaseMarkups(MarkupType type, int fromPosition, int delta) { QList::Iterator markIt = markupsBegin(type); QList::Iterator markEnd = markupsEnd(type); while (markIt != markEnd) { if (fromPosition <= markIt->lastChar) { // we need to modify the end of this markIt->lastChar += delta; } if (fromPosition < markIt->firstChar) { // we need to modify the end of this markIt->firstChar += delta; } ++markIt; } } void KoTextBlockData::setMarkupsLayoutValidity(MarkupType type, bool valid) { d->layoutedMarkupRanges[type] = valid; } bool KoTextBlockData::isMarkupsLayoutValid(MarkupType type) const { return d->layoutedMarkupRanges[type]; } QList::Iterator KoTextBlockData::markupsBegin(MarkupType type) { return d->markupRangesMap[type].begin(); } QList::Iterator KoTextBlockData::markupsEnd(MarkupType type) { return d->markupRangesMap[type].end(); } bool KoTextBlockData::hasCounterData() const { return d->counterWidth >= 0 && (!d->counterPlainText.isNull() || d->counterIsImage); } qreal KoTextBlockData::counterWidth() const { return qMax(qreal(0), d->counterWidth); } void KoTextBlockData::setBorder(KoTextBlockBorderData *border) { if (d->border && !d->border->deref()) delete d->border; d->border = border; if (d->border) d->border->ref(); } void KoTextBlockData::setCounterWidth(qreal width) { d->counterWidth = width; } qreal KoTextBlockData::counterSpacing() const { return d->counterSpacing; } void KoTextBlockData::setCounterSpacing(qreal spacing) { d->counterSpacing = spacing; } QString KoTextBlockData::counterText() const { return d->counterPrefix + d->counterPlainText + d->counterSuffix; } void KoTextBlockData::clearCounter() { d->partialCounterText.clear(); d->counterPlainText.clear(); d->counterPrefix.clear(); d->counterSuffix.clear(); d->counterSpacing = 0.0; d->counterWidth = 0.0; d->counterIsImage = false; } void KoTextBlockData::setPartialCounterText(const QString &text) { d->partialCounterText = text; } QString KoTextBlockData::partialCounterText() const { return d->partialCounterText; } void KoTextBlockData::setCounterPlainText(const QString &text) { d->counterPlainText = text; } QString KoTextBlockData::counterPlainText() const { return d->counterPlainText; } void KoTextBlockData::setCounterPrefix(const QString &text) { d->counterPrefix = text; } QString KoTextBlockData::counterPrefix() const { return d->counterPrefix; } void KoTextBlockData::setCounterSuffix(const QString &text) { d->counterSuffix = text; } QString KoTextBlockData::counterSuffix() const { return d->counterSuffix; } void KoTextBlockData::setCounterIsImage(bool isImage) { d->counterIsImage = isImage; } bool KoTextBlockData::counterIsImage() const { return d->counterIsImage; } void KoTextBlockData::setCounterIndex(int index) { d->counterIndex = index; } int KoTextBlockData::counterIndex() const { return d->counterIndex; } void KoTextBlockData::setCounterPosition(const QPointF &position) { d->counterPos = position; } QPointF KoTextBlockData::counterPosition() const { return d->counterPos; } void KoTextBlockData::setLabelFormat(const QTextCharFormat &format) { d->labelFormat = format; } QTextCharFormat KoTextBlockData::labelFormat() const { return d->labelFormat; } KoTextBlockBorderData *KoTextBlockData::border() const { return d->border; } void KoTextBlockData::setPaintStrategy(KoTextBlockPaintStrategyBase *paintStrategy) { delete d->paintStrategy; d->paintStrategy = paintStrategy; } KoTextBlockPaintStrategyBase *KoTextBlockData::paintStrategy() const { return d->paintStrategy; } bool KoTextBlockData::saveXmlID() const { - // as suggested by boemann, http://lists.kde.org/?l=calligra-devel&m=132396354701553&w=2 + // as suggested by boemann, https://marc.info/?l=calligra-devel&m=132396354701553&w=2 return d->paintStrategy != 0; } diff --git a/plugins/flake/textshape/kotext/KoVariable.cpp b/plugins/flake/textshape/kotext/KoVariable.cpp index b878eeefb5..c8889797ac 100644 --- a/plugins/flake/textshape/kotext/KoVariable.cpp +++ b/plugins/flake/textshape/kotext/KoVariable.cpp @@ -1,168 +1,168 @@ /* This file is part of the KDE project * Copyright (C) 2006-2009 Thomas Zander * * 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 "KoVariable.h" #include "KoInlineObject_p.h" #include #include #include #include #include #include "TextDebug.h" class KoVariablePrivate : public KoInlineObjectPrivate { public: KoVariablePrivate() : modified(true), document(0), lastPositionInDocument(-1) { } QDebug printDebug(QDebug dbg) const override { dbg.nospace() << "KoVariable value=" << value; return dbg.space(); } QString value; bool modified; const QTextDocument *document; int lastPositionInDocument; }; KoVariable::KoVariable(bool propertyChangeListener) : KoInlineObject(*(new KoVariablePrivate()), propertyChangeListener) { } KoVariable::~KoVariable() { } void KoVariable::setValue(const QString &value) { Q_D(KoVariable); if (d->value == value) return; d->value = value; d->modified = true; if (d->document) { const_cast(d->document)->markContentsDirty(d->lastPositionInDocument, 0); } } void KoVariable::updatePosition(const QTextDocument *document, int posInDocument, const QTextCharFormat & format) { Q_D(KoVariable); if (d->document) { disconnect(d->document, SIGNAL(destroyed()), this, SLOT(documentDestroyed())); } d->document = document; connect(d->document, SIGNAL(destroyed()), this, SLOT(documentDestroyed())); d->lastPositionInDocument = posInDocument; Q_UNUSED(format); // Variables are always 'in place' so the position is 100% defined by the text layout. variableMoved(d->document, posInDocument); } void KoVariable::resize(const QTextDocument *document, QTextInlineObject &object, int posInDocument, const QTextCharFormat &format, QPaintDevice *pd) { Q_D(KoVariable); Q_UNUSED(document); Q_UNUSED(posInDocument); if (d->modified == false) return; if (object.isValid() == false) return; d->modified = true; Q_ASSERT(format.isCharFormat()); QFontMetricsF fm(format.font(), pd); qreal width = qMax(qreal(0.0), fm.width(d->value)); qreal ascent = fm.ascent(); qreal descent = fm.descent(); if (object.width() != width) { object.setWidth(width); } if (object.ascent() != ascent) { object.setAscent(ascent); } if (object.descent() != descent) { object.setDescent(descent); } } void KoVariable::paint(QPainter &painter, QPaintDevice *pd, const QTextDocument *document, const QRectF &rect, const QTextInlineObject &object, int posInDocument, const QTextCharFormat &format) { Q_D(KoVariable); Q_UNUSED(document); Q_UNUSED(posInDocument); // TODO set all the font properties from the format (color etc) QFont font(format.font(), pd); QTextLayout layout(d->value, font, pd); layout.setCacheEnabled(true); QVector layouts; QTextLayout::FormatRange range; range.start = 0; range.length = d->value.length(); range.format = format; layouts.append(range); layout.setFormats(layouts); QTextOption option(Qt::AlignLeft | Qt::AlignAbsolute); if (object.isValid()) { option.setTextDirection(object.textDirection()); } layout.setTextOption(option); layout.beginLayout(); layout.createLine(); layout.endLayout(); layout.draw(&painter, rect.topLeft()); } void KoVariable::variableMoved(const QTextDocument *document, int posInDocument) { Q_UNUSED(document); Q_UNUSED(posInDocument); } QString KoVariable::value() const { Q_D(const KoVariable); return d->value; } int KoVariable::positionInDocument() const { Q_D(const KoVariable); return d->lastPositionInDocument; } void KoVariable::documentDestroyed() { // deleteLater(); does not work when closing a document as the inline object manager is deleted before the control is given back to the event loop // therefore commit suicide. - // See http://www.parashift.com/c++-faq-lite/delete-this.html + // See https://isocpp.org/wiki/faq/freestore-mgmt#delete-this delete(this); } diff --git a/plugins/flake/textshape/kotext/styles/KoCharacterStyle.cpp b/plugins/flake/textshape/kotext/styles/KoCharacterStyle.cpp index 0c82bf4715..bac90fbecd 100644 --- a/plugins/flake/textshape/kotext/styles/KoCharacterStyle.cpp +++ b/plugins/flake/textshape/kotext/styles/KoCharacterStyle.cpp @@ -1,2268 +1,2268 @@ /* This file is part of the KDE project * Copyright (C) 2006-2009 Thomas Zander * Copyright (C) 2007 Sebastian Sauer * Copyright (C) 2008 Thorsten Zachmann * Copyright (C) 2008 Girish Ramakrishnan * Copyright (C) 2011 Stuart Dickson * * 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 "KoCharacterStyle.h" #include "Styles_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoTextDocument.h" #ifdef SHOULD_BUILD_FONT_CONVERSION #include #include #include #include #include FT_FREETYPE_H #include FT_GLYPH_H #include FT_TYPES_H #include FT_OUTLINE_H #include FT_RENDER_H #include FT_TRUETYPE_TABLES_H #include FT_SFNT_NAMES_H #endif #include "TextDebug.h" #include "KoTextDebug.h" #ifdef SHOULD_BUILD_FONT_CONVERSION QMap textScaleMap; #endif //SHOULD_BUILD_FONT_CONVERSION class Q_DECL_HIDDEN KoCharacterStyle::Private { public: Private(); ~Private() { } void setProperty(int key, const QVariant &value) { stylesPrivate.add(key, value); } qreal propertyDouble(int key) const { QVariant variant = stylesPrivate.value(key); if (variant.isNull()) { if (parentStyle) return parentStyle->d->propertyDouble(key); else if (defaultStyle) return defaultStyle->d->propertyDouble(key); return 0.0; } return variant.toDouble(); } int propertyInt(int key) const { QVariant variant = stylesPrivate.value(key); if (variant.isNull()) { if (parentStyle) return parentStyle->d->propertyInt(key); else if (defaultStyle) return defaultStyle->d->propertyInt(key); return 0; } return variant.toInt(); } QString propertyString(int key) const { QVariant variant = stylesPrivate.value(key); if (variant.isNull()) { if (parentStyle) return parentStyle->d->propertyString(key); else if (defaultStyle) return defaultStyle->d->propertyString(key); return QString(); } return qvariant_cast(variant); } bool propertyBoolean(int key) const { QVariant variant = stylesPrivate.value(key); if (variant.isNull()) { if (parentStyle) return parentStyle->d->propertyBoolean(key); else if (defaultStyle) return defaultStyle->d->propertyBoolean(key); return false; } return variant.toBool(); } QColor propertyColor(int key) const { QVariant variant = stylesPrivate.value(key); if (variant.isNull()) { if (parentStyle) return parentStyle->d->propertyColor(key); else if (defaultStyle) return defaultStyle->d->propertyColor(key); return QColor(); } return variant.value(); } // problem with fonts in linux and windows is that true type fonts have more than one metric // they have normal metric placed in font header table // microsoft metric placed in os2 table // apple metric placed in os2 table // ms-word is probably using CreateFontIndirect and GetOutlineTextMetric function to calculate line height // and this functions are using windows gdi environment which is using microsoft font metric placed in os2 table // qt on linux is using normal font metric // this two metrics are different and change from font to font // this font stretch is needed if we want to have exact line height as in ms-word and oo // // font_size * font_stretch = windows_font_height qreal calculateFontYStretch(const QString &fontFamily); StylePrivate hardCodedDefaultStyle; QString name; StylePrivate stylesPrivate; KoCharacterStyle *parentStyle; KoCharacterStyle *defaultStyle; bool m_inUse; }; KoCharacterStyle::Private::Private() : parentStyle(0), defaultStyle(0), m_inUse(false) { //set the minimal default properties hardCodedDefaultStyle.add(QTextFormat::FontFamily, QString("Sans Serif")); hardCodedDefaultStyle.add(QTextFormat::FontPointSize, 12.0); hardCodedDefaultStyle.add(QTextFormat::ForegroundBrush, QBrush(Qt::black)); hardCodedDefaultStyle.add(KoCharacterStyle::FontYStretch, 1); hardCodedDefaultStyle.add(QTextFormat::FontHintingPreference, QFont::PreferNoHinting); } void KoCharacterStyle::ensureMinimalProperties(QTextCharFormat &format) const { if (d->defaultStyle) { QMap props = d->defaultStyle->d->stylesPrivate.properties(); QMap::const_iterator it = props.constBegin(); while (it != props.constEnd()) { // in case there is already a foreground color don't apply the use window font color as then the foreground color // should be used. if (it.key() == KoCharacterStyle::UseWindowFontColor && format.hasProperty(QTextFormat::ForegroundBrush)) { ++it; continue; } // in case there is already a use window font color don't apply the foreground brush as this overwrite the foreground color if (it.key() == QTextFormat::ForegroundBrush && format.hasProperty(KoCharacterStyle::UseWindowFontColor)) { ++it; continue; } if (!it.value().isNull() && !format.hasProperty(it.key())) { format.setProperty(it.key(), it.value()); } ++it; } } QMap props = d->hardCodedDefaultStyle.properties(); QMap::const_iterator it = props.constBegin(); while (it != props.constEnd()) { if (!it.value().isNull() && !format.hasProperty(it.key())) { if (it.key() == QTextFormat::ForegroundBrush && format.hasProperty(KoCharacterStyle::UseWindowFontColor)) { ++it; continue; } format.setProperty(it.key(), it.value()); } ++it; } } qreal KoCharacterStyle::Private::calculateFontYStretch(const QString &fontFamily) { qreal stretch = 1; #ifdef SHOULD_BUILD_FONT_CONVERSION if (textScaleMap.contains(fontFamily)) { return textScaleMap.value(fontFamily); } FcResult result = FcResultMatch; FT_Library library; FT_Face face; int id = 0; int error = 0; QByteArray fontName = fontFamily.toLatin1(); - //TODO http://freedesktop.org/software/fontconfig/fontconfig-devel/x19.html + //TODO https://freedesktop.org/software/fontconfig/fontconfig-devel/x19.html // we should specify slant and weight too FcPattern *font = FcPatternBuild (0, FC_FAMILY, FcTypeString,fontName.data(), FC_SIZE, FcTypeDouble, (qreal)11, 0); if (font == 0) { return 1; } // find font FcPattern *matched = 0; matched = FcFontMatch (0, font, &result); if (matched == 0) { FcPatternDestroy (font); return 1; } // get font family name char * str = 0; result = FcPatternGetString (matched, FC_FAMILY, 0,(FcChar8**) &str); if (result != FcResultMatch || str == 0) { FcPatternDestroy (font); FcPatternDestroy (matched); return 1; } // check if right font was found QByteArray foundFontFamily = QByteArray::fromRawData(str, strlen(str)); if (foundFontFamily != fontName) { FcPatternDestroy (font); FcPatternDestroy (matched); return 1; } // get path to font str = 0; result = FcPatternGetString (matched, FC_FILE, 0,(FcChar8**) &str); if (result != FcResultMatch) { FcPatternDestroy (font); FcPatternDestroy (matched); return 1; } // get index of font inside the font file result = FcPatternGetInteger (matched, FC_INDEX, 0, &id); if (result != FcResultMatch) { FcPatternDestroy (font); FcPatternDestroy (matched); return 1; } // initialize freetype error = FT_Init_FreeType( &library ); if (error) { FcPatternDestroy (font); FcPatternDestroy (matched); return 1; } // get font metric error = FT_New_Face (library,(char *) str, id, &face); if (error) { FT_Done_FreeType(library); FcPatternDestroy (font); FcPatternDestroy (matched); return 1; } // get font metric os2 table TT_OS2 *os2; os2 = (TT_OS2 *) FT_Get_Sfnt_Table (face, ft_sfnt_os2); if(os2 == 0) { FT_Done_Face(face); FT_Done_FreeType(library); FcPatternDestroy (font); FcPatternDestroy (matched); return 1; } // get font metric header table TT_Header *header; header = (TT_Header *) FT_Get_Sfnt_Table (face, ft_sfnt_head); if(header == 0) { FT_Done_Face(face); FT_Done_FreeType(library); FcPatternDestroy (font); FcPatternDestroy (matched); return 1; } // check if the data is valid if (header->Units_Per_EM == 0 || (os2->usWinAscent + os2->usWinDescent) == 0) { FT_Done_Face(face); FT_Done_FreeType(library); FcPatternDestroy (font); FcPatternDestroy (matched); return 1; } // compute font height stretch // font_size * font_stretch = windows_font_height qreal height = os2->usWinAscent + os2->usWinDescent; height = height * (2048 / header->Units_Per_EM); stretch = (1.215 * height)/2500; stretch = (1.15 * height)/2500; // seems a better guess but probably not right FT_Done_Face(face); FT_Done_FreeType(library); FcPatternDestroy (font); FcPatternDestroy (matched); textScaleMap.insert(fontFamily, stretch); #else Q_UNUSED(fontFamily); #endif //SHOULD_BUILD_FONT_CONVERSION return stretch; } KoCharacterStyle::KoCharacterStyle(QObject *parent) : QObject(parent), d(new Private()) { } KoCharacterStyle::KoCharacterStyle(const QTextCharFormat &format, QObject *parent) : QObject(parent), d(new Private()) { copyProperties(format); } KoCharacterStyle::Type KoCharacterStyle::styleType() const { return KoCharacterStyle::CharacterStyle; } void KoCharacterStyle::copyProperties(const KoCharacterStyle *style) { d->stylesPrivate = style->d->stylesPrivate; setName(style->name()); // make sure we emit property change d->parentStyle = style->d->parentStyle; d->defaultStyle = style->d->defaultStyle; } void KoCharacterStyle::copyProperties(const QTextCharFormat &format) { d->stylesPrivate = format.properties(); } KoCharacterStyle *KoCharacterStyle::clone(QObject *parent) const { KoCharacterStyle *newStyle = new KoCharacterStyle(parent); newStyle->copyProperties(this); return newStyle; } KoCharacterStyle::~KoCharacterStyle() { delete d; } void KoCharacterStyle::setDefaultStyle(KoCharacterStyle *defaultStyle) { d->defaultStyle = defaultStyle; } void KoCharacterStyle::setParentStyle(KoCharacterStyle *parent) { d->parentStyle = parent; } KoCharacterStyle *KoCharacterStyle::parentStyle() const { return d->parentStyle; } QPen KoCharacterStyle::textOutline() const { QVariant variant = value(QTextFormat::TextOutline); if (variant.isNull()) { return QPen(Qt::NoPen); } return qvariant_cast(variant); } QBrush KoCharacterStyle::background() const { QVariant variant = value(QTextFormat::BackgroundBrush); if (variant.isNull()) { return QBrush(); } return qvariant_cast(variant); } void KoCharacterStyle::clearBackground() { d->stylesPrivate.remove(QTextCharFormat::BackgroundBrush); } QBrush KoCharacterStyle::foreground() const { QVariant variant = value(QTextFormat::ForegroundBrush); if (variant.isNull()) { return QBrush(); } return qvariant_cast(variant); } void KoCharacterStyle::clearForeground() { d->stylesPrivate.remove(QTextCharFormat::ForegroundBrush); } void KoCharacterStyle::applyStyle(QTextCharFormat &format, bool emitSignal) const { if (d->parentStyle) { d->parentStyle->applyStyle(format); } bool fontSizeSet = false; // if this style has already set size don't apply the relatives const QMap props = d->stylesPrivate.properties(); QMap::const_iterator it = props.begin(); QList clearProperty; while (it != props.end()) { if (!it.value().isNull()) { if (it.key() == KoCharacterStyle::PercentageFontSize && !fontSizeSet) { qreal size = it.value().toDouble() / 100.0; if (format.hasProperty(QTextFormat::FontPointSize)) { size *= format.doubleProperty(QTextFormat::FontPointSize); } else { size *= 12.0; } format.setProperty(QTextFormat::FontPointSize, size); } else if (it.key() == KoCharacterStyle::AdditionalFontSize && !fontSizeSet) { qreal size = it.value().toDouble() / 100.0; if (format.hasProperty(QTextFormat::FontPointSize)) { size += format.doubleProperty(QTextFormat::FontPointSize); } else { size += 12.0; } format.setProperty(QTextFormat::FontPointSize, size); } else if (it.key() == QTextFormat::FontFamily) { if (!props.contains(QTextFormat::FontStyleHint)) { clearProperty.append(QTextFormat::FontStyleHint); } if (!props.contains(QTextFormat::FontFixedPitch)) { clearProperty.append(QTextFormat::FontFixedPitch); } if (!props.contains(KoCharacterStyle::FontCharset)) { clearProperty.append(KoCharacterStyle::FontCharset); } format.setProperty(it.key(), it.value()); } else { debugText << "setProperty" << it.key() << it.value(); format.setProperty(it.key(), it.value()); } if (it.key() == QTextFormat::FontPointSize) { fontSizeSet = true; } if (it.key() == QTextFormat::ForegroundBrush) { clearProperty.append(KoCharacterStyle::UseWindowFontColor); } else if (it.key() == KoCharacterStyle::UseWindowFontColor) { clearProperty.append(QTextFormat::ForegroundBrush); } } ++it; } foreach (int property, clearProperty) { debugText << "clearProperty" << property; format.clearProperty(property); } if (emitSignal) { emit styleApplied(this); d->m_inUse = true; } } KoCharacterStyle *KoCharacterStyle::autoStyle(const QTextCharFormat &format, QTextCharFormat blockCharFormat) const { KoCharacterStyle *autoStyle = new KoCharacterStyle(format); applyStyle(blockCharFormat, false); ensureMinimalProperties(blockCharFormat); autoStyle->removeDuplicates(blockCharFormat); autoStyle->setParentStyle(const_cast(this)); // remove StyleId if it is there as it is not a property of the style itself and will not be written out // so it should not be part of the autostyle. As otherwise it can happen that the StyleId is the only // property left and then we write out an empty style which is unneeded. // we also need to remove the properties of links as they are saved differently autoStyle->d->stylesPrivate.remove(StyleId); autoStyle->d->stylesPrivate.remove(QTextFormat::IsAnchor); autoStyle->d->stylesPrivate.remove(QTextFormat::AnchorHref); autoStyle->d->stylesPrivate.remove(QTextFormat::AnchorName); return autoStyle; } struct FragmentData { FragmentData(const QTextCharFormat &format, int position, int length) : format(format) , position(position) , length(length) {} QTextCharFormat format; int position; int length; }; void KoCharacterStyle::applyStyle(QTextBlock &block) const { QTextCursor cursor(block); QTextCharFormat cf = block.charFormat(); if (!cf.isTableCellFormat()) { cf = KoTextDocument(block.document()).frameCharFormat(); } applyStyle(cf); ensureMinimalProperties(cf); cursor.setBlockCharFormat(cf); // be sure that we keep the InlineInstanceId, anchor information and ChangeTrackerId when applying a style QList fragments; for (QTextBlock::iterator it = block.begin(); it != block.end(); ++it) { QTextFragment currentFragment = it.fragment(); if (currentFragment.isValid()) { QTextCharFormat format(cf); QVariant v = currentFragment.charFormat().property(InlineInstanceId); if (!v.isNull()) { format.setProperty(InlineInstanceId, v); } v = currentFragment.charFormat().property(ChangeTrackerId); if (!v.isNull()) { format.setProperty(ChangeTrackerId, v); } if (currentFragment.charFormat().isAnchor()) { format.setAnchor(true); format.setAnchorHref(currentFragment.charFormat().anchorHref()); } fragments.append(FragmentData(format, currentFragment.position(), currentFragment.length())); } } foreach (const FragmentData &fragment, fragments) { cursor.setPosition(fragment.position); cursor.setPosition(fragment.position + fragment.length, QTextCursor::KeepAnchor); cursor.setCharFormat(fragment.format); } } void KoCharacterStyle::applyStyle(QTextCursor *selection) const { // FIXME below should be done for each frament in the selection QTextCharFormat cf = selection->charFormat(); applyStyle(cf); ensureMinimalProperties(cf); selection->setCharFormat(cf); } void KoCharacterStyle::unapplyStyle(QTextCharFormat &format) const { if (d->parentStyle) d->parentStyle->unapplyStyle(format); QMap props = d->stylesPrivate.properties(); QMap::const_iterator it = props.constBegin(); while (it != props.constEnd()) { if (!it.value().isNull() && it.value() == format.property(it.key())) { format.clearProperty(it.key()); } ++it; } props = d->hardCodedDefaultStyle.properties(); it = props.constBegin(); while (it != props.constEnd()) { if (!it.value().isNull() && !format.hasProperty(it.key())) { format.setProperty(it.key(), it.value()); } ++it; } } bool KoCharacterStyle::isApplied() const { return d->m_inUse; } void KoCharacterStyle::unapplyStyle(QTextBlock &block) const { QTextCursor cursor(block); QTextCharFormat cf = cursor.blockCharFormat(); unapplyStyle(cf); cursor.setBlockCharFormat(cf); if (block.length() == 1) // only the linefeed return; QTextBlock::iterator iter = block.end(); do { --iter; QTextFragment fragment = iter.fragment(); cursor.setPosition(fragment.position() + 1); cf = cursor.charFormat(); unapplyStyle(cf); cursor.setPosition(fragment.position()); cursor.setPosition(fragment.position() + fragment.length(), QTextCursor::KeepAnchor); cursor.setCharFormat(cf); } while (iter != block.begin()); } // OASIS 14.2.29 static void parseOdfLineWidth(const QString &width, KoCharacterStyle::LineWeight &lineWeight, qreal &lineWidth) { lineWidth = 0; lineWeight = KoCharacterStyle::AutoLineWeight; if (width.isEmpty() || width == "auto") lineWeight = KoCharacterStyle::AutoLineWeight; else if (width == "normal") lineWeight = KoCharacterStyle::NormalLineWeight; else if (width == "bold") lineWeight = KoCharacterStyle::BoldLineWeight; else if (width == "thin") lineWeight = KoCharacterStyle::ThinLineWeight; else if (width == "dash") lineWeight = KoCharacterStyle::DashLineWeight; else if (width == "medium") lineWeight = KoCharacterStyle::MediumLineWeight; else if (width == "thick") lineWeight = KoCharacterStyle::ThickLineWeight; else if (width.endsWith('%')) { lineWeight = KoCharacterStyle::PercentLineWeight; lineWidth = width.mid(0, width.length() - 1).toDouble(); } else if (width[width.length()-1].isNumber()) { lineWeight = KoCharacterStyle::LengthLineWeight; lineWidth = width.toDouble(); } else { lineWeight = KoCharacterStyle::LengthLineWeight; lineWidth = KoUnit::parseValue(width); } } // OASIS 14.2.29 static void importOdfLine(const QString &type, const QString &style, KoCharacterStyle::LineStyle &lineStyle, KoCharacterStyle::LineType &lineType) { lineStyle = KoCharacterStyle::NoLineStyle; lineType = KoCharacterStyle::NoLineType; QString fixedType = type; QString fixedStyle = style; if (fixedStyle == "none") fixedType.clear(); else if (fixedType.isEmpty() && !fixedStyle.isEmpty()) fixedType = "single"; else if (!fixedType.isEmpty() && fixedType != "none" && fixedStyle.isEmpty()) { // don't set a style when the type is none fixedStyle = "solid"; } if (fixedType == "single") lineType = KoCharacterStyle::SingleLine; else if (fixedType == "double") lineType = KoCharacterStyle::DoubleLine; if (fixedStyle == "solid") lineStyle = KoCharacterStyle::SolidLine; else if (fixedStyle == "dotted") lineStyle = KoCharacterStyle::DottedLine; else if (fixedStyle == "dash") lineStyle = KoCharacterStyle::DashLine; else if (fixedStyle == "long-dash") lineStyle = KoCharacterStyle::LongDashLine; else if (fixedStyle == "dot-dash") lineStyle = KoCharacterStyle::DotDashLine; else if (fixedStyle == "dot-dot-dash") lineStyle = KoCharacterStyle::DotDotDashLine; else if (fixedStyle == "wave") lineStyle = KoCharacterStyle::WaveLine; } static QString exportOdfLineType(KoCharacterStyle::LineType lineType) { switch (lineType) { case KoCharacterStyle::NoLineType: return "none"; case KoCharacterStyle::SingleLine: return "single"; case KoCharacterStyle::DoubleLine: return "double"; default: return ""; } } static QString exportOdfLineStyle(KoCharacterStyle::LineStyle lineStyle) { switch (lineStyle) { case KoCharacterStyle::NoLineStyle: return "none"; case KoCharacterStyle::SolidLine: return "solid"; case KoCharacterStyle::DottedLine: return "dotted"; case KoCharacterStyle::DashLine: return "dash"; case KoCharacterStyle::LongDashLine: return "long-dash"; case KoCharacterStyle::DotDashLine: return "dot-dash"; case KoCharacterStyle::DotDotDashLine: return "dot-dot-dash"; case KoCharacterStyle::WaveLine: return "wave"; default: return ""; } } static QString exportOdfLineMode(KoCharacterStyle::LineMode lineMode) { switch (lineMode) { case KoCharacterStyle::ContinuousLineMode: return "continuous"; case KoCharacterStyle::SkipWhiteSpaceLineMode: return "skip-white-space"; default: return ""; } } static QString exportOdfLineWidth(KoCharacterStyle::LineWeight lineWeight, qreal lineWidth) { switch (lineWeight) { case KoCharacterStyle::AutoLineWeight: return "auto"; case KoCharacterStyle::NormalLineWeight: return "normal"; case KoCharacterStyle::BoldLineWeight: return "bold"; case KoCharacterStyle::ThinLineWeight: return "thin"; case KoCharacterStyle::DashLineWeight: return "dash"; case KoCharacterStyle::MediumLineWeight: return "medium"; case KoCharacterStyle::ThickLineWeight: return "thick"; case KoCharacterStyle::PercentLineWeight: return QString("%1%").arg(lineWidth); case KoCharacterStyle::LengthLineWeight: return QString("%1pt").arg(lineWidth); default: return QString(); } } static QString exportOdfFontStyleHint(QFont::StyleHint hint) { switch (hint) { case QFont::Serif: return "roman"; case QFont::SansSerif: return "swiss"; case QFont::TypeWriter: return "modern"; case QFont::Decorative: return "decorative"; case QFont::System: return "system"; /*case QFont::Script */ default: return ""; } } void KoCharacterStyle::setFontFamily(const QString &family) { d->setProperty(QTextFormat::FontFamily, family); setFontYStretch(d->calculateFontYStretch(family)); } QString KoCharacterStyle::fontFamily() const { return d->propertyString(QTextFormat::FontFamily); } void KoCharacterStyle::setFontPointSize(qreal size) { d->setProperty(QTextFormat::FontPointSize, size); } void KoCharacterStyle::clearFontPointSize() { d->stylesPrivate.remove(QTextFormat::FontPointSize); } qreal KoCharacterStyle::fontPointSize() const { return d->propertyDouble(QTextFormat::FontPointSize); } void KoCharacterStyle::setFontWeight(int weight) { d->setProperty(QTextFormat::FontWeight, weight); } int KoCharacterStyle::fontWeight() const { return d->propertyInt(QTextFormat::FontWeight); } void KoCharacterStyle::setFontItalic(bool italic) { d->setProperty(QTextFormat::FontItalic, italic); } bool KoCharacterStyle::fontItalic() const { return d->propertyBoolean(QTextFormat::FontItalic); } ///TODO Review legacy fontOverline functions and testing (consider removal) /* void KoCharacterStyle::setFontOverline(bool overline) { d->setProperty(QTextFormat::FontOverline, overline); } bool KoCharacterStyle::fontOverline() const { return d->propertyBoolean(QTextFormat::FontOverline); } */ void KoCharacterStyle::setFontFixedPitch(bool fixedPitch) { d->setProperty(QTextFormat::FontFixedPitch, fixedPitch); } bool KoCharacterStyle::fontFixedPitch() const { return d->propertyBoolean(QTextFormat::FontFixedPitch); } void KoCharacterStyle::setFontStyleHint(QFont::StyleHint styleHint) { d->setProperty(QTextFormat::FontStyleHint, styleHint); } QFont::StyleHint KoCharacterStyle::fontStyleHint() const { return static_cast(d->propertyInt(QTextFormat::FontStyleHint)); } void KoCharacterStyle::setFontKerning(bool enable) { d->setProperty(QTextFormat::FontKerning, enable); } bool KoCharacterStyle::fontKerning() const { return d->propertyBoolean(QTextFormat::FontKerning); } void KoCharacterStyle::setVerticalAlignment(QTextCharFormat::VerticalAlignment alignment) { d->setProperty(QTextFormat::TextVerticalAlignment, alignment); } QTextCharFormat::VerticalAlignment KoCharacterStyle::verticalAlignment() const { return static_cast(d->propertyInt(QTextFormat::TextVerticalAlignment)); } void KoCharacterStyle::setTextOutline(const QPen &pen) { d->setProperty(QTextFormat::TextOutline, pen); } void KoCharacterStyle::setBackground(const QBrush &brush) { d->setProperty(QTextFormat::BackgroundBrush, brush); } void KoCharacterStyle::setForeground(const QBrush &brush) { d->setProperty(QTextFormat::ForegroundBrush, brush); } void KoCharacterStyle::setFontAutoColor(bool use) { d->setProperty(KoCharacterStyle::UseWindowFontColor, use); } QString KoCharacterStyle::name() const { return d->name; } void KoCharacterStyle::setName(const QString &name) { if (name == d->name) return; d->name = name; emit nameChanged(name); } int KoCharacterStyle::styleId() const { return d->propertyInt(StyleId); } void KoCharacterStyle::setStyleId(int id) { d->setProperty(StyleId, id); } QFont KoCharacterStyle::font() const { QFont font; if (d->stylesPrivate.contains(QTextFormat::FontFamily)) font.setFamily(fontFamily()); if (d->stylesPrivate.contains(QTextFormat::FontPointSize)) font.setPointSizeF(fontPointSize()); if (d->stylesPrivate.contains(QTextFormat::FontWeight)) font.setWeight(fontWeight()); if (d->stylesPrivate.contains(QTextFormat::FontItalic)) font.setItalic(fontItalic()); return font; } void KoCharacterStyle::setHasHyphenation(bool on) { d->setProperty(HasHyphenation, on); } bool KoCharacterStyle::hasHyphenation() const { return d->propertyBoolean(HasHyphenation); } void KoCharacterStyle::setHyphenationPushCharCount(int count) { if (count > 0) d->setProperty(HyphenationPushCharCount, count); else d->stylesPrivate.remove(HyphenationPushCharCount); } int KoCharacterStyle::hyphenationPushCharCount() const { if (hasProperty(HyphenationPushCharCount)) return d->propertyInt(HyphenationPushCharCount); return 0; } void KoCharacterStyle::setHyphenationRemainCharCount(int count) { if (count > 0) d->setProperty(HyphenationRemainCharCount, count); else d->stylesPrivate.remove(HyphenationRemainCharCount); } int KoCharacterStyle::hyphenationRemainCharCount() const { if (hasProperty(HyphenationRemainCharCount)) return d->propertyInt(HyphenationRemainCharCount); return 0; } void KoCharacterStyle::setStrikeOutStyle(KoCharacterStyle::LineStyle strikeOut) { d->setProperty(StrikeOutStyle, strikeOut); } KoCharacterStyle::LineStyle KoCharacterStyle::strikeOutStyle() const { return (KoCharacterStyle::LineStyle) d->propertyInt(StrikeOutStyle); } void KoCharacterStyle::setStrikeOutType(LineType lineType) { d->setProperty(StrikeOutType, lineType); } KoCharacterStyle::LineType KoCharacterStyle::strikeOutType() const { return (KoCharacterStyle::LineType) d->propertyInt(StrikeOutType); } void KoCharacterStyle::setStrikeOutColor(const QColor &color) { d->setProperty(StrikeOutColor, color); } QColor KoCharacterStyle::strikeOutColor() const { return d->propertyColor(StrikeOutColor); } void KoCharacterStyle::setStrikeOutWidth(LineWeight weight, qreal width) { d->setProperty(KoCharacterStyle::StrikeOutWeight, weight); d->setProperty(KoCharacterStyle::StrikeOutWidth, width); } void KoCharacterStyle::strikeOutWidth(LineWeight &weight, qreal &width) const { weight = (KoCharacterStyle::LineWeight) d->propertyInt(KoCharacterStyle::StrikeOutWeight); width = d->propertyDouble(KoCharacterStyle::StrikeOutWidth); } void KoCharacterStyle::setStrikeOutMode(LineMode lineMode) { d->setProperty(StrikeOutMode, lineMode); } void KoCharacterStyle::setStrikeOutText(const QString &text) { d->setProperty(StrikeOutText, text); } QString KoCharacterStyle::strikeOutText() const { return d->propertyString(StrikeOutText); } KoCharacterStyle::LineMode KoCharacterStyle::strikeOutMode() const { return (KoCharacterStyle::LineMode) d->propertyInt(StrikeOutMode); } void KoCharacterStyle::setOverlineStyle(KoCharacterStyle::LineStyle overline) { d->setProperty(OverlineStyle, overline); } KoCharacterStyle::LineStyle KoCharacterStyle::overlineStyle() const { return (KoCharacterStyle::LineStyle) d->propertyInt(OverlineStyle); } void KoCharacterStyle::setOverlineType(LineType lineType) { d->setProperty(OverlineType, lineType); } KoCharacterStyle::LineType KoCharacterStyle::overlineType() const { return (KoCharacterStyle::LineType) d->propertyInt(OverlineType); } void KoCharacterStyle::setOverlineColor(const QColor &color) { d->setProperty(KoCharacterStyle::OverlineColor, color); } QColor KoCharacterStyle::overlineColor() const { return d->propertyColor(KoCharacterStyle::OverlineColor); } void KoCharacterStyle::setOverlineWidth(LineWeight weight, qreal width) { d->setProperty(KoCharacterStyle::OverlineWeight, weight); d->setProperty(KoCharacterStyle::OverlineWidth, width); } void KoCharacterStyle::overlineWidth(LineWeight &weight, qreal &width) const { weight = (KoCharacterStyle::LineWeight) d->propertyInt(KoCharacterStyle::OverlineWeight); width = d->propertyDouble(KoCharacterStyle::OverlineWidth); } void KoCharacterStyle::setOverlineMode(LineMode mode) { d->setProperty(KoCharacterStyle::OverlineMode, mode); } KoCharacterStyle::LineMode KoCharacterStyle::overlineMode() const { return static_cast(d->propertyInt(KoCharacterStyle::OverlineMode)); } void KoCharacterStyle::setUnderlineStyle(KoCharacterStyle::LineStyle underline) { d->setProperty(UnderlineStyle, underline); } KoCharacterStyle::LineStyle KoCharacterStyle::underlineStyle() const { return (KoCharacterStyle::LineStyle) d->propertyInt(UnderlineStyle); } void KoCharacterStyle::setUnderlineType(LineType lineType) { d->setProperty(UnderlineType, lineType); } KoCharacterStyle::LineType KoCharacterStyle::underlineType() const { return (KoCharacterStyle::LineType) d->propertyInt(UnderlineType); } void KoCharacterStyle::setUnderlineColor(const QColor &color) { d->setProperty(QTextFormat::TextUnderlineColor, color); } QColor KoCharacterStyle::underlineColor() const { return d->propertyColor(QTextFormat::TextUnderlineColor); } void KoCharacterStyle::setUnderlineWidth(LineWeight weight, qreal width) { d->setProperty(KoCharacterStyle::UnderlineWeight, weight); d->setProperty(KoCharacterStyle::UnderlineWidth, width); } void KoCharacterStyle::underlineWidth(LineWeight &weight, qreal &width) const { weight = (KoCharacterStyle::LineWeight) d->propertyInt(KoCharacterStyle::UnderlineWeight); width = d->propertyDouble(KoCharacterStyle::UnderlineWidth); } void KoCharacterStyle::setUnderlineMode(LineMode mode) { d->setProperty(KoCharacterStyle::UnderlineMode, mode); } KoCharacterStyle::LineMode KoCharacterStyle::underlineMode() const { return static_cast(d->propertyInt(KoCharacterStyle::UnderlineMode)); } void KoCharacterStyle::setFontLetterSpacing(qreal spacing) { d->setProperty(KoCharacterStyle::FontLetterSpacing, spacing); } qreal KoCharacterStyle::fontLetterSpacing() const { return d->propertyDouble(KoCharacterStyle::FontLetterSpacing); } void KoCharacterStyle::setFontWordSpacing(qreal spacing) { d->setProperty(QTextCharFormat::FontWordSpacing, spacing); } qreal KoCharacterStyle::fontWordSpacing() const { return d->propertyDouble(QTextCharFormat::FontWordSpacing); } void KoCharacterStyle::setFontCapitalization(QFont::Capitalization capitalization) { d->setProperty(QTextFormat::FontCapitalization, capitalization); } QFont::Capitalization KoCharacterStyle::fontCapitalization() const { return (QFont::Capitalization) d->propertyInt(QTextFormat::FontCapitalization); } void KoCharacterStyle::setFontYStretch(qreal stretch) { d->setProperty(KoCharacterStyle::FontYStretch, stretch); } qreal KoCharacterStyle::fontYStretch() const { return d->propertyDouble(KoCharacterStyle::FontYStretch); } void KoCharacterStyle::setCountry(const QString &country) { if (country.isEmpty()) d->stylesPrivate.remove(KoCharacterStyle::Country); else d->setProperty(KoCharacterStyle::Country, country); } void KoCharacterStyle::setLanguage(const QString &language) { if (language.isEmpty()) d->stylesPrivate.remove(KoCharacterStyle::Language); else d->setProperty(KoCharacterStyle::Language, language); } QString KoCharacterStyle::country() const { return value(KoCharacterStyle::Country).toString(); } QString KoCharacterStyle::language() const { return d->propertyString(KoCharacterStyle::Language); } bool KoCharacterStyle::blinking() const { return d->propertyBoolean(Blink); } void KoCharacterStyle::setBlinking(bool blink) { d->setProperty(KoCharacterStyle::Blink, blink); } bool KoCharacterStyle::hasProperty(int key) const { return d->stylesPrivate.contains(key); } static QString rotationScaleToString(KoCharacterStyle::RotationScale rotationScale) { QString scale = "line-height"; if (rotationScale == KoCharacterStyle::Fixed) { scale = "fixed"; } return scale; } static KoCharacterStyle::RotationScale stringToRotationScale(const QString &scale) { KoCharacterStyle::RotationScale rotationScale = KoCharacterStyle::LineHeight; if (scale == "fixed") { rotationScale = KoCharacterStyle::Fixed; } return rotationScale; } void KoCharacterStyle::setTextRotationAngle(qreal angle) { d->setProperty(TextRotationAngle, angle); } qreal KoCharacterStyle::textRotationAngle() const { return d->propertyDouble(TextRotationAngle); } void KoCharacterStyle::setTextRotationScale(RotationScale scale) { d->setProperty(TextRotationScale, rotationScaleToString(scale)); } KoCharacterStyle::RotationScale KoCharacterStyle::textRotationScale() const { return stringToRotationScale(d->propertyString(TextRotationScale)); } void KoCharacterStyle::setTextScale(int scale) { d->setProperty(TextScale, scale); } int KoCharacterStyle::textScale() const { return d->propertyInt(TextScale); } void KoCharacterStyle::setTextShadow(const KoShadowStyle& shadow) { d->setProperty(TextShadow, qVariantFromValue(shadow)); } KoShadowStyle KoCharacterStyle::textShadow() const { if (hasProperty(TextShadow)) { QVariant shadow = value(TextShadow); if (shadow.canConvert()) return shadow.value(); } return KoShadowStyle(); } void KoCharacterStyle::setTextCombine(KoCharacterStyle::TextCombineType type) { d->setProperty(TextCombine, type); } KoCharacterStyle::TextCombineType KoCharacterStyle::textCombine() const { if (hasProperty(TextCombine)) { return (KoCharacterStyle::TextCombineType) d->propertyInt(TextCombine); } return NoTextCombine; } QChar KoCharacterStyle::textCombineEndChar() const { if (hasProperty(TextCombineEndChar)) { QString val = d->propertyString(TextCombineEndChar); if (val.length() > 0) return val.at(0); } return QChar(); } void KoCharacterStyle::setTextCombineEndChar(const QChar& character) { d->setProperty(TextCombineEndChar, character); } QChar KoCharacterStyle::textCombineStartChar() const { if (hasProperty(TextCombineStartChar)) { QString val = d->propertyString(TextCombineStartChar); if (val.length() > 0) return val.at(0); } return QChar(); } void KoCharacterStyle::setTextCombineStartChar(const QChar& character) { d->setProperty(TextCombineStartChar, character); } void KoCharacterStyle::setFontRelief(KoCharacterStyle::ReliefType relief) { d->setProperty(FontRelief, relief); } KoCharacterStyle::ReliefType KoCharacterStyle::fontRelief() const { if (hasProperty(FontRelief)) return (KoCharacterStyle::ReliefType) d->propertyInt(FontRelief); return KoCharacterStyle::NoRelief; } KoCharacterStyle::EmphasisPosition KoCharacterStyle::textEmphasizePosition() const { if (hasProperty(TextEmphasizePosition)) return (KoCharacterStyle::EmphasisPosition) d->propertyInt(TextEmphasizePosition); return KoCharacterStyle::EmphasisAbove; } void KoCharacterStyle::setTextEmphasizePosition(KoCharacterStyle::EmphasisPosition position) { d->setProperty(TextEmphasizePosition, position); } KoCharacterStyle::EmphasisStyle KoCharacterStyle::textEmphasizeStyle() const { if (hasProperty(TextEmphasizeStyle)) return (KoCharacterStyle::EmphasisStyle) d->propertyInt(TextEmphasizeStyle); return KoCharacterStyle::NoEmphasis; } void KoCharacterStyle::setTextEmphasizeStyle(KoCharacterStyle::EmphasisStyle emphasis) { d->setProperty(TextEmphasizeStyle, emphasis); } void KoCharacterStyle::setPercentageFontSize(qreal percent) { d->setProperty(KoCharacterStyle::PercentageFontSize, percent); } qreal KoCharacterStyle::percentageFontSize() const { return d->propertyDouble(KoCharacterStyle::PercentageFontSize); } void KoCharacterStyle::setAdditionalFontSize(qreal percent) { d->setProperty(KoCharacterStyle::AdditionalFontSize, percent); } qreal KoCharacterStyle::additionalFontSize() const { return d->propertyDouble(KoCharacterStyle::AdditionalFontSize); } void KoCharacterStyle::loadOdf(const KoXmlElement *element, KoShapeLoadingContext &scontext, bool loadParents) { KoOdfLoadingContext &context = scontext.odfLoadingContext(); const QString name(element->attributeNS(KoXmlNS::style, "display-name", QString())); if (!name.isEmpty()) { d->name = name; } else { d->name = element->attributeNS(KoXmlNS::style, "name", QString()); } QString family = element->attributeNS(KoXmlNS::style, "family", "text"); context.styleStack().save(); if (loadParents) { context.addStyles(element, family.toLocal8Bit().constData()); // Load all parent } else { context.styleStack().push(*element); } context.styleStack().setTypeProperties("text"); // load the style:text-properties loadOdfProperties(scontext); context.styleStack().restore(); } void KoCharacterStyle::loadOdfProperties(KoShapeLoadingContext &scontext) { KoStyleStack &styleStack = scontext.odfLoadingContext().styleStack(); d->stylesPrivate = StylePrivate(); // The fo:color attribute specifies the foreground color of text. const QString color(styleStack.property(KoXmlNS::fo, "color")); if (!color.isEmpty()) { QColor c(color); if (c.isValid()) { // 3.10.3 setForeground(QBrush(c)); } } QString fontName(styleStack.property(KoXmlNS::fo, "font-family")); if (!fontName.isEmpty()) { // Specify whether a font has a fixed or variable width. // These attributes are ignored if there is no corresponding fo:font-family attribute attached to the same formatting properties element. const QString fontPitch(styleStack.property(KoXmlNS::style, "font-pitch")); if (!fontPitch.isEmpty()) { setFontFixedPitch(fontPitch == "fixed"); } const QString genericFamily(styleStack.property(KoXmlNS::style, "font-family-generic")); if (!genericFamily.isEmpty()) { if (genericFamily == "roman") setFontStyleHint(QFont::Serif); else if (genericFamily == "swiss") setFontStyleHint(QFont::SansSerif); else if (genericFamily == "modern") setFontStyleHint(QFont::TypeWriter); else if (genericFamily == "decorative") setFontStyleHint(QFont::Decorative); else if (genericFamily == "system") setFontStyleHint(QFont::System); else if (genericFamily == "script") { ; // TODO: no hint available in Qt yet, we should at least store it as a property internally! } } const QString fontCharset(styleStack.property(KoXmlNS::style, "font-charset")); if (!fontCharset.isEmpty()) { // this property is not required by Qt, since Qt auto selects the right font based on the text // The only charset of interest to us is x-symbol - this should disable spell checking d->setProperty(KoCharacterStyle::FontCharset, fontCharset); } } const QString fontFamily(styleStack.property(KoXmlNS::style, "font-family")); if (!fontFamily.isEmpty()) fontName = fontFamily; if (styleStack.hasProperty(KoXmlNS::style, "font-name")) { // This font name is a reference to a font face declaration. KoOdfStylesReader &stylesReader = scontext.odfLoadingContext().stylesReader(); const KoXmlElement *fontFace = stylesReader.findStyle(styleStack.property(KoXmlNS::style, "font-name")); if (fontFace != 0) { fontName = fontFace->attributeNS(KoXmlNS::svg, "font-family", ""); KoXmlElement fontFaceElem; forEachElement(fontFaceElem, (*fontFace)) { if (fontFaceElem.tagName() == "font-face-src") { KoXmlElement fontUriElem; forEachElement(fontUriElem, fontFaceElem) { if (fontUriElem.tagName() == "font-face-uri") { QString filename = fontUriElem.attributeNS(KoXmlNS::xlink, "href"); KoStore *store = scontext.odfLoadingContext().store(); if (store->open(filename)) { KoStoreDevice device(store); QByteArray data = device.readAll(); if (device.open(QIODevice::ReadOnly)) { QFontDatabase::addApplicationFontFromData(data); } } } } } } } } if (!fontName.isEmpty()) { // Hmm, the remove "'" could break it's in the middle of the fontname... fontName = fontName.remove('\''); // 'Thorndale' is not known outside OpenOffice so we substitute it // with 'Times New Roman' that looks nearly the same. if (fontName == "Thorndale") fontName = "Times New Roman"; // 'StarSymbol' is written by OpenOffice but they actually mean // 'OpenSymbol'. if (fontName == "StarSymbol") fontName = "OpenSymbol"; fontName.remove(QRegExp("\\sCE$")); // Arial CE -> Arial setFontFamily(fontName); } // Specify the size of a font. The value of these attribute is either an absolute length or a percentage if (styleStack.hasProperty(KoXmlNS::fo, "font-size")) { const QString fontSize(styleStack.property(KoXmlNS::fo, "font-size")); if (!fontSize.isEmpty()) { if (fontSize.endsWith('%')) { setPercentageFontSize(fontSize.left(fontSize.length() - 1).toDouble()); } else { setFontPointSize(KoUnit::parseValue(fontSize)); } } } else { const QString fontSizeRel(styleStack.property(KoXmlNS::style, "font-size-rel")); if (!fontSizeRel.isEmpty()) { setAdditionalFontSize(KoUnit::parseValue(fontSizeRel)); } } // Specify the weight of a font. The permitted values are normal, bold, and numeric values 100-900, in steps of 100. Unsupported numerical values are rounded off to the next supported value. const QString fontWeight(styleStack.property(KoXmlNS::fo, "font-weight")); if (!fontWeight.isEmpty()) { // 3.10.24 int boldness; if (fontWeight == "normal") boldness = 50; else if (fontWeight == "bold") boldness = 75; else // XSL/CSS has 100,200,300...900. Not the same scale as Qt! // See http://www.w3.org/TR/2001/REC-xsl-20011015/slice7.html#font-weight boldness = fontWeight.toInt() / 10; setFontWeight(boldness); } // Specify whether to use normal or italic font face. const QString fontStyle(styleStack.property(KoXmlNS::fo, "font-style" )); if (!fontStyle.isEmpty()) { // 3.10.19 if (fontStyle == "italic" || fontStyle == "oblique") { // no difference in kotext setFontItalic(true); } else { setFontItalic(false); } } //TODO #if 0 d->m_bWordByWord = styleStack.property(KoXmlNS::style, "text-underline-mode") == "skip-white-space"; // TODO style:text-line-through-mode /* // OO compat code, to move to OO import filter d->m_bWordByWord = (styleStack.hasProperty( KoXmlNS::fo, "score-spaces")) // 3.10.25 && (styleStack.property( KoXmlNS::fo, "score-spaces") == "false"); if( styleStack.hasProperty( KoXmlNS::style, "text-crossing-out" )) { // 3.10.6 QString strikeOutType = styleStack.property( KoXmlNS::style, "text-crossing-out" ); if( strikeOutType =="double-line") m_strikeOutType = S_DOUBLE; else if( strikeOutType =="single-line") m_strikeOutType = S_SIMPLE; else if( strikeOutType =="thick-line") m_strikeOutType = S_SIMPLE_BOLD; // not supported by Words: "slash" and "X" // not supported by OO: stylelines (solid, dash, dot, dashdot, dashdotdot) } */ #endif // overline modes const QString textOverlineMode(styleStack.property( KoXmlNS::style, "text-overline-mode")); if (!textOverlineMode.isEmpty()) { if (textOverlineMode == "skip-white-space") { setOverlineMode(SkipWhiteSpaceLineMode); } else if (textOverlineMode == "continuous") { setOverlineMode(ContinuousLineMode); } } // Specifies whether text is overlined, and if so, whether a single or qreal line will be used for overlining. const QString textOverlineType(styleStack.property(KoXmlNS::style, "text-overline-type")); const QString textOverlineStyle(styleStack.property(KoXmlNS::style, "text-overline-style")); if (!textOverlineType.isEmpty() || !textOverlineStyle.isEmpty()) { // OASIS 14.4.28 LineStyle overlineStyle; LineType overlineType; importOdfLine(textOverlineType, textOverlineStyle, overlineStyle, overlineType); setOverlineStyle(overlineStyle); setOverlineType(overlineType); } const QString textOverlineWidth(styleStack.property(KoXmlNS::style, "text-overline-width")); if (!textOverlineWidth.isEmpty()) { qreal overlineWidth; LineWeight overlineWeight; parseOdfLineWidth(textOverlineWidth, overlineWeight, overlineWidth); setOverlineWidth(overlineWeight, overlineWidth); } // Specifies the color that is used to overline text. The value of this attribute is either font-color or a color. If the value is font-color, the current text color is used for overlining. QString overLineColor = styleStack.property(KoXmlNS::style, "text-overline-color"); // OO 3.10.23, OASIS 14.4.31 if (!overLineColor.isEmpty() && overLineColor != "font-color") { setOverlineColor(QColor(overLineColor)); } else if (overLineColor == "font-color") { setOverlineColor(QColor()); } // underline modes const QString textUndelineMode(styleStack.property( KoXmlNS::style, "text-underline-mode")); if (!textUndelineMode.isEmpty()) { if (textUndelineMode == "skip-white-space") { setUnderlineMode(SkipWhiteSpaceLineMode); } else if (textUndelineMode == "continuous") { setUnderlineMode(ContinuousLineMode); } } // Specifies whether text is underlined, and if so, whether a single or qreal line will be used for underlining. const QString textUnderlineType(styleStack.property(KoXmlNS::style, "text-underline-type")); const QString textUnderlineStyle(styleStack.property(KoXmlNS::style, "text-underline-style")); if (!textUnderlineType.isEmpty() || !textUnderlineStyle.isEmpty()) { // OASIS 14.4.28 LineStyle underlineStyle; LineType underlineType; importOdfLine(textUnderlineType, textUnderlineStyle, underlineStyle, underlineType); setUnderlineStyle(underlineStyle); setUnderlineType(underlineType); } const QString textUnderlineWidth(styleStack.property(KoXmlNS::style, "text-underline-width")); if (!textUnderlineWidth.isEmpty()) { qreal underlineWidth; LineWeight underlineWeight; parseOdfLineWidth(textUnderlineWidth, underlineWeight, underlineWidth); setUnderlineWidth(underlineWeight, underlineWidth); } // Specifies the color that is used to underline text. The value of this attribute is either font-color or a color. If the value is font-color, the current text color is used for underlining. QString underLineColor = styleStack.property(KoXmlNS::style, "text-underline-color"); // OO 3.10.23, OASIS 14.4.31 if (!underLineColor.isEmpty() && underLineColor != "font-color") { setUnderlineColor(QColor(underLineColor)); } else if (underLineColor == "font-color") { setUnderlineColor(QColor()); } const QString textLineThroughType(styleStack.property(KoXmlNS::style, "text-line-through-type")); const QString textLineThroughStyle(styleStack.property(KoXmlNS::style, "text-line-through-style")); if (!textLineThroughType.isEmpty() || !textLineThroughStyle.isEmpty()) { // OASIS 14.4.7 KoCharacterStyle::LineStyle throughStyle; LineType throughType; importOdfLine(textLineThroughType,textLineThroughStyle, throughStyle, throughType); setStrikeOutStyle(throughStyle); setStrikeOutType(throughType); const QString textLineThroughText(styleStack.property(KoXmlNS::style, "text-line-through-text")); if (!textLineThroughText.isEmpty()) { setStrikeOutText(textLineThroughText); } } const QString textLineThroughWidth(styleStack.property(KoXmlNS::style, "text-line-through-width")); if (!textLineThroughWidth.isEmpty()) { qreal throughWidth; LineWeight throughWeight; parseOdfLineWidth(textLineThroughWidth, throughWeight, throughWidth); setStrikeOutWidth(throughWeight, throughWidth); } const QString lineThroughColor(styleStack.property(KoXmlNS::style, "text-line-through-color")); // OO 3.10.23, OASIS 14.4.31 if (!lineThroughColor.isEmpty() && lineThroughColor != "font-color") { setStrikeOutColor(QColor(lineThroughColor)); } const QString lineThroughMode(styleStack.property(KoXmlNS::style, "text-line-through-mode")); if (lineThroughMode == "continuous") { setStrikeOutMode(ContinuousLineMode); } else if (lineThroughMode == "skip-white-space") { setStrikeOutMode(SkipWhiteSpaceLineMode); } const QString textPosition(styleStack.property(KoXmlNS::style, "text-position")); if (!textPosition.isEmpty()) { // OO 3.10.7 if (textPosition.startsWith("super")) setVerticalAlignment(QTextCharFormat::AlignSuperScript); else if (textPosition.startsWith("sub")) setVerticalAlignment(QTextCharFormat::AlignSubScript); else { QRegExp re("(-?[\\d.]+)%.*"); if (re.exactMatch(textPosition)) { int percent = re.capturedTexts()[1].toInt(); if (percent > 0) setVerticalAlignment(QTextCharFormat::AlignSuperScript); else if (percent < 0) setVerticalAlignment(QTextCharFormat::AlignSubScript); else // set explicit to overwrite inherited text-position's setVerticalAlignment(QTextCharFormat::AlignNormal); } } } // The fo:font-variant attribute provides the option to display text as small capitalized letters. const QString textVariant(styleStack.property(KoXmlNS::fo, "font-variant")); if (!textVariant.isEmpty()) { if (textVariant == "small-caps") setFontCapitalization(QFont::SmallCaps); else if (textVariant == "normal") setFontCapitalization(QFont::MixedCase); } // The fo:text-transform attribute specifies text transformations to uppercase, lowercase, and capitalization. else { const QString textTransform(styleStack.property(KoXmlNS::fo, "text-transform")); if (!textTransform.isEmpty()) { if (textTransform == "uppercase") setFontCapitalization(QFont::AllUppercase); else if (textTransform == "lowercase") setFontCapitalization(QFont::AllLowercase); else if (textTransform == "capitalize") setFontCapitalization(QFont::Capitalize); else if (textTransform == "none") setFontCapitalization(QFont::MixedCase); } } const QString foLanguage(styleStack.property(KoXmlNS::fo, "language")); if (!foLanguage.isEmpty()) { setLanguage(foLanguage); } const QString foCountry(styleStack.property(KoXmlNS::fo, "country")); if (!foCountry.isEmpty()) { setCountry(foCountry); } // The fo:background-color attribute specifies the background color of a paragraph. const QString bgcolor(styleStack.property(KoXmlNS::fo, "background-color")); if (!bgcolor.isEmpty()) { QBrush brush = background(); if (bgcolor == "transparent") brush.setStyle(Qt::NoBrush); else { if (brush.style() == Qt::NoBrush) brush.setStyle(Qt::SolidPattern); brush.setColor(bgcolor); // #rrggbb format } setBackground(brush); } // The style:use-window-font-color attribute specifies whether or not the window foreground color should be as used as the foreground color for a light background color and white for a dark background color. const QString useWindowFont(styleStack.property(KoXmlNS::style, "use-window-font-color")); if (!useWindowFont.isEmpty()) { setFontAutoColor(useWindowFont == "true"); } const QString letterKerning(styleStack.property( KoXmlNS::style, "letter-kerning")); if (!letterKerning.isEmpty()) { setFontKerning(letterKerning == "true"); } const QString letterSpacing(styleStack.property(KoXmlNS::fo, "letter-spacing")); if ((!letterSpacing.isEmpty()) && (letterSpacing != "normal")) { qreal space = KoUnit::parseValue(letterSpacing); setFontLetterSpacing(space); } const QString textOutline(styleStack.property(KoXmlNS::style, "text-outline")); if (!textOutline.isEmpty()) { if (textOutline == "true") { setTextOutline(QPen((foreground().style() != Qt::NoBrush)?foreground():QBrush(Qt::black) , 0)); setForeground(Qt::transparent); } else { setTextOutline(QPen(Qt::NoPen)); } } const QString textRotationAngle(styleStack.property(KoXmlNS::style, "text-rotation-angle")); if (!textRotationAngle.isEmpty()) { setTextRotationAngle(KoUnit::parseAngle(textRotationAngle)); } const QString textRotationScale(styleStack.property(KoXmlNS::style, "text-rotation-scale")); if (!textRotationScale.isEmpty()) { setTextRotationScale(stringToRotationScale(textRotationScale)); } const QString textScale(styleStack.property(KoXmlNS::style, "text-scale")); if (!textScale.isEmpty()) { const int scale = (textScale.endsWith('%') ? textScale.left(textScale.length()-1) : textScale).toInt(); setTextScale(scale); } const QString textShadow(styleStack.property(KoXmlNS::fo, "text-shadow")); if (!textShadow.isEmpty()) { KoShadowStyle shadow; if (shadow.loadOdf(textShadow)) setTextShadow(shadow); } const QString textCombine(styleStack.property(KoXmlNS::style, "text-combine")); if (!textCombine.isEmpty()) { if (textCombine == "letters") setTextCombine(TextCombineLetters); else if (textCombine == "lines") setTextCombine(TextCombineLines); else if (textCombine == "none") setTextCombine(NoTextCombine); } const QString textCombineEndChar(styleStack.property(KoXmlNS::style, "text-combine-end-char")); if (!textCombineEndChar.isEmpty()) { setTextCombineEndChar(textCombineEndChar.at(0)); } const QString textCombineStartChar(styleStack.property(KoXmlNS::style, "text-combine-start-char")); if (!textCombineStartChar.isEmpty()) { setTextCombineStartChar(textCombineStartChar.at(0)); } const QString fontRelief(styleStack.property(KoXmlNS::style, "font-relief")); if (!fontRelief.isEmpty()) { if (fontRelief == "none") setFontRelief(KoCharacterStyle::NoRelief); else if (fontRelief == "embossed") setFontRelief(KoCharacterStyle::Embossed); else if (fontRelief == "engraved") setFontRelief(KoCharacterStyle::Engraved); } const QString fontEmphasize(styleStack.property(KoXmlNS::style, "text-emphasize")); if (!fontEmphasize.isEmpty()) { QString style, position; QStringList parts = fontEmphasize.split(' '); style = parts[0]; if (parts.length() > 1) position = parts[1]; if (style == "none") { setTextEmphasizeStyle(NoEmphasis); } else if (style == "accent") { setTextEmphasizeStyle(AccentEmphasis); } else if (style == "circle") { setTextEmphasizeStyle(CircleEmphasis); } else if (style == "disc") { setTextEmphasizeStyle(DiscEmphasis); } else if (style == "dot") { setTextEmphasizeStyle(DotEmphasis); } if (position == "below") { setTextEmphasizePosition(EmphasisBelow); } else if (position == "above") { setTextEmphasizePosition(EmphasisAbove); } } if (styleStack.hasProperty(KoXmlNS::fo, "hyphenate")) setHasHyphenation(styleStack.property(KoXmlNS::fo, "hyphenate") == "true"); if (styleStack.hasProperty(KoXmlNS::fo, "hyphenation-remain-char-count")) { bool ok = false; int count = styleStack.property(KoXmlNS::fo, "hyphenation-remain-char-count").toInt(&ok); if (ok) setHyphenationRemainCharCount(count); } if (styleStack.hasProperty(KoXmlNS::fo, "hyphenation-push-char-count")) { bool ok = false; int count = styleStack.property(KoXmlNS::fo, "hyphenation-push-char-count").toInt(&ok); if (ok) setHyphenationPushCharCount(count); } if (styleStack.hasProperty(KoXmlNS::style, "text-blinking")) { setBlinking(styleStack.property(KoXmlNS::style, "text-blinking") == "true"); } //TODO #if 0 /* Missing properties: style:font-style-name, 3.10.11 - can be ignored, says DV, the other ways to specify a font are more precise fo:letter-spacing, 3.10.16 - not implemented in kotext style:text-relief, 3.10.20 - not implemented in kotext style:text-blinking, 3.10.27 - not implemented in kotext IIRC style:text-combine, 3.10.29/30 - not implemented, see http://www.w3.org/TR/WD-i18n-format/ style:text-emphasis, 3.10.31 - not implemented in kotext style:text-scale, 3.10.33 - not implemented in kotext style:text-rotation-angle, 3.10.34 - not implemented in kotext (kpr rotates whole objects) style:text-rotation-scale, 3.10.35 - not implemented in kotext (kpr rotates whole objects) style:punctuation-wrap, 3.10.36 - not implemented in kotext */ d->m_underLineWidth = 1.0; generateKey(); addRef(); #endif } bool KoCharacterStyle::operator==(const KoCharacterStyle &other) const { return compareCharacterProperties(other); } bool KoCharacterStyle::operator!=(const KoCharacterStyle &other) const { return !compareCharacterProperties(other); } bool KoCharacterStyle::compareCharacterProperties(const KoCharacterStyle &other) const { return other.d->stylesPrivate == d->stylesPrivate; } void KoCharacterStyle::removeDuplicates(const KoCharacterStyle &other) { // In case the current style doesn't have the flag UseWindowFontColor set but the other has it set and they use the same color // remove duplicates will remove the color. However to make it work correctly we need to store the color with the style so it // will be loaded again. We don't store a use-window-font-color="false" as that is not compatible to the way OO/LO does work. // So save the color and restore it after the remove duplicates QBrush brush; if (other.d->propertyBoolean(KoCharacterStyle::UseWindowFontColor) && !d->propertyBoolean(KoCharacterStyle::UseWindowFontColor)) { brush = foreground(); } // this properties should need to be kept if there is a font family defined as these are only evaluated if there is also a font family int keepProperties[] = { QTextFormat::FontStyleHint, QTextFormat::FontFixedPitch, KoCharacterStyle::FontCharset }; QMap keep; for (unsigned int i = 0; i < sizeof(keepProperties)/sizeof(*keepProperties); ++i) { if (hasProperty(keepProperties[i])) { keep.insert(keepProperties[i], value(keepProperties[i])); } } this->d->stylesPrivate.removeDuplicates(other.d->stylesPrivate); if (brush.style() != Qt::NoBrush) { setForeground(brush); } // in case the char style has any of the following properties it also needs to have the fontFamily as otherwise // these values will be ignored when loading according to the odf spec if (!hasProperty(QTextFormat::FontFamily)) { if (hasProperty(QTextFormat::FontStyleHint) || hasProperty(QTextFormat::FontFixedPitch) || hasProperty(KoCharacterStyle::FontCharset)) { QString fontFamily = other.fontFamily(); if (!fontFamily.isEmpty()) { setFontFamily(fontFamily); } } } else { for (QMap::const_iterator it(keep.constBegin()); it != keep.constEnd(); ++it) { this->d->stylesPrivate.add(it.key(), it.value()); } } } void KoCharacterStyle::removeDuplicates(const QTextCharFormat &otherFormat) { KoCharacterStyle other(otherFormat); removeDuplicates(other); } void KoCharacterStyle::remove(int key) { d->stylesPrivate.remove(key); } bool KoCharacterStyle::isEmpty() const { return d->stylesPrivate.isEmpty(); } void KoCharacterStyle::saveOdf(KoGenStyle &style) const { if (!d->name.isEmpty() && !style.isDefaultStyle()) { style.addAttribute("style:display-name", d->name); } QList keys = d->stylesPrivate.keys(); Q_FOREACH (int key, keys) { if (key == QTextFormat::FontWeight) { bool ok = false; int boldness = d->stylesPrivate.value(key).toInt(&ok); if (ok) { if (boldness == QFont::Normal) { style.addProperty("fo:font-weight", "normal", KoGenStyle::TextType); } else if (boldness == QFont::Bold) { style.addProperty("fo:font-weight", "bold", KoGenStyle::TextType); } else { // Remember : Qt and CSS/XSL doesn't have the same scale. Its 100-900 instead of Qts 0-100 style.addProperty("fo:font-weight", qBound(10, boldness, 90) * 10, KoGenStyle::TextType); } } } else if (key == QTextFormat::FontItalic) { if (d->stylesPrivate.value(key).toBool()) { style.addProperty("fo:font-style", "italic", KoGenStyle::TextType); } else { style.addProperty("fo:font-style", "normal", KoGenStyle::TextType); } } else if (key == QTextFormat::FontFamily) { QString fontFamily = d->stylesPrivate.value(key).toString(); style.addProperty("fo:font-family", fontFamily, KoGenStyle::TextType); } else if (key == QTextFormat::FontFixedPitch) { bool fixedPitch = d->stylesPrivate.value(key).toBool(); style.addProperty("style:font-pitch", fixedPitch ? "fixed" : "variable", KoGenStyle::TextType); // if this property is saved we also need to save the fo:font-family attribute as otherwise it will be ignored on loading as defined in the spec style.addProperty("fo:font-family", fontFamily(), KoGenStyle::TextType); } else if (key == QTextFormat::FontStyleHint) { bool ok = false; int styleHint = d->stylesPrivate.value(key).toInt(&ok); if (ok) { QString generic = exportOdfFontStyleHint((QFont::StyleHint) styleHint); if (!generic.isEmpty()) { style.addProperty("style:font-family-generic", generic, KoGenStyle::TextType); } // if this property is saved we also need to save the fo:font-family attribute as otherwise it will be ignored on loading as defined in the spec style.addProperty("fo:font-family", fontFamily(), KoGenStyle::TextType); } } else if (key == QTextFormat::FontKerning) { style.addProperty("style:letter-kerning", fontKerning() ? "true" : "false", KoGenStyle::TextType); } else if (key == QTextFormat::FontCapitalization) { switch (fontCapitalization()) { case QFont::SmallCaps: style.addProperty("fo:font-variant", "small-caps", KoGenStyle::TextType); break; case QFont::MixedCase: style.addProperty("fo:font-variant", "normal", KoGenStyle::TextType); style.addProperty("fo:text-transform", "none", KoGenStyle::TextType); break; case QFont::AllUppercase: style.addProperty("fo:text-transform", "uppercase", KoGenStyle::TextType); break; case QFont::AllLowercase: style.addProperty("fo:text-transform", "lowercase", KoGenStyle::TextType); break; case QFont::Capitalize: style.addProperty("fo:text-transform", "capitalize", KoGenStyle::TextType); break; } } else if (key == OverlineStyle) { bool ok = false; int styleId = d->stylesPrivate.value(key).toInt(&ok); if (ok) { style.addProperty("style:text-overline-style", exportOdfLineStyle((KoCharacterStyle::LineStyle) styleId), KoGenStyle::TextType); } } else if (key == OverlineType) { bool ok = false; int type = d->stylesPrivate.value(key).toInt(&ok); if (ok) { style.addProperty("style:text-overline-type", exportOdfLineType((KoCharacterStyle::LineType) type), KoGenStyle::TextType); } } else if (key == OverlineColor) { QColor color = d->stylesPrivate.value(key).value(); if (color.isValid()) style.addProperty("style:text-overline-color", color.name(), KoGenStyle::TextType); else style.addProperty("style:text-overline-color", "font-color", KoGenStyle::TextType); } else if (key == OverlineMode) { bool ok = false; int mode = d->stylesPrivate.value(key).toInt(&ok); if (ok) { style.addProperty("style:text-overline-mode", exportOdfLineMode((KoCharacterStyle::LineMode) mode), KoGenStyle::TextType); } } else if (key == OverlineWidth) { KoCharacterStyle::LineWeight weight; qreal width; overlineWidth(weight, width); style.addProperty("style:text-overline-width", exportOdfLineWidth(weight, width), KoGenStyle::TextType); } else if (key == UnderlineStyle) { bool ok = false; int styleId = d->stylesPrivate.value(key).toInt(&ok); if (ok) style.addProperty("style:text-underline-style", exportOdfLineStyle((KoCharacterStyle::LineStyle) styleId), KoGenStyle::TextType); } else if (key == UnderlineType) { bool ok = false; int type = d->stylesPrivate.value(key).toInt(&ok); if (ok) style.addProperty("style:text-underline-type", exportOdfLineType((KoCharacterStyle::LineType) type), KoGenStyle::TextType); } else if (key == QTextFormat::TextUnderlineColor) { QColor color = d->stylesPrivate.value(key).value(); if (color.isValid()) style.addProperty("style:text-underline-color", color.name(), KoGenStyle::TextType); else style.addProperty("style:text-underline-color", "font-color", KoGenStyle::TextType); } else if (key == UnderlineMode) { bool ok = false; int mode = d->stylesPrivate.value(key).toInt(&ok); if (ok) style.addProperty("style:text-underline-mode", exportOdfLineMode((KoCharacterStyle::LineMode) mode), KoGenStyle::TextType); } else if (key == UnderlineWidth) { KoCharacterStyle::LineWeight weight; qreal width; underlineWidth(weight, width); style.addProperty("style:text-underline-width", exportOdfLineWidth(weight, width), KoGenStyle::TextType); } else if (key == StrikeOutStyle) { bool ok = false; int styleId = d->stylesPrivate.value(key).toInt(&ok); if (ok) style.addProperty("style:text-line-through-style", exportOdfLineStyle((KoCharacterStyle::LineStyle) styleId), KoGenStyle::TextType); } else if (key == StrikeOutType) { bool ok = false; int type = d->stylesPrivate.value(key).toInt(&ok); if (ok) style.addProperty("style:text-line-through-type", exportOdfLineType((KoCharacterStyle::LineType) type), KoGenStyle::TextType); } else if (key == StrikeOutText) { style.addProperty("style:text-line-through-text", d->stylesPrivate.value(key).toString(), KoGenStyle::TextType); } else if (key == StrikeOutColor) { QColor color = d->stylesPrivate.value(key).value(); if (color.isValid()) style.addProperty("style:text-line-through-color", color.name(), KoGenStyle::TextType); } else if (key == StrikeOutMode) { bool ok = false; int mode = d->stylesPrivate.value(key).toInt(&ok); if (ok) style.addProperty("style:text-line-through-mode", exportOdfLineMode((KoCharacterStyle::LineMode) mode), KoGenStyle::TextType); } else if (key == StrikeOutWidth) { KoCharacterStyle::LineWeight weight; qreal width; strikeOutWidth(weight, width); style.addProperty("style:text-line-through-width", exportOdfLineWidth(weight, width), KoGenStyle::TextType); } else if (key == QTextFormat::BackgroundBrush) { QBrush brush = d->stylesPrivate.value(key).value(); if (brush.style() == Qt::NoBrush) style.addProperty("fo:background-color", "transparent", KoGenStyle::TextType); else style.addProperty("fo:background-color", brush.color().name(), KoGenStyle::TextType); } else if (key == QTextFormat::ForegroundBrush) { QBrush brush = d->stylesPrivate.value(key).value(); if (brush.style() != Qt::NoBrush) { style.addProperty("fo:color", brush.color().name(), KoGenStyle::TextType); } } else if (key == KoCharacterStyle::UseWindowFontColor) { bool use = d->stylesPrivate.value(key).toBool(); style.addProperty("style:use-window-font-color", use ? "true" : "false", KoGenStyle::TextType); } else if (key == QTextFormat::TextVerticalAlignment) { if (verticalAlignment() == QTextCharFormat::AlignSuperScript) style.addProperty("style:text-position", "super", KoGenStyle::TextType); else if (verticalAlignment() == QTextCharFormat::AlignSubScript) style.addProperty("style:text-position", "sub", KoGenStyle::TextType); else if (d->stylesPrivate.contains(QTextFormat::TextVerticalAlignment)) // no superscript or subscript style.addProperty("style:text-position", "0% 100%", KoGenStyle::TextType); } else if (key == QTextFormat::FontPointSize) { // when there is percentageFontSize!=100% property ignore the fontSize property and store the percentage property if ( (!hasProperty(KoCharacterStyle::PercentageFontSize)) || (percentageFontSize()==100)) style.addPropertyPt("fo:font-size", fontPointSize(), KoGenStyle::TextType); } else if (key == KoCharacterStyle::PercentageFontSize) { if(percentageFontSize()!=100) { style.addProperty("fo:font-size", QString::number(percentageFontSize()) + '%', KoGenStyle::TextType); } } else if (key == KoCharacterStyle::Country) { style.addProperty("fo:country", d->stylesPrivate.value(KoCharacterStyle::Country).toString(), KoGenStyle::TextType); } else if (key == KoCharacterStyle::Language) { style.addProperty("fo:language", d->stylesPrivate.value(KoCharacterStyle::Language).toString(), KoGenStyle::TextType); } else if (key == KoCharacterStyle::FontLetterSpacing) { qreal space = fontLetterSpacing(); style.addPropertyPt("fo:letter-spacing", space, KoGenStyle::TextType); } else if (key == QTextFormat::TextOutline) { QPen outline = textOutline(); style.addProperty("style:text-outline", outline.style() == Qt::NoPen ? "false" : "true", KoGenStyle::TextType); } else if (key == KoCharacterStyle::FontCharset) { style.addProperty("style:font-charset", d->stylesPrivate.value(KoCharacterStyle::FontCharset).toString(), KoGenStyle::TextType); // if this property is saved we also need to save the fo:font-family attribute as otherwise it will be ignored on loading as defined in the spec style.addProperty("fo:font-family", fontFamily(), KoGenStyle::TextType); } else if (key == KoCharacterStyle::TextRotationAngle) { style.addProperty("style:text-rotation-angle", QString::number(textRotationAngle()), KoGenStyle::TextType); } else if (key == KoCharacterStyle::TextRotationScale) { RotationScale scale = textRotationScale(); style.addProperty("style:text-rotation-scale", rotationScaleToString(scale), KoGenStyle::TextType); } else if (key == KoCharacterStyle::TextScale) { int scale = textScale(); style.addProperty("style:text-scale", QString::number(scale) + '%', KoGenStyle::TextType); } else if (key == KoCharacterStyle::TextShadow) { KoShadowStyle shadow = textShadow(); style.addProperty("fo:text-shadow", shadow.saveOdf(), KoGenStyle::TextType); } else if (key == KoCharacterStyle::TextCombine) { KoCharacterStyle::TextCombineType textCombineType = textCombine(); switch (textCombineType) { case KoCharacterStyle::NoTextCombine: style.addProperty("style:text-combine", "none", KoGenStyle::TextType); break; case KoCharacterStyle::TextCombineLetters: style.addProperty("style:text-combine", "letters", KoGenStyle::TextType); break; case KoCharacterStyle::TextCombineLines: style.addProperty("style:text-combine", "lines", KoGenStyle::TextType); break; } } else if (key == KoCharacterStyle::TextCombineEndChar) { style.addProperty("style:text-combine-end-char", textCombineEndChar(), KoGenStyle::TextType); } else if (key == KoCharacterStyle::TextCombineStartChar) { style.addProperty("style:text-combine-start-char", textCombineStartChar(), KoGenStyle::TextType); } else if (key == KoCharacterStyle::FontRelief) { KoCharacterStyle::ReliefType relief = fontRelief(); switch (relief) { case KoCharacterStyle::NoRelief: style.addProperty("style:font-relief", "none", KoGenStyle::TextType); break; case KoCharacterStyle::Embossed: style.addProperty("style:font-relief", "embossed", KoGenStyle::TextType); break; case KoCharacterStyle::Engraved: style.addProperty("style:font-relief", "engraved", KoGenStyle::TextType); break; } } else if (key == KoCharacterStyle::TextEmphasizeStyle) { KoCharacterStyle::EmphasisStyle emphasisStyle = textEmphasizeStyle(); KoCharacterStyle::EmphasisPosition position = textEmphasizePosition(); QString odfEmphasis; switch (emphasisStyle) { case KoCharacterStyle::NoEmphasis: odfEmphasis = "none"; break; case KoCharacterStyle::AccentEmphasis: odfEmphasis = "accent"; break; case KoCharacterStyle::CircleEmphasis: odfEmphasis = "circle"; break; case KoCharacterStyle::DiscEmphasis: odfEmphasis = "disc"; break; case KoCharacterStyle::DotEmphasis: odfEmphasis = "dot"; break; } if (hasProperty(KoCharacterStyle::TextEmphasizePosition)) { if (position == KoCharacterStyle::EmphasisAbove) odfEmphasis += " above"; else odfEmphasis += " below"; } style.addProperty("style:text-emphasize", odfEmphasis, KoGenStyle::TextType); } else if (key == KoCharacterStyle::HasHyphenation) { if (hasHyphenation()) style.addProperty("fo:hyphenate", "true", KoGenStyle::TextType); else style.addProperty("fo:hyphenate", "false", KoGenStyle::TextType); } else if (key == KoCharacterStyle::HyphenationPushCharCount) { style.addProperty("fo:hyphenation-push-char-count", hyphenationPushCharCount(), KoGenStyle::TextType); } else if (key == KoCharacterStyle::HyphenationRemainCharCount) { style.addProperty("fo:hyphenation-remain-char-count", hyphenationRemainCharCount(), KoGenStyle::TextType); } else if (key == KoCharacterStyle::Blink) { style.addProperty("style:text-blinking", blinking(), KoGenStyle::TextType); } } //TODO: font name and family } QVariant KoCharacterStyle::value(int key) const { QVariant variant = d->stylesPrivate.value(key); if (variant.isNull()) { if (d->parentStyle) variant = d->parentStyle->value(key); else if (d->defaultStyle) variant = d->defaultStyle->value(key); } return variant; } void KoCharacterStyle::removeHardCodedDefaults() { d->hardCodedDefaultStyle.clearAll(); } diff --git a/plugins/flake/textshape/textlayout/KoTextDocumentLayout.cpp b/plugins/flake/textshape/textlayout/KoTextDocumentLayout.cpp index de30a44dd9..19f7d9199a 100644 --- a/plugins/flake/textshape/textlayout/KoTextDocumentLayout.cpp +++ b/plugins/flake/textshape/textlayout/KoTextDocumentLayout.cpp @@ -1,1025 +1,1025 @@ /* This file is part of the KDE project * Copyright (C) 2006-2007, 2009-2010 Thomas Zander * Copyright (C) 2010 Johannes Simon * Copyright (C) 2011-2013 KO GmbH * Copyright (C) 2011-2013 C.Boemann * * 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 "KoTextDocumentLayout.h" #include "styles/KoStyleManager.h" #include "KoTextBlockData.h" #include "KoInlineTextObjectManager.h" #include "KoTextLayoutRootArea.h" #include "KoTextLayoutRootAreaProvider.h" #include "KoTextLayoutObstruction.h" #include "FrameIterator.h" #include "InlineAnchorStrategy.h" #include "FloatingAnchorStrategy.h" #include "AnchorStrategy.h" #include "IndexGeneratorManager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern int qt_defaultDpiY(); KoInlineObjectExtent::KoInlineObjectExtent(qreal ascent, qreal descent) : m_ascent(ascent), m_descent(descent) { } class Q_DECL_HIDDEN KoTextDocumentLayout::Private { public: Private(KoTextDocumentLayout *) : styleManager(0) , changeTracker(0) , inlineTextObjectManager(0) , textRangeManager(0) , provider(0) , layoutPosition(0) , anchoringRootArea(0) , anchoringIndex(0) , anAnchorIsPlaced(false) , anchoringSoftBreak(INT_MAX) , allowPositionInlineObject(true) , continuationObstruction(0) , referencedLayout(0) , defaultTabSizing(0) , y(0) , isLayouting(false) , layoutScheduled(false) , continuousLayout(true) , layoutBlocked(false) , changesBlocked(false) , restartLayout(false) , wordprocessingMode(false) , showInlineObjectVisualization(false) { } KoStyleManager *styleManager; KoChangeTracker *changeTracker; KoInlineTextObjectManager *inlineTextObjectManager; KoTextRangeManager *textRangeManager; KoTextLayoutRootAreaProvider *provider; KoPostscriptPaintDevice *paintDevice; QList rootAreaList; FrameIterator *layoutPosition; QHash inlineObjectExtents; // maps text-position to whole-line-height of an inline object int inlineObjectOffset; QList textAnchors; // list of all inserted inline objects QList foundAnchors; // anchors found in an iteration run KoTextLayoutRootArea *anchoringRootArea; int anchoringIndex; // index of last not positioned inline object inside textAnchors bool anAnchorIsPlaced; int anchoringSoftBreak; QRectF anchoringParagraphRect; QRectF anchoringParagraphContentRect; QRectF anchoringLayoutEnvironmentRect; bool allowPositionInlineObject; QHash anchoredObstructions; // all obstructions created because KoShapeAnchor from m_textAnchors is in text QList freeObstructions; // obstructions affecting the current rootArea, and not anchored to text KoTextLayoutObstruction *continuationObstruction; KoTextDocumentLayout *referencedLayout; QHash rootAreaForInlineObject; qreal defaultTabSizing; qreal y; bool isLayouting; bool layoutScheduled; bool continuousLayout; bool layoutBlocked; bool changesBlocked; bool restartLayout; bool wordprocessingMode; bool showInlineObjectVisualization; }; // ------------------- KoTextDocumentLayout -------------------- KoTextDocumentLayout::KoTextDocumentLayout(QTextDocument *doc, KoTextLayoutRootAreaProvider *provider) : QAbstractTextDocumentLayout(doc), d(new Private(this)) { d->paintDevice = new KoPostscriptPaintDevice(); d->provider = provider; setPaintDevice(d->paintDevice); d->styleManager = KoTextDocument(document()).styleManager(); d->changeTracker = KoTextDocument(document()).changeTracker(); d->inlineTextObjectManager = KoTextDocument(document()).inlineTextObjectManager(); d->textRangeManager = KoTextDocument(document()).textRangeManager(); setTabSpacing(MM_TO_POINT(23)); // use same default as open office d->layoutPosition = new FrameIterator(doc->rootFrame()); } KoTextDocumentLayout::~KoTextDocumentLayout() { delete d->paintDevice; delete d->layoutPosition; qDeleteAll(d->freeObstructions); qDeleteAll(d->anchoredObstructions); qDeleteAll(d->textAnchors); delete d; } KoTextLayoutRootAreaProvider *KoTextDocumentLayout::provider() const { return d->provider; } void KoTextDocumentLayout::setWordprocessingMode() { d->wordprocessingMode = true; } bool KoTextDocumentLayout::wordprocessingMode() const { return d->wordprocessingMode; } bool KoTextDocumentLayout::relativeTabs(const QTextBlock &block) const { return KoTextDocument(document()).relativeTabs() && KoTextDocument(block.document()).relativeTabs(); } KoInlineTextObjectManager *KoTextDocumentLayout::inlineTextObjectManager() const { return d->inlineTextObjectManager; } void KoTextDocumentLayout::setInlineTextObjectManager(KoInlineTextObjectManager *manager) { d->inlineTextObjectManager = manager; } KoTextRangeManager *KoTextDocumentLayout::textRangeManager() const { return d->textRangeManager; } void KoTextDocumentLayout::setTextRangeManager(KoTextRangeManager *manager) { d->textRangeManager = manager; } KoChangeTracker *KoTextDocumentLayout::changeTracker() const { return d->changeTracker; } void KoTextDocumentLayout::setChangeTracker(KoChangeTracker *tracker) { d->changeTracker = tracker; } KoStyleManager *KoTextDocumentLayout::styleManager() const { return d->styleManager; } void KoTextDocumentLayout::setStyleManager(KoStyleManager *manager) { d->styleManager = manager; } QRectF KoTextDocumentLayout::blockBoundingRect(const QTextBlock &block) const { QTextLayout *layout = block.layout(); return layout->boundingRect(); } QSizeF KoTextDocumentLayout::documentSize() const { return QSizeF(); } QRectF KoTextDocumentLayout::selectionBoundingBox(QTextCursor &cursor) const { QRectF retval; Q_FOREACH (const KoTextLayoutRootArea *rootArea, d->rootAreaList) { if (!rootArea->isDirty()) { QRectF areaBB = rootArea->selectionBoundingBox(cursor); if (areaBB.isValid()) { retval |= areaBB; } } } return retval; } void KoTextDocumentLayout::draw(QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context) { // WARNING Text shapes ask their root area directly to paint. // It saves a lot of extra traversal, that is quite costly for big // documents Q_UNUSED(painter); Q_UNUSED(context); } int KoTextDocumentLayout::hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const { Q_UNUSED(point); Q_UNUSED(accuracy); Q_ASSERT(false); //we should no longer call this method. // There is no need and is just slower than needed // call rootArea->hitTest() directly // root area is available through KoTextShapeData return -1; } int KoTextDocumentLayout::pageCount() const { return 1; } void KoTextDocumentLayout::setTabSpacing(qreal spacing) { d->defaultTabSizing = spacing; } qreal KoTextDocumentLayout::defaultTabSpacing() const { return d->defaultTabSizing; } // this method is called on every char inserted or deleted, on format changes, setting/moving of variables or objects. void KoTextDocumentLayout::documentChanged(int position, int charsRemoved, int charsAdded) { if (d->changesBlocked) { return; } int from = position; const int to = from + charsAdded; while (from < to) { // find blocks that have been added QTextBlock block = document()->findBlock(from); if (! block.isValid()) break; if (from == block.position() && block.textList()) { KoTextBlockData data(block); data.setCounterWidth(-1); } from = block.position() + block.length(); } // Mark the to the position corresponding root-areas as dirty. If there is no root-area for the position then we // don't need to mark anything dirty but still need to go on to force a scheduled relayout. if (!d->rootAreaList.isEmpty()) { KoTextLayoutRootArea *fromArea; if (position) { fromArea = rootAreaForPosition(position-1); } else { fromArea = d->rootAreaList.at(0); } int startIndex = fromArea ? qMax(0, d->rootAreaList.indexOf(fromArea)) : 0; int endIndex = startIndex; if (charsRemoved != 0 || charsAdded != 0) { // If any characters got removed or added make sure to also catch other root-areas that may be // affected by this change. Note that adding, removing or formatting text will always charsRemoved>0 // and charsAdded>0 cause they are changing a range of characters. One case where both is zero is if // the content of a variable changed (see KoVariable::setValue which calls publicDocumentChanged). In // those cases we only need to relayout the root-area dirty where the variable is on. KoTextLayoutRootArea *toArea = fromArea ? rootAreaForPosition(position + qMax(charsRemoved, charsAdded) + 1) : 0; if (toArea) { if (toArea != fromArea) { endIndex = qMax(startIndex, d->rootAreaList.indexOf(toArea)); } else { endIndex = startIndex; } } else { endIndex = d->rootAreaList.count() - 1; } // The previous and following root-area of that range are selected too cause they can also be affect by // changes done to the range of root-areas. if (startIndex >= 1) --startIndex; if (endIndex + 1 < d->rootAreaList.count()) ++endIndex; } // Mark all selected root-areas as dirty so they are relayouted. for(int i = startIndex; i <= endIndex; ++i) { if (d->rootAreaList.size() > i && d->rootAreaList[i]) d->rootAreaList[i]->setDirty(); } } // Once done we emit the layoutIsDirty signal. The consumer (e.g. the TextShape) will then layout dirty // root-areas and if needed following ones which got dirty cause content moved to them. Also this will // created new root-areas using KoTextLayoutRootAreaProvider::provide if needed. emitLayoutIsDirty(); } KoTextLayoutRootArea *KoTextDocumentLayout::rootAreaForPosition(int position) const { QTextBlock block = document()->findBlock(position); if (!block.isValid()) return 0; QTextLine line = block.layout()->lineForTextPosition(position - block.position()); if (!line.isValid()) return 0; foreach (KoTextLayoutRootArea *rootArea, d->rootAreaList) { QRectF rect = rootArea->boundingRect(); // should already be normalized() if (rect.width() <= 0.0 && rect.height() <= 0.0) // ignore the rootArea if it has a size of QSizeF(0,0) continue; QPointF pos = line.position(); qreal x = pos.x(); qreal y = pos.y(); //0.125 needed since Qt Scribe works with fixed point if (x + 0.125 >= rect.x() && x<= rect.right() && y + line.height() + 0.125 >= rect.y() && y <= rect.bottom()) { return rootArea; } } return 0; } KoTextLayoutRootArea *KoTextDocumentLayout::rootAreaForPoint(const QPointF &point) const { Q_FOREACH (KoTextLayoutRootArea *rootArea, d->rootAreaList) { if (!rootArea->isDirty()) { if (rootArea->boundingRect().contains(point)) { return rootArea; } } } return 0; } void KoTextDocumentLayout::showInlineObjectVisualization(bool show) { d->showInlineObjectVisualization = show; } void KoTextDocumentLayout::drawInlineObject(QPainter *painter, const QRectF &rect, QTextInlineObject object, int position, const QTextFormat &format) { Q_ASSERT(format.isCharFormat()); if (d->inlineTextObjectManager == 0) return; QTextCharFormat cf = format.toCharFormat(); if (d->showInlineObjectVisualization) { QColor color = cf.foreground().color(); // initial idea was to use Qt::gray (#A0A0A4) // for non-black text on non-white background it was derived to use // the text color with a transparency of 0x5F, so white-gray (0xFF-0xA0) color.setAlpha(0x5F); cf.setBackground(QBrush(color)); } KoInlineObject *obj = d->inlineTextObjectManager->inlineTextObject(cf); if (obj) obj->paint(*painter, paintDevice(), document(), rect, object, position, cf); } QList KoTextDocumentLayout::textAnchors() const { return d->textAnchors; } void KoTextDocumentLayout::registerAnchoredObstruction(KoTextLayoutObstruction *obstruction) { d->anchoredObstructions.insert(obstruction->shape(), obstruction); } qreal KoTextDocumentLayout::maxYOfAnchoredObstructions(int firstCursorPosition, int lastCursorPosition) const { qreal y = 0.0; int index = 0; while (index < d->anchoringIndex) { Q_ASSERT(index < d->textAnchors.count()); KoShapeAnchor *anchor = d->textAnchors[index]; if (anchor->flowWithText()) { if (anchor->textLocation()->position() >= firstCursorPosition && anchor->textLocation()->position() <= lastCursorPosition) { y = qMax(y, anchor->shape()->boundingRect().bottom() - anchor->shape()->parent()->boundingRect().y()); } } ++index; } return y; } int KoTextDocumentLayout::anchoringSoftBreak() const { return d->anchoringSoftBreak; } void KoTextDocumentLayout::positionAnchoredObstructions() { if (!d->anchoringRootArea) return; KoTextPage *page = d->anchoringRootArea->page(); if (!page) return; if (d->anAnchorIsPlaced) return; // The specs define 3 different anchor modes using the // draw:wrap-influence-on-position. We only implement the // once-successive and decided against supporting the other // two modes cause; // 1. The first mode, once-concurrently, is only for backward-compatibility // with pre OpenOffice.org 1.1. No other application supports that. It // should never have been added to the specs. // 2. The iterative mode is undocumented and it's absolute unclear how to // implement it in a way that we would earn 100% the same results OO.org // produces. In fact by looking at the OO.org source-code there seem to // be lot of extra-conditions, assumptions and OO.org related things going // on to handle that mode. We tried to support that mode once and it did // hit us bad, our source-code become way more worse, layouting slower and // the result was still different from OO.org. So, we decided it's not // worth it. - // 3. The explanation provided at http://lists.oasis-open.org/archives/office/200409/msg00018.html + // 3. The explanation provided at https://lists.oasis-open.org/archives/office/200409/msg00018.html // why the specs support those 3 anchor modes is, well, poor. It just doesn't // make sense. The specs should be fixed. // 4. The only support mode, the once-successive, is the one (only) support by // MSOffice. It's clear, logical, easy and needs to be supported by all // major office-suites that like to be compatible with MSOffice and OO.org. if (d->anchoringIndex < d->textAnchors.count()) { KoShapeAnchor *textAnchor = d->textAnchors[d->anchoringIndex]; AnchorStrategy *strategy = static_cast(textAnchor->placementStrategy()); strategy->setPageRect(page->rect()); strategy->setPageContentRect(page->contentRect()); strategy->setPageNumber(page->pageNumber()); if (strategy->moveSubject()) { ++d->anchoringIndex; d->anAnchorIsPlaced = true; } } } void KoTextDocumentLayout::setAnchoringParagraphRect(const QRectF ¶graphRect) { d->anchoringParagraphRect = paragraphRect; } void KoTextDocumentLayout::setAnchoringParagraphContentRect(const QRectF ¶graphContentRect) { d->anchoringParagraphContentRect = paragraphContentRect; } void KoTextDocumentLayout::setAnchoringLayoutEnvironmentRect(const QRectF &layoutEnvironmentRect) { d->anchoringLayoutEnvironmentRect = layoutEnvironmentRect; } void KoTextDocumentLayout::allowPositionInlineObject(bool allow) { d->allowPositionInlineObject = allow; } // This method is called by qt every time QTextLine.setWidth()/setNumColumns() is called void KoTextDocumentLayout::positionInlineObject(QTextInlineObject item, int position, const QTextFormat &format) { // Note: "item" used to be what was positioned. We don't actually use qtextinlineobjects anymore // for our inline objects, but get the id from the format. Q_UNUSED(item); //We are called before layout so that we can position objects Q_ASSERT(format.isCharFormat()); if (d->inlineTextObjectManager == 0) return; if (!d->allowPositionInlineObject) return; QTextCharFormat cf = format.toCharFormat(); KoInlineObject *obj = d->inlineTextObjectManager->inlineTextObject(cf); // We need some special treatment for anchors as they need to position their object during // layout and not this early KoAnchorInlineObject *anchorObject = dynamic_cast(obj); if (anchorObject && d->anchoringRootArea->associatedShape()) { // The type can only be KoShapeAnchor::AnchorAsCharacter since it's inline KoShapeAnchor *anchor = anchorObject->anchor(); d->foundAnchors.append(anchor); // if there is no anchor strategy set then create one if (!anchor->placementStrategy()) { anchor->setPlacementStrategy(new InlineAnchorStrategy(anchorObject, d->anchoringRootArea)); d->textAnchors.append(anchor); anchorObject->updatePosition(document(), position, cf); // by extension calls updateContainerModel } static_cast(anchor->placementStrategy())->setParagraphRect(d->anchoringParagraphRect); static_cast(anchor->placementStrategy())->setParagraphContentRect(d->anchoringParagraphContentRect); static_cast(anchor->placementStrategy())->setLayoutEnvironmentRect(d->anchoringLayoutEnvironmentRect); } else if (obj) { obj->updatePosition(document(), position, cf); } } // This method is called by KoTextLauoutArea every time it encounters a KoAnchorTextRange void KoTextDocumentLayout::positionAnchorTextRanges(int pos, int length, const QTextDocument *effectiveDocument) { if (!d->allowPositionInlineObject) return; if (!textRangeManager()) { return; } QHash ranges = textRangeManager()->textRangesChangingWithin(effectiveDocument, pos, pos+length, pos, pos+length); Q_FOREACH (KoTextRange *range, ranges.values()) { KoAnchorTextRange *anchorRange = dynamic_cast(range); if (anchorRange) { // We need some special treatment for anchors as they need to position their object during // layout and not this early KoShapeAnchor *anchor = anchorRange->anchor(); d->foundAnchors.append(anchor); // At the beginAnchorCollecting the strategy is cleared, so this if will be entered // every time we layout a page (though not every time for the inner repeats due to anchors) if (!anchor->placementStrategy()) { int index = d->textAnchors.count(); anchor->setPlacementStrategy(new FloatingAnchorStrategy(anchorRange, d->anchoringRootArea)); // The purpose of following code-block is to be sure that our paragraph-anchors are // properly sorted by their z-index so the FloatingAnchorStrategy::checkStacking // logic stack in the proper order. Bug 274512 has a testdoc for this attached. if (index > 0 && anchor->anchorType() == KoShapeAnchor::AnchorParagraph && (anchor->horizontalRel() == KoShapeAnchor::HParagraph || anchor->horizontalRel() == KoShapeAnchor::HParagraphContent) && (anchor->horizontalPos() == KoShapeAnchor::HLeft || anchor->horizontalPos() == KoShapeAnchor::HRight)) { QTextBlock anchorBlock = document()->findBlock(anchorRange->position()); for(int i = index - 1; i >= 0; --i) { KoShapeAnchor *a = d->textAnchors[i]; if (a->anchorType() != anchor->anchorType()) break; if (a->horizontalPos() != anchor->horizontalPos()) break; if (document()->findBlock(a->textLocation()->position()) != anchorBlock) break; if (a->shape()->zIndex() < anchor->shape()->zIndex()) break; --index; } } d->textAnchors.insert(index, anchor); anchorRange->updateContainerModel(); } static_cast(anchor->placementStrategy())->setParagraphRect(d->anchoringParagraphRect); static_cast(anchor->placementStrategy())->setParagraphContentRect(d->anchoringParagraphContentRect); static_cast(anchor->placementStrategy())->setLayoutEnvironmentRect(d->anchoringLayoutEnvironmentRect); } KoAnnotation *annotation = dynamic_cast(range); if (annotation) { int position = range->rangeStart(); QTextBlock block = range->document()->findBlock(position); QTextLine line = block.layout()->lineForTextPosition(position - block.position()); QPointF refPos(line.cursorToX(position - block.position()), line.y()); KoShape *refShape = d->anchoringRootArea->associatedShape(); //KoTextShapeData *refTextShapeData; //refPos += QPointF(refTextShapeData->leftPadding(), -refTextShapeData->documentOffset() + refTextShapeData->topPadding()); refPos += QPointF(0, -d->anchoringRootArea->top()); refPos = refShape->absoluteTransformation(0).map(refPos); //FIXME we need a more precise position than anchorParagraph Rect emit foundAnnotation(annotation->annotationShape(), refPos); } } } void KoTextDocumentLayout::beginAnchorCollecting(KoTextLayoutRootArea *rootArea) { for(int i = 0; itextAnchors.size(); i++ ) { d->textAnchors[i]->setPlacementStrategy(0); } qDeleteAll(d->anchoredObstructions); d->anchoredObstructions.clear(); d->textAnchors.clear(); d->anchoringIndex = 0; d->anAnchorIsPlaced = false; d->anchoringRootArea = rootArea; d->allowPositionInlineObject = true; d->anchoringSoftBreak = INT_MAX; } void KoTextDocumentLayout::resizeInlineObject(QTextInlineObject item, int position, const QTextFormat &format) { // Note: This method is called by qt during layout AND during paint Q_ASSERT(format.isCharFormat()); if (d->inlineTextObjectManager == 0) return; QTextCharFormat cf = format.toCharFormat(); KoInlineObject *obj = d->inlineTextObjectManager->inlineTextObject(cf); if (!obj) { return; } if (d->isLayouting) { d->rootAreaForInlineObject[obj] = d->anchoringRootArea; } KoTextLayoutRootArea *rootArea = d->rootAreaForInlineObject.value(obj); if (rootArea == 0 || rootArea->associatedShape() == 0) return; QTextDocument *doc = document(); QVariant v; v.setValue(rootArea->page()); doc->addResource(KoTextDocument::LayoutTextPage, KoTextDocument::LayoutTextPageUrl, v); obj->resize(doc, item, position, cf, paintDevice()); registerInlineObject(item); } void KoTextDocumentLayout::emitLayoutIsDirty() { emit layoutIsDirty(); } void KoTextDocumentLayout::layout() { if (d->layoutBlocked) { return; } if (IndexGeneratorManager::instance(document())->generate()) { return; } Q_ASSERT(!d->isLayouting); d->isLayouting = true; bool finished; do { // Try to layout as long as d->restartLayout==true. This can happen for example if // a schedule layout call interrupts the layouting and asks for a new layout run. finished = doLayout(); } while (d->restartLayout); Q_ASSERT(d->isLayouting); d->isLayouting = false; if (finished) { // We are only finished with layouting if continuousLayout()==true. emit finishedLayout(); } } RootAreaConstraint constraintsForPosition(QTextFrame::iterator it, bool previousIsValid) { RootAreaConstraint constraints; constraints.masterPageName.clear(); constraints.visiblePageNumber = -1; constraints.newPageForced = false; QTextBlock block = it.currentBlock(); QTextTable *table = qobject_cast(it.currentFrame()); if (block.isValid()) { constraints.masterPageName = block.blockFormat().stringProperty(KoParagraphStyle::MasterPageName); if (block.blockFormat().hasProperty(KoParagraphStyle::PageNumber)) { constraints.visiblePageNumber = block.blockFormat().intProperty(KoParagraphStyle::PageNumber); } constraints.newPageForced = block.blockFormat().intProperty(KoParagraphStyle::BreakBefore) == KoText::PageBreak; } if (table) { constraints.masterPageName = table->frameFormat().stringProperty(KoTableStyle::MasterPageName); if (table->frameFormat().hasProperty(KoTableStyle::PageNumber)) { constraints.visiblePageNumber = table->frameFormat().intProperty(KoTableStyle::PageNumber); } constraints.newPageForced = table->frameFormat().intProperty(KoTableStyle::BreakBefore) == KoText::PageBreak; } if (!constraints.masterPageName.isEmpty()) { constraints.newPageForced = true; } if (previousIsValid && !constraints.newPageForced) { it--; block = it.currentBlock(); table = qobject_cast(it.currentFrame()); if (block.isValid()) { constraints.newPageForced = block.blockFormat().intProperty(KoParagraphStyle::BreakAfter) == KoText::PageBreak; } if (table) { constraints.newPageForced = table->frameFormat().intProperty(KoTableStyle::BreakAfter) == KoText::PageBreak; } } return constraints; } bool KoTextDocumentLayout::doLayout() { delete d->layoutPosition; d->layoutPosition = new FrameIterator(document()->rootFrame()); d->y = 0; d->layoutScheduled = false; d->restartLayout = false; FrameIterator *transferedFootNoteCursor = 0; KoInlineNote *transferedContinuedNote = 0; int footNoteAutoCount = 0; KoTextLayoutRootArea *rootArea = 0; d->rootAreaList.clear(); int currentAreaNumber = 0; do { if (d->restartLayout) { return false; // Abort layouting to restart from the beginning. } // Build our request for our rootArea provider RootAreaConstraint constraints = constraintsForPosition(d->layoutPosition->it, currentAreaNumber > 0); // Request a new root-area. If 0 is returned then layouting is finished. bool newRootArea = false; rootArea = d->provider->provide(this, constraints, currentAreaNumber, &newRootArea); if (!rootArea) { // Out of space ? Nothing more to do break; } d->rootAreaList.append(rootArea); bool shouldLayout = false; if (rootArea->top() != d->y) { shouldLayout = true; } else if (rootArea->isDirty()) { shouldLayout = true; } else if (!rootArea->isStartingAt(d->layoutPosition)) { shouldLayout = true; } else if (newRootArea) { shouldLayout = true; } if (shouldLayout) { QRectF rect = d->provider->suggestRect(rootArea); d->freeObstructions = d->provider->relevantObstructions(rootArea); rootArea->setReferenceRect(rect.left(), rect.right(), d->y + rect.top(), d->y + rect.bottom()); beginAnchorCollecting(rootArea); // Layout all that can fit into that root area bool finished; FrameIterator *tmpPosition = 0; do { rootArea->setFootNoteCountInDoc(footNoteAutoCount); rootArea->setFootNoteFromPrevious(transferedFootNoteCursor, transferedContinuedNote); d->foundAnchors.clear(); delete tmpPosition; tmpPosition = new FrameIterator(d->layoutPosition); finished = rootArea->layoutRoot(tmpPosition); if (d->anAnchorIsPlaced) { d->anAnchorIsPlaced = false; } else { ++d->anchoringIndex; } } while (d->anchoringIndex < d->textAnchors.count()); foreach (KoShapeAnchor *anchor, d->textAnchors) { if (!d->foundAnchors.contains(anchor)) { d->anchoredObstructions.remove(anchor->shape()); d->anchoringSoftBreak = qMin(d->anchoringSoftBreak, anchor->textLocation()->position()); } } if (d->textAnchors.count() > 0) { delete tmpPosition; tmpPosition = new FrameIterator(d->layoutPosition); finished = rootArea->layoutRoot(tmpPosition); } delete d->layoutPosition; d->layoutPosition = tmpPosition; d->provider->doPostLayout(rootArea, newRootArea); updateProgress(d->layoutPosition->it); if (finished && !rootArea->footNoteCursorToNext()) { d->provider->releaseAllAfter(rootArea); // We must also delete them from our own list too int newsize = d->rootAreaList.indexOf(rootArea) + 1; while (d->rootAreaList.size() > newsize) { d->rootAreaList.removeLast(); } return true; // Finished layouting } if (d->layoutPosition->it == document()->rootFrame()->end()) { return true; // Finished layouting } if (!continuousLayout()) { return false; // Let's take a break. We are not finished layouting yet. } } else { // Drop following rootAreas delete d->layoutPosition; d->layoutPosition = new FrameIterator(rootArea->nextStartOfArea()); if (d->layoutPosition->it == document()->rootFrame()->end() && !rootArea->footNoteCursorToNext()) { d->provider->releaseAllAfter(rootArea); // We must also delete them from our own list too int newsize = d->rootAreaList.indexOf(rootArea) + 1; while (d->rootAreaList.size() > newsize) { d->rootAreaList.removeLast(); } return true; // Finished layouting } } transferedFootNoteCursor = rootArea->footNoteCursorToNext(); transferedContinuedNote = rootArea->continuedNoteToNext(); footNoteAutoCount += rootArea->footNoteAutoCount(); d->y = rootArea->bottom() + qreal(50); // (post)Layout method(s) just set this // 50 just to separate pages currentAreaNumber++; } while (transferedFootNoteCursor || d->layoutPosition->it != document()->rootFrame()->end()); return true; // Finished layouting } void KoTextDocumentLayout::scheduleLayout() { // Compress multiple scheduleLayout calls into one executeScheduledLayout. if (d->layoutScheduled) { return; } d->layoutScheduled = true; QTimer::singleShot(0, this, SLOT(executeScheduledLayout())); } void KoTextDocumentLayout::executeScheduledLayout() { // Only do the actual layout if it wasn't done meanwhile by someone else. if (!d->layoutScheduled) { return; } d->layoutScheduled = false; if (d->isLayouting) { // Since we are already layouting ask for a restart to be sure to also include // root-areas that got dirty and are before the currently processed root-area. d->restartLayout = true; } else { layout(); } } bool KoTextDocumentLayout::continuousLayout() const { return d->continuousLayout; } void KoTextDocumentLayout::setContinuousLayout(bool continuous) { d->continuousLayout = continuous; } void KoTextDocumentLayout::setBlockLayout(bool block) { d->layoutBlocked = block; } bool KoTextDocumentLayout::layoutBlocked() const { return d->layoutBlocked; } void KoTextDocumentLayout::setBlockChanges(bool block) { d->changesBlocked = block; } bool KoTextDocumentLayout::changesBlocked() const { return d->changesBlocked; } KoTextDocumentLayout* KoTextDocumentLayout::referencedLayout() const { return d->referencedLayout; } void KoTextDocumentLayout::setReferencedLayout(KoTextDocumentLayout *layout) { d->referencedLayout = layout; } QRectF KoTextDocumentLayout::frameBoundingRect(QTextFrame*) const { return QRectF(); } void KoTextDocumentLayout::clearInlineObjectRegistry(const QTextBlock &block) { d->inlineObjectExtents.clear(); d->inlineObjectOffset = block.position(); } void KoTextDocumentLayout::registerInlineObject(const QTextInlineObject &inlineObject) { KoInlineObjectExtent pos(inlineObject.ascent(),inlineObject.descent()); d->inlineObjectExtents.insert(d->inlineObjectOffset + inlineObject.textPosition(), pos); } KoInlineObjectExtent KoTextDocumentLayout::inlineObjectExtent(const QTextFragment &fragment) { if (d->inlineObjectExtents.contains(fragment.position())) return d->inlineObjectExtents[fragment.position()]; return KoInlineObjectExtent(); } void KoTextDocumentLayout::setContinuationObstruction(KoTextLayoutObstruction *continuationObstruction) { if (d->continuationObstruction) { delete d->continuationObstruction; } d->continuationObstruction = continuationObstruction; } QList KoTextDocumentLayout::currentObstructions() { if (d->continuationObstruction) { // () is needed so we append to a local list and not anchoredObstructions return (d->freeObstructions + d->anchoredObstructions.values()) << d->continuationObstruction; } else { return d->freeObstructions + d->anchoredObstructions.values(); } } QList KoTextDocumentLayout::rootAreas() const { return d->rootAreaList; } void KoTextDocumentLayout::removeRootArea(KoTextLayoutRootArea *rootArea) { int indexOf = rootArea ? qMax(0, d->rootAreaList.indexOf(rootArea)) : 0; for(int i = d->rootAreaList.count() - 1; i >= indexOf; --i) d->rootAreaList.removeAt(i); } QList KoTextDocumentLayout::shapes() const { QList listOfShapes; foreach (KoTextLayoutRootArea *rootArea, d->rootAreaList) { if (rootArea->associatedShape()) listOfShapes.append(rootArea->associatedShape()); } return listOfShapes; } void KoTextDocumentLayout::updateProgress(const QTextFrame::iterator &it) { QTextBlock block = it.currentBlock(); if (block.isValid()) { int percent = block.position() / qreal(document()->rootFrame()->lastPosition()) * 100.0; emit layoutProgressChanged(percent); } else if (it.currentFrame()) { int percent = it.currentFrame()->firstPosition() / qreal(document()->rootFrame()->lastPosition()) * 100.0; emit layoutProgressChanged(percent); } } diff --git a/plugins/flake/textshape/textlayout/KoTextLayoutArea_paint.cpp b/plugins/flake/textshape/textlayout/KoTextLayoutArea_paint.cpp index ff90af68da..e569fc12da 100644 --- a/plugins/flake/textshape/textlayout/KoTextLayoutArea_paint.cpp +++ b/plugins/flake/textshape/textlayout/KoTextLayoutArea_paint.cpp @@ -1,1200 +1,1200 @@ /* This file is part of the KDE project * Copyright (C) 2006-2010 Thomas Zander * Copyright (C) 2008 Thorsten Zachmann * Copyright (C) 2008 Girish Ramakrishnan * Copyright (C) 2008 Roopesh Chander * Copyright (C) 2007-2008 Pierre Ducroquet * Copyright (C) 2009-2011 KO GmbH * Copyright (C) 2009-2012 C. Boemann * Copyright (C) 2010 Nandita Suri * Copyright (C) 2010 Ajay Pundhir * Copyright (C) 2011 Lukáš Tvrdý * Copyright (C) 2011 Gopalakrishna Bhat A * Copyright (C) 2011 Stuart Dickson * Copyright (C) 2014 Denis Kuplyakov * * 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 "KoTextLayoutArea.h" #include "KoTextLayoutEndNotesArea.h" #include "KoTextLayoutTableArea.h" #include "KoTextLayoutNoteArea.h" #include "TableIterator.h" #include "ListItemsHelper.h" #include "RunAroundHelper.h" #include "KoTextDocumentLayout.h" #include "FrameIterator.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_painting_tweaks.h" extern int qt_defaultDpiY(); Q_DECLARE_METATYPE(QTextDocument *) #define DropCapsAdditionalFormattingId 25602902 #include "KoTextLayoutArea_p.h" void KoTextLayoutArea::paint(QPainter *painter, const KoTextDocumentLayout::PaintContext &context) { if (d->startOfArea == 0 || d->endOfArea == 0) // We have not been layouted yet return; /* struct Timer { QTime d->time; Timer() { d->time.start(); } ~Timer() { warnTextLayout << "elapsed=" << d->time.elapsed(); } }; Timer timer; */ painter->save(); painter->translate(0, d->verticalAlignOffset); painter->setPen(context.textContext.palette.color(QPalette::Text)); // for text that has no color. const QRegion clipRegion = KisPaintingTweaks::safeClipRegion(*painter); // fetch after painter->translate so the clipRegion is correct KoTextBlockBorderData *lastBorder = 0; QRectF lastBorderRect; QTextFrame::iterator it = d->startOfArea->it; QTextFrame::iterator stop = d->endOfArea->it; if (!stop.atEnd()) { if (!stop.currentBlock().isValid() || d->endOfArea->lineTextStart >= 0) { // Last thing we show is a frame (table) or first part of a paragraph split in two // The stop point should be the object after that // However if stop is already atEnd we shouldn't increment further ++stop; } } int tableAreaIndex = 0; int blockIndex = 0; int tocIndex = 0; for (; it != stop && !it.atEnd(); ++it) { QTextBlock block = it.currentBlock(); QTextTable *table = qobject_cast(it.currentFrame()); QTextFrame *subFrame = it.currentFrame(); QTextBlockFormat format = block.blockFormat(); if (!block.isValid()) { if (lastBorder) { // draw previous block's border lastBorder->paint(*painter, lastBorderRect); lastBorder = 0; } } if (table) { if (tableAreaIndex >= d->tableAreas.size()) { continue; } d->tableAreas[tableAreaIndex]->paint(painter, context); ++tableAreaIndex; continue; } else if (subFrame) { if (subFrame->format().intProperty(KoText::SubFrameType) == KoText::AuxillaryFrameType) { d->endNotesArea->paint(painter, context); } continue; } else { if (!block.isValid()) { continue; } } if (block.blockFormat().hasProperty(KoParagraphStyle::GeneratedDocument)) { // Possibly paint the selection of the entire Table of Contents // but since it's a secondary document we need to create a fake selection QVariant data = block.blockFormat().property(KoParagraphStyle::GeneratedDocument); QTextDocument *generatedDocument = data.value(); KoTextDocumentLayout::PaintContext tocContext = context; tocContext.textContext.selections = QVector(); bool pure = true; Q_FOREACH (const QAbstractTextDocumentLayout::Selection &selection, context.textContext.selections) { if (selection.cursor.selectionStart() <= block.position() && selection.cursor.selectionEnd() >= block.position()) { painter->fillRect(d->generatedDocAreas[tocIndex]->boundingRect(), selection.format.background()); if (pure) { tocContext.textContext.selections.append(QAbstractTextDocumentLayout::Selection()); tocContext.textContext.selections[0].cursor = QTextCursor(generatedDocument); tocContext.textContext.selections[0].cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); tocContext.textContext.selections[0].format = selection.format; pure = false; } } } d->generatedDocAreas[tocIndex]->paint(painter, tocContext); ++tocIndex; continue; } QTextLayout *layout = block.layout(); KoTextBlockBorderData *border = 0; if (blockIndex >= d->blockRects.count()) break; QRectF br = d->blockRects[blockIndex]; ++blockIndex; if (!painter->hasClipping() || clipRegion.intersects(br.toRect())) { KoTextBlockData blockData(block); border = blockData.border(); KoTextBlockPaintStrategyBase *paintStrategy = blockData.paintStrategy(); KoTextBlockPaintStrategyBase dummyPaintStrategy; if (paintStrategy == 0) { paintStrategy = &dummyPaintStrategy; } if (!paintStrategy->isVisible()) { if (lastBorder) { // draw previous block's border lastBorder->paint(*painter, lastBorderRect); lastBorder = 0; } continue; // this paragraph shouldn't be shown so just skip it } // Check and update border drawing code if (lastBorder == 0) { lastBorderRect = br; } else if (lastBorder != border || lastBorderRect.width() != br.width() || lastBorderRect.x() != br.x()) { lastBorder->paint(*painter, lastBorderRect); lastBorderRect = br; } else { lastBorderRect = lastBorderRect.united(br); } lastBorder = border; painter->save(); QBrush bg = paintStrategy->background(block.blockFormat().background()); if (bg != Qt::NoBrush ) { painter->fillRect(br, bg); } else { bg = context.background; } paintStrategy->applyStrategy(painter); painter->save(); drawListItem(painter, block); painter->restore(); QVector selections; if (context.showSelections) { Q_FOREACH (const QAbstractTextDocumentLayout::Selection & selection, context.textContext.selections) { QTextCursor cursor = selection.cursor; int begin = cursor.position(); int end = cursor.anchor(); if (begin > end) std::swap(begin, end); if (end < block.position() || begin > block.position() + block.length()) continue; // selection does not intersect this block. if (selection.cursor.hasComplexSelection()) { continue; // selections of several table cells are covered by the within drawBorders above. } if (d->documentLayout->changeTracker() && !d->documentLayout->changeTracker()->displayChanges() && d->documentLayout->changeTracker()->containsInlineChanges(selection.format) && d->documentLayout->changeTracker()->elementById(selection.format.property(KoCharacterStyle::ChangeTrackerId).toInt())->isEnabled() && d->documentLayout->changeTracker()->elementById(selection.format.property(KoCharacterStyle::ChangeTrackerId).toInt())->getChangeType() == KoGenChange::DeleteChange) { continue; // Deletions should not be shown. } QTextLayout::FormatRange fr; fr.start = begin - block.position(); fr.length = end - begin; fr.format = selection.format; selections.append(fr); } } // this is a workaround to fix text getting cut of when format ranges are used. There // is a bug in Qt that can hit when text lines overlap each other. In case a format range // is used for formatting it can clip the lines above/below as Qt creates a clip rect for // the places it already painted for the format range which results in clippling. So use // the format range always to paint the text. QVector workaroundFormatRanges; for (QTextBlock::iterator it = block.begin(); !(it.atEnd()); ++it) { QTextFragment currentFragment = it.fragment(); if (currentFragment.isValid()) { bool formatChanged = false; QTextCharFormat format = currentFragment.charFormat(); int changeId = format.intProperty(KoCharacterStyle::ChangeTrackerId); if (changeId && d->documentLayout->changeTracker() && d->documentLayout->changeTracker()->displayChanges()) { KoChangeTrackerElement *changeElement = d->documentLayout->changeTracker()->elementById(changeId); switch(changeElement->getChangeType()) { case (KoGenChange::InsertChange): format.setBackground(QBrush(d->documentLayout->changeTracker()->getInsertionBgColor())); break; case (KoGenChange::FormatChange): format.setBackground(QBrush(d->documentLayout->changeTracker()->getFormatChangeBgColor())); break; case (KoGenChange::DeleteChange): format.setBackground(QBrush(d->documentLayout->changeTracker()->getDeletionBgColor())); break; case (KoGenChange::UNKNOWN): break; } formatChanged = true; } if (format.isAnchor()) { if (!format.hasProperty(KoCharacterStyle::UnderlineStyle)) format.setFontUnderline(true); if (!format.hasProperty(QTextFormat::ForegroundBrush)) format.setForeground(Qt::blue); formatChanged = true; } if (format.boolProperty(KoCharacterStyle::UseWindowFontColor)) { QBrush backbrush = bg; if (format.background() != Qt::NoBrush) { backbrush = format.background(); } QBrush frontBrush; frontBrush.setStyle(Qt::SolidPattern); // use the same luma calculation and threshold as msoffice - // see http://social.msdn.microsoft.com/Forums/en-US/os_binaryfile/thread/a02a9a24-efb6-4ba0-a187-0e3d2704882b + // see https://social.msdn.microsoft.com/Forums/en-US/os_binaryfile/thread/a02a9a24-efb6-4ba0-a187-0e3d2704882b int luma = ((5036060/2) * backbrush.color().red() + (9886846/2) * backbrush.color().green() + (1920103/2) * backbrush.color().blue()) >> 23; if (luma > 60) { frontBrush.setColor(QColor(Qt::black)); } else { frontBrush.setColor(QColor(Qt::white)); } format.setForeground(frontBrush); formatChanged = true; } if (formatChanged) { QTextLayout::FormatRange fr; fr.start = currentFragment.position() - block.position(); fr.length = currentFragment.length(); if (!format.hasProperty(KoCharacterStyle::InlineInstanceId)) { if (format.background().style() == Qt::NoBrush) { format.setBackground(QBrush(QColor(0, 0, 0, 0))); } if (format.foreground().style() == Qt::NoBrush) { format.setForeground(QBrush(QColor(0, 0, 0))); } } fr.format = format; // the prepend is done so the selections are at the end. selections.prepend(fr); } else { if (!format.hasProperty(KoCharacterStyle::InlineInstanceId)) { QTextLayout::FormatRange fr; fr.start = currentFragment.position() - block.position(); fr.length = currentFragment.length(); QTextCharFormat f; if (format.background().style() == Qt::NoBrush) { f.setBackground(QBrush(QColor(0, 0, 0, 0))); } else { f.setBackground(format.background()); } if (format.foreground().style() == Qt::NoBrush) { f.setForeground(QBrush(QColor(0, 0, 0))); } else { f.setForeground(format.foreground()); } fr.format = f; workaroundFormatRanges.append(fr); } } } } if (!selections.isEmpty()) { selections = workaroundFormatRanges + selections; } //We set clip because layout-draw doesn't clip text to it correctly after all //and adjust to make sure we don't clip edges of glyphs. The clipping is //important for paragraph split across two pages. //20pt enlargement seems safe as pages is split by 50pt and this helps unwanted //glyph cutting painter->setClipRect(br.adjusted(-20,-20,20,20), Qt::IntersectClip); layout->draw(painter, QPointF(0, 0), selections); if (context.showSectionBounds) { decorateParagraphSections(painter, block); } decorateParagraph(painter, block, context.showFormattingCharacters, context.showSpellChecking); painter->restore(); } else { if (lastBorder) { lastBorder->paint(*painter, lastBorderRect); lastBorder = 0; } } } if (lastBorder) { lastBorder->paint(*painter, lastBorderRect); } painter->translate(0, -d->verticalAlignOffset); painter->translate(0, bottom() - d->footNotesHeight); Q_FOREACH (KoTextLayoutNoteArea *footerArea, d->footNoteAreas) { footerArea->paint(painter, context); painter->translate(0, footerArea->bottom() - footerArea->top()); } painter->restore(); } void KoTextLayoutArea::drawListItem(QPainter *painter, QTextBlock &block) { KoTextBlockData blockData(block); QTextList *list = block.textList(); if (list && blockData.hasCounterData()) { QTextListFormat listFormat = list->format(); if (! blockData.counterText().isEmpty()) { QFont font(blockData.labelFormat().font(), d->documentLayout->paintDevice()); KoListStyle::Style listStyle = static_cast(listFormat.style()); QString result = blockData.counterText(); QTextLayout layout(result, font, d->documentLayout->paintDevice()); QList layouts; QTextLayout::FormatRange format; format.start = 0; format.length = blockData.counterText().length(); format.format = blockData.labelFormat(); layouts.append(format); layout.setAdditionalFormats(layouts); Qt::Alignment alignment = static_cast(listFormat.intProperty(KoListStyle::Alignment)); if (alignment == 0) { alignment = Qt::AlignLeft | Qt::AlignAbsolute; } if (d->isRtl && (alignment & Qt::AlignAbsolute) == 0) { if (alignment & Qt::AlignLeft) { alignment = Qt::AlignRight; } else if (alignment & Qt::AlignRight) { alignment = Qt::AlignLeft; } } alignment |= Qt::AlignAbsolute; QTextOption option(alignment); option.setTextDirection(block.layout()->textOption().textDirection()); /* if (option.textDirection() == Qt::RightToLeft || blockData.counterText().isRightToLeft()) { option.setAlignment(Qt::AlignRight); } */ layout.setTextOption(option); layout.beginLayout(); QTextLine line = layout.createLine(); line.setLineWidth(blockData.counterWidth()); layout.endLayout(); QPointF counterPosition = blockData.counterPosition(); if (block.layout()->lineCount() > 0) { // if there is text, then baseline align the counter. QTextLine firstParagLine = block.layout()->lineAt(0); if (KoListStyle::isNumberingStyle(listStyle)) { //if numbered list baseline align counterPosition += QPointF(0, firstParagLine.ascent() - layout.lineAt(0).ascent()); } else { //for unnumbered list center align counterPosition += QPointF(0, (firstParagLine.height() - layout.lineAt(0).height())/2.0); } } layout.draw(painter, counterPosition); //decorate the list label iff it is a numbered list if (KoListStyle::isNumberingStyle(listStyle)) { painter->save(); decorateListLabel(painter, blockData, layout.lineAt(0), block); painter->restore(); } } KoListStyle::Style listStyle = static_cast(listFormat.style()); if (listStyle == KoListStyle::ImageItem) { QFontMetricsF fm(blockData.labelFormat().font(), d->documentLayout->paintDevice()); qreal x = qMax(qreal(1), blockData.counterPosition().x()); qreal width = qMax(listFormat.doubleProperty(KoListStyle::Width), (qreal)1.0); qreal height = qMax(listFormat.doubleProperty(KoListStyle::Height), (qreal)1.0); qreal y = blockData.counterPosition().y() + fm.ascent() - fm.xHeight()/2 - height/2; // centered KoImageData *idata = listFormat.property(KoListStyle::BulletImage).value(); if (idata) { painter->drawPixmap(x, y, width, height, idata->pixmap()); } } } } void KoTextLayoutArea::decorateListLabel(QPainter *painter, const KoTextBlockData &blockData, const QTextLine &listLabelLine, const QTextBlock &listItem) { const QTextCharFormat listLabelCharFormat = blockData.labelFormat(); painter->setFont(listLabelCharFormat.font()); int startOfFragmentInBlock = 0; Q_ASSERT_X(listLabelLine.isValid(), __FUNCTION__, QString("Invalid list label").toLocal8Bit()); if (!listLabelLine.isValid()) { return; } int fragmentToLineOffset = 0; qreal x1 = blockData.counterPosition().x(); qreal x2 = listItem.layout()->lineAt(0).x(); if (x2 != x1) { drawStrikeOuts(painter, listLabelCharFormat, blockData.counterText(), listItem.layout()->lineAt(0), x1, x2, startOfFragmentInBlock, fragmentToLineOffset); drawOverlines(painter, listLabelCharFormat, blockData.counterText(), listItem.layout()->lineAt(0), x1, x2, startOfFragmentInBlock, fragmentToLineOffset); drawUnderlines(painter, listLabelCharFormat, blockData.counterText(), listItem.layout()->lineAt(0), x1, x2, startOfFragmentInBlock, fragmentToLineOffset); } } /** * Draw a line. Typically meant to underline text or similar. * @param painter the painter to paint on. * @param color the pen color to for the decoration line * @param type The type * @param style the type of line to draw. * @param width The thickness of the line, in pixels (the painter will be prescaled to points coordinate system). * @param x1 we are always drawing horizontal lines, this is the start point. * @param x2 we are always drawing horizontal lines, this is the end point. * @param y the y-offset to paint on. */ static void drawDecorationLine(QPainter *painter, const QColor &color, KoCharacterStyle::LineType type, KoCharacterStyle::LineStyle style, qreal width, const qreal x1, const qreal x2, const qreal y) { QPen penBackup = painter->pen(); QPen pen = painter->pen(); pen.setColor(color); pen.setWidthF(width); if (style == KoCharacterStyle::WaveLine) { // Ok, try the waves :) pen.setStyle(Qt::SolidLine); painter->setPen(pen); qreal x = x1; const qreal halfWaveWidth = 0.5 * width; const qreal halfWaveLength = 2 * width; const int startAngle = 0 * 16; const int middleAngle = 180 * 16; const int endAngle = 180 * 16; while (x < x2) { QRectF rectangle1(x, y, halfWaveLength, 2*halfWaveWidth); if (type == KoCharacterStyle::DoubleLine) { painter->translate(0, -pen.width()); painter->drawArc(rectangle1, startAngle, middleAngle); painter->translate(0, 2*pen.width()); painter->drawArc(rectangle1, startAngle, middleAngle); painter->translate(0, -pen.width()); } else { painter->drawArc(rectangle1, startAngle, middleAngle); } if (x + halfWaveLength > x2) break; QRectF rectangle2(x + halfWaveLength, y, halfWaveLength, 2*halfWaveWidth); if (type == KoCharacterStyle::DoubleLine) { painter->translate(0, -pen.width()); painter->drawArc(rectangle2, middleAngle, endAngle); painter->translate(0, 2*pen.width()); painter->drawArc(rectangle2, middleAngle, endAngle); painter->translate(0, -pen.width()); } else { painter->drawArc(rectangle2, middleAngle, endAngle); } x = x + 2 * halfWaveLength; } } else { if (style == KoCharacterStyle::LongDashLine) { QVector dashes; dashes << 12 << 2; pen.setDashPattern(dashes); } else { pen.setStyle((Qt::PenStyle)style); } painter->setPen(pen); if (type == KoCharacterStyle::DoubleLine) { painter->translate(0, -pen.width()); painter->drawLine(QPointF(x1, y), QPointF(x2, y)); painter->translate(0, 2*pen.width()); painter->drawLine(QPointF(x1, y), QPointF(x2, y)); painter->translate(0, -pen.width()); } else { painter->drawLine(QPointF(x1, y), QPointF(x2, y)); } } painter->setPen(penBackup); } static void drawDecorationText(QPainter *painter, const QTextLine &line, const QColor &color, const QString& decorText, qreal x1, qreal x2) { qreal y = line.position().y(); QPen oldPen = painter->pen(); painter->setPen(QPen(color)); do { QRectF br; painter->drawText(QRectF(QPointF(x1, y), QPointF(x2, y + line.height())), Qt::AlignLeft | Qt::AlignVCenter, decorText, &br); x1 = br.right(); } while (x1 <= x2); painter->setPen(oldPen); } static void drawDecorationWords(QPainter *painter, const QTextLine &line, const QString &text, const QColor &color, KoCharacterStyle::LineType type, KoCharacterStyle::LineStyle style, const QString& decorText, qreal width, const qreal y, const int fragmentToLineOffset, const int startOfFragmentInBlock) { qreal wordBeginX = -1; int j = line.textStart()+fragmentToLineOffset; while (j < line.textLength() + line.textStart() && j-startOfFragmentInBlockpen(); QPen pen = painter->pen(); pen.setWidth(1); pen.setColor(Qt::gray); painter->setPen(pen); qreal xl = layout->boundingRect().left(); qreal xr = qMax(layout->boundingRect().right(), layout->boundingRect().left() + width()); qreal yu = layout->boundingRect().top(); qreal yd = layout->boundingRect().bottom(); qreal bracketSize = painter->fontMetrics().height() / 2; const qreal levelShift = 3; QList openList = KoSectionUtils::sectionStartings(bf); for (int i = 0; i < openList.size(); i++) { int sectionLevel = openList[i]->level(); if (i == 0) { painter->drawLine(xl + sectionLevel * levelShift, yu, xr - sectionLevel * levelShift, yu); } painter->drawLine(xl + sectionLevel * levelShift, yu, xl + sectionLevel * levelShift, yu + bracketSize); painter->drawLine(xr - sectionLevel * levelShift, yu, xr - sectionLevel * levelShift, yu + bracketSize); } QList closeList = KoSectionUtils::sectionEndings(bf); for (int i = 0; i < closeList.size(); i++) { int sectionLevel = closeList[i]->correspondingSection()->level(); if (i == closeList.count() - 1) { painter->drawLine(xl + sectionLevel * levelShift, yd, xr - sectionLevel * levelShift, yd); } painter->drawLine(xl + sectionLevel * levelShift, yd, xl + sectionLevel * levelShift, yd - bracketSize); painter->drawLine(xr - sectionLevel * levelShift, yd, xr - sectionLevel * levelShift, yd - bracketSize); } painter->setPen(penBackup); } void KoTextLayoutArea::decorateParagraph(QPainter *painter, QTextBlock &block, bool showFormattingCharacters, bool showSpellChecking) { QTextLayout *layout = block.layout(); QTextBlockFormat bf = block.blockFormat(); QVariantList tabList = bf.property(KoParagraphStyle::TabPositions).toList(); QFont oldFont = painter->font(); QTextBlock::iterator it; int startOfBlock = -1; int currentTabStop = 0; // qDebug() << "\n-------------------" // << "\nGoing to decorate block\n" // << block.text() // << "\n-------------------"; // loop over text fragments in this paragraph and draw the underline and line through. for (it = block.begin(); !it.atEnd(); ++it) { QTextFragment currentFragment = it.fragment(); if (currentFragment.isValid()) { // qDebug() << "\tGoing to layout fragment:" << currentFragment.text(); QTextCharFormat fmt = currentFragment.charFormat(); painter->setFont(fmt.font()); // a block doesn't have a real start position, so use our own counter. Initialize // it with the position of the first text fragment in the block. if (startOfBlock == -1) { startOfBlock = currentFragment.position(); // start of this block w.r.t. the document } // the start of our fragment in the block is the absolute position of the fragment // in the document minus the start of the block in the document. int startOfFragmentInBlock = currentFragment.position() - startOfBlock; // a fragment can span multiple lines, but we paint the decorations per line. int firstLine = layout->lineForTextPosition(currentFragment.position() - startOfBlock).lineNumber(); int lastLine = layout->lineForTextPosition(currentFragment.position() + currentFragment.length() - startOfBlock).lineNumber(); // qDebug() << "\tfirst line:" << firstLine << "last line:" << lastLine; for (int i = firstLine ; i <= lastLine ; ++i) { QTextLine line = layout->lineAt(i); // qDebug() << "\n\t\tcurrent line:" << i // << "\n\t\tline length:" << line.textLength() << "width:"<< line.width() << "natural width" << line.naturalTextWidth() // << "\n\t\tvalid:" << layout->isValidCursorPosition(currentFragment.position() - startOfBlock) // << "\n\t\tcurrentFragment.position:" << currentFragment.position() // << "\n\t\tstartOfBlock:" << startOfBlock // << "\n\t\tstartOfFragmentInBlock:" << startOfFragmentInBlock; if (layout->isValidCursorPosition(currentFragment.position() - startOfBlock)) { // the start position for painting the decoration is the position of the fragment // inside, but after the first line, the decoration always starts at the beginning // of the line. See bug: 264471 int p1 = startOfFragmentInBlock; if (i > firstLine) { p1 = line.textStart(); } // qDebug() << "\n\t\tblock.text.length:" << block.text().length() << "p1" << p1; if (block.text().length() > p1 && block.text().at(p1) != QChar::ObjectReplacementCharacter) { Q_ASSERT_X(line.isValid(), __FUNCTION__, QString("Invalid line=%1 first=%2 last=%3").arg(i).arg(firstLine).arg(lastLine).toLocal8Bit()); // see bug 278682 if (!line.isValid()) continue; // end position: note that x2 can be smaller than x1 when we are handling RTL int p2 = startOfFragmentInBlock + currentFragment.length(); int lineEndWithoutPreedit = line.textStart() + line.textLength(); if (block.layout()->preeditAreaPosition() >= block.position() + line.textStart() && block.layout()->preeditAreaPosition() <= block.position() + line.textStart() + line.textLength()) { lineEndWithoutPreedit -= block.layout()->preeditAreaText().length(); } while (lineEndWithoutPreedit > line.textStart() && block.text().at(lineEndWithoutPreedit - 1) == ' ') { --lineEndWithoutPreedit; } if (lineEndWithoutPreedit < p2) { //line caps p2 = lineEndWithoutPreedit; } int fragmentToLineOffset = qMax(startOfFragmentInBlock - line.textStart(), 0); qreal x1 = line.cursorToX(p1); qreal x2 = line.cursorToX(p2); //qDebug() << "\n\t\t\tp1:" << p1 << "x1:" << x1 // << "\n\t\t\tp2:" << p2 << "x2:" << x2 // << "\n\t\t\tlineEndWithoutPreedit" << lineEndWithoutPreedit; if (x1 != x2) { drawStrikeOuts(painter, fmt, currentFragment.text(), line, x1, x2, startOfFragmentInBlock, fragmentToLineOffset); drawOverlines(painter, fmt, currentFragment.text(), line, x1, x2, startOfFragmentInBlock, fragmentToLineOffset); drawUnderlines(painter, fmt, currentFragment.text(), line, x1, x2, startOfFragmentInBlock, fragmentToLineOffset); } decorateTabsAndFormatting(painter, currentFragment, line, startOfFragmentInBlock, tabList, currentTabStop, showFormattingCharacters); // underline preedit strings if (layout->preeditAreaPosition() > -1) { int start = block.layout()->preeditAreaPosition(); int end = block.layout()->preeditAreaPosition() + block.layout()->preeditAreaText().length(); QTextCharFormat underline; underline.setFontUnderline(true); underline.setUnderlineStyle(QTextCharFormat::DashUnderline); //qDebug() << "underline style" << underline.underlineStyle(); //qDebug() << "line start: " << block.position() << line.textStart(); qreal z1 = 0; qreal z2 = 0; // preedit start in this line, end in this line if ( start >= block.position() + line.textStart() && end <= block.position() + line.textStart() + line.textLength() ) { z1 = line.cursorToX(start); z2 = line.cursorToX(end); } // preedit start in this line, end after this line if ( start >= block.position() + line.textStart() && end > block.position() + line.textStart() + line.textLength() ) { z1 = line.cursorToX(start); z2 = line.cursorToX(block.position() + line.textStart() + line.textLength()); } // preedit start before this line, end in this line if ( start < block.position() + line.textStart() && end <= block.position() + line.textStart() + line.textLength() ) { z1 = line.cursorToX(block.position() + line.textStart()); z2 = line.cursorToX(end); } // preedit start before this line, end after this line if ( start < block.position() + line.textStart() && end > block.position() + line.textStart() + line.textLength() ) { z1 = line.cursorToX(block.position() + line.textStart()); z2 = line.cursorToX(block.position() + line.textStart() + line.textLength()); } if (z2 > z1) { //qDebug() << "z1: " << z1 << "z2: " << z2; KoCharacterStyle::LineStyle fontUnderLineStyle = KoCharacterStyle::DashLine; KoCharacterStyle::LineType fontUnderLineType = KoCharacterStyle::SingleLine; QTextCharFormat::VerticalAlignment valign = fmt.verticalAlignment(); QFont font(fmt.font()); if (valign == QTextCharFormat::AlignSubScript || valign == QTextCharFormat::AlignSuperScript) font.setPointSize(font.pointSize() * 2 / 3); QFontMetricsF metrics(font, d->documentLayout->paintDevice()); qreal y = line.position().y(); if (valign == QTextCharFormat::AlignSubScript) y += line.height() - metrics.descent() + metrics.underlinePos(); else if (valign == QTextCharFormat::AlignSuperScript) y += metrics.ascent() + metrics.underlinePos(); else y += line.ascent() + metrics.underlinePos(); QColor color = fmt.foreground().color(); qreal width = computeWidth( // line thickness (KoCharacterStyle::LineWeight) underline.intProperty(KoCharacterStyle::UnderlineWeight), underline.doubleProperty(KoCharacterStyle::UnderlineWidth), font); if (valign == QTextCharFormat::AlignSubScript || valign == QTextCharFormat::AlignSuperScript) // adjust size. width = width * 2 / 3; drawDecorationLine(painter, color, fontUnderLineType, fontUnderLineStyle, width, z1, z2, y); } } } } } } } if (showFormattingCharacters) { QTextLine line = layout->lineForTextPosition(block.length()-1); qreal y = line.position().y() + line.ascent(); qreal x = line.cursorToX(block.length()-1); painter->drawText(QPointF(x, y), QChar((ushort)0x00B6)); } if (showSpellChecking) { // Finally let's paint our own spelling markings // TODO Should we make this optional at this point (right on/off handled by the plugin) // also we might want to provide alternative ways of drawing it KoTextBlockData blockData(block); QPen penBackup = painter->pen(); QPen pen; pen.setColor(QColor(Qt::red)); pen.setWidthF(1.5); QVector pattern; pattern << 1 << 2; pen.setDashPattern(pattern); painter->setPen(pen); QList::Iterator markIt = blockData.markupsBegin(KoTextBlockData::Misspell); QList::Iterator markEnd = blockData.markupsEnd(KoTextBlockData::Misspell); for (int i = 0 ; i < layout->lineCount(); ++i) { if (markIt == markEnd) { break; } QTextLine line = layout->lineAt(i); // the y position is placed half way between baseline and descent of the line // this is fast and sufficient qreal y = line.position().y() + line.ascent() + 0.5 * line.descent(); // first handle all those marking ranges that end on this line while (markIt != markEnd && markIt->lastChar < line.textStart() + line.textLength() && line.textStart() + line.textLength() <= block.length()) { if (!blockData.isMarkupsLayoutValid(KoTextBlockData::Misspell)) { if (markIt->firstChar > line.textStart()) { markIt->startX = line.cursorToX(markIt->firstChar); } markIt->endX = line.cursorToX(qMin(markIt->lastChar, block.length())); } qreal x1 = (markIt->firstChar > line.textStart()) ? markIt->startX : line.cursorToX(0); painter->drawLine(QPointF(x1, y), QPointF(markIt->endX, y)); ++markIt; } // there may be a markup range on this line that extends to the next line if (markIt != markEnd && markIt->firstChar < line.textStart() + line.textLength() && line.textStart() + line.textLength() <= block.length()) { if (!blockData.isMarkupsLayoutValid(KoTextBlockData::Misspell)) { if (markIt->firstChar > line.textStart()) { markIt->startX = line.cursorToX(markIt->firstChar); } } qreal x1 = (markIt->firstChar > line.textStart()) ? markIt->startX : line.cursorToX(0); painter->drawLine(QPointF(x1, y), QPointF(line.cursorToX(line.textStart() + line.textLength()), y)); // since it extends to next line we don't increment the iterator } } blockData.setMarkupsLayoutValidity(KoTextBlockData::Misspell, true); painter->setPen(penBackup); } painter->setFont(oldFont); } void KoTextLayoutArea::drawStrikeOuts(QPainter *painter, const QTextCharFormat ¤tCharFormat, const QString &text, const QTextLine &line, qreal x1, qreal x2, const int startOfFragmentInBlock, const int fragmentToLineOffset) const { KoCharacterStyle::LineStyle strikeOutStyle = (KoCharacterStyle::LineStyle) currentCharFormat.intProperty(KoCharacterStyle::StrikeOutStyle); KoCharacterStyle::LineType strikeOutType = (KoCharacterStyle::LineType) currentCharFormat.intProperty(KoCharacterStyle::StrikeOutType); if ((strikeOutStyle != KoCharacterStyle::NoLineStyle) && (strikeOutType != KoCharacterStyle::NoLineType)) { QTextCharFormat::VerticalAlignment valign = currentCharFormat.verticalAlignment(); QFont font(currentCharFormat.font()); if (valign == QTextCharFormat::AlignSubScript || valign == QTextCharFormat::AlignSuperScript) font.setPointSize(qRound(font.pointSize() * 2 / 3.)); QFontMetricsF metrics(font, d->documentLayout->paintDevice()); qreal y = line.position().y(); if (valign == QTextCharFormat::AlignSubScript) y += line.height() - metrics.descent() - metrics.strikeOutPos(); else if (valign == QTextCharFormat::AlignSuperScript) y += metrics.ascent() - metrics.strikeOutPos(); else y += line.ascent() - metrics.strikeOutPos(); QColor color = currentCharFormat.colorProperty(KoCharacterStyle::StrikeOutColor); if (!color.isValid()) color = currentCharFormat.foreground().color(); KoCharacterStyle::LineMode strikeOutMode = (KoCharacterStyle::LineMode) currentCharFormat.intProperty(KoCharacterStyle::StrikeOutMode); QString strikeOutText = currentCharFormat.stringProperty(KoCharacterStyle::StrikeOutText); qreal width = 0; // line thickness if (strikeOutText.isEmpty()) { width = computeWidth( (KoCharacterStyle::LineWeight) currentCharFormat.intProperty(KoCharacterStyle::StrikeOutWeight), currentCharFormat.doubleProperty(KoCharacterStyle::StrikeOutWidth), font); } if (valign == QTextCharFormat::AlignSubScript || valign == QTextCharFormat::AlignSuperScript) // adjust size. width = width * 2 / 3; if (strikeOutMode == KoCharacterStyle::SkipWhiteSpaceLineMode) { drawDecorationWords(painter, line, text, color, strikeOutType, strikeOutStyle, strikeOutText, width, y, fragmentToLineOffset, startOfFragmentInBlock); } else { if (strikeOutText.isEmpty()) drawDecorationLine(painter, color, strikeOutType, strikeOutStyle, width, x1, x2, y); else drawDecorationText(painter, line, color, strikeOutText, x1, x2); } } } void KoTextLayoutArea::drawOverlines(QPainter *painter, const QTextCharFormat ¤tCharFormat, const QString &text, const QTextLine &line, qreal x1, qreal x2, const int startOfFragmentInBlock, const int fragmentToLineOffset) const { KoCharacterStyle::LineStyle fontOverLineStyle = (KoCharacterStyle::LineStyle) currentCharFormat.intProperty(KoCharacterStyle::OverlineStyle); KoCharacterStyle::LineType fontOverLineType = (KoCharacterStyle::LineType) currentCharFormat.intProperty(KoCharacterStyle::OverlineType); if ((fontOverLineStyle != KoCharacterStyle::NoLineStyle) && (fontOverLineType != KoCharacterStyle::NoLineType)) { QTextCharFormat::VerticalAlignment valign = currentCharFormat.verticalAlignment(); QFont font(currentCharFormat.font()); if (valign == QTextCharFormat::AlignSubScript || valign == QTextCharFormat::AlignSuperScript) font.setPointSize(font.pointSize() * 2 / 3); QFontMetricsF metrics(font, d->documentLayout->paintDevice()); qreal y = line.position().y(); if (valign == QTextCharFormat::AlignSubScript) y += line.height() - metrics.descent() - metrics.overlinePos(); else if (valign == QTextCharFormat::AlignSuperScript) y += metrics.ascent() - metrics.overlinePos(); else y += line.ascent() - metrics.overlinePos(); QColor color = currentCharFormat.colorProperty(KoCharacterStyle::OverlineColor); if (!color.isValid()) color = currentCharFormat.foreground().color(); KoCharacterStyle::LineMode overlineMode = (KoCharacterStyle::LineMode) currentCharFormat.intProperty(KoCharacterStyle::OverlineMode); qreal width = computeWidth( // line thickness (KoCharacterStyle::LineWeight) currentCharFormat.intProperty(KoCharacterStyle::OverlineWeight), currentCharFormat.doubleProperty(KoCharacterStyle::OverlineWidth), font); if (valign == QTextCharFormat::AlignSubScript || valign == QTextCharFormat::AlignSuperScript) // adjust size. width = width * 2 / 3; if (overlineMode == KoCharacterStyle::SkipWhiteSpaceLineMode) { drawDecorationWords(painter, line, text, color, fontOverLineType, fontOverLineStyle, QString(), width, y, fragmentToLineOffset, startOfFragmentInBlock); } else { drawDecorationLine(painter, color, fontOverLineType, fontOverLineStyle, width, x1, x2, y); } } } void KoTextLayoutArea::drawUnderlines(QPainter *painter, const QTextCharFormat ¤tCharFormat,const QString &text, const QTextLine &line, qreal x1, qreal x2, const int startOfFragmentInBlock, const int fragmentToLineOffset) const { KoCharacterStyle::LineStyle fontUnderLineStyle = (KoCharacterStyle::LineStyle) currentCharFormat.intProperty(KoCharacterStyle::UnderlineStyle); KoCharacterStyle::LineType fontUnderLineType = (KoCharacterStyle::LineType) currentCharFormat.intProperty(KoCharacterStyle::UnderlineType); if ((fontUnderLineStyle != KoCharacterStyle::NoLineStyle) && (fontUnderLineType != KoCharacterStyle::NoLineType)) { QTextCharFormat::VerticalAlignment valign = currentCharFormat.verticalAlignment(); QFont font(currentCharFormat.font()); if (valign == QTextCharFormat::AlignSubScript || valign == QTextCharFormat::AlignSuperScript) font.setPointSize(font.pointSize() * 2 / 3); QFontMetricsF metrics(font, d->documentLayout->paintDevice()); qreal y = line.position().y(); if (valign == QTextCharFormat::AlignSubScript) y += line.height() - metrics.descent() + metrics.underlinePos(); else if (valign == QTextCharFormat::AlignSuperScript) y += metrics.ascent() + metrics.underlinePos(); else y += line.ascent() + metrics.underlinePos(); QColor color = currentCharFormat.underlineColor(); if (!color.isValid()) color = currentCharFormat.foreground().color(); KoCharacterStyle::LineMode underlineMode = (KoCharacterStyle::LineMode) currentCharFormat.intProperty(KoCharacterStyle::UnderlineMode); qreal width = computeWidth( // line thickness (KoCharacterStyle::LineWeight) currentCharFormat.intProperty(KoCharacterStyle::UnderlineWeight), currentCharFormat.doubleProperty(KoCharacterStyle::UnderlineWidth), font); if (valign == QTextCharFormat::AlignSubScript || valign == QTextCharFormat::AlignSuperScript) // adjust size. width = width * 2 / 3; if (underlineMode == KoCharacterStyle::SkipWhiteSpaceLineMode) { drawDecorationWords(painter, line, text, color, fontUnderLineType, fontUnderLineStyle, QString(), width, y, fragmentToLineOffset, startOfFragmentInBlock); } else { drawDecorationLine(painter, color, fontUnderLineType, fontUnderLineStyle, width, x1, x2, y); } } } // Decorate any tabs ('\t's) in 'currentFragment' and laid out in 'line'. int KoTextLayoutArea::decorateTabsAndFormatting(QPainter *painter, const QTextFragment& currentFragment, const QTextLine &line, const int startOfFragmentInBlock, const QVariantList& tabList, int currentTabStop, bool showFormattingCharacters) { // If a line in the layout represent multiple text fragments, this function will // be called multiple times on the same line, with different fragments. // Likewise, if a fragment spans two lines, then this function will be called twice // on the same fragment, once for each line. QString fragText = currentFragment.text(); QFontMetricsF fm(currentFragment.charFormat().font(), d->documentLayout->paintDevice()); qreal tabStyleLineMargin = fm.averageCharWidth() / 4; // leave some margin for the tab decoration line // currentFragment.position() : start of this fragment w.r.t. the document // startOfFragmentInBlock : start of this fragment w.r.t. the block // line.textStart() : start of this line w.r.t. the block int searchForCharFrom; // search for \t from this point onwards in fragText int searchForCharTill; // search for \t till this point in fragText if (line.textStart() >= startOfFragmentInBlock) { // fragment starts at or before the start of line // we are concerned with only that part of the fragment displayed in this line searchForCharFrom = line.textStart() - startOfFragmentInBlock; // It's a new line. So we should look at the first tab-stop properties for the next \t. currentTabStop = 0; } else { // fragment starts in the middle of the line searchForCharFrom = 0; } if (line.textStart() + line.textLength() > startOfFragmentInBlock + currentFragment.length()) { // fragment ends before the end of line. need to see only till the end of the fragment. searchForCharTill = currentFragment.length(); } else { // line ends before the fragment ends. need to see only till the end of this line. // but then, we need to convert the end of line to an index into fragText searchForCharTill = line.textLength() + line.textStart() - startOfFragmentInBlock; } for (int i = searchForCharFrom ; i < searchForCharTill; i++) { if (currentTabStop >= tabList.size() && !showFormattingCharacters) // no more decorations break; if (fragText[i] == '\t') { qreal x1(0.0); qreal x2(0.0); if (showFormattingCharacters) { x1 = line.cursorToX(startOfFragmentInBlock + i); x2 = line.cursorToX(startOfFragmentInBlock + i + 1); qreal y = line.position().y() + line.ascent() - fm.xHeight()/2.0; qreal arrowDim = fm.xHeight()/2.0; QPen penBackup = painter->pen(); QPen pen = painter->pen(); pen.setWidthF(fm.ascent()/10.0); pen.setStyle(Qt::SolidLine); painter->setPen(pen); painter->drawLine(QPointF(x1, y), QPointF(x2, y)); painter->drawLine(QPointF(x2 - arrowDim, y - arrowDim), QPointF(x2, y)); painter->drawLine(QPointF(x2 - arrowDim, y + arrowDim), QPointF(x2, y)); painter->setPen(penBackup); } if (currentTabStop < tabList.size()) { // still tabsstops worth examining if (!showFormattingCharacters) { // only then was it not calculated x1 = line.cursorToX(startOfFragmentInBlock + i); } // find a tab-stop decoration for this tab position // for eg., if there's a tab-stop at 1in, but the text before \t already spans 1.2in, // we should look at the next tab-stop KoText::Tab tab; do { tab = qvariant_cast(tabList[currentTabStop]); currentTabStop++; // comparing with x1 should work for all of left/right/center/char tabs } while (tab.position <= x1 && currentTabStop < tabList.size()); if (tab.position > x1) { if (!showFormattingCharacters) { // only then was it not calculated x2 = line.cursorToX(startOfFragmentInBlock + i + 1); } qreal tabStyleLeftLineMargin = tabStyleLineMargin; qreal tabStyleRightLineMargin = tabStyleLineMargin; // no margin if its adjacent char is also a tab if (i > searchForCharFrom && fragText[i-1] == '\t') tabStyleLeftLineMargin = 0; if (i < (searchForCharTill - 1) && fragText[i+1] == '\t') tabStyleRightLineMargin = 0; qreal y = line.position().y() + line.ascent() - 1; x1 += tabStyleLeftLineMargin; x2 -= tabStyleRightLineMargin; QColor tabDecorColor = currentFragment.charFormat().foreground().color(); if (tab.leaderColor.isValid()) tabDecorColor = tab.leaderColor; qreal width = computeWidth(tab.leaderWeight, tab.leaderWidth, painter->font()); if (x1 < x2) { if (tab.leaderText.isEmpty()) { drawDecorationLine(painter, tabDecorColor, tab.leaderType, tab.leaderStyle, width, x1, x2, y); } else { drawDecorationText(painter, line, tabDecorColor, tab.leaderText, x1, x2); } } } } } else if (showFormattingCharacters) { if (fragText[i] == ' ' || fragText[i] == QChar::Nbsp) { qreal x = line.cursorToX(startOfFragmentInBlock + i); qreal y = line.position().y() + line.ascent(); painter->drawText(QPointF(x, y), QChar((ushort)0xb7)); } else if (fragText[i] == QChar::LineSeparator){ qreal x = line.cursorToX(startOfFragmentInBlock + i); qreal y = line.position().y() + line.ascent(); painter->drawText(QPointF(x, y), QChar((ushort)0x21B5)); } } } return currentTabStop; } diff --git a/plugins/flake/textshape/textlayout/ListItemsHelper.cpp b/plugins/flake/textshape/textlayout/ListItemsHelper.cpp index aa30260f90..d3b2709dd3 100644 --- a/plugins/flake/textshape/textlayout/ListItemsHelper.cpp +++ b/plugins/flake/textshape/textlayout/ListItemsHelper.cpp @@ -1,503 +1,503 @@ /* This file is part of the KDE project * Copyright (C) 2006-2007, 2010 Thomas Zander * Copyright (C) 2008 Girish Ramakrishnan * * 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 "ListItemsHelper.h" #include #include #include #include #include #include #include using namespace Lists; QString Lists::intToRoman(int n) { static const QString RNUnits[] = {"", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"}; static const QString RNTens[] = {"", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"}; static const QString RNHundreds[] = {"", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"}; static const QString RNThousands[] = {"", "m", "mm", "mmm", "mmmm", "mmmmm", "mmmmmm", "mmmmmmm", "mmmmmmmm", "mmmmmmmmm"}; if (n <= 0) { warnTextLayout << "intToRoman called with negative number: n=" << n; return QString::number(n); } return RNThousands[(n / 1000)] + RNHundreds[(n / 100) % 10 ] + RNTens[(n / 10) % 10 ] + RNUnits[(n) % 10 ]; } QString Lists::intToAlpha(int n, Capitalisation caps, bool letterSynchronization) { const char offset = caps == Uppercase ? 'A' : 'a'; QString answer; if (letterSynchronization) { int digits = 1; for (; n > 26; n -= 26) digits += 1; for (int i = 0; i < digits; i++) answer.prepend(QChar(offset + n - 1)); return answer; } else { char bottomDigit; while (n > 26) { bottomDigit = (n - 1) % 26; n = (n - 1) / 26; answer.prepend(QChar(offset + bottomDigit)); } } answer.prepend(QChar(offset + n - 1)); return answer; } QString Lists::intToScript(int n, KoListStyle::Style type) { // 10-base static const int bengali = 0x9e6; static const int gujarati = 0xae6; static const int gurumukhi = 0xa66; static const int kannada = 0xce6; static const int malayalam = 0xd66; static const int oriya = 0xb66; static const int tamil = 0x0be6; static const int telugu = 0xc66; static const int tibetan = 0xf20; static const int thai = 0xe50; int offset; switch (type) { case KoListStyle::Bengali: offset = bengali; break; case KoListStyle::Gujarati: offset = gujarati; break; case KoListStyle::Gurumukhi: offset = gurumukhi; break; case KoListStyle::Kannada: offset = kannada; break; case KoListStyle::Malayalam: offset = malayalam; break; case KoListStyle::Oriya: offset = oriya; break; case KoListStyle::Tamil: offset = tamil; break; case KoListStyle::Telugu: offset = telugu; break; case KoListStyle::Tibetan: offset = tibetan; break; case KoListStyle::Thai: offset = thai; break; default: return QString::number(n); } QString answer; while (n > 0) { answer.prepend(QChar(offset + n % 10)); n = n / 10; } return answer; } QString Lists::intToScriptList(int n, KoListStyle::Style type) { // 1 time Sequences // note; the leading X is to make these 1 based. static const char* const Abjad[] = { "أ", "ب", "ج", "د", "ﻫ", "و", "ز", "ح", "ط", "ي", "ك", "ل", "م", "ن", "س", "ع", "ف", "ص", "ق", "ر", "ش", "ت", "ث", "خ", "ذ", "ض", "ظ", "غ" }; static const char* const Abjad2[] = { "ﺃ", "ﺏ", "ﺝ", "ﺩ", "ﻫ", "ﻭ", "ﺯ", "ﺡ", "ﻁ", "ﻱ", "ﻙ", "ﻝ", "ﻡ", "ﻥ", "ﺹ", "ﻉ", "ﻑ", "ﺽ", "ﻕ", "ﺭ", "ﺱ", "ﺕ", "ﺙ", "ﺥ", "ﺫ", "ﻅ", "ﻍ", "ﺵ" }; static const char* const ArabicAlphabet[] = {"ا", "ب", "ت", "ث", "ج", "ح", "خ", "د", "ذ", "ر", "ز", "س", "ش", "ص", "ض", "ط", "ظ", "ع", "غ", "ف", "ق", "ك", "ل", "م", "ن", "ه", "و", "ي" }; /* // see this page for the 10, 100, 1000 etc http://en.wikipedia.org/wiki/Chinese_numerals static const char* const chinese1[] = { '零','壹','貳','叄','肆','伍','陸','柒','捌','玖' }; static const char* const chinese2[] = { '〇','一','二','三','四','五','六','七','八','九' }; - TODO: http://en.wikipedia.org/wiki/Korean_numerals - http://en.wikipedia.org/wiki/Japanese_numerals - 'http://en.wikipedia.org/wiki/Hebrew_numerals' - 'http://en.wikipedia.org/wiki/Armenian_numerals' - 'http://en.wikipedia.org/wiki/Greek_numerals' - 'http://en.wikipedia.org/wiki/Cyrillic_numerals' - 'http://en.wikipedia.org/wiki/Sanskrit_numerals' - 'http://en.wikipedia.org/wiki/Ge%27ez_alphabet#Numerals' - 'http://en.wikipedia.org/wiki/Abjad_numerals' + TODO: https://en.wikipedia.org/wiki/Korean_numerals + https://en.wikipedia.org/wiki/Japanese_numerals + 'https://en.wikipedia.org/wiki/Hebrew_numerals' + 'https://en.wikipedia.org/wiki/Armenian_numerals' + 'https://en.wikipedia.org/wiki/Greek_numerals' + 'https://en.wikipedia.org/wiki/Cyrillic_numerals' + 'https://en.wikipedia.org/wiki/Sanskrit_numerals' + 'https://en.wikipedia.org/wiki/Ge%27ez_alphabet#Numerals' + 'https://en.wikipedia.org/wiki/Abjad_numerals' */ switch (type) { case KoListStyle::Abjad: if (n > 22) return "*"; return QString::fromUtf8(Abjad[n-1]); case KoListStyle::AbjadMinor: if (n > 22) return "*"; return QString::fromUtf8(Abjad2[n-1]); case KoListStyle::ArabicAlphabet: if (n > 28) return "*"; return QString::fromUtf8(ArabicAlphabet[n-1]); default: return QString::number(n); } } QString Lists::intToNumberingStyle(int index, KoListStyle::Style listStyle, bool letterSynchronization) { QString counterText; switch(listStyle) { case KoListStyle::DecimalItem: counterText = QString::number(index); break; case KoListStyle::AlphaLowerItem: counterText = intToAlpha(index, Lowercase, letterSynchronization); break; case KoListStyle::UpperAlphaItem: counterText = intToAlpha(index, Uppercase, letterSynchronization); break; case KoListStyle::RomanLowerItem: counterText = intToRoman(index); break; case KoListStyle::UpperRomanItem: counterText = intToRoman(index).toUpper(); break; case KoListStyle::Bengali: case KoListStyle::Gujarati: case KoListStyle::Gurumukhi: case KoListStyle::Kannada: case KoListStyle::Malayalam: case KoListStyle::Oriya: case KoListStyle::Tamil: case KoListStyle::Telugu: case KoListStyle::Tibetan: case KoListStyle::Thai: counterText = intToScript(index, listStyle); break; case KoListStyle::Abjad: case KoListStyle::ArabicAlphabet: case KoListStyle::AbjadMinor: counterText = intToScriptList(index, listStyle); break; default: counterText = QString::number(index); } return counterText; } QList Lists::genericListStyleItems() { QList answer; answer.append(ListStyleItem(i18nc("Text list-style", "None"), KoListStyle::None)); answer.append(ListStyleItem(i18n("Small Bullet"), KoListStyle::Bullet)); answer.append(ListStyleItem(i18n("Circle Bullet"), KoListStyle::CircleItem)); answer.append(ListStyleItem(i18n("Square Bullet"), KoListStyle::SquareItem)); answer.append(ListStyleItem(i18n("Rhombus Bullet"), KoListStyle::RhombusItem)); answer.append(ListStyleItem(i18n("Check Mark Bullet"), KoListStyle::HeavyCheckMarkItem)); answer.append(ListStyleItem(i18n("Rightwards Arrow Bullet"), KoListStyle::RightArrowItem)); answer.append(ListStyleItem(i18n("Arabic"), KoListStyle::DecimalItem)); answer.append(ListStyleItem(i18n("Lower Alphabetical"), KoListStyle::AlphaLowerItem)); answer.append(ListStyleItem(i18n("Upper Alphabetical"), KoListStyle::UpperAlphaItem)); answer.append(ListStyleItem(i18n("Lower Roman"), KoListStyle::RomanLowerItem)); answer.append(ListStyleItem(i18n("Upper Roman"), KoListStyle::UpperRomanItem)); return answer; } QList Lists::otherListStyleItems() { QList answer; answer.append(ListStyleItem(i18n("Large Bullet"), KoListStyle::BlackCircle)); answer.append(ListStyleItem(i18n("Ballot X Bullet"), KoListStyle::BallotXItem)); answer.append(ListStyleItem(i18n("Rightwards Arrow Head Bullet"), KoListStyle::RightArrowHeadItem)); answer.append(ListStyleItem(i18n("Bengali"), KoListStyle::Bengali)); answer.append(ListStyleItem(i18n("Gujarati"), KoListStyle::Gujarati)); answer.append(ListStyleItem(i18n("Gurumukhi"), KoListStyle::Gurumukhi)); answer.append(ListStyleItem(i18n("Kannada"), KoListStyle::Kannada)); answer.append(ListStyleItem(i18n("Malayalam"), KoListStyle::Malayalam)); answer.append(ListStyleItem(i18n("Oriya"), KoListStyle::Oriya)); answer.append(ListStyleItem(i18n("Tamil"), KoListStyle::Tamil)); answer.append(ListStyleItem(i18n("Telugu"), KoListStyle::Telugu)); answer.append(ListStyleItem(i18n("Tibetan"), KoListStyle::Tibetan)); answer.append(ListStyleItem(i18n("Thai"), KoListStyle::Thai)); answer.append(ListStyleItem(i18n("Abjad"), KoListStyle::Abjad)); answer.append(ListStyleItem(i18n("AbjadMinor"), KoListStyle::AbjadMinor)); answer.append(ListStyleItem(i18n("ArabicAlphabet"), KoListStyle::ArabicAlphabet)); answer.append(ListStyleItem(i18n("Image"), KoListStyle::ImageItem)); return answer; } // ------------------- ListItemsHelper ------------ /// \internal helper class for calculating text-lists prefixes and indents ListItemsHelper::ListItemsHelper(QTextList *textList, const QFont &font) : m_textList(textList) , m_fm(font, textList->document()->documentLayout()->paintDevice()) { } void ListItemsHelper::recalculateBlock(QTextBlock &block) { //warnTextLayout; const QTextListFormat format = m_textList->format(); const KoListStyle::Style listStyle = static_cast(format.style()); const QString prefix = format.stringProperty(KoListStyle::ListItemPrefix); const QString suffix = format.stringProperty(KoListStyle::ListItemSuffix); const int level = format.intProperty(KoListStyle::Level); int dp = format.intProperty(KoListStyle::DisplayLevel); if (dp > level) dp = level; const int displayLevel = dp ? dp : 1; QTextBlockFormat blockFormat = block.blockFormat(); // Look if we have a block that is inside a header. We need to special case them cause header-lists are // different from any other kind of list and they do build up there own global list (table of content). bool isOutline = blockFormat.intProperty(KoParagraphStyle::OutlineLevel) > 0; int startValue = 1; if (format.hasProperty(KoListStyle::StartValue)) startValue = format.intProperty(KoListStyle::StartValue); int index = startValue; bool fixed = false; if (blockFormat.boolProperty(KoParagraphStyle::RestartListNumbering)) { index = format.intProperty(KoListStyle::StartValue); fixed = true; } const int paragIndex = blockFormat.intProperty(KoParagraphStyle::ListStartValue); if (paragIndex > 0) { index = paragIndex; fixed = true; } if (!fixed) { //if this is the first item then find if the list has to be continued from any other list KoList *listContinued = 0; if (m_textList->itemNumber(block) == 0 && KoTextDocument(m_textList->document()).list(m_textList) && (listContinued = KoTextDocument(m_textList->document()).list(m_textList)->listContinuedFrom())) { //find the previous list of the same level QTextList *previousTextList = listContinued->textLists().at(level - 1).data(); if (previousTextList) { QTextBlock textBlock = previousTextList->item(previousTextList->count() - 1); if (textBlock.isValid()) { index = KoTextBlockData(textBlock).counterIndex() + 1; //resume the previous list count } } } else if (m_textList->itemNumber(block) > 0) { QTextBlock textBlock = m_textList->item(m_textList->itemNumber(block) - 1); if (textBlock.isValid()) { index = KoTextBlockData(textBlock).counterIndex() + 1; //resume the previous list count } } } qreal width = 0.0; KoTextBlockData blockData(block); if (blockFormat.boolProperty(KoParagraphStyle::UnnumberedListItem) || blockFormat.boolProperty(KoParagraphStyle::IsListHeader)) { blockData.setCounterPlainText(QString()); blockData.setCounterPrefix(QString()); blockData.setCounterSuffix(QString()); blockData.setPartialCounterText(QString()); // set the counter for the current un-numbered list to the counter index of the previous list item. // index-1 because the list counter would have already incremented by one blockData.setCounterIndex(index - 1); if (blockFormat.boolProperty(KoParagraphStyle::IsListHeader)) { blockData.setCounterWidth(format.doubleProperty(KoListStyle::MinimumWidth)); blockData.setCounterSpacing(0); } return; } QString item; if (displayLevel > 1) { int checkLevel = level; int tmpDisplayLevel = displayLevel; bool counterResetRequired = true; for (QTextBlock b = block.previous(); tmpDisplayLevel > 1 && b.isValid(); b = b.previous()) { if (b.textList() == 0) continue; QTextListFormat lf = b.textList()->format(); if (lf.property(KoListStyle::StyleId) != format.property(KoListStyle::StyleId)) continue; // uninteresting for us if (isOutline != bool(b.blockFormat().intProperty(KoParagraphStyle::OutlineLevel))) continue; // also uninteresting cause the one is an outline-listitem while the other is not if (! KoListStyle::isNumberingStyle(static_cast(lf.style()))) { continue; } if (b.blockFormat().boolProperty(KoParagraphStyle::UnnumberedListItem)) { continue; //unnumbered listItems are irrelevant } const int otherLevel = lf.intProperty(KoListStyle::Level); if (isOutline && checkLevel == otherLevel) { counterResetRequired = false; } if (checkLevel <= otherLevel) continue; KoTextBlockData otherData(b); if (!otherData.hasCounterData()) { continue; } if (tmpDisplayLevel - 1 < otherLevel) { // can't just copy it fully since we are // displaying less then the full counter item += otherData.partialCounterText(); tmpDisplayLevel--; checkLevel--; for (int i = otherLevel + 1; i < level; i++) { tmpDisplayLevel--; item += "." + intToNumberingStyle(index, listStyle, m_textList->format().boolProperty(KoListStyle::LetterSynchronization)); // add missing counters. } } else { // just copy previous counter as prefix QString otherPrefix = lf.stringProperty(KoListStyle::ListItemPrefix); QString otherSuffix = lf.stringProperty(KoListStyle::ListItemSuffix); QString pureCounter = otherData.counterText().mid(otherPrefix.size()); pureCounter = pureCounter.left(pureCounter.size() - otherSuffix.size()); item += pureCounter; for (int i = otherLevel + 1; i < level; i++) item += "." + intToNumberingStyle(index, listStyle, m_textList->format().boolProperty(KoListStyle::LetterSynchronization)); // add missing counters. tmpDisplayLevel = 0; if (isOutline && counterResetRequired) { index = 1; } break; } } for (int i = 1; i < tmpDisplayLevel; i++) item = intToNumberingStyle(index, listStyle, m_textList->format().boolProperty(KoListStyle::LetterSynchronization)) + "." + item; // add missing counters. } if ((listStyle == KoListStyle::DecimalItem || listStyle == KoListStyle::AlphaLowerItem || listStyle == KoListStyle::UpperAlphaItem || listStyle == KoListStyle::RomanLowerItem || listStyle == KoListStyle::UpperRomanItem) && !(item.isEmpty() || item.endsWith('.') || item.endsWith(' '))) { item += '.'; } bool calcWidth = true; QString partialCounterText; if (KoListStyle::isNumberingStyle(listStyle)) { partialCounterText = intToNumberingStyle(index, listStyle, m_textList->format().boolProperty(KoListStyle::LetterSynchronization)); } else { switch (listStyle) { case KoListStyle::SquareItem: case KoListStyle::Bullet: case KoListStyle::BlackCircle: case KoListStyle::DiscItem: case KoListStyle::CircleItem: case KoListStyle::HeavyCheckMarkItem: case KoListStyle::BallotXItem: case KoListStyle::RightArrowItem: case KoListStyle::RightArrowHeadItem: case KoListStyle::RhombusItem: case KoListStyle::BoxItem: { calcWidth = false; if (format.intProperty(KoListStyle::BulletCharacter)) item = QString(QChar(format.intProperty(KoListStyle::BulletCharacter))); width = m_fm.width(item); int percent = format.intProperty(KoListStyle::RelativeBulletSize); if (percent > 0) width = width * (percent / 100.0); break; } case KoListStyle::CustomCharItem: calcWidth = false; if (format.intProperty(KoListStyle::BulletCharacter)) item = QString(QChar(format.intProperty(KoListStyle::BulletCharacter))); width = m_fm.width(item); break; case KoListStyle::None: calcWidth = false; width = 0.0; break; case KoListStyle::ImageItem: calcWidth = false; width = qMax(format.doubleProperty(KoListStyle::Width), (qreal)1.0); break; default: // others we ignore. calcWidth = false; } } blockData.setCounterIsImage(listStyle == KoListStyle::ImageItem); blockData.setPartialCounterText(partialCounterText); blockData.setCounterIndex(index); item += partialCounterText; blockData.setCounterPlainText(item); blockData.setCounterPrefix(prefix); blockData.setCounterSuffix(suffix); if (calcWidth) width = m_fm.width(item); index++; width += m_fm.width(prefix + suffix); qreal counterSpacing = 0; if (format.boolProperty(KoListStyle::AlignmentMode)) { // for AlignmentMode spacing should be 0 counterSpacing = 0; } else { if (listStyle != KoListStyle::None) { // see ODF spec 1.2 item 20.422 counterSpacing = format.doubleProperty(KoListStyle::MinimumDistance); if (width < format.doubleProperty(KoListStyle::MinimumWidth)) { counterSpacing -= format.doubleProperty(KoListStyle::MinimumWidth) - width; } counterSpacing = qMax(counterSpacing, qreal(0.0)); } width = qMax(width, format.doubleProperty(KoListStyle::MinimumWidth)); } blockData.setCounterWidth(width); blockData.setCounterSpacing(counterSpacing); //warnTextLayout; } // static bool ListItemsHelper::needsRecalc(QTextList *textList) { Q_ASSERT(textList); QTextBlock tb = textList->item(0); KoTextBlockData blockData(tb); return !blockData.hasCounterData(); } diff --git a/plugins/impex/kra/kra_converter.cpp b/plugins/impex/kra/kra_converter.cpp index 55e20d7b40..8f9618f226 100644 --- a/plugins/impex/kra/kra_converter.cpp +++ b/plugins/impex/kra/kra_converter.cpp @@ -1,407 +1,437 @@ /* * Copyright (C) 2016 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kra_converter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include static const char CURRENT_DTD_VERSION[] = "2.0"; KraConverter::KraConverter(KisDocument *doc) : m_doc(doc) , m_image(doc->savingImage()) { } KraConverter::KraConverter(KisDocument *doc, QPointer updater) : m_doc(doc) , m_image(doc->savingImage()) , m_updater(updater) { } KraConverter::~KraConverter() { delete m_store; delete m_kraSaver; delete m_kraLoader; } +void fixCloneLayers(KisImageSP image, KisNodeSP root) +{ + KisNodeSP first = root->firstChild(); + KisNodeSP node = first; + while (!node.isNull()) { + if (node->inherits("KisCloneLayer")) { + KisCloneLayer* layer = dynamic_cast(node.data()); + if (layer && layer->copyFrom().isNull()) { + KisLayerSP reincarnation = layer->reincarnateAsPaintLayer(); + image->addNode(reincarnation, node->parent(), node->prevSibling()); + image->removeNode(node); + node = reincarnation; + } + } else if (node->childCount() > 0) { + fixCloneLayers(image, node); + } + node = node->nextSibling(); + } +} + KisImportExportErrorCode KraConverter::buildImage(QIODevice *io) { m_store = KoStore::createStore(io, KoStore::Read, "", KoStore::Zip); if (m_store->bad()) { m_doc->setErrorMessage(i18n("Not a valid Krita file")); return ImportExportCodes::FileFormatIncorrect; } - bool success; + bool success = false; { if (m_store->hasFile("root") || m_store->hasFile("maindoc.xml")) { // Fallback to "old" file format (maindoc.xml) KoXmlDocument doc; KisImportExportErrorCode res = oldLoadAndParse(m_store, "root", doc); if (res.isOk()) res = loadXML(doc, m_store); if (!res.isOk()) { return res; } } else { errUI << "ERROR: No maindoc.xml" << endl; m_doc->setErrorMessage(i18n("Invalid document: no file 'maindoc.xml'.")); return ImportExportCodes::FileFormatIncorrect; } if (m_store->hasFile("documentinfo.xml")) { KoXmlDocument doc; - if (oldLoadAndParse(m_store, "documentinfo.xml", doc).isOk()) { + KisImportExportErrorCode resultHere = oldLoadAndParse(m_store, "documentinfo.xml", doc); + if (resultHere.isOk()) { m_doc->documentInfo()->load(doc); } } success = completeLoading(m_store); } + fixCloneLayers(m_image, m_image->root()); + return success ? ImportExportCodes::OK : ImportExportCodes::Failure; } KisImageSP KraConverter::image() { return m_image; } vKisNodeSP KraConverter::activeNodes() { return m_activeNodes; } QList KraConverter::assistants() { return m_assistants; } KisImportExportErrorCode KraConverter::buildFile(QIODevice *io, const QString &filename) { setProgress(5); m_store = KoStore::createStore(io, KoStore::Write, m_doc->nativeFormatMimeType(), KoStore::Zip); if (m_store->bad()) { m_doc->setErrorMessage(i18n("Could not create the file for saving")); return ImportExportCodes::CannotCreateFile; } setProgress(20); m_kraSaver = new KisKraSaver(m_doc, filename); KisImportExportErrorCode resultCode = saveRootDocuments(m_store); if (!resultCode.isOk()) { return resultCode; } setProgress(40); bool result; result = m_kraSaver->saveKeyframes(m_store, m_doc->url().toLocalFile(), true); if (!result) { qWarning() << "saving key frames failed"; } setProgress(60); result = m_kraSaver->saveBinaryData(m_store, m_image, m_doc->url().toLocalFile(), true, m_doc->isAutosaving()); if (!result) { qWarning() << "saving binary data failed"; } setProgress(70); result = m_kraSaver->savePalettes(m_store, m_image, m_doc->url().toLocalFile()); if (!result) { qWarning() << "saving palettes data failed"; } setProgress(80); if (!m_store->finalize()) { return ImportExportCodes::Failure; } if (!m_kraSaver->errorMessages().isEmpty()) { m_doc->setErrorMessage(m_kraSaver->errorMessages().join(".\n")); return ImportExportCodes::Failure; } setProgress(90); return ImportExportCodes::OK; } KisImportExportErrorCode KraConverter::saveRootDocuments(KoStore *store) { dbgFile << "Saving root"; if (store->open("root")) { KoStoreDevice dev(store); if (!saveToStream(&dev) || !store->close()) { dbgUI << "saveToStream failed"; return ImportExportCodes::NoAccessToWrite; } } else { m_doc->setErrorMessage(i18n("Not able to write '%1'. Partition full?", QString("maindoc.xml"))); return ImportExportCodes::ErrorWhileWriting; } if (store->open("documentinfo.xml")) { QDomDocument doc = KisDocument::createDomDocument("document-info" /*DTD name*/, "document-info" /*tag name*/, "1.1"); doc = m_doc->documentInfo()->save(doc); KoStoreDevice dev(store); QByteArray s = doc.toByteArray(); // this is already Utf8! bool success = dev.write(s.data(), s.size()); if (!success) { return ImportExportCodes::ErrorWhileWriting; } store->close(); } else { return ImportExportCodes::Failure; } if (store->open("preview.png")) { // ### TODO: missing error checking (The partition could be full!) KisImportExportErrorCode result = savePreview(store); (void)store->close(); if (!result.isOk()) { return result; } } else { return ImportExportCodes::Failure; } dbgUI << "Saving done of url:" << m_doc->url().toLocalFile(); return ImportExportCodes::OK; } bool KraConverter::saveToStream(QIODevice *dev) { QDomDocument doc = createDomDocument(); // Save to buffer QByteArray s = doc.toByteArray(); // utf8 already dev->open(QIODevice::WriteOnly); int nwritten = dev->write(s.data(), s.size()); if (nwritten != (int)s.size()) { warnUI << "wrote " << nwritten << "- expected" << s.size(); } return nwritten == (int)s.size(); } QDomDocument KraConverter::createDomDocument() { QDomDocument doc = m_doc->createDomDocument("DOC", CURRENT_DTD_VERSION); QDomElement root = doc.documentElement(); root.setAttribute("editor", "Krita"); root.setAttribute("syntaxVersion", "2"); root.setAttribute("kritaVersion", KritaVersionWrapper::versionString(false)); root.appendChild(m_kraSaver->saveXML(doc, m_image)); if (!m_kraSaver->errorMessages().isEmpty()) { m_doc->setErrorMessage(m_kraSaver->errorMessages().join(".\n")); } return doc; } KisImportExportErrorCode KraConverter::savePreview(KoStore *store) { QPixmap pix = m_doc->generatePreview(QSize(256, 256)); QImage preview(pix.toImage().convertToFormat(QImage::Format_ARGB32, Qt::ColorOnly)); if (preview.size() == QSize(0,0)) { QSize newSize = m_doc->savingImage()->bounds().size(); newSize.scale(QSize(256, 256), Qt::KeepAspectRatio); preview = QImage(newSize, QImage::Format_ARGB32); preview.fill(QColor(0, 0, 0, 0)); } KoStoreDevice io(store); if (!io.open(QIODevice::WriteOnly)) { return ImportExportCodes::NoAccessToWrite; } bool ret = preview.save(&io, "PNG"); io.close(); return ret ? ImportExportCodes::OK : ImportExportCodes::ErrorWhileWriting; } KisImportExportErrorCode KraConverter::oldLoadAndParse(KoStore *store, const QString &filename, KoXmlDocument &xmldoc) { //dbgUI <<"Trying to open" << filename; if (!store->open(filename)) { warnUI << "Entry " << filename << " not found!"; m_doc->setErrorMessage(i18n("Could not find %1", filename)); return ImportExportCodes::FileNotExist; } // Error variables for QDomDocument::setContent QString errorMsg; int errorLine, errorColumn; bool ok = xmldoc.setContent(store->device(), &errorMsg, &errorLine, &errorColumn); store->close(); if (!ok) { errUI << "Parsing error in " << filename << "! Aborting!" << endl << " In line: " << errorLine << ", column: " << errorColumn << endl << " Error message: " << errorMsg << endl; m_doc->setErrorMessage(i18n("Parsing error in %1 at line %2, column %3\nError message: %4", filename, errorLine, errorColumn, QCoreApplication::translate("QXml", errorMsg.toUtf8(), 0))); return ImportExportCodes::FileFormatIncorrect; } dbgUI << "File" << filename << " loaded and parsed"; return ImportExportCodes::OK; } KisImportExportErrorCode KraConverter::loadXML(const KoXmlDocument &doc, KoStore *store) { Q_UNUSED(store); KoXmlElement root; KoXmlNode node; if (doc.doctype().name() != "DOC") { errUI << "The format is not supported or the file is corrupted"; m_doc->setErrorMessage(i18n("The format is not supported or the file is corrupted")); return ImportExportCodes::FileFormatIncorrect; } root = doc.documentElement(); int syntaxVersion = root.attribute("syntaxVersion", "3").toInt(); if (syntaxVersion > 2) { errUI << "The file is too new for this version of Krita:" << syntaxVersion; m_doc->setErrorMessage(i18n("The file is too new for this version of Krita (%1).", syntaxVersion)); return ImportExportCodes::FormatFeaturesUnsupported; } if (!root.hasChildNodes()) { errUI << "The file has no layers."; m_doc->setErrorMessage(i18n("The file has no layers.")); return ImportExportCodes::FileFormatIncorrect; } m_kraLoader = new KisKraLoader(m_doc, syntaxVersion); // reset the old image before loading the next one m_doc->setCurrentImage(0, false); for (node = root.firstChild(); !node.isNull(); node = node.nextSibling()) { if (node.isElement()) { if (node.nodeName() == "IMAGE") { KoXmlElement elem = node.toElement(); if (!(m_image = m_kraLoader->loadXML(elem))) { if (m_kraLoader->errorMessages().isEmpty()) { errUI << "Unknown error while opening the .kra file."; m_doc->setErrorMessage(i18n("Unknown error.")); } else { m_doc->setErrorMessage(m_kraLoader->errorMessages().join("\n")); errUI << m_kraLoader->errorMessages().join("\n"); } return ImportExportCodes::Failure; } // HACK ALERT! m_doc->hackPreliminarySetImage(m_image); return ImportExportCodes::OK; } else { if (m_kraLoader->errorMessages().isEmpty()) { m_doc->setErrorMessage(i18n("The file does not contain an image.")); } return ImportExportCodes::FileFormatIncorrect; } } } return ImportExportCodes::Failure; } bool KraConverter::completeLoading(KoStore* store) { if (!m_image) { if (m_kraLoader->errorMessages().isEmpty()) { m_doc->setErrorMessage(i18n("Unknown error.")); } else { m_doc->setErrorMessage(m_kraLoader->errorMessages().join("\n")); } return false; } m_image->blockUpdates(); QString layerPathName = m_kraLoader->imageName(); if (!m_store->hasDirectory(layerPathName)) { // We might be hitting an encoding problem. Get the only folder in the toplevel Q_FOREACH (const QString &entry, m_store->directoryList()) { if (entry.contains("/layers/")) { layerPathName = entry.split("/layers/").first(); m_store->setSubstitution(m_kraLoader->imageName(), layerPathName); break; } } } m_kraLoader->loadBinaryData(store, m_image, m_doc->localFilePath(), true); m_kraLoader->loadPalettes(store, m_doc); + if (!m_kraLoader->errorMessages().isEmpty()) { + m_doc->setErrorMessage(m_kraLoader->errorMessages().join("\n")); + return false; + } + m_image->unblockUpdates(); if (!m_kraLoader->warningMessages().isEmpty()) { // warnings do not interrupt loading process, so we do not return here m_doc->setWarningMessage(m_kraLoader->warningMessages().join("\n")); } m_activeNodes = m_kraLoader->selectedNodes(); m_assistants = m_kraLoader->assistants(); return true; + return m_kraLoader->errorMessages().isEmpty(); } void KraConverter::cancel() { m_stop = true; } void KraConverter::setProgress(int progress) { if (m_updater) { m_updater->setProgress(progress); } } diff --git a/plugins/impex/libkra/kis_kra_load_visitor.cpp b/plugins/impex/libkra/kis_kra_load_visitor.cpp index c11dc50054..d495abd1ff 100644 --- a/plugins/impex/libkra/kis_kra_load_visitor.cpp +++ b/plugins/impex/libkra/kis_kra_load_visitor.cpp @@ -1,784 +1,789 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2005 C. Boemann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_kra_load_visitor.h" #include "kis_kra_tags.h" #include "flake/kis_shape_layer.h" #include "flake/KisReferenceImagesLayer.h" #include "KisReferenceImage.h" #include #include #include #include #include #include #include #include #include #include #include #include #include // kritaimage #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_transform_mask_params_factory_registry.h" #include #include #include #include #include "kis_shape_selection.h" #include "kis_colorize_dom_utils.h" #include "kis_dom_utils.h" #include "kis_raster_keyframe_channel.h" #include "kis_paint_device_frames_interface.h" #include "kis_filter_registry.h" using namespace KRA; QString expandEncodedDirectory(const QString& _intern) { QString intern = _intern; QString result; int pos; while ((pos = intern.indexOf('/')) != -1) { if (QChar(intern.at(0)).isDigit()) result += "part"; result += intern.left(pos + 1); // copy numbers (or "pictures") + "/" intern = intern.mid(pos + 1); // remove the dir we just processed } if (!intern.isEmpty() && QChar(intern.at(0)).isDigit()) result += "part"; result += intern; return result; } KisKraLoadVisitor::KisKraLoadVisitor(KisImageSP image, KoStore *store, KoShapeControllerBase *shapeController, QMap &layerFilenames, QMap &keyframeFilenames, const QString & name, int syntaxVersion) : KisNodeVisitor() , m_image(image) , m_store(store) , m_external(false) , m_layerFilenames(layerFilenames) , m_keyframeFilenames(keyframeFilenames) , m_name(name) , m_shapeController(shapeController) { m_store->pushDirectory(); if (!m_store->enterDirectory(m_name)) { QStringList directories = m_store->directoryList(); dbgKrita << directories; if (directories.size() > 0) { dbgFile << "Could not locate the directory, maybe some encoding issue? Grab the first directory, that'll be the image one." << m_name << directories; m_name = directories.first(); } else { dbgFile << "Could not enter directory" << m_name << ", probably an old-style file with 'part' added."; m_name = expandEncodedDirectory(m_name); } } else { m_store->popDirectory(); } m_syntaxVersion = syntaxVersion; } void KisKraLoadVisitor::setExternalUri(const QString &uri) { m_external = true; m_uri = uri; } bool KisKraLoadVisitor::visit(KisExternalLayer * layer) { bool result = false; if (auto *referencesLayer = dynamic_cast(layer)) { Q_FOREACH(KoShape *shape, referencesLayer->shapes()) { auto *reference = dynamic_cast(shape); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(reference, false); while (!reference->loadImage(m_store)) { if (reference->embed()) { m_errorMessages << i18n("Could not load embedded reference image %1 ", reference->internalFile()); break; } else { QString msg = i18nc( "@info", "A reference image linked to an external file could not be loaded.\n\n" "Path: %1\n\n" "Do you want to select another location?", reference->filename()); int locateManually = QMessageBox::warning(0, i18nc("@title:window", "File not found"), msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); QString url; if (locateManually == QMessageBox::Yes) { KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument"); dialog.setMimeTypeFilters(KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import)); url = dialog.filename(); } if (url.isEmpty()) { break; } else { reference->setFilename(url); } } } } } else if (KisShapeLayer *shapeLayer = dynamic_cast(layer)) { if (!loadMetaData(layer)) { return false; } m_store->pushDirectory(); m_store->enterDirectory(getLocation(layer, DOT_SHAPE_LAYER)) ; result = shapeLayer->loadLayer(m_store); m_store->popDirectory(); } result = visitAll(layer) && result; return result; } bool KisKraLoadVisitor::visit(KisPaintLayer *layer) { loadNodeKeyframes(layer); if (!loadPaintDevice(layer->paintDevice(), getLocation(layer))) { return false; } if (!loadProfile(layer->paintDevice(), getLocation(layer, DOT_ICC))) { return false; } if (!loadMetaData(layer)) { return false; } if (m_syntaxVersion == 1) { // Check whether there is a file with a .mask extension in the // layer directory, if so, it's an old-style transparency mask // that should be converted. QString location = getLocation(layer, ".mask"); if (m_store->open(location)) { KisSelectionSP selection = KisSelectionSP(new KisSelection()); KisPixelSelectionSP pixelSelection = selection->pixelSelection(); if (!pixelSelection->read(m_store->device())) { pixelSelection->disconnect(); } else { KisTransparencyMask* mask = new KisTransparencyMask(); mask->setSelection(selection); m_image->addNode(mask, layer, layer->firstChild()); } m_store->close(); } } bool result = visitAll(layer); return result; } bool KisKraLoadVisitor::visit(KisGroupLayer *layer) { if (*layer->colorSpace() != *m_image->colorSpace()) { layer->resetCache(m_image->colorSpace()); } if (!loadMetaData(layer)) { return false; } bool result = visitAll(layer); return result; } bool KisKraLoadVisitor::visit(KisAdjustmentLayer* layer) { loadNodeKeyframes(layer); // Adjustmentlayers are tricky: there's the 1.x style and the 2.x // style, which has selections with selection components bool result = true; if (m_syntaxVersion == 1) { KisSelectionSP selection = new KisSelection(); KisPixelSelectionSP pixelSelection = selection->pixelSelection(); result = loadPaintDevice(pixelSelection, getLocation(layer, ".selection")); layer->setInternalSelection(selection); } else if (m_syntaxVersion == 2) { result = loadSelection(getLocation(layer), layer->internalSelection()); } else { // We use the default, empty selection } if (!loadMetaData(layer)) { return false; } loadFilterConfiguration(layer, getLocation(layer, DOT_FILTERCONFIG)); fixOldFilterConfigurations(layer->filter()); result = visitAll(layer); return result; } bool KisKraLoadVisitor::visit(KisGeneratorLayer *layer) { if (!loadMetaData(layer)) { return false; } bool result = true; loadNodeKeyframes(layer); result = loadSelection(getLocation(layer), layer->internalSelection()); // HACK ALERT: we set the same filter again to ensure the layer // is correctly updated result = loadFilterConfiguration(layer, getLocation(layer, DOT_FILTERCONFIG)); layer->setFilter(layer->filter()); result = visitAll(layer); return result; } bool KisKraLoadVisitor::visit(KisCloneLayer *layer) { if (!loadMetaData(layer)) { return false; } // the layer might have already been lazily initialized // from the mask loading code if (layer->copyFrom()) { return true; } KisNodeSP srcNode = layer->copyFromInfo().findNode(m_image->rootLayer()); - KisLayerSP srcLayer = qobject_cast(srcNode.data()); - Q_ASSERT(srcLayer); + if (!srcNode.isNull()) { + KisLayerSP srcLayer = qobject_cast(srcNode.data()); + Q_ASSERT(srcLayer); - layer->setCopyFrom(srcLayer); + layer->setCopyFrom(srcLayer); + } else { + m_warningMessages.append(i18nc("Loading a .kra file", "The file contains a clone layer that has an incorrect source node id. " + "This layer will be converted into a paint layer.")); + } // Clone layers have no data except for their masks bool result = visitAll(layer); return result; } void KisKraLoadVisitor::initSelectionForMask(KisMask *mask) { KisLayer *cloneLayer = dynamic_cast(mask->parent().data()); if (cloneLayer) { // the clone layers should be initialized out of order // and lazily, because their original() is still not // initialized cloneLayer->accept(*this); } KisLayer *parentLayer = qobject_cast(mask->parent().data()); // the KisKraLoader must have already set the parent for us Q_ASSERT(parentLayer); mask->initSelection(parentLayer); } bool KisKraLoadVisitor::visit(KisFilterMask *mask) { initSelectionForMask(mask); loadNodeKeyframes(mask); bool result = true; result = loadSelection(getLocation(mask), mask->selection()); result = loadFilterConfiguration(mask, getLocation(mask, DOT_FILTERCONFIG)); fixOldFilterConfigurations(mask->filter()); return result; } bool KisKraLoadVisitor::visit(KisTransformMask *mask) { QString location = getLocation(mask, DOT_TRANSFORMCONFIG); if (m_store->hasFile(location)) { QByteArray data; m_store->open(location); data = m_store->read(m_store->size()); m_store->close(); if (!data.isEmpty()) { QDomDocument doc; doc.setContent(data); QDomElement rootElement = doc.documentElement(); QDomElement main; if (!KisDomUtils::findOnlyElement(rootElement, "main", &main/*, &m_errorMessages*/)) { return false; } QString id = main.attribute("id", "not-valid"); if (id == "not-valid") { m_errorMessages << i18n("Could not load \"id\" of the transform mask"); return false; } QDomElement data; if (!KisDomUtils::findOnlyElement(rootElement, "data", &data, &m_errorMessages)) { return false; } KisTransformMaskParamsInterfaceSP params = KisTransformMaskParamsFactoryRegistry::instance()->createParams(id, data); if (!params) { m_errorMessages << i18n("Could not create transform mask params"); return false; } mask->setTransformParams(params); loadNodeKeyframes(mask); params->clearChangedFlag(); return true; } } return false; } bool KisKraLoadVisitor::visit(KisTransparencyMask *mask) { initSelectionForMask(mask); loadNodeKeyframes(mask); return loadSelection(getLocation(mask), mask->selection()); } bool KisKraLoadVisitor::visit(KisSelectionMask *mask) { initSelectionForMask(mask); return loadSelection(getLocation(mask), mask->selection()); } bool KisKraLoadVisitor::visit(KisColorizeMask *mask) { m_store->pushDirectory(); QString location = getLocation(mask, DOT_COLORIZE_MASK); m_store->enterDirectory(location) ; QByteArray data; if (!m_store->extractFile("content.xml", data)) return false; QDomDocument doc; if (!doc.setContent(data)) return false; QVector strokes; if (!KisDomUtils::loadValue(doc.documentElement(), COLORIZE_KEYSTROKES_SECTION, &strokes, mask->colorSpace())) return false; int i = 0; Q_FOREACH (const KisLazyFillTools::KeyStroke &stroke, strokes) { const QString fileName = QString("%1_%2").arg(COLORIZE_KEYSTROKE).arg(i++); loadPaintDevice(stroke.dev, fileName); } mask->setKeyStrokesDirect(QList::fromVector(strokes)); loadPaintDevice(mask->coloringProjection(), COLORIZE_COLORING_DEVICE); mask->resetCache(); m_store->popDirectory(); return true; } QStringList KisKraLoadVisitor::errorMessages() const { return m_errorMessages; } QStringList KisKraLoadVisitor::warningMessages() const { return m_warningMessages; } struct SimpleDevicePolicy { bool read(KisPaintDeviceSP dev, QIODevice *stream) { return dev->read(stream); } void setDefaultPixel(KisPaintDeviceSP dev, const KoColor &defaultPixel) const { return dev->setDefaultPixel(defaultPixel); } }; struct FramedDevicePolicy { FramedDevicePolicy(int frameId) : m_frameId(frameId) {} bool read(KisPaintDeviceSP dev, QIODevice *stream) { return dev->framesInterface()->readFrame(stream, m_frameId); } void setDefaultPixel(KisPaintDeviceSP dev, const KoColor &defaultPixel) const { return dev->framesInterface()->setFrameDefaultPixel(defaultPixel, m_frameId); } int m_frameId; }; bool KisKraLoadVisitor::loadPaintDevice(KisPaintDeviceSP device, const QString& location) { // Layer data KisPaintDeviceFramesInterface *frameInterface = device->framesInterface(); QList frames; if (frameInterface) { frames = device->framesInterface()->frames(); } if (!frameInterface || frames.count() <= 1) { return loadPaintDeviceFrame(device, location, SimpleDevicePolicy()); } else { KisRasterKeyframeChannel *keyframeChannel = device->keyframeChannel(); for (int i = 0; i < frames.count(); i++) { int id = frames[i]; if (keyframeChannel->frameFilename(id).isEmpty()) { m_warningMessages << i18n("Could not find keyframe pixel data for frame %1 in %2.", id, location); } else { Q_ASSERT(!keyframeChannel->frameFilename(id).isEmpty()); QString frameFilename = getLocation(keyframeChannel->frameFilename(id)); Q_ASSERT(!frameFilename.isEmpty()); if (!loadPaintDeviceFrame(device, frameFilename, FramedDevicePolicy(id))) { m_warningMessages << i18n("Could not load keyframe pixel data for frame %1 in %2.", id, location); } } } } return true; } template bool KisKraLoadVisitor::loadPaintDeviceFrame(KisPaintDeviceSP device, const QString &location, DevicePolicy policy) { { const int pixelSize = device->colorSpace()->pixelSize(); KoColor color(Qt::transparent, device->colorSpace()); if (m_store->open(location + ".defaultpixel")) { if (m_store->size() == pixelSize) { m_store->read((char*)color.data(), pixelSize); } m_store->close(); } policy.setDefaultPixel(device, color); } if (m_store->open(location)) { if (!policy.read(device, m_store->device())) { m_warningMessages << i18n("Could not read pixel data: %1.", location); device->disconnect(); m_store->close(); return true; } m_store->close(); } else { m_warningMessages << i18n("Could not load pixel data: %1.", location); return true; } return true; } bool KisKraLoadVisitor::loadProfile(KisPaintDeviceSP device, const QString& location) { if (m_store->hasFile(location)) { m_store->open(location); QByteArray data; data.resize(m_store->size()); dbgFile << "Data to load: " << m_store->size() << " from " << location << " with color space " << device->colorSpace()->id(); int read = m_store->read(data.data(), m_store->size()); dbgFile << "Profile size: " << data.size() << " " << m_store->atEnd() << " " << m_store->device()->bytesAvailable() << " " << read; m_store->close(); KoHashGenerator *hashGenerator = KoHashGeneratorProvider::instance()->getGenerator("MD5"); QByteArray hash = hashGenerator->generateHash(data); if (m_profileCache.contains(hash)) { if (device->setProfile(m_profileCache[hash], 0)) { return true; } } else { // Create a colorspace with the embedded profile const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(device->colorSpace()->colorModelId().id(), device->colorSpace()->colorDepthId().id(), data); m_profileCache[hash] = profile; if (device->setProfile(profile, 0)) { return true; } } } m_warningMessages << i18n("Could not load profile: %1.", location); return true; } bool KisKraLoadVisitor::loadFilterConfiguration(KisNodeFilterInterface *nodeInterface, const QString& location) { KisFilterConfigurationSP kfc = nodeInterface->filter(); if (m_store->hasFile(location)) { QByteArray data; m_store->open(location); data = m_store->read(m_store->size()); m_store->close(); if (!data.isEmpty()) { QDomDocument doc; doc.setContent(data); QDomElement e = doc.documentElement(); if (e.tagName() == "filterconfig") { kfc->fromLegacyXML(e); } else { kfc->fromXML(e); } loadDeprecatedFilter(kfc); return true; } } m_warningMessages << i18n("Could not filter configuration %1.", location); return true; } void KisKraLoadVisitor::fixOldFilterConfigurations(KisFilterConfigurationSP kfc) { KisFilterSP filter = KisFilterRegistry::instance()->value(kfc->name()); KIS_SAFE_ASSERT_RECOVER_RETURN(filter); if (!filter->configurationAllowedForMask(kfc)) { filter->fixLoadedFilterConfigurationForMasks(kfc); } KIS_SAFE_ASSERT_RECOVER_NOOP(filter->configurationAllowedForMask(kfc)); } bool KisKraLoadVisitor::loadMetaData(KisNode* node) { KisLayer* layer = qobject_cast(node); if (!layer) return true; KisMetaData::IOBackend* backend = KisMetaData::IOBackendRegistry::instance()->get("xmp"); if (!backend || !backend->supportLoading()) { if (backend) dbgFile << "Backend " << backend->id() << " does not support loading."; else dbgFile << "Could not load the XMP backend at all"; return true; } QString location = getLocation(node, QString(".") + backend->id() + DOT_METADATA); dbgFile << "going to load " << backend->id() << ", " << backend->name() << " from " << location; if (m_store->hasFile(location)) { QByteArray data; m_store->open(location); data = m_store->read(m_store->size()); m_store->close(); QBuffer buffer(&data); if (!backend->loadFrom(layer->metaData(), &buffer)) { m_warningMessages << i18n("Could not load metadata for layer %1.", layer->name()); } } return true; } bool KisKraLoadVisitor::loadSelection(const QString& location, KisSelectionSP dstSelection) { // by default the selection is expected to be fully transparent { KisPixelSelectionSP pixelSelection = dstSelection->pixelSelection(); KoColor transparent(Qt::transparent, pixelSelection->colorSpace()); pixelSelection->setDefaultPixel(transparent); } // Pixel selection bool result = true; QString pixelSelectionLocation = location + DOT_PIXEL_SELECTION; if (m_store->hasFile(pixelSelectionLocation)) { KisPixelSelectionSP pixelSelection = dstSelection->pixelSelection(); result = loadPaintDevice(pixelSelection, pixelSelectionLocation); if (!result) { m_warningMessages << i18n("Could not load raster selection %1.", location); } pixelSelection->invalidateOutlineCache(); } // Shape selection QString shapeSelectionLocation = location + DOT_SHAPE_SELECTION; if (m_store->hasFile(shapeSelectionLocation + "/content.svg") || m_store->hasFile(shapeSelectionLocation + "/content.xml")) { m_store->pushDirectory(); m_store->enterDirectory(shapeSelectionLocation) ; KisShapeSelection* shapeSelection = new KisShapeSelection(m_shapeController, m_image, dstSelection); dstSelection->setShapeSelection(shapeSelection); result = shapeSelection->loadSelection(m_store); m_store->popDirectory(); if (!result) { m_warningMessages << i18n("Could not load vector selection %1.", location); } } return true; } QString KisKraLoadVisitor::getLocation(KisNode* node, const QString& suffix) { return getLocation(m_layerFilenames[node], suffix); } QString KisKraLoadVisitor::getLocation(const QString &filename, const QString& suffix) { QString location = m_external ? QString() : m_uri; location += m_name + LAYER_PATH + filename + suffix; return location; } void KisKraLoadVisitor::loadNodeKeyframes(KisNode *node) { if (!m_keyframeFilenames.contains(node)) return; node->enableAnimation(); const QString &location = getLocation(m_keyframeFilenames[node]); if (!m_store->open(location)) { m_errorMessages << i18n("Could not load keyframes from %1.", location); return; } QString errorMsg; int errorLine; int errorColumn; QDomDocument dom; bool ok = dom.setContent(m_store->device(), &errorMsg, &errorLine, &errorColumn); m_store->close(); if (!ok) { m_errorMessages << i18n("parsing error in the keyframe file %1 at line %2, column %3\nError message: %4", location, errorLine, errorColumn, i18n(errorMsg.toUtf8())); return; } QDomElement root = dom.firstChildElement(); for (QDomElement child = root.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) { if (child.nodeName().toUpper() == "CHANNEL") { QString id = child.attribute("name"); KisKeyframeChannel *channel = node->getKeyframeChannel(id, true); if (!channel) { m_warningMessages << i18n("unknown keyframe channel type: %1 in %2", id, location); continue; } channel->loadXML(child); } } } void KisKraLoadVisitor::loadDeprecatedFilter(KisFilterConfigurationSP cfg) { if (cfg->getString("legacy") == "left edge detections") { cfg->setProperty("horizRadius", 1); cfg->setProperty("vertRadius", 1); cfg->setProperty("type", "prewitt"); cfg->setProperty("output", "yFall"); cfg->setProperty("lockAspect", true); cfg->setProperty("transparency", false); } else if (cfg->getString("legacy") == "right edge detections") { cfg->setProperty("horizRadius", 1); cfg->setProperty("vertRadius", 1); cfg->setProperty("type", "prewitt"); cfg->setProperty("output", "yGrowth"); cfg->setProperty("lockAspect", true); cfg->setProperty("transparency", false); } else if (cfg->getString("legacy") == "top edge detections") { cfg->setProperty("horizRadius", 1); cfg->setProperty("vertRadius", 1); cfg->setProperty("type", "prewitt"); cfg->setProperty("output", "xGrowth"); cfg->setProperty("lockAspect", true); cfg->setProperty("transparency", false); } else if (cfg->getString("legacy") == "bottom edge detections") { cfg->setProperty("horizRadius", 1); cfg->setProperty("vertRadius", 1); cfg->setProperty("type", "prewitt"); cfg->setProperty("output", "xFall"); cfg->setProperty("lockAspect", true); cfg->setProperty("transparency", false); } } diff --git a/plugins/impex/psd/psd_additional_layer_info_block.h b/plugins/impex/psd/psd_additional_layer_info_block.h index 4dc020ac26..24cab8e122 100644 --- a/plugins/impex/psd/psd_additional_layer_info_block.h +++ b/plugins/impex/psd/psd_additional_layer_info_block.h @@ -1,295 +1,295 @@ /* * Copyright (c) 2014 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PSD_ADDITIONAL_LAYER_INFO_BLOCK_H #define PSD_ADDITIONAL_LAYER_INFO_BLOCK_H #include #include #include #include #include #include #include #include #include #include #include "psd.h" #include "psd_header.h" // additional layer information // LEVELS // Level record struct psd_layer_level_record { quint16 input_floor; // (0...253) quint16 input_ceiling; // (2...255) quint16 output_floor; // 255). Matched to input floor. quint16 output_ceiling; // (0...255) float gamma; // Short integer from 10...999 representing 0.1...9.99. Applied to all image data. }; // Levels settings files are loaded and saved in the Levels dialog. struct psd_layer_levels { psd_layer_level_record record[29]; // 29 sets of level records, each level containing 5 qint8 integers // Photoshop CS (8.0) Additional information // At the end of the Version 2 file is the following information: quint16 extra_level_count; // Count of total level record structures. Subtract the legacy number of level record structures, 29, to determine how many are remaining in the file for reading. psd_layer_level_record *extra_record; // Additianol level records according to count quint8 lookup_table[3][256]; }; // CURVES // The following is the data for each curve specified by count above struct psd_layer_curves_data { quint16 channel_index; // Before each curve is a channel index. quint16 point_count; // Count of points in the curve (qint8 integer from 2...19) quint16 output_value[19]; // All coordinates have range 0 to 255 quint16 input_value[19]; }; // Curves file format struct psd_layer_curves { quint16 curve_count; // Count of curves in the file. psd_layer_curves_data * curve; quint8 lookup_table[3][256]; }; // BRIGHTNESS AND CONTRAST struct psd_layer_brightness_contrast { qint8 brightness; qint8 contrast; qint8 mean_value; // for brightness and contrast qint8 Lab_color; quint8 lookup_table[256]; }; // COLOR BALANCE struct psd_layer_color_balance { qint8 cyan_red[3]; // (-100...100). shadows, midtones, highlights qint8 magenta_green[3]; qint8 yellow_blue[3]; bool preserve_luminosity; quint8 lookup_table[3][256]; }; // HUE/SATURATION // Hue/Saturation settings files are loaded and saved in Photoshop¡¯s Hue/Saturation dialog struct psd_layer_hue_saturation { quint8 hue_or_colorization; // 0 = Use settings for hue-adjustment; 1 = Use settings for colorization. qint8 colorization_hue; // Photoshop 5.0: The actual values are stored for the new version. Hue is - 180...180, Saturation is 0...100, and Lightness is -100...100. qint8 colorization_saturation;// Photoshop 4.0: Three qint8 integers Hue, Saturation, and Lightness from ¨C100...100. qint8 colorization_lightness; // The user interface represents hue as ¨C180...180, saturation as 0...100, and Lightness as -100...1000, as the traditional HSB color wheel, with red = 0. qint8 master_hue; // Master hue, saturation and lightness values. qint8 master_saturation; qint8 master_lightness; qint8 range_values[6][4]; // For RGB and CMYK, those values apply to each of the six hextants in the HSB color wheel: those image pixels nearest to red, yellow, green, cyan, blue, or magenta. These numbers appear in the user interface from ¨C60...60, however the slider will reflect each of the possible 201 values from ¨C100...100. qint8 setting_values[6][3]; // For Lab, the first four of the six values are applied to image pixels in the four Lab color quadrants, yellow, green, blue, and magenta. The other two values are ignored ( = 0). The values appear in the user interface from ¨C90 to 90. quint8 lookup_table[6][360]; }; // SELECTIVE COLOR // Selective Color settings files are loaded and saved in Photoshop¡¯s Selective Color dialog. struct psd_layer_selective_color { quint16 correction_method; // 0 = Apply color correction in relative mode; 1 = Apply color correction in absolute mode. qint8 cyan_correction[10]; // Amount of cyan correction. Short integer from ¨C100...100. qint8 magenta_correction[10]; // Amount of magenta correction. Short integer from ¨C100...100. qint8 yellow_correction[10]; // Amount of yellow correction. Short integer from ¨C100...100. qint8 black_correction[10]; // Amount of black correction. Short integer from ¨C100...100. }; // THRESHOLD struct psd_layer_threshold { quint16 level; // (1...255) } ; // INVERT // no parameter // POSTERIZE struct psd_layer_posterize { quint16 levels; // (2...255) quint8 lookup_table[256]; }; // CHANNEL MIXER struct psd_layer_channel_mixer { bool monochrome; qint8 red_cyan[4]; // RGB or CMYK color plus constant for the mixer settings. 4 * 2 bytes of color with 2 bytes of constant. qint8 green_magenta[4]; // (-200...200) qint8 blue_yellow[4]; qint8 black[4]; qint8 constant[4]; }; // PHOTO FILTER struct psd_layer_photo_filter { qint32 x_color; // 4 bytes each for XYZ color qint32 y_color; qint32 z_color; qint32 density; // (1...100) bool preserve_luminosity; }; #include struct psd_layer_solid_color { quint32 id; QColor fill_color; }; struct psd_layer_gradient_fill { quint32 id; double angle; psd_gradient_style style; qint32 scale; bool reverse; // Is gradient reverse bool dithered; // Is gradient dithered bool align_with_layer; psd_gradient_color gradient_color; }; struct psd_layer_pattern_fill { quint32 id; psd_pattern_info pattern_info; qint32 scale; }; struct psd_layer_type_face { qint8 mark; // Mark value qint32 font_type; // Font type data qint8 font_name[256]; // Pascal string of font name qint8 font_family_name[256]; // Pascal string of font family name qint8 font_style_name[256]; // Pascal string of font style name qint8 script; // Script value qint32 number_axes_vector; // Number of design axes vector to follow qint32 * vector; // Design vector value }; struct psd_layer_type_style { qint8 mark; // Mark value qint8 face_mark; // Face mark value qint32 size; // Size value qint32 tracking; // Tracking value qint32 kerning; // Kerning value qint32 leading; // Leading value qint32 base_shift; // Base shift value bool auto_kern; // Auto kern on/off bool rotate; // Rotate up/down }; struct psd_layer_type_line { qint32 char_count; // Character count value qint8 orientation; // Orientation value qint8 alignment; // Alignment value qint8 actual_char; // Actual character as a double byte character qint8 style; // Style value }; struct psd_layer_type_tool { double transform_info[6]; // 6 * 8 double precision numbers for the transform information qint8 faces_count; // Count of faces psd_layer_type_face * face; qint8 styles_count; // Count of styles psd_layer_type_style * style; qint8 type; // Type value qint32 scaling_factor; // Scaling factor value qint32 character_count; // Character count value qint32 horz_place; // Horizontal placement qint32 vert_place; // Vertical placement qint32 select_start; // Select start value qint32 select_end; // Select end value qint8 lines_count; // Line count psd_layer_type_line * line; QColor color; bool anti_alias; // Anti alias on/off }; /** * @brief The PsdAdditionalLayerInfoBlock class implements the Additional Layer Information block * - * See: http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_71546 + * See: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_71546 */ class PsdAdditionalLayerInfoBlock { public: PsdAdditionalLayerInfoBlock(const PSDHeader& header); typedef boost::function ExtraLayerInfoBlockHandler; void setExtraLayerInfoBlockHandler(ExtraLayerInfoBlockHandler handler); bool read(QIODevice* io); bool write(QIODevice* io, KisNodeSP node); void writeLuniBlockEx(QIODevice* io, const QString &layerName); void writeLsctBlockEx(QIODevice* io, psd_section_type sectionType, bool isPassThrough, const QString &blendModeKey); void writeLfx2BlockEx(QIODevice* io, const QDomDocument &stylesXmlDoc, bool useLfxsLayerStyleFormat); void writePattBlockEx(QIODevice* io, const QDomDocument &patternsXmlDoc); bool valid(); const PSDHeader &m_header; QString error; QStringList keys; // List of all the keys that we've seen QString unicodeLayerName; QDomDocument layerStyleXml; QVector embeddedPatterns; psd_section_type sectionDividerType; QString sectionDividerBlendMode; private: void readImpl(QIODevice* io); private: ExtraLayerInfoBlockHandler m_layerInfoBlockHandler; }; #endif // PSD_ADDITIONAL_LAYER_INFO_BLOCK_H diff --git a/plugins/impex/psd/psd_layer_section.h b/plugins/impex/psd/psd_layer_section.h index 55db74f209..fe8424dec0 100644 --- a/plugins/impex/psd/psd_layer_section.h +++ b/plugins/impex/psd/psd_layer_section.h @@ -1,72 +1,72 @@ /* * Copyright (c) 2009 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PSD_LAYER_SECTION_H #define PSD_LAYER_SECTION_H #include class QIODevice; #include #include "psd_header.h" #include "psd_layer_record.h" class PSDLayerMaskSection { public: PSDLayerMaskSection(const PSDHeader& header); ~PSDLayerMaskSection(); bool read(QIODevice* io); bool write(QIODevice* io, KisNodeSP rootLayer); QString error; - // http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_21849 + // https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_21849 quint64 layerMaskBlockSize; // Length of the layer and mask information section - // layer info: http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_16000 + // layer info: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_16000 bool hasTransparency; qint16 nLayers; // If layer count is a negative number, its absolute value is the number of layers and the first alpha channel contains the transparency data for the merged result. QVector layers; - // mask info: http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_17115 + // mask info: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_17115 struct GlobalLayerMaskInfo { quint16 overlayColorSpace; // Overlay color space (undocumented). quint16 colorComponents[4]; // 4 * 2 byte color components quint16 opacity; // Opacity. 0 = transparent, 100 = opaque. quint8 kind; // Kind. 0 = Color selected--i.e. inverted; 1 = Color protected;128 = use value stored per layer. This value is preferred. The others are for backward compatibility with beta versions. }; GlobalLayerMaskInfo globalLayerMaskInfo; PsdAdditionalLayerInfoBlock globalInfoSection; private: bool readLayerInfoImpl(QIODevice* io); bool readImpl(QIODevice* io); void writeImpl(QIODevice* io, KisNodeSP rootLayer); private: const PSDHeader m_header; }; #endif // PSD_LAYER_SECTION_H diff --git a/plugins/impex/psd/psd_loader.cpp b/plugins/impex/psd/psd_loader.cpp index f68e47a503..d538d215c7 100644 --- a/plugins/impex/psd/psd_loader.cpp +++ b/plugins/impex/psd/psd_loader.cpp @@ -1,380 +1,380 @@ /* * Copyright (c) 2009 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "psd_loader.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisResourceServerProvider.h" #include "psd.h" #include "psd_header.h" #include "psd_colormode_block.h" #include "psd_utils.h" #include "psd_resource_section.h" #include "psd_layer_section.h" #include "psd_resource_block.h" #include "psd_image_data.h" PSDLoader::PSDLoader(KisDocument *doc) : m_image(0) , m_doc(doc) , m_stop(false) { } PSDLoader::~PSDLoader() { } KisImportExportErrorCode PSDLoader::decode(QIODevice *io) { // open the file dbgFile << "pos:" << io->pos(); PSDHeader header; if (!header.read(io)) { dbgFile << "failed reading header: " << header.error; return ImportExportCodes::FileFormatIncorrect; } dbgFile << header; dbgFile << "Read header. pos:" << io->pos(); PSDColorModeBlock colorModeBlock(header.colormode); if (!colorModeBlock.read(io)) { dbgFile << "failed reading colormode block: " << colorModeBlock.error; return ImportExportCodes::FileFormatIncorrect; } dbgFile << "Read color mode block. pos:" << io->pos(); PSDImageResourceSection resourceSection; if (!resourceSection.read(io)) { dbgFile << "failed image reading resource section: " << resourceSection.error; return ImportExportCodes::FileFormatIncorrect; } dbgFile << "Read image resource section. pos:" << io->pos(); PSDLayerMaskSection layerSection(header); if (!layerSection.read(io)) { dbgFile << "failed reading layer/mask section: " << layerSection.error; return ImportExportCodes::FileFormatIncorrect; } dbgFile << "Read layer/mask section. " << layerSection.nLayers << "layers. pos:" << io->pos(); // Done reading, except possibly for the image data block, which is only relevant if there // are no layers. // Get the right colorspace QPair colorSpaceId = psd_colormode_to_colormodelid(header.colormode, header.channelDepth); if (colorSpaceId.first.isNull()) { dbgFile << "Unsupported colorspace" << header.colormode << header.channelDepth; return ImportExportCodes::FormatColorSpaceUnsupported; } // Get the icc profile from the image resource section const KoColorProfile* profile = 0; if (resourceSection.resources.contains(PSDImageResourceSection::ICC_PROFILE)) { ICC_PROFILE_1039 *iccProfileData = dynamic_cast(resourceSection.resources[PSDImageResourceSection::ICC_PROFILE]->resource); if (iccProfileData ) { profile = KoColorSpaceRegistry::instance()->createColorProfile(colorSpaceId.first, colorSpaceId.second, iccProfileData->icc); dbgFile << "Loaded ICC profile" << profile->name(); delete resourceSection.resources.take(PSDImageResourceSection::ICC_PROFILE); } } // Create the colorspace const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(colorSpaceId.first, colorSpaceId.second, profile); if (!cs) { return ImportExportCodes::FormatColorSpaceUnsupported; } // Creating the KisImage QFile *file = dynamic_cast(io); QString name = file ? file->fileName() : "Imported"; m_image = new KisImage(m_doc->createUndoStore(), header.width, header.height, cs, name); Q_CHECK_PTR(m_image); m_image->lock(); // set the correct resolution if (resourceSection.resources.contains(PSDImageResourceSection::RESN_INFO)) { RESN_INFO_1005 *resInfo = dynamic_cast(resourceSection.resources[PSDImageResourceSection::RESN_INFO]->resource); if (resInfo) { // check resolution size is not zero if (resInfo->hRes * resInfo->vRes > 0) m_image->setResolution(POINT_TO_INCH(resInfo->hRes), POINT_TO_INCH(resInfo->vRes)); // let's skip the unit for now; we can only set that on the KisDocument, and krita doesn't use it. delete resourceSection.resources.take(PSDImageResourceSection::RESN_INFO); } } // Preserve all the annotations Q_FOREACH (PSDResourceBlock *resourceBlock, resourceSection.resources.values()) { m_image->addAnnotation(resourceBlock); } // Preserve the duotone colormode block for saving back to psd if (header.colormode == DuoTone) { KisAnnotationSP annotation = new KisAnnotation("DuotoneColormodeBlock", i18n("Duotone Colormode Block"), colorModeBlock.data); m_image->addAnnotation(annotation); } // Read the projection into our single layer. Since we only read the projection when // we have just one layer, we don't need to later on apply the alpha channel of the // first layer to the projection if the number of layers is negative/ - // See http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_16000. + // See https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_16000. if (layerSection.nLayers == 0) { dbgFile << "Position" << io->pos() << "Going to read the projection into the first layer, which Photoshop calls 'Background'"; KisPaintLayerSP layer = new KisPaintLayer(m_image, i18n("Background"), OPACITY_OPAQUE_U8); PSDImageData imageData(&header); imageData.read(io, layer->paintDevice()); m_image->addNode(layer, m_image->rootLayer()); // Only one layer, the background layer, so we're done. m_image->unlock(); return ImportExportCodes::OK; } // More than one layer, so now construct the Krita image from the info we read. QStack groupStack; groupStack.push(m_image->rootLayer()); /** * PSD has a weird "optimization": if a group layer has only one * child layer, it omits it's 'psd_bounding_divider' section. So * fi you ever see an unbalanced layers group in PSD, most * probably, it is just a single layered group. */ KisNodeSP lastAddedLayer; typedef QPair LayerStyleMapping; QVector allStylesXml; // read the channels for the various layers for(int i = 0; i < layerSection.nLayers; ++i) { PSDLayerRecord* layerRecord = layerSection.layers.at(i); dbgFile << "Going to read channels for layer" << i << layerRecord->layerName; KisLayerSP newLayer; if (layerRecord->infoBlocks.keys.contains("lsct") && layerRecord->infoBlocks.sectionDividerType != psd_other) { if (layerRecord->infoBlocks.sectionDividerType == psd_bounding_divider && !groupStack.isEmpty()) { KisGroupLayerSP groupLayer = new KisGroupLayer(m_image, "temp", OPACITY_OPAQUE_U8); m_image->addNode(groupLayer, groupStack.top()); groupStack.push(groupLayer); newLayer = groupLayer; } else if ((layerRecord->infoBlocks.sectionDividerType == psd_open_folder || layerRecord->infoBlocks.sectionDividerType == psd_closed_folder) && (groupStack.size() > 1 || (lastAddedLayer && !groupStack.isEmpty()))) { KisGroupLayerSP groupLayer; if (groupStack.size() <= 1) { groupLayer = new KisGroupLayer(m_image, "temp", OPACITY_OPAQUE_U8); m_image->addNode(groupLayer, groupStack.top()); m_image->moveNode(lastAddedLayer, groupLayer, KisNodeSP()); } else { groupLayer = groupStack.pop(); } const QDomDocument &styleXml = layerRecord->infoBlocks.layerStyleXml; if (!styleXml.isNull()) { allStylesXml << LayerStyleMapping(styleXml, groupLayer); } groupLayer->setName(layerRecord->layerName); groupLayer->setVisible(layerRecord->visible); QString compositeOp = psd_blendmode_to_composite_op(layerRecord->infoBlocks.sectionDividerBlendMode); // Krita doesn't support pass-through blend // mode. Instead it is just a property of a group // layer, so flip it if (compositeOp == COMPOSITE_PASS_THROUGH) { compositeOp = COMPOSITE_OVER; groupLayer->setPassThroughMode(true); } groupLayer->setCompositeOpId(compositeOp); newLayer = groupLayer; } else { /** * In some files saved by PS CS6 the group layer sections seem * to be unbalanced. I don't know why it happens because the * reporter didn't provide us an example file. So here we just * check if the new layer was created, and if not, skip the * initialization of masks. * * See bug: 357559 */ warnKrita << "WARNING: Provided PSD has unbalanced group " << "layer markers. Some masks and/or layers can " << "be lost while loading this file. Please " << "report a bug to Krita developers and attach " << "this file to the bugreport\n" << " " << ppVar(layerRecord->layerName) << "\n" << " " << ppVar(layerRecord->infoBlocks.sectionDividerType) << "\n" << " " << ppVar(groupStack.size()); continue; } } else { KisPaintLayerSP layer = new KisPaintLayer(m_image, layerRecord->layerName, layerRecord->opacity); layer->setCompositeOpId(psd_blendmode_to_composite_op(layerRecord->blendModeKey)); const QDomDocument &styleXml = layerRecord->infoBlocks.layerStyleXml; if (!styleXml.isNull()) { allStylesXml << LayerStyleMapping(styleXml, layer); } if (!layerRecord->readPixelData(io, layer->paintDevice())) { dbgFile << "failed reading channels for layer: " << layerRecord->layerName << layerRecord->error; return ImportExportCodes::FileFormatIncorrect; } if (!groupStack.isEmpty()) { m_image->addNode(layer, groupStack.top()); } else { m_image->addNode(layer, m_image->root()); } layer->setVisible(layerRecord->visible); newLayer = layer; } Q_FOREACH (ChannelInfo *channelInfo, layerRecord->channelInfoRecords) { if (channelInfo->channelId < -1) { KisTransparencyMaskSP mask = new KisTransparencyMask(); mask->setName(i18n("Transparency Mask")); mask->initSelection(newLayer); if (!layerRecord->readMask(io, mask->paintDevice(), channelInfo)) { dbgFile << "failed reading masks for layer: " << layerRecord->layerName << layerRecord->error; } m_image->addNode(mask, newLayer); } } lastAddedLayer = newLayer; } const QVector &embeddedPatterns = layerSection.globalInfoSection.embeddedPatterns; KisAslLayerStyleSerializer serializer; if (!embeddedPatterns.isEmpty()) { Q_FOREACH (const QDomDocument &doc, embeddedPatterns) { serializer.registerPSDPattern(doc); } } QVector allStylesForServer; if (!allStylesXml.isEmpty()) { Q_FOREACH (const LayerStyleMapping &mapping, allStylesXml) { serializer.readFromPSDXML(mapping.first); if (serializer.styles().size() == 1) { KisPSDLayerStyleSP layerStyle = serializer.styles().first(); KisLayerSP layer = mapping.second; layerStyle->setName(layer->name()); allStylesForServer << layerStyle; layer->setLayerStyle(layerStyle->clone()); } else { warnKrita << "WARNING: Couldn't read layer style!" << ppVar(serializer.styles()); } } } if (!allStylesForServer.isEmpty()) { KisPSDLayerStyleCollectionResource *collection = new KisPSDLayerStyleCollectionResource("Embedded PSD Styles.asl"); collection->setName(i18nc("Auto-generated layer style collection name for embedded styles (collection)", "<%1> (embedded)", m_image->objectName())); KIS_SAFE_ASSERT_RECOVER_NOOP(!collection->valid()); collection->setLayerStyles(allStylesForServer); KIS_SAFE_ASSERT_RECOVER_NOOP(collection->valid()); KoResourceServer *server = KisResourceServerProvider::instance()->layerStyleCollectionServer(); server->addResource(collection, false); } m_image->unlock(); return ImportExportCodes::OK; } KisImportExportErrorCode PSDLoader::buildImage(QIODevice *io) { return decode(io); } KisImageSP PSDLoader::image() { return m_image; } void PSDLoader::cancel() { m_stop = true; } diff --git a/plugins/impex/raw/3rdparty/libkdcraw/AUTHORS b/plugins/impex/raw/3rdparty/libkdcraw/AUTHORS index 4ece2f5b77..0b6b91fa4a 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/AUTHORS +++ b/plugins/impex/raw/3rdparty/libkdcraw/AUTHORS @@ -1,15 +1,15 @@ AUTHORS AND MAINTAINERS : Caulier Gilles Marcel Wiesweg CONTRIBUTORS: Angelo Naselli Gerhard Kulzer Achim Bohnet Guillaume Castagnino THANKS: -Alex Tutubalin from LibRaw project (http://www.libraw.org) \ No newline at end of file +Alex Tutubalin from LibRaw project (https://www.libraw.org) diff --git a/plugins/impex/raw/3rdparty/libkdcraw/README b/plugins/impex/raw/3rdparty/libkdcraw/README index d85be7c116..47462fd8e6 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/README +++ b/plugins/impex/raw/3rdparty/libkdcraw/README @@ -1,61 +1,61 @@ LibRaw C++ interface for KDE -This library is a part of digiKam project (http://www.digikam.org) +This library is a part of digiKam project (https://www.digikam.org) -- AUTHORS ----------------------------------------------------------- See AUTHORS file for details. -- ABOUT ------------------------------------------------------------- Libkdcraw is a C++ interface around LibRaw library used to decode RAW -picture files. More information about LibRaw can be found at http://www.libraw.org. +picture files. More information about LibRaw can be found at https://www.libraw.org. This library is used by kipi-plugins, digiKam and others kipi host programs. The library documentation is available on header files. -- DEPENDENCIES ------------------------------------------------------- -CMake >= 2.8.12 http://www.cmake.org +CMake >= 2.8.12 https://www.cmake.org ECM >= 1.1.0 https://projects.kde.org/projects/kdesupport/extra-cmake-modules -libqt >= 5.2.0 http://qt-project.org -libkde >= 5.0.0 http://www.kde.org -libraw >= 0.16.x http://www.libraw.org +libqt >= 5.2.0 https://qt.io +libkde >= 5.0.0 https://www.kde.org +libraw >= 0.16.x https://www.libraw.org Note: all library dependencies require development and binary packages installed on your computer to compile digiKam. -- INSTALL ------------------------------------------------------------ Usual CMake options: -DCMAKE_INSTALL_PREFIX : decide where the program will be install on your computer. -DCMAKE_BUILD_TYPE : decide which type of build you want. You can chose between "debug", "profile", "relwithdebinfo" and "release". The default is "relwithdebinfo" (-O2 -g). Note: To know KDE install path on your computer, use 'kf5-config --prefix' command line like this (with debug object enabled): "cmake . -DCMAKE_BUILD_TYPE=debug -DCMAKE_INSTALL_PREFIX=`kf5-config --prefix`" -- CONTACT ------------------------------------------------------------ If you have questions, comments, suggestions to make do email at : digikam-devel@kde.org IRC channel from freenode.net server: #digikam -- BUGS --------------------------------------------------------------- IMPORTANT : the bugreports and the wishlist are hosted by the KDE bugs report system who can be contacted by the standard Kde help menu of the plugins dialog. A mail will be automatically sent to the digiKam devel mailing list. There is no need to contact directly the digiKam mailing list for a bug report or a devel wish. The current digiKam bugs and devel wish reported to the Kde bugs report can be seen at this url: -http://bugs.kde.org/buglist.cgi?product=digikam&component=libkdcraw&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED +https://bugs.kde.org/buglist.cgi?product=digikam&component=libkdcraw&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED diff --git a/plugins/impex/raw/3rdparty/libkdcraw/cmake/modules/FindLibRaw.cmake b/plugins/impex/raw/3rdparty/libkdcraw/cmake/modules/FindLibRaw.cmake index d6966dbc04..0baddc03cd 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/cmake/modules/FindLibRaw.cmake +++ b/plugins/impex/raw/3rdparty/libkdcraw/cmake/modules/FindLibRaw.cmake @@ -1,79 +1,79 @@ # - Find LibRaw -# Find the LibRaw library +# Find the LibRaw library # This module defines # LibRaw_VERSION_STRING, the version string of LibRaw # LibRaw_INCLUDE_DIR, where to find libraw.h # LibRaw_LIBRARIES, the libraries needed to use LibRaw (non-thread-safe) # LibRaw_r_LIBRARIES, the libraries needed to use LibRaw (thread-safe) # LibRaw_DEFINITIONS, the definitions needed to use LibRaw (non-thread-safe) # LibRaw_r_DEFINITIONS, the definitions needed to use LibRaw (thread-safe) # # Copyright (c) 2013, Pino Toscano # Copyright (c) 2013, Gilles Caulier # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. FIND_PACKAGE(PkgConfig) IF(PKG_CONFIG_FOUND) PKG_CHECK_MODULES(PC_LIBRAW libraw) SET(LibRaw_DEFINITIONS ${PC_LIBRAW_CFLAGS_OTHER}) PKG_CHECK_MODULES(PC_LIBRAW_R libraw_r) SET(LibRaw_r_DEFINITIONS ${PC_LIBRAW_R_CFLAGS_OTHER}) ENDIF() FIND_PATH(LibRaw_INCLUDE_DIR libraw.h HINTS ${PC_LIBRAW_INCLUDEDIR} ${PC_LibRaw_INCLUDE_DIRS} PATH_SUFFIXES libraw ) FIND_LIBRARY(LibRaw_LIBRARIES NAMES raw HINTS ${PC_LIBRAW_LIBDIR} ${PC_LIBRAW_LIBRARY_DIRS} ) FIND_LIBRARY(LibRaw_r_LIBRARIES NAMES raw_r HINTS ${PC_LIBRAW_R_LIBDIR} ${PC_LIBRAW_R_LIBRARY_DIRS} ) IF(LibRaw_INCLUDE_DIR) FILE(READ ${LibRaw_INCLUDE_DIR}/libraw_version.h _libraw_version_content) STRING(REGEX MATCH "#define LIBRAW_MAJOR_VERSION[ \t]*([0-9]*)\n" _version_major_match ${_libraw_version_content}) SET(_libraw_version_major "${CMAKE_MATCH_1}") STRING(REGEX MATCH "#define LIBRAW_MINOR_VERSION[ \t]*([0-9]*)\n" _version_minor_match ${_libraw_version_content}) SET(_libraw_version_minor "${CMAKE_MATCH_1}") STRING(REGEX MATCH "#define LIBRAW_PATCH_VERSION[ \t]*([0-9]*)\n" _version_patch_match ${_libraw_version_content}) SET(_libraw_version_patch "${CMAKE_MATCH_1}") IF(_version_major_match AND _version_minor_match AND _version_patch_match) SET(LibRaw_VERSION_STRING "${_libraw_version_major}.${_libraw_version_minor}.${_libraw_version_patch}") ELSE() IF(NOT LibRaw_FIND_QUIETLY) MESSAGE(STATUS "Failed to get version information from ${LibRaw_INCLUDE_DIR}/libraw_version.h") ENDIF() ENDIF() ENDIF() INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibRaw REQUIRED_VARS LibRaw_LIBRARIES LibRaw_INCLUDE_DIR VERSION_VAR LibRaw_VERSION_STRING ) MARK_AS_ADVANCED(LibRaw_VERSION_STRING LibRaw_INCLUDE_DIR LibRaw_LIBRARIES LibRaw_r_LIBRARIES LibRaw_DEFINITIONS LibRaw_r_DEFINITIONS ) diff --git a/plugins/impex/raw/3rdparty/libkdcraw/cmake/templates/libkdcraw.pc.cmake b/plugins/impex/raw/3rdparty/libkdcraw/cmake/templates/libkdcraw.pc.cmake index 3e4cb29116..30e2e85cec 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/cmake/templates/libkdcraw.pc.cmake +++ b/plugins/impex/raw/3rdparty/libkdcraw/cmake/templates/libkdcraw.pc.cmake @@ -1,12 +1,12 @@ prefix=${CMAKE_INSTALL_PREFIX} exec_prefix=${BIN_INSTALL_DIR} libdir=${LIB_INSTALL_DIR} includedir=${INCLUDE_INSTALL_DIR} Name: ${PROJECT_NAME} Description: A C++ wrapper around LibRaw library to decode RAW pictures. This library is used by digiKam and kipi-plugins. -URL: http://www.digikam.org/sharedlibs +URL: https://commits.kde.org/libkdcraw Requires: Version: ${DCRAW_LIB_VERSION_STRING} Libs: -L${LIB_INSTALL_DIR} -lkdcraw Cflags: -I${INCLUDE_INSTALL_DIR} diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/Mainpage.dox b/plugins/impex/raw/3rdparty/libkdcraw/src/Mainpage.dox index 7b96e9e556..a02a96f450 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/Mainpage.dox +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/Mainpage.dox @@ -1,7 +1,7 @@ /** @mainpage libKDcraw -libKDcraw is a thread-safe wrapper around libraw. Have a look at KDcrawIface::KDcraw to get started. +libKDcraw is a thread-safe wrapper around libraw. Have a look at KDcrawIface::KDcraw to get started. @see KDcrawIface::KDcraw */ diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawinfocontainer.cpp b/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawinfocontainer.cpp index b82ac4c653..65655df64f 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawinfocontainer.cpp +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawinfocontainer.cpp @@ -1,173 +1,173 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2007-05-02 * @brief RAW file identification information container * * @author Copyright (C) 2007-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, 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. * * ============================================================ */ // Local includes #include "dcrawinfocontainer.h" namespace KDcrawIface { DcrawInfoContainer::DcrawInfoContainer() { sensitivity = -1.0; exposureTime = -1.0; aperture = -1.0; focalLength = -1.0; pixelAspectRatio = 1.0; // Default value. This can be unavailable (depending of camera model). rawColors = -1; rawImages = -1; hasIccProfile = false; isDecodable = false; daylightMult[0] = 0.0; daylightMult[1] = 0.0; daylightMult[2] = 0.0; cameraMult[0] = 0.0; cameraMult[1] = 0.0; cameraMult[2] = 0.0; cameraMult[3] = 0.0; blackPoint = 0; for (int ch=0; ch<4; ch++) { blackPointCh[ch] = 0; } whitePoint = 0; topMargin = 0; leftMargin = 0; orientation = ORIENTATION_NONE; for (int x=0 ; x<3 ; x++) { for (int y=0 ; y<4 ; y++) { cameraColorMatrix1[x][y] = 0.0; cameraColorMatrix2[x][y] = 0.0; cameraXYZMatrix[y][x] = 0.0; // NOTE: see B.K.O # 253911 : [y][x] not [x][y] } } } DcrawInfoContainer::~DcrawInfoContainer() { } bool DcrawInfoContainer::isEmpty() { if (make.isEmpty() && model.isEmpty() && filterPattern.isEmpty() && colorKeys.isEmpty() && DNGVersion.isEmpty() && exposureTime == -1.0 && aperture == -1.0 && focalLength == -1.0 && pixelAspectRatio == 1.0 && sensitivity == -1.0 && rawColors == -1 && rawImages == -1 && blackPoint == 0 && blackPointCh[0] == 0 && blackPointCh[1] == 0 && blackPointCh[2] == 0 && blackPointCh[3] == 0 && whitePoint == 0 && topMargin == 0 && leftMargin == 0 && !dateTime.isValid() && !imageSize.isValid() && !fullSize.isValid() && !outputSize.isValid() && !thumbSize.isValid() && cameraColorMatrix1[0][0] == 0.0 && cameraColorMatrix1[0][1] == 0.0 && cameraColorMatrix1[0][2] == 0.0 && cameraColorMatrix1[0][3] == 0.0 && cameraColorMatrix1[1][0] == 0.0 && cameraColorMatrix1[1][1] == 0.0 && cameraColorMatrix1[1][2] == 0.0 && cameraColorMatrix1[1][3] == 0.0 && cameraColorMatrix1[2][0] == 0.0 && cameraColorMatrix1[2][1] == 0.0 && cameraColorMatrix1[2][2] == 0.0 && cameraColorMatrix1[2][3] == 0.0 && cameraColorMatrix2[0][0] == 0.0 && cameraColorMatrix2[0][1] == 0.0 && cameraColorMatrix2[0][2] == 0.0 && cameraColorMatrix2[0][3] == 0.0 && cameraColorMatrix2[1][0] == 0.0 && cameraColorMatrix2[1][1] == 0.0 && cameraColorMatrix2[1][2] == 0.0 && cameraColorMatrix2[1][3] == 0.0 && cameraColorMatrix2[2][0] == 0.0 && cameraColorMatrix2[2][1] == 0.0 && cameraColorMatrix2[2][2] == 0.0 && cameraColorMatrix2[2][3] == 0.0 && cameraXYZMatrix[0][0] == 0.0 && cameraXYZMatrix[0][1] == 0.0 && cameraXYZMatrix[0][2] == 0.0 && cameraXYZMatrix[1][0] == 0.0 && cameraXYZMatrix[1][1] == 0.0 && cameraXYZMatrix[1][2] == 0.0 && cameraXYZMatrix[2][0] == 0.0 && cameraXYZMatrix[2][1] == 0.0 && cameraXYZMatrix[2][2] == 0.0 && cameraXYZMatrix[3][0] == 0.0 && cameraXYZMatrix[3][1] == 0.0 && cameraXYZMatrix[3][2] == 0.0 && orientation == ORIENTATION_NONE ) { return true; } else { return false; } } QDebug operator<<(QDebug dbg, const DcrawInfoContainer& c) { dbg.nospace() << "DcrawInfoContainer::sensitivity: " << c.sensitivity << ", "; dbg.nospace() << "DcrawInfoContainer::exposureTime: " << c.exposureTime << ", "; dbg.nospace() << "DcrawInfoContainer::aperture: " << c.aperture << ", "; dbg.nospace() << "DcrawInfoContainer::focalLength: " << c.focalLength << ", "; dbg.nospace() << "DcrawInfoContainer::pixelAspectRatio: " << c.pixelAspectRatio << ", "; dbg.nospace() << "DcrawInfoContainer::rawColors: " << c.rawColors << ", "; dbg.nospace() << "DcrawInfoContainer::rawImages: " << c.rawImages << ", "; dbg.nospace() << "DcrawInfoContainer::hasIccProfile: " << c.hasIccProfile << ", "; dbg.nospace() << "DcrawInfoContainer::isDecodable: " << c.isDecodable << ", "; dbg.nospace() << "DcrawInfoContainer::daylightMult: " << c.daylightMult << ", "; dbg.nospace() << "DcrawInfoContainer::cameraMult: " << c.cameraMult << ", "; dbg.nospace() << "DcrawInfoContainer::blackPoint: " << c.blackPoint << ", "; dbg.nospace() << "DcrawInfoContainer::whitePoint: " << c.whitePoint << ", "; dbg.nospace() << "DcrawInfoContainer::topMargin: " << c.topMargin << ", "; dbg.nospace() << "DcrawInfoContainer::leftMargin: " << c.leftMargin << ", "; dbg.nospace() << "DcrawInfoContainer::orientation: " << c.orientation; return dbg.space(); } } // namespace KDcrawIface diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawinfocontainer.h b/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawinfocontainer.h index bf0773a78b..739b9ef4c5 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawinfocontainer.h +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawinfocontainer.h @@ -1,158 +1,158 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2007-05-02 * @brief RAW file identification information container * * @author Copyright (C) 2007-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, 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. * * ============================================================ */ #ifndef DCRAW_INFO_CONTAINER_H #define DCRAW_INFO_CONTAINER_H // Qt includes #include #include #include #include // Local includes namespace KDcrawIface { class DcrawInfoContainer { public: /** The RAW image orientation values */ enum ImageOrientation { ORIENTATION_NONE = 0, ORIENTATION_180 = 3, ORIENTATION_Mirror90CCW = 4, ORIENTATION_90CCW = 5, ORIENTATION_90CW = 6 }; public: /** Standard constructor */ DcrawInfoContainer(); /** Standard destructor */ virtual ~DcrawInfoContainer(); /** Return 'true' if container is empty, else 'false' */ bool isEmpty(); public: /** True if RAW file include an ICC color profile. */ bool hasIccProfile; /** True is RAW file is decodable by dcraw. */ bool isDecodable; /** The number of RAW colors. */ int rawColors; /** The number of RAW images. */ int rawImages; /** Black level from Raw histogram. */ unsigned int blackPoint; /** Channel black levels from Raw histogram. */ unsigned int blackPointCh[4]; /** White level from Raw histogram. */ unsigned int whitePoint; /** Top margin of raw image. */ unsigned int topMargin; /** Left margin of raw image. */ unsigned int leftMargin; /** The raw image orientation */ ImageOrientation orientation; /** The sensitivity in ISO used by camera to take the picture. */ float sensitivity; /** ==> 1/exposureTime = exposure time in seconds. */ float exposureTime; /** ==> Aperture value in APEX. */ float aperture; /** ==> Focal Length value in mm. */ float focalLength; /** The pixel Aspect Ratio if != 1.0. NOTE: if == 1.0, dcraw do not show this value. */ float pixelAspectRatio; /** White color balance settings. */ double daylightMult[3]; /** Camera multipliers used for White Balance adjustments */ double cameraMult[4]; /** Camera Color Matrix */ float cameraColorMatrix1[3][4]; float cameraColorMatrix2[3][4]; float cameraXYZMatrix[4][3]; /** The used Color Keys */ QString colorKeys; /** The camera maker. */ QString make; /** The camera model. */ QString model; /** The artist name who have picture owner. */ QString owner; /** The demosaising filter pattern. */ QString filterPattern; /** The DNG version. NOTE: it is only shown with DNG RAW files. */ QString DNGVersion; /** Date & time when the picture has been taken. */ QDateTime dateTime; /** The image dimensions in pixels. */ QSize imageSize; /** The thumb dimensions in pixels. */ QSize thumbSize; /** The full RAW image dimensions in pixels. */ QSize fullSize; /** The output dimensions in pixels. */ QSize outputSize; }; //! qDebug() stream operator. Writes container @a c to the debug output in a nicely formatted way. QDebug operator<<(QDebug dbg, const DcrawInfoContainer& c); } // namespace KDcrawIface #endif /* DCRAW_INFO_CONTAINER_H */ diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawsettingswidget.cpp b/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawsettingswidget.cpp index a7d64ce10b..7b242d2b10 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawsettingswidget.cpp +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawsettingswidget.cpp @@ -1,1324 +1,1324 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2006-09-13 * @brief LibRaw settings widgets * * @author Copyright (C) 2006-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2006-2011 by Marcel Wiesweg * marcel dot wiesweg at gmx dot de * @author Copyright (C) 2007-2008 by Guillaume Castagnino * casta at xwing dot info * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "dcrawsettingswidget.h" // C++ includes #include // Qt includes #include #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "kdcraw.h" #include "rnuminput.h" #include "rcombobox.h" #include "rexpanderbox.h" #include "libkdcraw_debug.h" #include namespace KDcrawIface { class Q_DECL_HIDDEN DcrawSettingsWidget::Private { public: Private() { autoBrightnessBox = 0; sixteenBitsImage = 0; fourColorCheckBox = 0; brightnessLabel = 0; brightnessSpinBox = 0; blackPointCheckBox = 0; blackPointSpinBox = 0; whitePointCheckBox = 0; whitePointSpinBox = 0; whiteBalanceComboBox = 0; whiteBalanceLabel = 0; customWhiteBalanceSpinBox = 0; customWhiteBalanceLabel = 0; customWhiteBalanceGreenSpinBox = 0; customWhiteBalanceGreenLabel = 0; unclipColorLabel = 0; dontStretchPixelsCheckBox = 0; RAWQualityComboBox = 0; RAWQualityLabel = 0; noiseReductionComboBox = 0; NRSpinBox1 = 0; NRSpinBox2 = 0; NRLabel1 = 0; NRLabel2 = 0; enableCACorrectionBox = 0; autoCACorrectionBox = 0; caRedMultSpinBox = 0; caBlueMultSpinBox = 0; caRedMultLabel = 0; caBlueMultLabel = 0; unclipColorComboBox = 0; reconstructLabel = 0; reconstructSpinBox = 0; outputColorSpaceLabel = 0; outputColorSpaceComboBox = 0; demosaicingSettings = 0; whiteBalanceSettings = 0; correctionsSettings = 0; colormanSettings = 0; medianFilterPassesSpinBox = 0; medianFilterPassesLabel = 0; inIccUrlEdit = 0; outIccUrlEdit = 0; inputColorSpaceLabel = 0; inputColorSpaceComboBox = 0; fixColorsHighlightsBox = 0; refineInterpolationBox = 0; noiseReductionLabel = 0; expoCorrectionBox = 0; expoCorrectionShiftSpinBox = 0; expoCorrectionHighlightSpinBox = 0; expoCorrectionShiftLabel = 0; expoCorrectionHighlightLabel = 0; } /** Convert Exposure correction shift E.V value from GUI to Linear value needs by libraw decoder. */ double shiftExpoFromEvToLinear(double ev) const { // From GUI : -2.0EV => 0.25 // +3.0EV => 8.00 return (1.55*ev + 3.35); } /** Convert Exposure correction shift Linear value from liraw decoder to E.V value needs by GUI. */ double shiftExpoFromLinearToEv(double lin) const { // From GUI : 0.25 => -2.0EV // 8.00 => +3.0EV return ((lin-3.35) / 1.55); } public: QWidget* demosaicingSettings; QWidget* whiteBalanceSettings; QWidget* correctionsSettings; QWidget* colormanSettings; QLabel* whiteBalanceLabel; QLabel* customWhiteBalanceLabel; QLabel* customWhiteBalanceGreenLabel; QLabel* brightnessLabel; QLabel* RAWQualityLabel; QLabel* NRLabel1; QLabel* NRLabel2; QLabel* caRedMultLabel; QLabel* caBlueMultLabel; QLabel* unclipColorLabel; QLabel* reconstructLabel; QLabel* inputColorSpaceLabel; QLabel* outputColorSpaceLabel; QLabel* medianFilterPassesLabel; QLabel* noiseReductionLabel; QLabel* expoCorrectionShiftLabel; QLabel* expoCorrectionHighlightLabel; QCheckBox* blackPointCheckBox; QCheckBox* whitePointCheckBox; QCheckBox* sixteenBitsImage; QCheckBox* autoBrightnessBox; QCheckBox* fourColorCheckBox; QCheckBox* dontStretchPixelsCheckBox; QCheckBox* enableCACorrectionBox; QCheckBox* autoCACorrectionBox; QCheckBox* fixColorsHighlightsBox; QCheckBox* refineInterpolationBox; QCheckBox* expoCorrectionBox; RFileSelector* inIccUrlEdit; RFileSelector* outIccUrlEdit; RComboBox* noiseReductionComboBox; RComboBox* whiteBalanceComboBox; RComboBox* RAWQualityComboBox; RComboBox* unclipColorComboBox; RComboBox* inputColorSpaceComboBox; RComboBox* outputColorSpaceComboBox; RIntNumInput* customWhiteBalanceSpinBox; RIntNumInput* reconstructSpinBox; RIntNumInput* blackPointSpinBox; RIntNumInput* whitePointSpinBox; RIntNumInput* NRSpinBox1; RIntNumInput* NRSpinBox2; RIntNumInput* medianFilterPassesSpinBox; RDoubleNumInput* customWhiteBalanceGreenSpinBox; RDoubleNumInput* caRedMultSpinBox; RDoubleNumInput* caBlueMultSpinBox; RDoubleNumInput* brightnessSpinBox; RDoubleNumInput* expoCorrectionShiftSpinBox; RDoubleNumInput* expoCorrectionHighlightSpinBox; }; DcrawSettingsWidget::DcrawSettingsWidget(QWidget* const parent, int advSettings) : RExpanderBox(parent), d(new Private) { setup(advSettings); } void DcrawSettingsWidget::setup(int advSettings) { setObjectName( QLatin1String("DCRawSettings Expander" )); // --------------------------------------------------------------- // DEMOSAICING Settings panel d->demosaicingSettings = new QWidget(this); QGridLayout* const demosaicingLayout = new QGridLayout(d->demosaicingSettings); int line = 0; d->sixteenBitsImage = new QCheckBox(i18nc("@option:check", "16 bits color depth"), d->demosaicingSettings); d->sixteenBitsImage->setToolTip(i18nc("@info:whatsthis", "

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

" "

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

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

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

" "

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

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

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

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

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

" // Original dcraw demosaicing methods "

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

" "

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

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

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

" "

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

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

This setting is available only for few Quality options:

" "

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

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

Configure the raw white balance:

" "

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

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

Set here the color temperature in Kelvin.

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

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

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

Select here the highlight clipping method:

" "

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

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

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

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

Turn on the exposure " "correction before interpolation.

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

Linear Shift of exposure correction before interpolation in E.V

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

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

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

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

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

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

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

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

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

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

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

Specify specific black point value of the output image.

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

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

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

Specify specific white point value of the output image.

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

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

" "

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

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

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

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

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

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

Set here the amount of correction on red-green axis

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

Set here the amount of correction on blue-yellow axis

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

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

" "

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

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

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

" "

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

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

Set here the noise reduction threshold value to use.

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

Amount of Luminance impulse noise reduction.

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

Amount of Chrominance impulse noise reduction.

")); d->NRSpinBox2->setVisible(true); d->NRLabel2->setVisible(true); break; default: d->NRSpinBox1->setEnabled(false); d->NRLabel1->setEnabled(false); d->NRSpinBox2->setEnabled(false); d->NRLabel2->setEnabled(false); d->NRSpinBox2->setVisible(false); d->NRLabel2->setVisible(false); break; } emit signalSettingsChanged(); } void DcrawSettingsWidget::slotCACorrectionToggled(bool b) { d->autoCACorrectionBox->setEnabled(b); slotAutoCAToggled(d->autoCACorrectionBox->isChecked()); } void DcrawSettingsWidget::slotAutoCAToggled(bool b) { if (b) { d->caRedMultSpinBox->setValue(0.0); d->caBlueMultSpinBox->setValue(0.0); } bool mult = (!b) && (d->autoCACorrectionBox->isEnabled()); d->caRedMultSpinBox->setEnabled(mult); d->caBlueMultSpinBox->setEnabled(mult); d->caRedMultLabel->setEnabled(mult); d->caBlueMultLabel->setEnabled(mult); emit signalSettingsChanged(); } void DcrawSettingsWidget::slotExposureCorrectionToggled(bool b) { d->expoCorrectionShiftLabel->setEnabled(b); d->expoCorrectionShiftSpinBox->setEnabled(b); d->expoCorrectionHighlightLabel->setEnabled(b); d->expoCorrectionHighlightSpinBox->setEnabled(b); slotExpoCorrectionShiftChanged(d->expoCorrectionShiftSpinBox->value()); } void DcrawSettingsWidget::slotExpoCorrectionShiftChanged(double ev) { // Only enable Highligh exposure correction if Shift correction is >= 1.0, else this settings do not take effect. bool b = (ev >= 1.0); d->expoCorrectionHighlightLabel->setEnabled(b); d->expoCorrectionHighlightSpinBox->setEnabled(b); emit signalSettingsChanged(); } void DcrawSettingsWidget::slotInputColorSpaceChanged(int item) { d->inIccUrlEdit->setEnabled(item == RawDecodingSettings::CUSTOMINPUTCS); } void DcrawSettingsWidget::slotOutputColorSpaceChanged(int item) { d->outIccUrlEdit->setEnabled(item == RawDecodingSettings::CUSTOMOUTPUTCS); } void DcrawSettingsWidget::slotRAWQualityChanged(int quality) { switch(quality) { case RawDecodingSettings::DCB: case RawDecodingSettings::VCD_AHD: // These options can be only available if Libraw use GPL2 pack. d->medianFilterPassesLabel->setEnabled(KDcraw::librawUseGPL2DemosaicPack()); d->medianFilterPassesSpinBox->setEnabled(KDcraw::librawUseGPL2DemosaicPack()); d->refineInterpolationBox->setEnabled(KDcraw::librawUseGPL2DemosaicPack()); break; case RawDecodingSettings::PL_AHD: case RawDecodingSettings::AFD: case RawDecodingSettings::VCD: case RawDecodingSettings::LMMSE: case RawDecodingSettings::AMAZE: d->medianFilterPassesLabel->setEnabled(false); d->medianFilterPassesSpinBox->setEnabled(false); d->refineInterpolationBox->setEnabled(false); break; default: // BILINEAR, VNG, PPG, AHD d->medianFilterPassesLabel->setEnabled(true); d->medianFilterPassesSpinBox->setEnabled(true); d->refineInterpolationBox->setEnabled(false); break; } emit signalSettingsChanged(); } void DcrawSettingsWidget::setEnabledBrightnessSettings(bool b) { d->brightnessLabel->setEnabled(b); d->brightnessSpinBox->setEnabled(b); } bool DcrawSettingsWidget::brightnessSettingsIsEnabled() const { return d->brightnessSpinBox->isEnabled(); } void DcrawSettingsWidget::setSettings(const RawDecodingSettings& settings) { d->sixteenBitsImage->setChecked(settings.sixteenBitsImage); switch(settings.whiteBalance) { case RawDecodingSettings::CAMERA: d->whiteBalanceComboBox->setCurrentIndex(1); break; case RawDecodingSettings::AUTO: d->whiteBalanceComboBox->setCurrentIndex(2); break; case RawDecodingSettings::CUSTOM: d->whiteBalanceComboBox->setCurrentIndex(3); break; default: d->whiteBalanceComboBox->setCurrentIndex(0); break; } slotWhiteBalanceToggled(d->whiteBalanceComboBox->currentIndex()); d->customWhiteBalanceSpinBox->setValue(settings.customWhiteBalance); d->customWhiteBalanceGreenSpinBox->setValue(settings.customWhiteBalanceGreen); d->fourColorCheckBox->setChecked(settings.RGBInterpolate4Colors); d->autoBrightnessBox->setChecked(settings.autoBrightness); d->fixColorsHighlightsBox->setChecked(settings.fixColorsHighlights); switch(settings.unclipColors) { case 0: d->unclipColorComboBox->setCurrentIndex(0); break; case 1: d->unclipColorComboBox->setCurrentIndex(1); break; case 2: d->unclipColorComboBox->setCurrentIndex(2); break; default: // Reconstruct Highlight method d->unclipColorComboBox->setCurrentIndex(3); d->reconstructSpinBox->setValue(settings.unclipColors-3); break; } slotUnclipColorActivated(d->unclipColorComboBox->currentIndex()); d->dontStretchPixelsCheckBox->setChecked(settings.DontStretchPixels); d->brightnessSpinBox->setValue(settings.brightness); d->blackPointCheckBox->setChecked(settings.enableBlackPoint); d->blackPointSpinBox->setEnabled(settings.enableBlackPoint); d->blackPointSpinBox->setValue(settings.blackPoint); d->whitePointCheckBox->setChecked(settings.enableWhitePoint); d->whitePointSpinBox->setEnabled(settings.enableWhitePoint); d->whitePointSpinBox->setValue(settings.whitePoint); int q = settings.RAWQuality; // If Libraw do not support GPL2 pack, reset to BILINEAR. if (!KDcraw::librawUseGPL2DemosaicPack()) { for (int i=RawDecodingSettings::DCB ; i <=RawDecodingSettings::LMMSE ; ++i) { if (q == i) { q = RawDecodingSettings::BILINEAR; qCDebug(LIBKDCRAW_LOG) << "Libraw GPL2 pack not available. Raw quality set to Bilinear"; break; } } } // If Libraw do not support GPL3 pack, reset to BILINEAR. if (!KDcraw::librawUseGPL3DemosaicPack() && (q == RawDecodingSettings::AMAZE)) { q = RawDecodingSettings::BILINEAR; qCDebug(LIBKDCRAW_LOG) << "Libraw GPL3 pack not available. Raw quality set to Bilinear"; } d->RAWQualityComboBox->setCurrentIndex(q); switch(q) { case RawDecodingSettings::DCB: d->medianFilterPassesSpinBox->setValue(settings.dcbIterations); d->refineInterpolationBox->setChecked(settings.dcbEnhanceFl); break; case RawDecodingSettings::VCD_AHD: d->medianFilterPassesSpinBox->setValue(settings.eeciRefine); d->refineInterpolationBox->setChecked(settings.eeciRefine); break; default: d->medianFilterPassesSpinBox->setValue(settings.medianFilterPasses); d->refineInterpolationBox->setChecked(false); // option not used. break; } slotRAWQualityChanged(q); d->inputColorSpaceComboBox->setCurrentIndex((int)settings.inputColorSpace); slotInputColorSpaceChanged((int)settings.inputColorSpace); d->outputColorSpaceComboBox->setCurrentIndex((int)settings.outputColorSpace); slotOutputColorSpaceChanged((int)settings.outputColorSpace); d->noiseReductionComboBox->setCurrentIndex(settings.NRType); slotNoiseReductionChanged(settings.NRType); d->NRSpinBox1->setValue(settings.NRThreshold); d->NRSpinBox2->setValue(settings.NRChroThreshold); d->enableCACorrectionBox->setChecked(settings.enableCACorrection); d->caRedMultSpinBox->setValue(settings.caMultiplier[0]); d->caBlueMultSpinBox->setValue(settings.caMultiplier[1]); d->autoCACorrectionBox->setChecked((settings.caMultiplier[0] == 0.0) && (settings.caMultiplier[1] == 0.0)); slotCACorrectionToggled(settings.enableCACorrection); d->expoCorrectionBox->setChecked(settings.expoCorrection); slotExposureCorrectionToggled(settings.expoCorrection); d->expoCorrectionShiftSpinBox->setValue(d->shiftExpoFromLinearToEv(settings.expoCorrectionShift)); d->expoCorrectionHighlightSpinBox->setValue(settings.expoCorrectionHighlight); d->inIccUrlEdit->lineEdit()->setText(settings.inputProfile); d->outIccUrlEdit->lineEdit()->setText(settings.outputProfile); } RawDecodingSettings DcrawSettingsWidget::settings() const { RawDecodingSettings prm; prm.sixteenBitsImage = d->sixteenBitsImage->isChecked(); switch(d->whiteBalanceComboBox->currentIndex()) { case 1: prm.whiteBalance = RawDecodingSettings::CAMERA; break; case 2: prm.whiteBalance = RawDecodingSettings::AUTO; break; case 3: prm.whiteBalance = RawDecodingSettings::CUSTOM; break; default: prm.whiteBalance = RawDecodingSettings::NONE; break; } prm.customWhiteBalance = d->customWhiteBalanceSpinBox->value(); prm.customWhiteBalanceGreen = d->customWhiteBalanceGreenSpinBox->value(); prm.RGBInterpolate4Colors = d->fourColorCheckBox->isChecked(); prm.autoBrightness = d->autoBrightnessBox->isChecked(); prm.fixColorsHighlights = d->fixColorsHighlightsBox->isChecked(); switch(d->unclipColorComboBox->currentIndex()) { case 0: prm.unclipColors = 0; break; case 1: prm.unclipColors = 1; break; case 2: prm.unclipColors = 2; break; default: // Reconstruct Highlight method prm.unclipColors = d->reconstructSpinBox->value()+3; break; } prm.DontStretchPixels = d->dontStretchPixelsCheckBox->isChecked(); prm.brightness = d->brightnessSpinBox->value(); prm.enableBlackPoint = d->blackPointCheckBox->isChecked(); prm.blackPoint = d->blackPointSpinBox->value(); prm.enableWhitePoint = d->whitePointCheckBox->isChecked(); prm.whitePoint = d->whitePointSpinBox->value(); prm.RAWQuality = (RawDecodingSettings::DecodingQuality)d->RAWQualityComboBox->currentIndex(); switch(prm.RAWQuality) { case RawDecodingSettings::DCB: prm.dcbIterations = d->medianFilterPassesSpinBox->value(); prm.dcbEnhanceFl = d->refineInterpolationBox->isChecked(); break; case RawDecodingSettings::VCD_AHD: prm.esMedPasses = d->medianFilterPassesSpinBox->value(); prm.eeciRefine = d->refineInterpolationBox->isChecked(); break; default: prm.medianFilterPasses = d->medianFilterPassesSpinBox->value(); break; } prm.NRType = (RawDecodingSettings::NoiseReduction)d->noiseReductionComboBox->currentIndex(); switch (prm.NRType) { case RawDecodingSettings::NONR: { prm.NRThreshold = 0; prm.NRChroThreshold = 0; break; } case RawDecodingSettings::WAVELETSNR: case RawDecodingSettings::FBDDNR: case RawDecodingSettings::LINENR: { prm.NRThreshold = d->NRSpinBox1->value(); prm.NRChroThreshold = 0; break; } default: // IMPULSENR { prm.NRThreshold = d->NRSpinBox1->value(); prm.NRChroThreshold = d->NRSpinBox2->value(); break; } } prm.enableCACorrection = d->enableCACorrectionBox->isChecked(); prm.caMultiplier[0] = d->caRedMultSpinBox->value(); prm.caMultiplier[1] = d->caBlueMultSpinBox->value(); prm.expoCorrection = d->expoCorrectionBox->isChecked(); prm.expoCorrectionShift = d->shiftExpoFromEvToLinear(d->expoCorrectionShiftSpinBox->value()); prm.expoCorrectionHighlight = d->expoCorrectionHighlightSpinBox->value(); prm.inputColorSpace = (RawDecodingSettings::InputColorSpace)(d->inputColorSpaceComboBox->currentIndex()); prm.outputColorSpace = (RawDecodingSettings::OutputColorSpace)(d->outputColorSpaceComboBox->currentIndex()); prm.inputProfile = d->inIccUrlEdit->lineEdit()->text(); prm.outputProfile = d->outIccUrlEdit->lineEdit()->text(); return prm; } void DcrawSettingsWidget::writeSettings(KConfigGroup& group) { RawDecodingSettings prm = settings(); prm.writeSettings(group); RExpanderBox::writeSettings(group); } void DcrawSettingsWidget::readSettings(KConfigGroup& group) { RawDecodingSettings prm; prm.readSettings(group); setSettings(prm); RExpanderBox::readSettings(group); } } // NameSpace KDcrawIface diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawsettingswidget.h b/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawsettingswidget.h index 1370cae405..9bca08791a 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawsettingswidget.h +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawsettingswidget.h @@ -1,126 +1,126 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2006-09-13 * @brief LibRaw settings widgets * * @author Copyright (C) 2006-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2006-2011 by Marcel Wiesweg * marcel dot wiesweg at gmx dot de * @author Copyright (C) 2007-2008 by Guillaume Castagnino * casta at xwing dot info * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DCRAW_SETTINGS_WIDGET_H #define DCRAW_SETTINGS_WIDGET_H // Qt includes #include // KDE includes #include // Local includes #include "rawdecodingsettings.h" #include "rexpanderbox.h" #include "rwidgetutils.h" namespace KDcrawIface { class DcrawSettingsWidget : public RExpanderBox { Q_OBJECT public: enum AdvancedSettingsOptions { SIXTEENBITS = 0x00000001, COLORSPACE = 0x00000002, POSTPROCESSING = 0x00000004, BLACKWHITEPOINTS = 0x00000008 }; enum SettingsTabs { DEMOSAICING = 0, WHITEBALANCE, CORRECTIONS, COLORMANAGEMENT }; public: /** * @param advSettings the default value is COLORSPACE */ explicit DcrawSettingsWidget(QWidget* const parent, int advSettings = COLORSPACE); ~DcrawSettingsWidget() override; RFileSelector* inputProfileUrlEdit() const; RFileSelector* outputProfileUrlEdit() const; void setup(int advSettings); void setEnabledBrightnessSettings(bool b); bool brightnessSettingsIsEnabled() const; void updateMinimumWidth(); void resetToDefault(); void setSettings(const RawDecodingSettings& settings); RawDecodingSettings settings() const; void readSettings(KConfigGroup& group) override; void writeSettings(KConfigGroup& group) override; Q_SIGNALS: void signalSixteenBitsImageToggled(bool); void signalSettingsChanged(); private Q_SLOTS: void slotWhiteBalanceToggled(int); void slotsixteenBitsImageToggled(bool); void slotUnclipColorActivated(int); void slotNoiseReductionChanged(int); void slotCACorrectionToggled(bool); void slotExposureCorrectionToggled(bool); void slotAutoCAToggled(bool); void slotInputColorSpaceChanged(int); void slotOutputColorSpaceChanged(int); void slotRAWQualityChanged(int); void slotExpoCorrectionShiftChanged(double); private: class Private; Private* const d; }; } // NameSpace KDcrawIface #endif /* DCRAW_SETTINGS_WIDGET_H */ diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/kdcraw.cpp b/plugins/impex/raw/3rdparty/libkdcraw/src/kdcraw.cpp index 3ca5011b3f..dcaecc6fa7 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/kdcraw.cpp +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/kdcraw.cpp @@ -1,583 +1,583 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2006-12-09 * @brief a tread-safe libraw C++ program interface * * @author Copyright (C) 2006-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2006-2013 by Marcel Wiesweg * marcel dot wiesweg at gmx dot de * @author Copyright (C) 2007-2008 by Guillaume Castagnino * casta at xwing dot info * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "kdcraw.h" #include "kdcraw_p.h" // Qt includes #include #include #include // KDE includes //#include // LibRaw includes #include #ifdef LIBRAW_HAS_CONFIG #include #endif // Local includes #include "libkdcraw_debug.h" #include "libkdcraw_version.h" #include "rawfiles.h" namespace KDcrawIface { KDcraw::KDcraw() : d(new Private(this)) { m_cancel = false; } KDcraw::~KDcraw() { cancel(); delete d; } QString KDcraw::version() { return QString(KDCRAW_VERSION_STRING); } void KDcraw::cancel() { m_cancel = true; } bool KDcraw::loadRawPreview(QImage& image, const QString& path) { // In first, try to extract the embedded JPEG preview. Very fast. bool ret = loadEmbeddedPreview(image, path); if (ret) return true; // In second, decode and half size of RAW picture. More slow. return (loadHalfPreview(image, path)); } bool KDcraw::loadEmbeddedPreview(QImage& image, const QString& path) { QByteArray imgData; if ( loadEmbeddedPreview(imgData, path) ) { qCDebug(LIBKDCRAW_LOG) << "Preview data size:" << imgData.size(); if (image.loadFromData( imgData )) { qCDebug(LIBKDCRAW_LOG) << "Using embedded RAW preview extraction"; return true; } } qCDebug(LIBKDCRAW_LOG) << "Failed to load embedded RAW preview"; return false; } bool KDcraw::loadEmbeddedPreview(QByteArray& imgData, const QString& path) { QFileInfo fileInfo(path); QString rawFilesExt(rawFiles()); QString ext = fileInfo.suffix().toUpper(); if (!fileInfo.exists() || ext.isEmpty() || !rawFilesExt.toUpper().contains(ext)) return false; LibRaw raw; int ret = raw.open_file((const char*)(QFile::encodeName(path)).constData()); if (ret != LIBRAW_SUCCESS) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run open_file: " << libraw_strerror(ret); raw.recycle(); return false; } return (Private::loadEmbeddedPreview(imgData, raw)); } bool KDcraw::loadEmbeddedPreview(QByteArray& imgData, const QBuffer& buffer) { QString rawFilesExt(KDcrawIface::KDcraw::rawFiles()); LibRaw raw; QByteArray inData = buffer.data(); int ret = raw.open_buffer((void*) inData.data(), (size_t) inData.size()); if (ret != LIBRAW_SUCCESS) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run open_buffer: " << libraw_strerror(ret); raw.recycle(); return false; } return (Private::loadEmbeddedPreview(imgData, raw)); } bool KDcraw::loadHalfPreview(QImage& image, const QString& path) { QFileInfo fileInfo(path); QString rawFilesExt(rawFiles()); QString ext = fileInfo.suffix().toUpper(); if (!fileInfo.exists() || ext.isEmpty() || !rawFilesExt.toUpper().contains(ext)) return false; qCDebug(LIBKDCRAW_LOG) << "Try to use reduced RAW picture extraction"; LibRaw raw; raw.imgdata.params.use_auto_wb = 1; // Use automatic white balance. raw.imgdata.params.use_camera_wb = 1; // Use camera white balance, if possible. raw.imgdata.params.half_size = 1; // Half-size color image (3x faster than -q). int ret = raw.open_file((const char*)(QFile::encodeName(path)).constData()); if (ret != LIBRAW_SUCCESS) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run open_file: " << libraw_strerror(ret); raw.recycle(); return false; } if(!Private::loadHalfPreview(image, raw)) { qCDebug(LIBKDCRAW_LOG) << "Failed to get half preview from LibRaw!"; return false; } qCDebug(LIBKDCRAW_LOG) << "Using reduced RAW picture extraction"; return true; } bool KDcraw::loadHalfPreview(QByteArray& imgData, const QString& path) { QFileInfo fileInfo(path); QString rawFilesExt(rawFiles()); QString ext = fileInfo.suffix().toUpper(); if (!fileInfo.exists() || ext.isEmpty() || !rawFilesExt.toUpper().contains(ext)) return false; qCDebug(LIBKDCRAW_LOG) << "Try to use reduced RAW picture extraction"; LibRaw raw; int ret = raw.open_file((const char*)(QFile::encodeName(path)).constData()); if (ret != LIBRAW_SUCCESS) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run dcraw_process: " << libraw_strerror(ret); raw.recycle(); return false; } QImage image; if (!Private::loadHalfPreview(image, raw)) { qCDebug(LIBKDCRAW_LOG) << "KDcraw: failed to get half preview: " << libraw_strerror(ret); return false; } QBuffer buffer(&imgData); buffer.open(QIODevice::WriteOnly); image.save(&buffer, "JPEG"); return true; } bool KDcraw::loadHalfPreview(QByteArray& imgData, const QBuffer& inBuffer) { QString rawFilesExt(KDcrawIface::KDcraw::rawFiles()); LibRaw raw; QByteArray inData = inBuffer.data(); int ret = raw.open_buffer((void*) inData.data(), (size_t) inData.size()); if (ret != LIBRAW_SUCCESS) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run dcraw_make_mem_image: " << libraw_strerror(ret); raw.recycle(); return false; } QImage image; if (!Private::loadHalfPreview(image, raw)) { qCDebug(LIBKDCRAW_LOG) << "KDcraw: failed to get half preview: " << libraw_strerror(ret); return false; } QBuffer buffer(&imgData); buffer.open(QIODevice::WriteOnly); image.save(&buffer, "JPG"); return true; } bool KDcraw::loadFullImage(QImage& image, const QString& path, const RawDecodingSettings& settings) { QFileInfo fileInfo(path); QString rawFilesExt(rawFiles()); QString ext = fileInfo.suffix().toUpper(); if (!fileInfo.exists() || ext.isEmpty() || !rawFilesExt.toUpper().contains(ext)) return false; qCDebug(LIBKDCRAW_LOG) << "Try to load full RAW picture..."; RawDecodingSettings prm = settings; prm.sixteenBitsImage = false; QByteArray imgData; int width, height, rgbmax; KDcraw decoder; bool ret = decoder.decodeRAWImage(path, prm, imgData, width, height, rgbmax); if (!ret) { qCDebug(LIBKDCRAW_LOG) << "Failled to load full RAW picture"; return false; } uchar* sptr = (uchar*)imgData.data(); uchar tmp8[2]; // Set RGB color components. for (int i = 0 ; i < width * height ; ++i) { // Swap Red and Blue tmp8[0] = sptr[2]; tmp8[1] = sptr[0]; sptr[0] = tmp8[0]; sptr[2] = tmp8[1]; sptr += 3; } image = QImage(width, height, QImage::Format_ARGB32); uint* dptr = reinterpret_cast(image.bits()); sptr = (uchar*)imgData.data(); for (int i = 0 ; i < width * height ; ++i) { *dptr++ = qRgba(sptr[2], sptr[1], sptr[0], 0xFF); sptr += 3; } qCDebug(LIBKDCRAW_LOG) << "Load full RAW picture done"; return true; } bool KDcraw::rawFileIdentify(DcrawInfoContainer& identify, const QString& path) { QFileInfo fileInfo(path); QString rawFilesExt(rawFiles()); QString ext = fileInfo.suffix().toUpper(); identify.isDecodable = false; if (!fileInfo.exists() || ext.isEmpty() || !rawFilesExt.toUpper().contains(ext)) return false; LibRaw raw; int ret = raw.open_file((const char*)(QFile::encodeName(path)).constData()); if (ret != LIBRAW_SUCCESS) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run open_file: " << libraw_strerror(ret); raw.recycle(); return false; } ret = raw.adjust_sizes_info_only(); if (ret != LIBRAW_SUCCESS) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run adjust_sizes_info_only: " << libraw_strerror(ret); raw.recycle(); return false; } Private::fillIndentifyInfo(&raw, identify); raw.recycle(); return true; } // ---------------------------------------------------------------------------------- bool KDcraw::extractRAWData(const QString& filePath, QByteArray& rawData, DcrawInfoContainer& identify, unsigned int shotSelect) { QFileInfo fileInfo(filePath); QString rawFilesExt(rawFiles()); QString ext = fileInfo.suffix().toUpper(); identify.isDecodable = false; if (!fileInfo.exists() || ext.isEmpty() || !rawFilesExt.toUpper().contains(ext)) return false; if (m_cancel) return false; d->setProgress(0.1); LibRaw raw; // Set progress call back function. raw.set_progress_handler(callbackForLibRaw, d); int ret = raw.open_file((const char*)(QFile::encodeName(filePath)).constData()); if (ret != LIBRAW_SUCCESS) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run open_file: " << libraw_strerror(ret); raw.recycle(); return false; } if (m_cancel) { raw.recycle(); return false; } d->setProgress(0.3); raw.imgdata.params.output_bps = 16; raw.imgdata.params.shot_select = shotSelect; ret = raw.unpack(); if (ret != LIBRAW_SUCCESS) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run unpack: " << libraw_strerror(ret); raw.recycle(); return false; } if (m_cancel) { raw.recycle(); return false; } d->setProgress(0.4); ret = raw.raw2image(); if (ret != LIBRAW_SUCCESS) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run raw2image: " << libraw_strerror(ret); raw.recycle(); return false; } if (m_cancel) { raw.recycle(); return false; } d->setProgress(0.6); Private::fillIndentifyInfo(&raw, identify); if (m_cancel) { raw.recycle(); return false; } d->setProgress(0.8); rawData = QByteArray(); if (raw.imgdata.idata.filters == 0) { rawData.resize((int)(raw.imgdata.sizes.iwidth * raw.imgdata.sizes.iheight * raw.imgdata.idata.colors * sizeof(unsigned short))); unsigned short* output = reinterpret_cast(rawData.data()); for (unsigned int row = 0; row < raw.imgdata.sizes.iheight; row++) { for (unsigned int col = 0; col < raw.imgdata.sizes.iwidth; col++) { for (int color = 0; color < raw.imgdata.idata.colors; color++) { *output = raw.imgdata.image[raw.imgdata.sizes.iwidth*row + col][color]; output++; } } } } else { int w = raw.imgdata.sizes.iwidth; int h = raw.imgdata.sizes.iheight; rawData.resize(w * h * sizeof(unsigned short)); unsigned short* output = reinterpret_cast(rawData.data()); for (uint row = 0; row < raw.imgdata.sizes.iheight; row++) { for (uint col = 0; col < raw.imgdata.sizes.iwidth; col++) { *output = raw.imgdata.image[raw.imgdata.sizes.iwidth*row + col][raw.COLOR(row, col)]; output++; } } } raw.recycle(); d->setProgress(1.0); return true; } bool KDcraw::decodeHalfRAWImage(const QString& filePath, const RawDecodingSettings& rawDecodingSettings, QByteArray& imageData, int& width, int& height, int& rgbmax) { m_rawDecodingSettings = rawDecodingSettings; m_rawDecodingSettings.halfSizeColorImage = true; return (d->loadFromLibraw(filePath, imageData, width, height, rgbmax)); } bool KDcraw::decodeRAWImage(const QString& filePath, const RawDecodingSettings& rawDecodingSettings, QByteArray& imageData, int& width, int& height, int& rgbmax) { m_rawDecodingSettings = rawDecodingSettings; return (d->loadFromLibraw(filePath, imageData, width, height, rgbmax)); } bool KDcraw::checkToCancelWaitingData() { return m_cancel; } void KDcraw::setWaitingDataProgress(double) { } const char* KDcraw::rawFiles() { return raw_file_extentions; } QStringList KDcraw::rawFilesList() { QString string = QString::fromLatin1(rawFiles()); return string.remove("*.").split(' '); } int KDcraw::rawFilesVersion() { return raw_file_extensions_version; } QStringList KDcraw::supportedCamera() { QStringList camera; const char** const list = LibRaw::cameraList(); for (int i = 0; i < LibRaw::cameraCount(); i++) camera.append(list[i]); return camera; } QString KDcraw::librawVersion() { return QString(LIBRAW_VERSION_STR).remove("-Release"); } int KDcraw::librawUseGomp() { #ifdef LIBRAW_HAS_CONFIG # ifdef LIBRAW_USE_OPENMP return true; # else return false; # endif #else return -1; #endif } int KDcraw::librawUseRawSpeed() { #ifdef LIBRAW_HAS_CONFIG # ifdef LIBRAW_USE_RAWSPEED return true; # else return false; # endif #else return -1; #endif } int KDcraw::librawUseGPL2DemosaicPack() { #ifdef LIBRAW_HAS_CONFIG # ifdef LIBRAW_USE_DEMOSAIC_PACK_GPL2 return true; # else return false; # endif #else return -1; #endif } int KDcraw::librawUseGPL3DemosaicPack() { #ifdef LIBRAW_HAS_CONFIG # ifdef LIBRAW_USE_DEMOSAIC_PACK_GPL3 return true; # else return false; # endif #else return -1; #endif } } // namespace KDcrawIface diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/kdcraw.h b/plugins/impex/raw/3rdparty/libkdcraw/src/kdcraw.h index 61f7471f5b..549ad9158a 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/kdcraw.h +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/kdcraw.h @@ -1,267 +1,267 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2006-12-09 * @brief a tread-safe libraw C++ program interface * * @author Copyright (C) 2006-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2006-2013 by Marcel Wiesweg * marcel dot wiesweg at gmx dot de * @author Copyright (C) 2007-2008 by Guillaume Castagnino * casta at xwing dot info * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef KDCRAW_H #define KDCRAW_H // C++ includes #include // Qt includes #include #include #include #include // Local includes #include "rawdecodingsettings.h" #include "dcrawinfocontainer.h" /** @brief Main namespace of libKDcraw */ namespace KDcrawIface { class KDcraw : public QObject { Q_OBJECT public: /** Standard constructor. */ KDcraw(); /** Standard destructor. */ ~KDcraw() override; public: /** Return a string version of libkdcraw release */ static QString version(); /** Get the preview of RAW picture as a QImage. It tries loadEmbeddedPreview() first and if it fails, calls loadHalfPreview(). */ static bool loadRawPreview(QImage& image, const QString& path); /** Get the preview of RAW picture as a QByteArray holding JPEG data. It tries loadEmbeddedPreview() first and if it fails, calls loadHalfPreview(). */ static bool loadRawPreview(QByteArray& imgData, const QString& path); /** Get the preview of RAW picture passed in QBuffer as a QByteArray holding JPEG data. It tries loadEmbeddedPreview() first and if it fails, calls loadHalfPreview(). */ static bool loadRawPreview(QByteArray& imgData, const QBuffer& inBuffer); /** Get the embedded JPEG preview image from RAW picture as a QByteArray which will include Exif Data. This is fast and non cancelable. This method does not require a class instance to run. */ static bool loadEmbeddedPreview(QByteArray& imgData, const QString& path); /** Get the embedded JPEG preview image from RAW picture as a QImage. This is fast and non cancelable This method does not require a class instance to run. */ static bool loadEmbeddedPreview(QImage& image, const QString& path); /** Get the embedded JPEG preview image from RAW image passed in QBuffer as a QByteArray which will include Exif Data. This is fast and non cancelable. This method does not require a class instance to run. */ static bool loadEmbeddedPreview(QByteArray& imgData, const QBuffer& inBuffer); /** Get the half decoded RAW picture. This is slower than loadEmbeddedPreview() method and non cancelable. This method does not require a class instance to run. */ static bool loadHalfPreview(QImage& image, const QString& path); /** Get the half decoded RAW picture as JPEG data in QByteArray. This is slower than loadEmbeddedPreview() method and non cancelable. This method does not require a class instance to run. */ static bool loadHalfPreview(QByteArray& imgData, const QString& path); /** Get the half decoded RAW picture passed in QBuffer as JPEG data in QByteArray. This is slower than loadEmbeddedPreview() method and non cancelable. This method does not require a class instance to run. */ static bool loadHalfPreview(QByteArray& imgData, const QBuffer& inBuffer); /** Get the full decoded RAW picture. This is a more slower than loadHalfPreview() method and non cancelable. This method does not require a class instance to run. */ static bool loadFullImage(QImage& image, const QString& path, const RawDecodingSettings& settings = RawDecodingSettings()); /** Get the camera settings witch have taken RAW file. Look into dcrawinfocontainer.h for more details. This is a fast and non cancelable method witch do not require a class instance to run. */ static bool rawFileIdentify(DcrawInfoContainer& identify, const QString& path); /** Return the string of all RAW file type mime supported. */ static const char* rawFiles(); /** Return the list of all RAW file type mime supported, as a QStringList, without wildcard and suffix dot. */ static QStringList rawFilesList(); /** Returns a version number for the list of supported RAW file types. This version is incremented if the list of supported formats has changed between library releases. */ static int rawFilesVersion(); /** Provide a list of supported RAW Camera name. */ static QStringList supportedCamera(); /** Return LibRaw version string. */ static QString librawVersion(); /** Return true or false if LibRaw use parallel demosaicing or not (libgomp support). * Return -1 if undefined. */ static int librawUseGomp(); /** Return true or false if LibRaw use RawSpeed codec or not. * Return -1 if undefined. */ static int librawUseRawSpeed(); /** Return true or false if LibRaw use Demosaic Pack GPL2 or not. * Return -1 if undefined. */ static int librawUseGPL2DemosaicPack(); /** Return true or false if LibRaw use Demosaic Pack GPL3 or not. * Return -1 if undefined. */ static int librawUseGPL3DemosaicPack(); public: /** Extract Raw image data undemosaiced and without post processing from 'filePath' picture file. This is a cancelable method which require a class instance to run because RAW pictures loading can take a while. This method return: - A byte array container 'rawData' with raw data. - All info about Raw image into 'identify' container. - 'false' is returned if loadding failed, else 'true'. */ bool extractRAWData(const QString& filePath, QByteArray& rawData, DcrawInfoContainer& identify, unsigned int shotSelect=0); /** Extract a small size of decode RAW data from 'filePath' picture file using 'rawDecodingSettings' settings. This is a cancelable method which require a class instance to run because RAW pictures decoding can take a while. This method return: - A byte array container 'imageData' with picture data. Pixels order is RGB. Color depth can be 8 or 16. In 8 bits you can access to color component using (uchar*), in 16 bits using (ushort*). - Size size of image in number of pixels ('width' and 'height'). - The max average of RGB components from decoded picture. - 'false' is returned if decoding failed, else 'true'. */ bool decodeHalfRAWImage(const QString& filePath, const RawDecodingSettings& rawDecodingSettings, QByteArray& imageData, int& width, int& height, int& rgbmax); /** Extract a full size of RAW data from 'filePath' picture file using 'rawDecodingSettings' settings. This is a cancelable method which require a class instance to run because RAW pictures decoding can take a while. This method return: - A byte array container 'imageData' with picture data. Pixels order is RGB. Color depth can be 8 or 16. In 8 bits you can access to color component using (uchar*), in 16 bits using (ushort*). - Size size of image in number of pixels ('width' and 'height'). - The max average of RGB components from decoded picture. - 'false' is returned if decoding failed, else 'true'. */ bool decodeRAWImage(const QString& filePath, const RawDecodingSettings& rawDecodingSettings, QByteArray& imageData, int& width, int& height, int& rgbmax); /** To cancel 'decodeHalfRAWImage' and 'decodeRAWImage' methods running in a separate thread. */ void cancel(); protected: /** Used internally to cancel RAW decoding operation. Normally, you don't need to use it directly, excepted if you derivated this class. Usual way is to use cancel() method */ bool m_cancel; /** The settings container used to perform RAW pictures decoding. See 'rawdecodingsetting.h' for details. */ RawDecodingSettings m_rawDecodingSettings; protected: /** Re-implement this method to control the cancelisation of loop witch wait data from RAW decoding process with your propers envirronement. By default, this method check if m_cancel is true. */ virtual bool checkToCancelWaitingData(); /** Re-implement this method to control the pseudo progress value during RAW decoding (when dcraw run with an internal loop without feedback) with your proper environment. By default, this method does nothing. Progress value average for this stage is 0%-n%, with 'n' == 40% max (see setWaitingDataProgress() method). */ virtual void setWaitingDataProgress(double value); public: // Declared public to be called externally by callbackForLibRaw() static method. class Private; private: Private* const d; friend class Private; }; } // namespace KDcrawIface #endif /* KDCRAW_H */ diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/kdcraw_p.cpp b/plugins/impex/raw/3rdparty/libkdcraw/src/kdcraw_p.cpp index 29553fce35..6be94077f0 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/kdcraw_p.cpp +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/kdcraw_p.cpp @@ -1,698 +1,698 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2008-10-09 * @brief internal private container for KDcraw * * @author Copyright (C) 2008-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "kdcraw.h" #include "kdcraw_p.h" // Qt includes #include #include // Local includes #include "libkdcraw_debug.h" namespace KDcrawIface { int callbackForLibRaw(void* data, enum LibRaw_progress p, int iteration, int expected) { if (data) { KDcraw::Private* const d = static_cast(data); if (d) { return d->progressCallback(p, iteration, expected); } } return 0; } // -------------------------------------------------------------------------------------------------- KDcraw::Private::Private(KDcraw* const p) { m_progress = 0.0; m_parent = p; } KDcraw::Private::~Private() { } void KDcraw::Private::createPPMHeader(QByteArray& imgData, libraw_processed_image_t* const img) { QString header = QString("P%1\n%2 %3\n%4\n").arg(img->colors == 3 ? "6" : "5") .arg(img->width) .arg(img->height) .arg((1 << img->bits)-1); imgData.append(header.toLatin1()); imgData.append(QByteArray((const char*)img->data, (int)img->data_size)); } int KDcraw::Private::progressCallback(enum LibRaw_progress p, int iteration, int expected) { qCDebug(LIBKDCRAW_LOG) << "LibRaw progress: " << libraw_strprogress(p) << " pass " << iteration << " of " << expected; // post a little change in progress indicator to show raw processor activity. setProgress(progressValue()+0.01); // Clean processing termination by user... if (m_parent->checkToCancelWaitingData()) { qCDebug(LIBKDCRAW_LOG) << "LibRaw process terminaison invoked..."; m_parent->m_cancel = true; m_progress = 0.0; return 1; } // Return 0 to continue processing... return 0; } void KDcraw::Private::setProgress(double value) { m_progress = value; m_parent->setWaitingDataProgress(m_progress); } double KDcraw::Private::progressValue() const { return m_progress; } void KDcraw::Private::fillIndentifyInfo(LibRaw* const raw, DcrawInfoContainer& identify) { identify.dateTime.setSecsSinceEpoch(raw->imgdata.other.timestamp); identify.make = QString(raw->imgdata.idata.make); identify.model = QString(raw->imgdata.idata.model); identify.owner = QString(raw->imgdata.other.artist); identify.DNGVersion = QString::number(raw->imgdata.idata.dng_version); identify.sensitivity = raw->imgdata.other.iso_speed; identify.exposureTime = raw->imgdata.other.shutter; identify.aperture = raw->imgdata.other.aperture; identify.focalLength = raw->imgdata.other.focal_len; identify.imageSize = QSize(raw->imgdata.sizes.width, raw->imgdata.sizes.height); identify.fullSize = QSize(raw->imgdata.sizes.raw_width, raw->imgdata.sizes.raw_height); identify.outputSize = QSize(raw->imgdata.sizes.iwidth, raw->imgdata.sizes.iheight); identify.thumbSize = QSize(raw->imgdata.thumbnail.twidth, raw->imgdata.thumbnail.theight); identify.topMargin = raw->imgdata.sizes.top_margin; identify.leftMargin = raw->imgdata.sizes.left_margin; identify.hasIccProfile = raw->imgdata.color.profile ? true : false; identify.isDecodable = true; identify.pixelAspectRatio = raw->imgdata.sizes.pixel_aspect; identify.rawColors = raw->imgdata.idata.colors; identify.rawImages = raw->imgdata.idata.raw_count; identify.blackPoint = raw->imgdata.color.black; for (int ch = 0; ch < 4; ch++) { identify.blackPointCh[ch] = raw->imgdata.color.cblack[ch]; } identify.whitePoint = raw->imgdata.color.maximum; identify.orientation = (DcrawInfoContainer::ImageOrientation)raw->imgdata.sizes.flip; memcpy(&identify.cameraColorMatrix1, &raw->imgdata.color.cmatrix, sizeof(raw->imgdata.color.cmatrix)); memcpy(&identify.cameraColorMatrix2, &raw->imgdata.color.rgb_cam, sizeof(raw->imgdata.color.rgb_cam)); memcpy(&identify.cameraXYZMatrix, &raw->imgdata.color.cam_xyz, sizeof(raw->imgdata.color.cam_xyz)); if (raw->imgdata.idata.filters) { if (!raw->imgdata.idata.cdesc[3]) { raw->imgdata.idata.cdesc[3] = 'G'; } for (int i=0; i < 16; i++) { identify.filterPattern.append(raw->imgdata.idata.cdesc[raw->COLOR(i >> 1,i & 1)]); } identify.colorKeys = raw->imgdata.idata.cdesc; } for(int c = 0 ; c < raw->imgdata.idata.colors ; c++) { identify.daylightMult[c] = raw->imgdata.color.pre_mul[c]; } if (raw->imgdata.color.cam_mul[0] > 0) { for(int c = 0 ; c < 4 ; c++) { identify.cameraMult[c] = raw->imgdata.color.cam_mul[c]; } } } bool KDcraw::Private::loadFromLibraw(const QString& filePath, QByteArray& imageData, int& width, int& height, int& rgbmax) { m_parent->m_cancel = false; LibRaw raw; // Set progress call back function. raw.set_progress_handler(callbackForLibRaw, this); QByteArray deadpixelPath = QFile::encodeName(m_parent->m_rawDecodingSettings.deadPixelMap); QByteArray cameraProfile = QFile::encodeName(m_parent->m_rawDecodingSettings.inputProfile); QByteArray outputProfile = QFile::encodeName(m_parent->m_rawDecodingSettings.outputProfile); if (!m_parent->m_rawDecodingSettings.autoBrightness) { // Use a fixed white level, ignoring the image histogram. raw.imgdata.params.no_auto_bright = 1; } if (m_parent->m_rawDecodingSettings.sixteenBitsImage) { // (-4) 16bit ppm output raw.imgdata.params.output_bps = 16; } if (m_parent->m_rawDecodingSettings.halfSizeColorImage) { // (-h) Half-size color image (3x faster than -q). raw.imgdata.params.half_size = 1; } if (m_parent->m_rawDecodingSettings.RGBInterpolate4Colors) { // (-f) Interpolate RGB as four colors. raw.imgdata.params.four_color_rgb = 1; } if (m_parent->m_rawDecodingSettings.DontStretchPixels) { // (-j) Do not stretch the image to its correct aspect ratio. raw.imgdata.params.use_fuji_rotate = 1; } // (-H) Unclip highlight color. raw.imgdata.params.highlight = m_parent->m_rawDecodingSettings.unclipColors; if (m_parent->m_rawDecodingSettings.brightness != 1.0) { // (-b) Set Brightness value. raw.imgdata.params.bright = m_parent->m_rawDecodingSettings.brightness; } if (m_parent->m_rawDecodingSettings.enableBlackPoint) { // (-k) Set Black Point value. raw.imgdata.params.user_black = m_parent->m_rawDecodingSettings.blackPoint; } if (m_parent->m_rawDecodingSettings.enableWhitePoint) { // (-S) Set White Point value (saturation). raw.imgdata.params.user_sat = m_parent->m_rawDecodingSettings.whitePoint; } if (m_parent->m_rawDecodingSettings.medianFilterPasses > 0) { // (-m) After interpolation, clean up color artifacts by repeatedly applying a 3x3 median filter to the R-G and B-G channels. raw.imgdata.params.med_passes = m_parent->m_rawDecodingSettings.medianFilterPasses; } if (!m_parent->m_rawDecodingSettings.deadPixelMap.isEmpty()) { // (-P) Read the dead pixel list from this file. raw.imgdata.params.bad_pixels = deadpixelPath.data(); } switch (m_parent->m_rawDecodingSettings.whiteBalance) { case RawDecodingSettings::NONE: { break; } case RawDecodingSettings::CAMERA: { // (-w) Use camera white balance, if possible. raw.imgdata.params.use_camera_wb = 1; break; } case RawDecodingSettings::AUTO: { // (-a) Use automatic white balance. raw.imgdata.params.use_auto_wb = 1; break; } case RawDecodingSettings::CUSTOM: { /* Convert between Temperature and RGB. */ double T; double RGB[3]; double xD, yD, X, Y, Z; DcrawInfoContainer identify; T = m_parent->m_rawDecodingSettings.customWhiteBalance; /* Here starts the code picked and adapted from ufraw (0.12.1) to convert Temperature + green multiplier to RGB multipliers */ /* Convert between Temperature and RGB. * Base on information from http://www.brucelindbloom.com/ * The fit for D-illuminant between 4000K and 12000K are from CIE * The generalization to 2000K < T < 4000K and the blackbody fits * are my own and should be taken with a grain of salt. */ const double XYZ_to_RGB[3][3] = { { 3.24071, -0.969258, 0.0556352 }, {-1.53726, 1.87599, -0.203996 }, {-0.498571, 0.0415557, 1.05707 } }; // Fit for CIE Daylight illuminant if (T <= 4000) { xD = 0.27475e9/(T*T*T) - 0.98598e6/(T*T) + 1.17444e3/T + 0.145986; } else if (T <= 7000) { xD = -4.6070e9/(T*T*T) + 2.9678e6/(T*T) + 0.09911e3/T + 0.244063; } else { xD = -2.0064e9/(T*T*T) + 1.9018e6/(T*T) + 0.24748e3/T + 0.237040; } yD = -3*xD*xD + 2.87*xD - 0.275; X = xD/yD; Y = 1; Z = (1-xD-yD)/yD; RGB[0] = X*XYZ_to_RGB[0][0] + Y*XYZ_to_RGB[1][0] + Z*XYZ_to_RGB[2][0]; RGB[1] = X*XYZ_to_RGB[0][1] + Y*XYZ_to_RGB[1][1] + Z*XYZ_to_RGB[2][1]; RGB[2] = X*XYZ_to_RGB[0][2] + Y*XYZ_to_RGB[1][2] + Z*XYZ_to_RGB[2][2]; /* End of the code picked to ufraw */ RGB[1] = RGB[1] / m_parent->m_rawDecodingSettings.customWhiteBalanceGreen; /* By default, decraw override his default D65 WB We need to keep it as a basis : if not, colors with some DSLR will have a high dominant of color that will lead to a completely wrong WB */ if (rawFileIdentify(identify, filePath)) { RGB[0] = identify.daylightMult[0] / RGB[0]; RGB[1] = identify.daylightMult[1] / RGB[1]; RGB[2] = identify.daylightMult[2] / RGB[2]; } else { RGB[0] = 1.0 / RGB[0]; RGB[1] = 1.0 / RGB[1]; RGB[2] = 1.0 / RGB[2]; qCDebug(LIBKDCRAW_LOG) << "Warning: cannot get daylight multipliers"; } // (-r) set Raw Color Balance Multipliers. raw.imgdata.params.user_mul[0] = RGB[0]; raw.imgdata.params.user_mul[1] = RGB[1]; raw.imgdata.params.user_mul[2] = RGB[2]; raw.imgdata.params.user_mul[3] = RGB[1]; break; } case RawDecodingSettings::AERA: { // (-A) Calculate the white balance by averaging a rectangular area from image. raw.imgdata.params.greybox[0] = m_parent->m_rawDecodingSettings.whiteBalanceArea.left(); raw.imgdata.params.greybox[1] = m_parent->m_rawDecodingSettings.whiteBalanceArea.top(); raw.imgdata.params.greybox[2] = m_parent->m_rawDecodingSettings.whiteBalanceArea.width(); raw.imgdata.params.greybox[3] = m_parent->m_rawDecodingSettings.whiteBalanceArea.height(); break; } } // (-q) Use an interpolation method. raw.imgdata.params.user_qual = m_parent->m_rawDecodingSettings.RAWQuality; switch (m_parent->m_rawDecodingSettings.NRType) { case RawDecodingSettings::WAVELETSNR: { // (-n) Use wavelets to erase noise while preserving real detail. raw.imgdata.params.threshold = m_parent->m_rawDecodingSettings.NRThreshold; break; } case RawDecodingSettings::FBDDNR: { // (100 - 1000) => (1 - 10) conversion raw.imgdata.params.fbdd_noiserd = lround(m_parent->m_rawDecodingSettings.NRThreshold / 100.0); break; } #if !LIBRAW_COMPILE_CHECK_VERSION_NOTLESS(0, 19) case RawDecodingSettings::LINENR: { // (100 - 1000) => (0.001 - 0.02) conversion. raw.imgdata.params.linenoise = m_parent->m_rawDecodingSettings.NRThreshold * 2.11E-5 + 0.00111111; raw.imgdata.params.cfaline = true; break; } case RawDecodingSettings::IMPULSENR: { // (100 - 1000) => (0.005 - 0.05) conversion. raw.imgdata.params.lclean = m_parent->m_rawDecodingSettings.NRThreshold * 5E-5; raw.imgdata.params.cclean = m_parent->m_rawDecodingSettings.NRChroThreshold * 5E-5; raw.imgdata.params.cfa_clean = true; break; } #endif default: // No Noise Reduction { raw.imgdata.params.threshold = 0; raw.imgdata.params.fbdd_noiserd = 0; #if !LIBRAW_COMPILE_CHECK_VERSION_NOTLESS(0, 19) raw.imgdata.params.linenoise = 0; raw.imgdata.params.cfaline = false; raw.imgdata.params.lclean = 0; raw.imgdata.params.cclean = 0; raw.imgdata.params.cfa_clean = false; #endif break; } } #if !LIBRAW_COMPILE_CHECK_VERSION_NOTLESS(0, 19) // Chromatic aberration correction. raw.imgdata.params.ca_correc = m_parent->m_rawDecodingSettings.enableCACorrection; raw.imgdata.params.cared = m_parent->m_rawDecodingSettings.caMultiplier[0]; raw.imgdata.params.cablue = m_parent->m_rawDecodingSettings.caMultiplier[1]; #endif // Exposure Correction before interpolation. raw.imgdata.params.exp_correc = m_parent->m_rawDecodingSettings.expoCorrection; raw.imgdata.params.exp_shift = m_parent->m_rawDecodingSettings.expoCorrectionShift; raw.imgdata.params.exp_preser = m_parent->m_rawDecodingSettings.expoCorrectionHighlight; switch (m_parent->m_rawDecodingSettings.inputColorSpace) { case RawDecodingSettings::EMBEDDED: { // (-p embed) Use input profile from RAW file to define the camera's raw colorspace. raw.imgdata.params.camera_profile = (char*)"embed"; break; } case RawDecodingSettings::CUSTOMINPUTCS: { if (!m_parent->m_rawDecodingSettings.inputProfile.isEmpty()) { // (-p) Use input profile file to define the camera's raw colorspace. raw.imgdata.params.camera_profile = cameraProfile.data(); } break; } default: { // No input profile break; } } switch (m_parent->m_rawDecodingSettings.outputColorSpace) { case RawDecodingSettings::CUSTOMOUTPUTCS: { if (!m_parent->m_rawDecodingSettings.outputProfile.isEmpty()) { // (-o) Use ICC profile file to define the output colorspace. raw.imgdata.params.output_profile = outputProfile.data(); } break; } default: { // (-o) Define the output colorspace. raw.imgdata.params.output_color = m_parent->m_rawDecodingSettings.outputColorSpace; break; } } //-- Extended demosaicing settings ---------------------------------------------------------- raw.imgdata.params.dcb_iterations = m_parent->m_rawDecodingSettings.dcbIterations; raw.imgdata.params.dcb_enhance_fl = m_parent->m_rawDecodingSettings.dcbEnhanceFl; #if !LIBRAW_COMPILE_CHECK_VERSION_NOTLESS(0, 19) raw.imgdata.params.eeci_refine = m_parent->m_rawDecodingSettings.eeciRefine; raw.imgdata.params.es_med_passes = m_parent->m_rawDecodingSettings.esMedPasses; #endif //------------------------------------------------------------------------------------------- setProgress(0.1); qCDebug(LIBKDCRAW_LOG) << filePath; qCDebug(LIBKDCRAW_LOG) << m_parent->m_rawDecodingSettings; int ret = raw.open_file((const char*)(QFile::encodeName(filePath)).constData()); if (ret != LIBRAW_SUCCESS) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run open_file: " << libraw_strerror(ret); raw.recycle(); return false; } if (m_parent->m_cancel) { raw.recycle(); return false; } setProgress(0.2); ret = raw.unpack(); if (ret != LIBRAW_SUCCESS) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run unpack: " << libraw_strerror(ret); raw.recycle(); return false; } if (m_parent->m_cancel) { raw.recycle(); return false; } setProgress(0.25); if (m_parent->m_rawDecodingSettings.fixColorsHighlights) { qCDebug(LIBKDCRAW_LOG) << "Applying LibRaw highlights adjustments"; // 1.0 is fallback to default value raw.imgdata.params.adjust_maximum_thr = 1.0; } else { qCDebug(LIBKDCRAW_LOG) << "Disabling LibRaw highlights adjustments"; // 0.0 disables this feature raw.imgdata.params.adjust_maximum_thr = 0.0; } ret = raw.dcraw_process(); if (ret != LIBRAW_SUCCESS) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run dcraw_process: " << libraw_strerror(ret); raw.recycle(); return false; } if (m_parent->m_cancel) { raw.recycle(); return false; } setProgress(0.3); libraw_processed_image_t* img = raw.dcraw_make_mem_image(&ret); if(!img) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run dcraw_make_mem_image: " << libraw_strerror(ret); raw.recycle(); return false; } if (m_parent->m_cancel) { // Clear memory allocation. Introduced with LibRaw 0.11.0 raw.dcraw_clear_mem(img); raw.recycle(); return false; } setProgress(0.35); width = img->width; height = img->height; rgbmax = (1 << img->bits)-1; if (img->colors == 3) { imageData = QByteArray((const char*)img->data, (int)img->data_size); } else { // img->colors == 1 (Grayscale) : convert to RGB imageData = QByteArray(); for (int i = 0 ; i < (int)img->data_size ; ++i) { for (int j = 0 ; j < 3 ; ++j) { imageData.append(img->data[i]); } } } // Clear memory allocation. Introduced with LibRaw 0.11.0 raw.dcraw_clear_mem(img); raw.recycle(); if (m_parent->m_cancel) { return false; } setProgress(0.4); qCDebug(LIBKDCRAW_LOG) << "LibRaw: data info: width=" << width << " height=" << height << " rgbmax=" << rgbmax; return true; } bool KDcraw::Private::loadEmbeddedPreview(QByteArray& imgData, LibRaw& raw) { int ret = raw.unpack_thumb(); if (ret != LIBRAW_SUCCESS) { raw.recycle(); qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run unpack_thumb: " << libraw_strerror(ret); raw.recycle(); return false; } libraw_processed_image_t* const thumb = raw.dcraw_make_mem_thumb(&ret); if(!thumb) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run dcraw_make_mem_thumb: " << libraw_strerror(ret); raw.recycle(); return false; } if(thumb->type == LIBRAW_IMAGE_BITMAP) { createPPMHeader(imgData, thumb); } else { imgData = QByteArray((const char*)thumb->data, (int)thumb->data_size); } // Clear memory allocation. Introduced with LibRaw 0.11.0 raw.dcraw_clear_mem(thumb); raw.recycle(); if ( imgData.isEmpty() ) { qCDebug(LIBKDCRAW_LOG) << "Failed to load JPEG thumb from LibRaw!"; return false; } return true; } bool KDcraw::Private::loadHalfPreview(QImage& image, LibRaw& raw) { raw.imgdata.params.use_auto_wb = 1; // Use automatic white balance. raw.imgdata.params.use_camera_wb = 1; // Use camera white balance, if possible. raw.imgdata.params.half_size = 1; // Half-size color image (3x faster than -q). QByteArray imgData; int ret = raw.unpack(); if (ret != LIBRAW_SUCCESS) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run unpack: " << libraw_strerror(ret); raw.recycle(); return false; } ret = raw.dcraw_process(); if (ret != LIBRAW_SUCCESS) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run dcraw_process: " << libraw_strerror(ret); raw.recycle(); return false; } libraw_processed_image_t* halfImg = raw.dcraw_make_mem_image(&ret); if(!halfImg) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run dcraw_make_mem_image: " << libraw_strerror(ret); raw.recycle(); return false; } Private::createPPMHeader(imgData, halfImg); // Clear memory allocation. Introduced with LibRaw 0.11.0 raw.dcraw_clear_mem(halfImg); raw.recycle(); if ( imgData.isEmpty() ) { qCDebug(LIBKDCRAW_LOG) << "Failed to load half preview from LibRaw!"; return false; } if (!image.loadFromData(imgData)) { qCDebug(LIBKDCRAW_LOG) << "Failed to load PPM data from LibRaw!"; return false; } return true; } } // namespace KDcrawIface diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/kdcraw_p.h b/plugins/impex/raw/3rdparty/libkdcraw/src/kdcraw_p.h index 71b36b3e30..f233a18372 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/kdcraw_p.h +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/kdcraw_p.h @@ -1,109 +1,109 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2008-10-09 * @brief internal private container for KDcraw * * @author Copyright (C) 2008-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, 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. * * ============================================================ */ #ifndef KDCRAWPRIVATE_H #define KDCRAWPRIVATE_H // Qt includes #include // Pragma directives to reduce warnings from LibRaw header files. #if !defined(__APPLE__) && defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif #if defined(__APPLE__) && defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" #endif // LibRaw includes #include // Restore warnings #if !defined(__APPLE__) && defined(__GNUC__) #pragma GCC diagnostic pop #endif #if defined(__APPLE__) && defined(__clang__) #pragma clang diagnostic pop #endif // Local includes #include "dcrawinfocontainer.h" #include "kdcraw.h" namespace KDcrawIface { extern "C" { int callbackForLibRaw(void* data, enum LibRaw_progress p, int iteration, int expected); } class Q_DECL_HIDDEN KDcraw::Private { public: Private(KDcraw* const p); ~Private(); public: int progressCallback(enum LibRaw_progress p, int iteration, int expected); void setProgress(double value); double progressValue() const; bool loadFromLibraw(const QString& filePath, QByteArray& imageData, int& width, int& height, int& rgbmax); public: static void createPPMHeader(QByteArray& imgData, libraw_processed_image_t* const img); static void fillIndentifyInfo(LibRaw* const raw, DcrawInfoContainer& identify); static bool loadEmbeddedPreview(QByteArray&, LibRaw&); static bool loadHalfPreview(QImage&, LibRaw&); private: double m_progress; KDcraw* m_parent; friend class KDcraw; }; } // namespace KDcrawIface #endif /* KDCRAWPRIVATE_H */ diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/ractionjob.cpp b/plugins/impex/raw/3rdparty/libkdcraw/src/ractionjob.cpp index 11f2d7033d..c8e88b2556 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/ractionjob.cpp +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/ractionjob.cpp @@ -1,51 +1,51 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2014-15-11 * @brief QRunnable job extended with QObject features * * @author Copyright (C) 2011-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2014 by Veaceslav Munteanu * veaceslav dot munteanu90 at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "ractionjob.h" namespace KDcrawIface { RActionJob::RActionJob() : QObject(), QRunnable(), m_cancel(false) { setAutoDelete(false); } RActionJob::~RActionJob() { cancel(); } void RActionJob::cancel() { m_cancel = true; } } // namespace KDcrawIface diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/ractionjob.h b/plugins/impex/raw/3rdparty/libkdcraw/src/ractionjob.h index 7dd60915c5..12a42e9629 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/ractionjob.h +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/ractionjob.h @@ -1,93 +1,93 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2014-15-11 * @brief QRunnable job extended with QObject features * * @author Copyright (C) 2011-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2014 by Veaceslav Munteanu * veaceslav dot munteanu90 at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, 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. * * ============================================================ */ #ifndef RACTIONJOB_H #define RACTIONJOB_H // Qt includes #include #include // Local includes namespace KDcrawIface { class RActionJob : public QObject, public QRunnable { Q_OBJECT public: /** Constructor which delegate deletion of QRunnable instance to RActionThreadBase, not QThreadPool. */ RActionJob(); /** Re-implement destructor in you implementation. Don't forget to cancel job. */ ~RActionJob() override; Q_SIGNALS: /** Use this signal in your implementation to inform RActionThreadBase manager that job is started */ void signalStarted(); /** Use this signal in your implementation to inform RActionThreadBase manager the job progress */ void signalProgress(int); /** Use this signal in your implementation to inform RActionThreadBase manager the job is done. */ void signalDone(); public Q_SLOTS: /** Call this method to cancel job. */ void cancel(); protected: /** You can use this boolean in your implementation to know if job must be canceled. */ bool m_cancel; }; /** Define a map of job/priority to process by RActionThreadBase manager. * Priority value can be used to control the run queue's order of execution. * Zero priority want mean to process job with higher priority. */ typedef QMap RJobCollection; } // namespace KDcrawIface #endif // RACTIONJOB_H diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/ractionthreadbase.cpp b/plugins/impex/raw/3rdparty/libkdcraw/src/ractionthreadbase.cpp index 4fad77e4bf..db8d610894 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/ractionthreadbase.cpp +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/ractionthreadbase.cpp @@ -1,202 +1,202 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2011-12-28 * @brief re-implementation of action thread using threadweaver * * @author Copyright (C) 2011-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2014 by Veaceslav Munteanu * veaceslav dot munteanu90 at gmail dot com * @author Copyright (C) 2011-2012 by A Janardhan Reddy * annapareddyjanardhanreddy at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "ractionthreadbase.h" // Qt includes #include #include #include #include #include #include // Local includes #include "libkdcraw_debug.h" namespace KDcrawIface { class Q_DECL_HIDDEN RActionThreadBase::Private { public: Private() { running = false; pool = QThreadPool::globalInstance(); } volatile bool running; QWaitCondition condVarJobs; QMutex mutex; RJobCollection todo; RJobCollection pending; RJobCollection processed; QThreadPool* pool; }; RActionThreadBase::RActionThreadBase(QObject* const parent) : QThread(parent), d(new Private) { defaultMaximumNumberOfThreads(); } RActionThreadBase::~RActionThreadBase() { // cancel the thread cancel(); // wait for the thread to finish wait(); // Cleanup all jobs from memory Q_FOREACH (RActionJob* const job, d->todo.keys()) delete(job); Q_FOREACH (RActionJob* const job, d->pending.keys()) delete(job); Q_FOREACH (RActionJob* const job, d->processed.keys()) delete(job); delete d; } void RActionThreadBase::setMaximumNumberOfThreads(int n) { d->pool->setMaxThreadCount(n); qCDebug(LIBKDCRAW_LOG) << "Using " << n << " CPU core to run threads"; } int RActionThreadBase::maximumNumberOfThreads() const { return d->pool->maxThreadCount(); } void RActionThreadBase::defaultMaximumNumberOfThreads() { const int maximumNumberOfThreads = qMax(QThreadPool::globalInstance()->maxThreadCount(), 1); setMaximumNumberOfThreads(maximumNumberOfThreads); } void RActionThreadBase::slotJobFinished() { RActionJob* const job = dynamic_cast(sender()); if (!job) return; qCDebug(LIBKDCRAW_LOG) << "One job is done"; QMutexLocker lock(&d->mutex); d->processed.insert(job, 0); d->pending.remove(job); if (isEmpty()) { d->running = false; } d->condVarJobs.wakeAll(); } void RActionThreadBase::cancel() { qCDebug(LIBKDCRAW_LOG) << "Cancel Main Thread"; QMutexLocker lock(&d->mutex); d->todo.clear(); Q_FOREACH (RActionJob* const job, d->pending.keys()) { job->cancel(); d->processed.insert(job, 0); } d->pending.clear(); d->condVarJobs.wakeAll(); d->running = false; } bool RActionThreadBase::isEmpty() const { return d->pending.isEmpty(); } void RActionThreadBase::appendJobs(const RJobCollection& jobs) { QMutexLocker lock(&d->mutex); for (RJobCollection::const_iterator it = jobs.begin() ; it != jobs.end(); ++it) { d->todo.insert(it.key(), it.value()); } d->condVarJobs.wakeAll(); } void RActionThreadBase::run() { d->running = true; while (d->running) { QMutexLocker lock(&d->mutex); if (!d->todo.isEmpty()) { qCDebug(LIBKDCRAW_LOG) << "Action Thread run " << d->todo.count() << " new jobs"; for (RJobCollection::iterator it = d->todo.begin() ; it != d->todo.end(); ++it) { RActionJob* const job = it.key(); int priority = it.value(); connect(job, SIGNAL(signalDone()), this, SLOT(slotJobFinished())); d->pool->start(job, priority); d->pending.insert(job, priority); } d->todo.clear(); } else { d->condVarJobs.wait(&d->mutex); } } } } // namespace KDcrawIface diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/ractionthreadbase.h b/plugins/impex/raw/3rdparty/libkdcraw/src/ractionthreadbase.h index 8f1f653615..ed96335359 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/ractionthreadbase.h +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/ractionthreadbase.h @@ -1,98 +1,98 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2011-12-28 * @brief re-implementation of action thread using threadweaver * * @author Copyright (C) 2011-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2014 by Veaceslav Munteanu * veaceslav dot munteanu90 at gmail dot com * @author Copyright (C) 2011-2012 by A Janardhan Reddy * annapareddyjanardhanreddy at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, 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. * * ============================================================ */ #ifndef RACTION_THREAD_BASE_H #define RACTION_THREAD_BASE_H // Qt includes #include // Local includes #include "ractionjob.h" namespace KDcrawIface { class RActionThreadBase : public QThread { Q_OBJECT public: RActionThreadBase(QObject* const parent=0); ~RActionThreadBase() override; /** Adjust maximum number of threads used to parallelize collection of job processing. */ void setMaximumNumberOfThreads(int n); /** Return the maximum number of threads used to parallelize collection of job processing. */ int maximumNumberOfThreads() const; /** Reset maximum number of threads used to parallelize collection of job processing to max core detected on computer. * This method is called in contructor. */ void defaultMaximumNumberOfThreads(); /** Cancel processing of current jobs under progress. */ void cancel(); protected: /** Main thread loop used to process jobs in todo list. */ void run() override; /** Append a collection of jobs to process into QThreadPool. * Jobs are add to pending lists and will be deleted by RActionThreadBase, not QThreadPool. */ void appendJobs(const RJobCollection& jobs); /** Return true if list of pending jobs to process is empty. */ bool isEmpty() const; protected Q_SLOTS: void slotJobFinished(); private: class Private; Private* const d; }; } // namespace KDcrawIface #endif // RACTION_THREAD_BASE_H diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/rawdecodingsettings.cpp b/plugins/impex/raw/3rdparty/libkdcraw/src/rawdecodingsettings.cpp index 0736f12301..5a265d7908 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/rawdecodingsettings.cpp +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/rawdecodingsettings.cpp @@ -1,396 +1,396 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2006-12-09 * @brief Raw decoding settings * * @author Copyright (C) 2006-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2006-2013 by Marcel Wiesweg * marcel dot wiesweg at gmx dot de * @author Copyright (C) 2007-2008 by Guillaume Castagnino * casta at xwing dot info * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #define OPTIONFIXCOLORSHIGHLIGHTSENTRY "FixColorsHighlights" #define OPTIONDECODESIXTEENBITENTRY "SixteenBitsImage" #define OPTIONWHITEBALANCEENTRY "White Balance" #define OPTIONCUSTOMWHITEBALANCEENTRY "Custom White Balance" #define OPTIONCUSTOMWBGREENENTRY "Custom White Balance Green" #define OPTIONFOURCOLORRGBENTRY "Four Color RGB" #define OPTIONUNCLIPCOLORSENTRY "Unclip Color" // Wrong spelling, but do not fix it since it is a configuration key // krazy:cond=spelling #define OPTIONDONTSTRETCHPIXELSENTRY "Dont Stretch Pixels" // krazy:endcond=spelling #define OPTIONMEDIANFILTERPASSESENTRY "Median Filter Passes" #define OPTIONNOISEREDUCTIONTYPEENTRY "Noise Reduction Type" #define OPTIONNOISEREDUCTIONTHRESHOLDENTRY "Noise Reduction Threshold" #define OPTIONUSECACORRECTIONENTRY "EnableCACorrection" #define OPTIONCAREDMULTIPLIERENTRY "caRedMultiplier" #define OPTIONCABLUEMULTIPLIERENTRY "caBlueMultiplier" #define OPTIONAUTOBRIGHTNESSENTRY "AutoBrightness" #define OPTIONDECODINGQUALITYENTRY "Decoding Quality" #define OPTIONINPUTCOLORSPACEENTRY "Input Color Space" #define OPTIONOUTPUTCOLORSPACEENTRY "Output Color Space" #define OPTIONINPUTCOLORPROFILEENTRY "Input Color Profile" #define OPTIONOUTPUTCOLORPROFILEENTRY "Output Color Profile" #define OPTIONBRIGHTNESSMULTIPLIERENTRY "Brightness Multiplier" #define OPTIONUSEBLACKPOINTENTRY "Use Black Point" #define OPTIONBLACKPOINTENTRY "Black Point" #define OPTIONUSEWHITEPOINTENTRY "Use White Point" #define OPTIONWHITEPOINTENTRY "White Point" //-- Extended demosaicing settings ---------------------------------------------------------- #define OPTIONDCBITERATIONSENTRY "Dcb Iterations" #define OPTIONDCBENHANCEFLENTRY "Dcb Enhance Filter" #define OPTIONEECIREFINEENTRY "Eeci Refine" #define OPTIONESMEDPASSESENTRY "Es Median Filter Passes" #define OPTIONNRCHROMINANCETHRESHOLDENTRY "Noise Reduction Chrominance Threshold" #define OPTIONEXPOCORRECTIONENTRY "Expo Correction" #define OPTIONEXPOCORRECTIONSHIFTENTRY "Expo Correction Shift" #define OPTIONEXPOCORRECTIONHIGHLIGHTENTRY "Expo Correction Highlight" #include "rawdecodingsettings.h" namespace KDcrawIface { RawDecodingSettings::RawDecodingSettings() { fixColorsHighlights = false; autoBrightness = true; sixteenBitsImage = false; brightness = 1.0; RAWQuality = BILINEAR; inputColorSpace = NOINPUTCS; outputColorSpace = SRGB; RGBInterpolate4Colors = false; DontStretchPixels = false; unclipColors = 0; whiteBalance = CAMERA; customWhiteBalance = 6500; customWhiteBalanceGreen = 1.0; medianFilterPasses = 0; halfSizeColorImage = false; enableBlackPoint = false; blackPoint = 0; enableWhitePoint = false; whitePoint = 0; NRType = NONR; NRThreshold = 0; enableCACorrection = false; caMultiplier[0] = 0.0; caMultiplier[1] = 0.0; inputProfile = QString(); outputProfile = QString(); deadPixelMap = QString(); whiteBalanceArea = QRect(); //-- Extended demosaicing settings ---------------------------------------------------------- dcbIterations = -1; dcbEnhanceFl = false; eeciRefine = false; esMedPasses = 0; NRChroThreshold = 0; expoCorrection = false; expoCorrectionShift = 1.0; expoCorrectionHighlight = 0.0; } RawDecodingSettings::~RawDecodingSettings() { } RawDecodingSettings& RawDecodingSettings::operator=(const RawDecodingSettings& o) { fixColorsHighlights = o.fixColorsHighlights; autoBrightness = o.autoBrightness; sixteenBitsImage = o.sixteenBitsImage; brightness = o.brightness; RAWQuality = o.RAWQuality; inputColorSpace = o.inputColorSpace; outputColorSpace = o.outputColorSpace; RGBInterpolate4Colors = o.RGBInterpolate4Colors; DontStretchPixels = o.DontStretchPixels; unclipColors = o.unclipColors; whiteBalance = o.whiteBalance; customWhiteBalance = o.customWhiteBalance; customWhiteBalanceGreen = o.customWhiteBalanceGreen; halfSizeColorImage = o.halfSizeColorImage; enableBlackPoint = o.enableBlackPoint; blackPoint = o.blackPoint; enableWhitePoint = o.enableWhitePoint; whitePoint = o.whitePoint; NRType = o.NRType; NRThreshold = o.NRThreshold; enableCACorrection = o.enableCACorrection; caMultiplier[0] = o.caMultiplier[0]; caMultiplier[1] = o.caMultiplier[1]; medianFilterPasses = o.medianFilterPasses; inputProfile = o.inputProfile; outputProfile = o.outputProfile; deadPixelMap = o.deadPixelMap; whiteBalanceArea = o.whiteBalanceArea; //-- Extended demosaicing settings ---------------------------------------------------------- dcbIterations = o.dcbIterations; dcbEnhanceFl = o.dcbEnhanceFl; eeciRefine = o.eeciRefine; esMedPasses = o.esMedPasses; NRChroThreshold = o.NRChroThreshold; expoCorrection = o.expoCorrection; expoCorrectionShift = o.expoCorrectionShift; expoCorrectionHighlight = o.expoCorrectionHighlight; return *this; } bool RawDecodingSettings::operator==(const RawDecodingSettings& o) const { return fixColorsHighlights == o.fixColorsHighlights && autoBrightness == o.autoBrightness && sixteenBitsImage == o.sixteenBitsImage && brightness == o.brightness && RAWQuality == o.RAWQuality && inputColorSpace == o.inputColorSpace && outputColorSpace == o.outputColorSpace && RGBInterpolate4Colors == o.RGBInterpolate4Colors && DontStretchPixels == o.DontStretchPixels && unclipColors == o.unclipColors && whiteBalance == o.whiteBalance && customWhiteBalance == o.customWhiteBalance && customWhiteBalanceGreen == o.customWhiteBalanceGreen && halfSizeColorImage == o.halfSizeColorImage && enableBlackPoint == o.enableBlackPoint && blackPoint == o.blackPoint && enableWhitePoint == o.enableWhitePoint && whitePoint == o.whitePoint && NRType == o.NRType && NRThreshold == o.NRThreshold && enableCACorrection == o.enableCACorrection && caMultiplier[0] == o.caMultiplier[0] && caMultiplier[1] == o.caMultiplier[1] && medianFilterPasses == o.medianFilterPasses && inputProfile == o.inputProfile && outputProfile == o.outputProfile && deadPixelMap == o.deadPixelMap && whiteBalanceArea == o.whiteBalanceArea //-- Extended demosaicing settings ---------------------------------------------------------- && dcbIterations == o.dcbIterations && dcbEnhanceFl == o.dcbEnhanceFl && eeciRefine == o.eeciRefine && esMedPasses == o.esMedPasses && NRChroThreshold == o.NRChroThreshold && expoCorrection == o.expoCorrection && expoCorrectionShift == o.expoCorrectionShift && expoCorrectionHighlight == o.expoCorrectionHighlight ; } void RawDecodingSettings::optimizeTimeLoading() { fixColorsHighlights = false; autoBrightness = true; sixteenBitsImage = true; brightness = 1.0; RAWQuality = BILINEAR; inputColorSpace = NOINPUTCS; outputColorSpace = SRGB; RGBInterpolate4Colors = false; DontStretchPixels = false; unclipColors = 0; whiteBalance = CAMERA; customWhiteBalance = 6500; customWhiteBalanceGreen = 1.0; halfSizeColorImage = true; medianFilterPasses = 0; enableBlackPoint = false; blackPoint = 0; enableWhitePoint = false; whitePoint = 0; NRType = NONR; NRThreshold = 0; enableCACorrection = false; caMultiplier[0] = 0.0; caMultiplier[1] = 0.0; inputProfile = QString(); outputProfile = QString(); deadPixelMap = QString(); whiteBalanceArea = QRect(); //-- Extended demosaicing settings ---------------------------------------------------------- dcbIterations = -1; dcbEnhanceFl = false; eeciRefine = false; esMedPasses = 0; NRChroThreshold = 0; expoCorrection = false; expoCorrectionShift = 1.0; expoCorrectionHighlight = 0.0; } void RawDecodingSettings::readSettings(KConfigGroup& group) { RawDecodingSettings defaultPrm; fixColorsHighlights = group.readEntry(OPTIONFIXCOLORSHIGHLIGHTSENTRY, defaultPrm.fixColorsHighlights); sixteenBitsImage = group.readEntry(OPTIONDECODESIXTEENBITENTRY, defaultPrm.sixteenBitsImage); whiteBalance = (WhiteBalance) group.readEntry(OPTIONWHITEBALANCEENTRY, (int)defaultPrm.whiteBalance); customWhiteBalance = group.readEntry(OPTIONCUSTOMWHITEBALANCEENTRY, defaultPrm.customWhiteBalance); customWhiteBalanceGreen = group.readEntry(OPTIONCUSTOMWBGREENENTRY, defaultPrm.customWhiteBalanceGreen); RGBInterpolate4Colors = group.readEntry(OPTIONFOURCOLORRGBENTRY, defaultPrm.RGBInterpolate4Colors); unclipColors = group.readEntry(OPTIONUNCLIPCOLORSENTRY, defaultPrm.unclipColors); DontStretchPixels = group.readEntry(OPTIONDONTSTRETCHPIXELSENTRY, defaultPrm.DontStretchPixels); NRType = (NoiseReduction) group.readEntry(OPTIONNOISEREDUCTIONTYPEENTRY, (int)defaultPrm.NRType); brightness = group.readEntry(OPTIONBRIGHTNESSMULTIPLIERENTRY, defaultPrm.brightness); enableBlackPoint = group.readEntry(OPTIONUSEBLACKPOINTENTRY, defaultPrm.enableBlackPoint); blackPoint = group.readEntry(OPTIONBLACKPOINTENTRY, defaultPrm.blackPoint); enableWhitePoint = group.readEntry(OPTIONUSEWHITEPOINTENTRY, defaultPrm.enableWhitePoint); whitePoint = group.readEntry(OPTIONWHITEPOINTENTRY, defaultPrm.whitePoint); medianFilterPasses = group.readEntry(OPTIONMEDIANFILTERPASSESENTRY, defaultPrm.medianFilterPasses); NRThreshold = group.readEntry(OPTIONNOISEREDUCTIONTHRESHOLDENTRY, defaultPrm.NRThreshold); enableCACorrection = group.readEntry(OPTIONUSECACORRECTIONENTRY, defaultPrm.enableCACorrection); caMultiplier[0] = group.readEntry(OPTIONCAREDMULTIPLIERENTRY, defaultPrm.caMultiplier[0]); caMultiplier[1] = group.readEntry(OPTIONCABLUEMULTIPLIERENTRY, defaultPrm.caMultiplier[1]); RAWQuality = (DecodingQuality) group.readEntry(OPTIONDECODINGQUALITYENTRY, (int)defaultPrm.RAWQuality); outputColorSpace = (OutputColorSpace) group.readEntry(OPTIONOUTPUTCOLORSPACEENTRY, (int)defaultPrm.outputColorSpace); autoBrightness = group.readEntry(OPTIONAUTOBRIGHTNESSENTRY, defaultPrm.autoBrightness); //-- Extended demosaicing settings ---------------------------------------------------------- dcbIterations = group.readEntry(OPTIONDCBITERATIONSENTRY, defaultPrm.dcbIterations); dcbEnhanceFl = group.readEntry(OPTIONDCBENHANCEFLENTRY, defaultPrm.dcbEnhanceFl); eeciRefine = group.readEntry(OPTIONEECIREFINEENTRY, defaultPrm.eeciRefine); esMedPasses = group.readEntry(OPTIONESMEDPASSESENTRY, defaultPrm.esMedPasses); NRChroThreshold = group.readEntry(OPTIONNRCHROMINANCETHRESHOLDENTRY, defaultPrm.NRChroThreshold); expoCorrection = group.readEntry(OPTIONEXPOCORRECTIONENTRY, defaultPrm.expoCorrection); expoCorrectionShift = group.readEntry(OPTIONEXPOCORRECTIONSHIFTENTRY, defaultPrm.expoCorrectionShift); expoCorrectionHighlight = group.readEntry(OPTIONEXPOCORRECTIONHIGHLIGHTENTRY, defaultPrm.expoCorrectionHighlight); } void RawDecodingSettings::writeSettings(KConfigGroup& group) { group.writeEntry(OPTIONFIXCOLORSHIGHLIGHTSENTRY, fixColorsHighlights); group.writeEntry(OPTIONDECODESIXTEENBITENTRY, sixteenBitsImage); group.writeEntry(OPTIONWHITEBALANCEENTRY, (int)whiteBalance); group.writeEntry(OPTIONCUSTOMWHITEBALANCEENTRY, customWhiteBalance); group.writeEntry(OPTIONCUSTOMWBGREENENTRY, customWhiteBalanceGreen); group.writeEntry(OPTIONFOURCOLORRGBENTRY, RGBInterpolate4Colors); group.writeEntry(OPTIONUNCLIPCOLORSENTRY, unclipColors); group.writeEntry(OPTIONDONTSTRETCHPIXELSENTRY, DontStretchPixels); group.writeEntry(OPTIONNOISEREDUCTIONTYPEENTRY, (int)NRType); group.writeEntry(OPTIONBRIGHTNESSMULTIPLIERENTRY, brightness); group.writeEntry(OPTIONUSEBLACKPOINTENTRY, enableBlackPoint); group.writeEntry(OPTIONBLACKPOINTENTRY, blackPoint); group.writeEntry(OPTIONUSEWHITEPOINTENTRY, enableWhitePoint); group.writeEntry(OPTIONWHITEPOINTENTRY, whitePoint); group.writeEntry(OPTIONMEDIANFILTERPASSESENTRY, medianFilterPasses); group.writeEntry(OPTIONNOISEREDUCTIONTHRESHOLDENTRY, NRThreshold); group.writeEntry(OPTIONUSECACORRECTIONENTRY, enableCACorrection); group.writeEntry(OPTIONCAREDMULTIPLIERENTRY, caMultiplier[0]); group.writeEntry(OPTIONCABLUEMULTIPLIERENTRY, caMultiplier[1]); group.writeEntry(OPTIONDECODINGQUALITYENTRY, (int)RAWQuality); group.writeEntry(OPTIONOUTPUTCOLORSPACEENTRY, (int)outputColorSpace); group.writeEntry(OPTIONAUTOBRIGHTNESSENTRY, autoBrightness); //-- Extended demosaicing settings ---------------------------------------------------------- group.writeEntry(OPTIONDCBITERATIONSENTRY, dcbIterations); group.writeEntry(OPTIONDCBENHANCEFLENTRY, dcbEnhanceFl); group.writeEntry(OPTIONEECIREFINEENTRY, eeciRefine); group.writeEntry(OPTIONESMEDPASSESENTRY, esMedPasses); group.writeEntry(OPTIONNRCHROMINANCETHRESHOLDENTRY, NRChroThreshold); group.writeEntry(OPTIONEXPOCORRECTIONENTRY, expoCorrection); group.writeEntry(OPTIONEXPOCORRECTIONSHIFTENTRY, expoCorrectionShift); group.writeEntry(OPTIONEXPOCORRECTIONHIGHLIGHTENTRY, expoCorrectionHighlight); } QDebug operator<<(QDebug dbg, const RawDecodingSettings& s) { dbg.nospace() << endl; dbg.nospace() << "-- RAW DECODING SETTINGS --------------------------------" << endl; dbg.nospace() << "-- autoBrightness: " << s.autoBrightness << endl; dbg.nospace() << "-- sixteenBitsImage: " << s.sixteenBitsImage << endl; dbg.nospace() << "-- brightness: " << s.brightness << endl; dbg.nospace() << "-- RAWQuality: " << s.RAWQuality << endl; dbg.nospace() << "-- inputColorSpace: " << s.inputColorSpace << endl; dbg.nospace() << "-- outputColorSpace: " << s.outputColorSpace << endl; dbg.nospace() << "-- RGBInterpolate4Colors: " << s.RGBInterpolate4Colors << endl; dbg.nospace() << "-- DontStretchPixels: " << s.DontStretchPixels << endl; dbg.nospace() << "-- unclipColors: " << s.unclipColors << endl; dbg.nospace() << "-- whiteBalance: " << s.whiteBalance << endl; dbg.nospace() << "-- customWhiteBalance: " << s.customWhiteBalance << endl; dbg.nospace() << "-- customWhiteBalanceGreen: " << s.customWhiteBalanceGreen << endl; dbg.nospace() << "-- halfSizeColorImage: " << s.halfSizeColorImage << endl; dbg.nospace() << "-- enableBlackPoint: " << s.enableBlackPoint << endl; dbg.nospace() << "-- blackPoint: " << s.blackPoint << endl; dbg.nospace() << "-- enableWhitePoint: " << s.enableWhitePoint << endl; dbg.nospace() << "-- whitePoint: " << s.whitePoint << endl; dbg.nospace() << "-- NoiseReductionType: " << s.NRType << endl; dbg.nospace() << "-- NoiseReductionThreshold: " << s.NRThreshold << endl; dbg.nospace() << "-- enableCACorrection: " << s.enableCACorrection << endl; dbg.nospace() << "-- caMultiplier: " << s.caMultiplier[0] << ", " << s.caMultiplier[1] << endl; dbg.nospace() << "-- medianFilterPasses: " << s.medianFilterPasses << endl; dbg.nospace() << "-- inputProfile: " << s.inputProfile << endl; dbg.nospace() << "-- outputProfile: " << s.outputProfile << endl; dbg.nospace() << "-- deadPixelMap: " << s.deadPixelMap << endl; dbg.nospace() << "-- whiteBalanceArea: " << s.whiteBalanceArea << endl; //-- Extended demosaicing settings ---------------------------------------------------------- dbg.nospace() << "-- dcbIterations: " << s.dcbIterations << endl; dbg.nospace() << "-- dcbEnhanceFl: " << s.dcbEnhanceFl << endl; dbg.nospace() << "-- eeciRefine: " << s.eeciRefine << endl; dbg.nospace() << "-- esMedPasses: " << s.esMedPasses << endl; dbg.nospace() << "-- NRChrominanceThreshold: " << s.NRChroThreshold << endl; dbg.nospace() << "-- expoCorrection: " << s.expoCorrection << endl; dbg.nospace() << "-- expoCorrectionShift: " << s.expoCorrectionShift << endl; dbg.nospace() << "-- expoCorrectionHighlight: " << s.expoCorrectionHighlight << endl; dbg.nospace() << "---------------------------------------------------------" << endl; return dbg.space(); } } // namespace KDcrawIface diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/rawdecodingsettings.h b/plugins/impex/raw/3rdparty/libkdcraw/src/rawdecodingsettings.h index 5916fb2260..5927f4dd6b 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/rawdecodingsettings.h +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/rawdecodingsettings.h @@ -1,376 +1,376 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2006-12-09 * @brief Raw decoding settings * * @author Copyright (C) 2006-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2006-2013 by Marcel Wiesweg * marcel dot wiesweg at gmx dot de * @author Copyright (C) 2007-2008 by Guillaume Castagnino * casta at xwing dot info * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef RAW_DECODING_SETTINGS_H #define RAW_DECODING_SETTINGS_H // Qt includes #include #include #include // KDE includes #include // Local includes namespace KDcrawIface { class RawDecodingSettings { public: /** RAW decoding Interpolation methods * * Bilinear: use high-speed but low-quality bilinear * interpolation (default - for slow computer). In this method, * the red value of a non-red pixel is computed as the average of * the adjacent red pixels, and similar for blue and green. * VNG: use Variable Number of Gradients interpolation. * This method computes gradients near the pixel of interest and uses * the lower gradients (representing smoother and more similar parts * of the image) to make an estimate. * PPG: use Patterned Pixel Grouping interpolation. * Pixel Grouping uses assumptions about natural scenery in making estimates. * It has fewer color artifacts on natural images than the Variable Number of * Gradients method. * AHD: use Adaptive Homogeneity-Directed interpolation. * This method selects the direction of interpolation so as to * maximize a homogeneity metric, thus typically minimizing color artifacts. * DCB: DCB interpolation (see http://www.linuxphoto.org/html/dcb.html for details) * * NOTE: from GPL2/GPL3 demosaic packs - will not work with libraw>=0.19 * - * PL_AHD: modified AHD interpolation (see http://sites.google.com/site/demosaicalgorithms/modified-dcraw + * PL_AHD: modified AHD interpolation (see https://sites.google.com/site/demosaicalgorithms/modified-dcraw * for details). * AFD: demosaicing through 5 pass median filter from PerfectRaw project. * VCD: VCD interpolation. * VCD_AHD: mixed demosaicing between VCD and AHD. * LMMSE: LMMSE interpolation from PerfectRaw. * AMAZE: AMaZE interpolation and color aberration removal from RawTherapee project. * * NOTE: for libraw>=0.19 only * * DHT: DHT interpolation. * AAHD: Enhanced Adaptative AHD interpolation. */ enum DecodingQuality { BILINEAR = 0, VNG = 1, PPG = 2, AHD = 3, DCB = 4, PL_AHD = 5, AFD = 6, VCD = 7, VCD_AHD = 8, LMMSE = 9, AMAZE = 10, DHT = 11, AAHD = 12 }; /** White balances alternatives * NONE: no white balance used : reverts to standard daylight D65 WB. * CAMERA: Use the camera embedded WB if available. Reverts to NONE if not. * AUTO: Averages an auto WB on the entire image. * CUSTOM: Let use set it's own temperature and green factor (later converted to RGBG factors). * AERA: Let use an aera from image to average white balance (see whiteBalanceArea for details). */ enum WhiteBalance { NONE = 0, CAMERA = 1, AUTO = 2, CUSTOM = 3, AERA = 4 }; /** Noise Reduction method to apply before demosaicing * NONR: No noise reduction. * WAVELETSNR: wavelets correction to erase noise while preserving real detail. It's applied after interpolation. * FBDDNR: Fake Before Demosaicing Denoising noise reduction. It's applied before interpolation. * LINENR: CFA Line Denoise. It's applied after interpolation. * IMPULSENR: Impulse Denoise. It's applied after interpolation. */ enum NoiseReduction { NONR = 0, WAVELETSNR, FBDDNR, LINENR, IMPULSENR }; /** Input color profile used to decoded image * NOINPUTCS: No input color profile. * EMBEDDED: Use the camera profile embedded in RAW file if exist. * CUSTOMINPUTCS: Use a custom input color space profile. */ enum InputColorSpace { NOINPUTCS = 0, EMBEDDED, CUSTOMINPUTCS }; /** Output RGB color space used to decoded image * RAWCOLOR: No output color profile (Linear RAW). * SRGB: Use standard sRGB color space. * ADOBERGB: Use standard Adobe RGB color space. * WIDEGAMMUT: Use standard RGB Wide Gamut color space. * PROPHOTO: Use standard RGB Pro Photo color space. * CUSTOMOUTPUTCS: Use a custom workspace color profile. */ enum OutputColorSpace { RAWCOLOR = 0, SRGB, ADOBERGB, WIDEGAMMUT, PROPHOTO, CUSTOMOUTPUTCS }; /** Standard constructor with default settings */ RawDecodingSettings(); /** Equivalent to the copy constructor */ RawDecodingSettings& operator=(const RawDecodingSettings& prm); /** Compare for equality */ bool operator==(const RawDecodingSettings& o) const; /** Standard destructor */ virtual ~RawDecodingSettings(); /** Method to use a settings to optimize time loading, for example to compute image histogram */ void optimizeTimeLoading(); /** Methods to read/write settings from/to a config file */ void readSettings(KConfigGroup& group); void writeSettings(KConfigGroup& group); public: /** If true, images with overblown channels are processed much more accurate, * without 'pink clouds' (and blue highlights under tungsteen lamps). */ bool fixColorsHighlights; /** If false, use a fixed white level, ignoring the image histogram. */ bool autoBrightness; /** Turn on RAW file decoding in 16 bits per color per pixel instead 8 bits. */ bool sixteenBitsImage; /** Half-size color image decoding (twice as fast as "enableRAWQuality"). * Turn on this option to reduce time loading to render histogram for example, * no to render an image to screen. */ bool halfSizeColorImage; /** White balance type to use. See WhiteBalance values for detail */ WhiteBalance whiteBalance; /** The temperature and the green multiplier of the custom white balance */ int customWhiteBalance; double customWhiteBalanceGreen; /** Turn on RAW file decoding using RGB interpolation as four colors. */ bool RGBInterpolate4Colors; /** For cameras with non-square pixels, do not stretch the image to its * correct aspect ratio. In any case, this option guarantees that each * output pixel corresponds to one RAW pixel. */ bool DontStretchPixels; /** Unclip Highlight color level: * 0 = Clip all highlights to solid white. * 1 = Leave highlights unclipped in various shades of pink. * 2 = Blend clipped and unclipped values together for a gradual * fade to white. * 3-9 = Reconstruct highlights. Low numbers favor whites; high numbers * favor colors. */ int unclipColors; /** RAW quality decoding factor value. See DecodingQuality values * for details. */ DecodingQuality RAWQuality; /** After interpolation, clean up color artifacts by repeatedly applying * a 3x3 median filter to the R-G and B-G channels. */ int medianFilterPasses; /** Noise reduction method to apply before demosaicing. */ NoiseReduction NRType; /** Noise reduction threshold value. Null value disable NR. Range is between 100 and 1000. * For IMPULSENR : set the amount of Luminance impulse denoise. */ int NRThreshold; /** Turn on chromatic aberrations correction * @deprecated does not work with libraw>=0.19 */ bool enableCACorrection; /** Magnification factor for Red and Blue layers * - caMultiplier[0] = amount of correction on red-green axis. * - caMultiplier[1] = amount of correction on blue-yellow axis. * - Both values set to 0.0 = automatic CA correction. * @deprecated does not work with libraw>=0.19 */ double caMultiplier[2]; /** Brightness of output image. */ double brightness; /** Turn on the black point setting to decode RAW image. */ bool enableBlackPoint; /** Black Point value of output image. */ int blackPoint; /** Turn on the white point setting to decode RAW image. */ bool enableWhitePoint; /** White Point value of output image. */ int whitePoint; /** The input color profile used to decoded RAW data. See OutputColorProfile * values for details. */ InputColorSpace inputColorSpace; /** Path to custom input ICC profile to define the camera's raw colorspace. */ QString inputProfile; /** The output color profile used to decoded RAW data. See OutputColorProfile * values for details. */ OutputColorSpace outputColorSpace; /** Path to custom output ICC profile to define the color workspace. */ QString outputProfile; /** Path to text file including dead pixel list. */ QString deadPixelMap; /** Rectangle used to calculate the white balance by averaging the region of image. */ QRect whiteBalanceArea; //-- Extended demosaicing settings ---------------------------------------------------------- /// For DCB interpolation. /** Number of DCB median filtering correction passes. * -1 : disable (default) * 1-10 : DCB correction passes */ int dcbIterations; /** Turn on the DCB interpolation with enhance interpolated colors. */ bool dcbEnhanceFl; /// For VCD_AHD interpolation. /** Turn on the EECI refine for VCD Demosaicing. * @deprecated does not work with libraw>=0.19 */ bool eeciRefine; /** Use edge-sensitive median filtering for artifact supression after VCD demosaicing. * 0 : disable (default) * 1-10 : median filter passes. * @deprecated does not work with libraw>=0.19 */ int esMedPasses; /** For IMPULSENR Noise reduction. Set the amount of Chrominance impulse denoise. * Null value disable NR. Range is between 100 and 1000. * @deprecated does not work with libraw>=0.19 */ int NRChroThreshold; /** Turn on the Exposure Correction before interpolation. */ bool expoCorrection; /** Shift of Exposure Correction before interpolation in linear scale. * Usable range is from 0.25 (darken image 1 stop : -2EV) to 8.0 (lighten ~1.5 photographic stops : +3EV). */ double expoCorrectionShift; /** Amount of highlight preservation for exposure correction before interpolation in E.V. * Usable range is from 0.0 (linear exposure shift, highlights may blow) to 1.0 (maximum highlights preservation) * This settings can only take effect if expoCorrectionShift > 1.0. */ double expoCorrectionHighlight; }; //! qDebug() stream operator. Writes settings @a s to the debug output in a nicely formatted way. QDebug operator<<(QDebug dbg, const RawDecodingSettings& s); } // namespace KDcrawIface #endif /* RAW_DECODING_SETTINGS_H */ diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/rawfiles.h b/plugins/impex/raw/3rdparty/libkdcraw/src/rawfiles.h index 34783c43bf..c4b43e8e25 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/rawfiles.h +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/rawfiles.h @@ -1,99 +1,99 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2005-11-06 * @brief list of RAW file extensions supported by libraw * * @author Copyright (C) 2005-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, 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. * * ============================================================ */ #ifndef RAW_FILES_H #define RAW_FILES_H -// NOTE: extension list Version 1 and 2 are taken from http://www.cybercom.net/~dcoffin/dcraw/rawphoto.c +// NOTE: extension list Version 1 and 2 are taken from https://www.dechifro.org/dcraw/rawphoto.c // Ext Descriptions From // www.file-extensions.org // en.wikipedia.org/wiki/RAW_file_format // filext.com static const char raw_file_extentions[] = // NOTE: VERSION 1 "*.bay " // Casio Digital Camera Raw File Format. "*.bmq " // NuCore Raw Image File. "*.cr2 " // Canon Digital Camera RAW Image Format version 2.0. These images are based on the TIFF image standard. "*.crw " // Canon Digital Camera RAW Image Format version 1.0. "*.cs1 " // Capture Shop Raw Image File. "*.dc2 " // Kodak DC25 Digital Camera File. "*.dcr " // Kodak Digital Camera Raw Image Format for these models: Kodak DSC Pro SLR/c, Kodak DSC Pro SLR/n, Kodak DSC Pro 14N, Kodak DSC PRO 14nx. "*.dng " // Adobe Digital Negative: DNG is publicly available archival format for the raw files generated by digital cameras. By addressing the lack of an open standard for the raw files created by individual camera models, DNG helps ensure that photographers will be able to access their files in the future. "*.erf " // Epson Digital Camera Raw Image Format. "*.fff " // Imacon Digital Camera Raw Image Format. "*.hdr " // Leaf Raw Image File. "*.k25 " // Kodak DC25 Digital Camera Raw Image Format. "*.kdc " // Kodak Digital Camera Raw Image Format. "*.mdc " // Minolta RD175 Digital Camera Raw Image Format. "*.mos " // Mamiya Digital Camera Raw Image Format. "*.mrw " // Minolta Dimage Digital Camera Raw Image Format. "*.nef " // Nikon Digital Camera Raw Image Format. "*.orf " // Olympus Digital Camera Raw Image Format. "*.pef " // Pentax Digital Camera Raw Image Format. "*.pxn " // Logitech Digital Camera Raw Image Format. "*.raf " // Fuji Digital Camera Raw Image Format. "*.raw " // Panasonic Digital Camera Image Format. "*.rdc " // Digital Foto Maker Raw Image File. "*.sr2 " // Sony Digital Camera Raw Image Format. "*.srf " // Sony Digital Camera Raw Image Format for DSC-F828 8 megapixel digital camera or Sony DSC-R1 "*.x3f " // Sigma Digital Camera Raw Image Format for devices based on Foveon X3 direct image sensor. "*.arw " // Sony Digital Camera Raw Image Format for Alpha devices. // NOTE: VERSION 2 "*.3fr " // Hasselblad Digital Camera Raw Image Format. "*.cine " // Phantom Software Raw Image File. "*.ia " // Sinar Raw Image File. "*.kc2 " // Kodak DCS200 Digital Camera Raw Image Format. "*.mef " // Mamiya Digital Camera Raw Image Format. "*.nrw " // Nikon Digital Camera Raw Image Format. "*.qtk " // Apple Quicktake 100/150 Digital Camera Raw Image Format. "*.rw2 " // Panasonic LX3 Digital Camera Raw Image Format. "*.sti " // Sinar Capture Shop Raw Image File. // NOTE: VERSION 3 "*.rwl " // Leica Digital Camera Raw Image Format. // NOTE: VERSION 4 "*.srw "; // Samnsung Raw Image Format. /* TODO: check if these format are supported "*.drf " // Kodak Digital Camera Raw Image Format. "*.dsc " // Kodak Digital Camera Raw Image Format. "*.ptx " // Pentax Digital Camera Raw Image Format. "*.cap " // Phase One Digital Camera Raw Image Format. "*.iiq " // Phase One Digital Camera Raw Image Format. "*.rwz " // Rawzor Digital Camera Raw Image Format. */ // increment this number whenever you change the above string static const int raw_file_extensions_version = 4; #endif // RAW_FILES_H diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/rcombobox.cpp b/plugins/impex/raw/3rdparty/libkdcraw/src/rcombobox.cpp index c7e1d34d03..f41d89848f 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/rcombobox.cpp +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/rcombobox.cpp @@ -1,156 +1,156 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2008-08-16 * @brief a combo box widget re-implemented with a * reset button to switch to a default item * * @author Copyright (C) 2008-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "rcombobox.h" // Qt includes #include #include #include #include #include // KDE includes #include #include namespace KDcrawIface { class Q_DECL_HIDDEN RComboBox::Private { public: Private() { defaultIndex = 0; resetButton = 0; combo = 0; } int defaultIndex; QToolButton* resetButton; QComboBox* combo; }; RComboBox::RComboBox(QWidget* const parent) : QWidget(parent), d(new Private) { QHBoxLayout* const hlay = new QHBoxLayout(this); d->combo = new QComboBox(this); d->resetButton = new QToolButton(this); d->resetButton->setAutoRaise(true); d->resetButton->setFocusPolicy(Qt::NoFocus); d->resetButton->setIcon(KisIconUtils::loadIcon("document-revert").pixmap(16, 16)); d->resetButton->setToolTip(i18nc("@info:tooltip", "Reset to default value")); hlay->addWidget(d->combo); hlay->addWidget(d->resetButton); hlay->setStretchFactor(d->combo, 10); hlay->setMargin(0); hlay->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); // ------------------------------------------------------------- connect(d->resetButton, &QToolButton::clicked, this, &RComboBox::slotReset); connect(d->combo, static_cast(&QComboBox::activated), this, &RComboBox::slotItemActivated); connect(d->combo, static_cast(&QComboBox::currentIndexChanged), this, &RComboBox::slotCurrentIndexChanged); } RComboBox::~RComboBox() { delete d; } QComboBox* RComboBox::combo() const { return d->combo; } void RComboBox::addItem(const QString& t, int index) { d->combo->addItem(t, index); } void RComboBox::insertItem(int index, const QString& t) { d->combo->insertItem(index, t); } int RComboBox::currentIndex() const { return d->combo->currentIndex(); } void RComboBox::setCurrentIndex(int v) { d->combo->setCurrentIndex(v); } int RComboBox::defaultIndex() const { return d->defaultIndex; } void RComboBox::setDefaultIndex(int v) { d->defaultIndex = v; d->combo->setCurrentIndex(d->defaultIndex); slotItemActivated(v); } void RComboBox::slotReset() { d->combo->setCurrentIndex(d->defaultIndex); d->resetButton->setEnabled(false); slotItemActivated(d->defaultIndex); emit reset(); } void RComboBox::slotItemActivated(int v) { d->resetButton->setEnabled(v != d->defaultIndex); emit activated(v); } void RComboBox::slotCurrentIndexChanged(int v) { d->resetButton->setEnabled(v != d->defaultIndex); emit currentIndexChanged(v); } } // namespace KDcrawIface diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/rcombobox.h b/plugins/impex/raw/3rdparty/libkdcraw/src/rcombobox.h index 0dd29d2e3c..8b6c40c4b1 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/rcombobox.h +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/rcombobox.h @@ -1,86 +1,86 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2008-08-16 * @brief a combo box widget re-implemented with a * reset button to switch to a default item * * @author Copyright (C) 2008-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, 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. * * ============================================================ */ #ifndef RCOMBOBOX_H #define RCOMBOBOX_H // Qt includes #include #include // Local includes namespace KDcrawIface { class RComboBox : public QWidget { Q_OBJECT public: RComboBox(QWidget* const parent=0); ~RComboBox() override; void setCurrentIndex(int d); int currentIndex() const; void setDefaultIndex(int d); int defaultIndex() const; QComboBox* combo() const; void addItem(const QString& t, int index = -1); void insertItem(int index, const QString& t); Q_SIGNALS: void reset(); void activated(int); void currentIndexChanged(int); public Q_SLOTS: void slotReset(); private Q_SLOTS: void slotItemActivated(int); void slotCurrentIndexChanged(int); private: class Private; Private* const d; }; } // namespace KDcrawIface #endif /* RCOMBOBOX_H */ diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/rexpanderbox.cpp b/plugins/impex/raw/3rdparty/libkdcraw/src/rexpanderbox.cpp index 2d188a152d..2ed1176281 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/rexpanderbox.cpp +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/rexpanderbox.cpp @@ -1,826 +1,826 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2008-03-14 * @brief A widget to host settings as expander box * * @author Copyright (C) 2008-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2008-2013 by Marcel Wiesweg * marcel dot wiesweg at gmx dot de * @author Copyright (C) 2010 by Manuel Viet * contact at 13zenrv dot fr * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "rexpanderbox.h" // Qt includes #include #include #include #include #include #include #include #include // KDE includes #include #include namespace KDcrawIface { RClickLabel::RClickLabel(QWidget* const parent) : QLabel(parent) { setCursor(Qt::PointingHandCursor); } RClickLabel::RClickLabel(const QString& text, QWidget* const parent) : QLabel(text, parent) { setCursor(Qt::PointingHandCursor); } RClickLabel::~RClickLabel() { } void RClickLabel::mousePressEvent(QMouseEvent* event) { QLabel::mousePressEvent(event); /* * In some contexts, like QGraphicsView, there will be no * release event if the press event was not accepted. */ if (event->button() == Qt::LeftButton) { event->accept(); } } void RClickLabel::mouseReleaseEvent(QMouseEvent* event) { QLabel::mouseReleaseEvent(event); if (event->button() == Qt::LeftButton) { emit leftClicked(); emit activated(); event->accept(); } } void RClickLabel::keyPressEvent(QKeyEvent* e) { switch (e->key()) { case Qt::Key_Down: case Qt::Key_Right: case Qt::Key_Space: emit activated(); return; default: break; } QLabel::keyPressEvent(e); } // ------------------------------------------------------------------------ RSqueezedClickLabel::RSqueezedClickLabel(QWidget* const parent) : RAdjustableLabel(parent) { setCursor(Qt::PointingHandCursor); } RSqueezedClickLabel::RSqueezedClickLabel(const QString& text, QWidget* const parent) : RAdjustableLabel(parent) { setAdjustedText(text); setCursor(Qt::PointingHandCursor); } RSqueezedClickLabel::~RSqueezedClickLabel() { } void RSqueezedClickLabel::mouseReleaseEvent(QMouseEvent* event) { QLabel::mouseReleaseEvent(event); if (event->button() == Qt::LeftButton) { emit leftClicked(); emit activated(); event->accept(); } } void RSqueezedClickLabel::mousePressEvent(QMouseEvent* event) { QLabel::mousePressEvent(event); /* * In some contexts, like QGraphicsView, there will be no * release event if the press event was not accepted. */ if (event->button() == Qt::LeftButton) { event->accept(); } } void RSqueezedClickLabel::keyPressEvent(QKeyEvent* e) { switch (e->key()) { case Qt::Key_Down: case Qt::Key_Right: case Qt::Key_Space: emit activated(); return; default: break; } QLabel::keyPressEvent(e); } // ------------------------------------------------------------------------ RArrowClickLabel::RArrowClickLabel(QWidget* const parent) : QWidget(parent), m_arrowType(Qt::DownArrow) { setCursor(Qt::PointingHandCursor); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); m_size = 8; m_margin = 2; } void RArrowClickLabel::setArrowType(Qt::ArrowType type) { m_arrowType = type; update(); } RArrowClickLabel::~RArrowClickLabel() { } Qt::ArrowType RArrowClickLabel::arrowType() const { return m_arrowType; } void RArrowClickLabel::mousePressEvent(QMouseEvent* event) { /* * In some contexts, like QGraphicsView, there will be no * release event if the press event was not accepted. */ if (event->button() == Qt::LeftButton) { event->accept(); } } void RArrowClickLabel::mouseReleaseEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) { emit leftClicked(); } } void RArrowClickLabel::paintEvent(QPaintEvent*) { // Inspired by karrowbutton.cpp, // Copyright (C) 2001 Frerich Raabe QPainter p(this); QStyleOptionFrame opt; opt.init(this); opt.lineWidth = 2; opt.midLineWidth = 0; /* p.fillRect( rect(), palette().brush( QPalette::Background ) ); style()->drawPrimitive( QStyle::PE_Frame, &opt, &p, this); */ if (m_arrowType == Qt::NoArrow) return; if (width() < m_size + m_margin || height() < m_size + m_margin) return; // don't draw arrows if we are too small unsigned int x = 0, y = 0; if (m_arrowType == Qt::DownArrow) { x = (width() - m_size) / 2; y = height() - (m_size + m_margin); } else if (m_arrowType == Qt::UpArrow) { x = (width() - m_size) / 2; y = m_margin; } else if (m_arrowType == Qt::RightArrow) { x = width() - (m_size + m_margin); y = (height() - m_size) / 2; } else // arrowType == LeftArrow { x = m_margin; y = (height() - m_size) / 2; } /* if (isDown()) { ++x; ++y; } */ QStyle::PrimitiveElement e = QStyle::PE_IndicatorArrowLeft; switch (m_arrowType) { case Qt::LeftArrow: e = QStyle::PE_IndicatorArrowLeft; break; case Qt::RightArrow: e = QStyle::PE_IndicatorArrowRight; break; case Qt::UpArrow: e = QStyle::PE_IndicatorArrowUp; break; case Qt::DownArrow: e = QStyle::PE_IndicatorArrowDown; break; case Qt::NoArrow: break; } opt.state |= QStyle::State_Enabled; opt.rect = QRect( x, y, m_size, m_size); style()->drawPrimitive( e, &opt, &p, this ); } QSize RArrowClickLabel::sizeHint() const { return QSize(m_size + 2*m_margin, m_size + 2*m_margin); } // ------------------------------------------------------------------------ class Q_DECL_HIDDEN RLabelExpander::Private { public: Private() { clickLabel = 0; containerWidget = 0; pixmapLabel = 0; grid = 0; arrow = 0; line = 0; hbox = 0; checkBox = 0; expandByDefault = true; } bool expandByDefault; QCheckBox* checkBox; QLabel* pixmapLabel; QWidget* containerWidget; QGridLayout* grid; RLineWidget* line; QWidget* hbox; RArrowClickLabel* arrow; RClickLabel* clickLabel; }; RLabelExpander::RLabelExpander(QWidget* const parent) : QWidget(parent), d(new Private) { d->grid = new QGridLayout(this); d->line = new RLineWidget(Qt::Horizontal, this); d->hbox = new QWidget(this); d->arrow = new RArrowClickLabel(d->hbox); d->checkBox = new QCheckBox(d->hbox); d->pixmapLabel = new QLabel(d->hbox); d->clickLabel = new RClickLabel(d->hbox); QHBoxLayout* const hlay = new QHBoxLayout(d->hbox); hlay->addWidget(d->arrow); hlay->addWidget(d->checkBox); hlay->addWidget(d->pixmapLabel); hlay->addWidget(d->clickLabel, 10); hlay->setMargin(0); hlay->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); d->pixmapLabel->installEventFilter(this); d->pixmapLabel->setCursor(Qt::PointingHandCursor); d->hbox->setCursor(Qt::PointingHandCursor); setCheckBoxVisible(false); d->grid->addWidget(d->line, 0, 0, 1, 3); d->grid->addWidget(d->hbox, 1, 0, 1, 3); d->grid->setColumnStretch(2, 10); d->grid->setMargin(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); d->grid->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); connect(d->arrow, &RArrowClickLabel::leftClicked, this, &RLabelExpander::slotToggleContainer); connect(d->clickLabel, &RClickLabel::activated, this, &RLabelExpander::slotToggleContainer); connect(d->checkBox, &QCheckBox::toggled, this, &RLabelExpander::signalToggled); } RLabelExpander::~RLabelExpander() { delete d; } void RLabelExpander::setCheckBoxVisible(bool b) { d->checkBox->setVisible(b); } bool RLabelExpander::checkBoxIsVisible() const { return d->checkBox->isVisible(); } void RLabelExpander::setChecked(bool b) { d->checkBox->setChecked(b); } bool RLabelExpander::isChecked() const { return d->checkBox->isChecked(); } void RLabelExpander::setLineVisible(bool b) { d->line->setVisible(b); } bool RLabelExpander::lineIsVisible() const { return d->line->isVisible(); } void RLabelExpander::setText(const QString& txt) { d->clickLabel->setText(QString("%1").arg(txt)); } QString RLabelExpander::text() const { return d->clickLabel->text(); } void RLabelExpander::setIcon(const QIcon& icon) { d->pixmapLabel->setPixmap(icon.pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize))); } QIcon RLabelExpander::icon() const { return QIcon(*d->pixmapLabel->pixmap()); } void RLabelExpander::setWidget(QWidget* const widget) { if (widget) { d->containerWidget = widget; d->containerWidget->setParent(this); d->grid->addWidget(d->containerWidget, 2, 0, 1, 3); } } QWidget* RLabelExpander::widget() const { return d->containerWidget; } void RLabelExpander::setExpandByDefault(bool b) { d->expandByDefault = b; } bool RLabelExpander::isExpandByDefault() const { return d->expandByDefault; } void RLabelExpander::setExpanded(bool b) { if (d->containerWidget) { d->containerWidget->setVisible(b); if (b) d->arrow->setArrowType(Qt::DownArrow); else d->arrow->setArrowType(Qt::RightArrow); } emit signalExpanded(b); } bool RLabelExpander::isExpanded() const { return (d->arrow->arrowType() == Qt::DownArrow); } void RLabelExpander::slotToggleContainer() { if (d->containerWidget) setExpanded(!d->containerWidget->isVisible()); } bool RLabelExpander::eventFilter(QObject* obj, QEvent* ev) { if ( obj == d->pixmapLabel) { if ( ev->type() == QEvent::MouseButtonRelease) { slotToggleContainer(); return false; } else { return false; } } else { // pass the event on to the parent class return QWidget::eventFilter(obj, ev); } } // ------------------------------------------------------------------------ class Q_DECL_HIDDEN RExpanderBox::Private { public: Private(RExpanderBox* const box) { parent = box; vbox = 0; } void createItem(int index, QWidget* const w, const QIcon& icon, const QString& txt, const QString& objName, bool expandBydefault) { RLabelExpander* const exp = new RLabelExpander(parent->viewport()); exp->setText(txt); exp->setIcon(icon.pixmap(QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize))); exp->setWidget(w); exp->setLineVisible(!wList.isEmpty()); exp->setObjectName(objName); exp->setExpandByDefault(expandBydefault); if (index >= 0) { vbox->insertWidget(index, exp); wList.insert(index, exp); } else { vbox->addWidget(exp); wList.append(exp); } parent->connect(exp, SIGNAL(signalExpanded(bool)), parent, SLOT(slotItemExpanded(bool))); parent->connect(exp, SIGNAL(signalToggled(bool)), parent, SLOT(slotItemToggled(bool))); } public: QList wList; QVBoxLayout* vbox; RExpanderBox* parent; }; RExpanderBox::RExpanderBox(QWidget* const parent) : QScrollArea(parent), d(new Private(this)) { setFrameStyle(QFrame::NoFrame); setWidgetResizable(true); QWidget* const main = new QWidget(viewport()); d->vbox = new QVBoxLayout(main); d->vbox->setMargin(0); d->vbox->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); setWidget(main); setAutoFillBackground(false); viewport()->setAutoFillBackground(false); main->setAutoFillBackground(false); } RExpanderBox::~RExpanderBox() { d->wList.clear(); delete d; } void RExpanderBox::setCheckBoxVisible(int index, bool b) { if (index > d->wList.count() || index < 0) return; d->wList[index]->setCheckBoxVisible(b); } bool RExpanderBox::checkBoxIsVisible(int index) const { if (index > d->wList.count() || index < 0) return false; return d->wList[index]->checkBoxIsVisible(); } void RExpanderBox::setChecked(int index, bool b) { if (index > d->wList.count() || index < 0) return; d->wList[index]->setChecked(b); } bool RExpanderBox::isChecked(int index) const { if (index > d->wList.count() || index < 0) return false; return d->wList[index]->isChecked(); } void RExpanderBox::addItem(QWidget* const w, const QIcon& icon, const QString& txt, const QString& objName, bool expandBydefault) { d->createItem(-1, w, icon, txt, objName, expandBydefault); } void RExpanderBox::addItem(QWidget* const w, const QString& txt, const QString& objName, bool expandBydefault) { addItem(w, QIcon(), txt, objName, expandBydefault); } void RExpanderBox::addStretch() { d->vbox->addStretch(10); } void RExpanderBox::insertItem(int index, QWidget* const w, const QIcon& icon, const QString& txt, const QString& objName, bool expandBydefault) { d->createItem(index, w, icon, txt, objName, expandBydefault); } void RExpanderBox::slotItemExpanded(bool b) { RLabelExpander* const exp = dynamic_cast(sender()); if (exp) { int index = indexOf(exp); emit signalItemExpanded(index, b); } } void RExpanderBox::slotItemToggled(bool b) { RLabelExpander* const exp = dynamic_cast(sender()); if (exp) { int index = indexOf(exp); emit signalItemToggled(index, b); } } void RExpanderBox::insertItem(int index, QWidget* const w, const QString& txt, const QString& objName, bool expandBydefault) { insertItem(index, w, QIcon(), txt, objName, expandBydefault); } void RExpanderBox::insertStretch(int index) { d->vbox->insertStretch(index, 10); } void RExpanderBox::removeItem(int index) { if (index > d->wList.count() || index < 0) return; d->wList[index]->hide(); d->wList.removeAt(index); } void RExpanderBox::setItemText(int index, const QString& txt) { if (index > d->wList.count() || index < 0) return; d->wList[index]->setText(txt); } QString RExpanderBox::itemText(int index) const { if (index > d->wList.count() || index < 0) return QString(); return d->wList[index]->text(); } void RExpanderBox::setItemIcon(int index, const QIcon& icon) { if (index > d->wList.count() || index < 0) return; d->wList[index]->setIcon(icon.pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize))); } QIcon RExpanderBox::itemIcon(int index) const { if (index > d->wList.count() || index < 0) return QIcon(); return d->wList[index]->icon(); } int RExpanderBox::count() const { return d->wList.count(); } void RExpanderBox::setItemToolTip(int index, const QString& tip) { if (index > d->wList.count() || index < 0) return; d->wList[index]->setToolTip(tip); } QString RExpanderBox::itemToolTip(int index) const { if (index > d->wList.count() || index < 0) return QString(); return d->wList[index]->toolTip(); } void RExpanderBox::setItemEnabled(int index, bool enabled) { if (index > d->wList.count() || index < 0) return; d->wList[index]->setEnabled(enabled); } bool RExpanderBox::isItemEnabled(int index) const { if (index > d->wList.count() || index < 0) return false; return d->wList[index]->isEnabled(); } RLabelExpander* RExpanderBox::widget(int index) const { if (index > d->wList.count() || index < 0) return 0; return d->wList[index]; } int RExpanderBox::indexOf(RLabelExpander* const widget) const { for (int i = 0 ; i < count(); ++i) { RLabelExpander* const exp = d->wList[i]; if (widget == exp) return i; } return -1; } void RExpanderBox::setItemExpanded(int index, bool b) { if (index > d->wList.count() || index < 0) return; RLabelExpander* const exp = d->wList[index]; if (!exp) return; exp->setExpanded(b); } bool RExpanderBox::isItemExpanded(int index) const { if (index > d->wList.count() || index < 0) return false; RLabelExpander* const exp = d->wList[index]; if (!exp) return false; return (exp->isExpanded()); } void RExpanderBox::readSettings(KConfigGroup& group) { for (int i = 0 ; i < count(); ++i) { RLabelExpander* const exp = d->wList[i]; if (exp) { exp->setExpanded(group.readEntry(QString("%1 Expanded").arg(exp->objectName()), exp->isExpandByDefault())); } } } void RExpanderBox::writeSettings(KConfigGroup& group) { for (int i = 0 ; i < count(); ++i) { RLabelExpander* const exp = d->wList[i]; if (exp) { group.writeEntry(QString("%1 Expanded").arg(exp->objectName()), exp->isExpanded()); } } } // ------------------------------------------------------------------------ RExpanderBoxExclusive::RExpanderBoxExclusive(QWidget* const parent) : RExpanderBox(parent) { setIsToolBox(true); } RExpanderBoxExclusive::~RExpanderBoxExclusive() { } void RExpanderBoxExclusive::slotItemExpanded(bool b) { RLabelExpander* const exp = dynamic_cast(sender()); if (!exp) return; if (isToolBox() && b) { int item = 0; while (item < count()) { if (isItemExpanded(item) && item != indexOf(exp)) { setItemExpanded(item, false); } item++; } } emit signalItemExpanded(indexOf(exp), b); } void RExpanderBoxExclusive::setIsToolBox(bool b) { m_toolbox = b; } bool RExpanderBoxExclusive::isToolBox() const { return (m_toolbox); } } // namespace KDcrawIface diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/rexpanderbox.h b/plugins/impex/raw/3rdparty/libkdcraw/src/rexpanderbox.h index 8cd0675b57..13d36b5d00 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/rexpanderbox.h +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/rexpanderbox.h @@ -1,299 +1,299 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2008-03-14 * @brief A widget to host settings as expander box * * @author Copyright (C) 2008-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2008-2013 by Marcel Wiesweg * marcel dot wiesweg at gmx dot de * @author Copyright (C) 2010 by Manuel Viet * contact at 13zenrv dot fr * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef REXPANDERBOX_H #define REXPANDERBOX_H // Qt includes #include #include #include #include #include // KDE includes #include // Local includes #include "rwidgetutils.h" namespace KDcrawIface { class RClickLabel : public QLabel { Q_OBJECT public: RClickLabel(QWidget* const parent = 0); explicit RClickLabel(const QString& text, QWidget* const parent = 0); ~RClickLabel() override; Q_SIGNALS: /// Emitted when activated by left mouse click void leftClicked(); /// Emitted when activated, by mouse or key press void activated(); protected: void mousePressEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; void keyPressEvent(QKeyEvent* event) override; }; // ------------------------------------------------------------------------- class RSqueezedClickLabel : public RAdjustableLabel { Q_OBJECT public: RSqueezedClickLabel(QWidget* const parent = 0); explicit RSqueezedClickLabel(const QString& text, QWidget* const parent = 0); ~RSqueezedClickLabel() override; Q_SIGNALS: void leftClicked(); void activated(); protected: void mousePressEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; void keyPressEvent(QKeyEvent* event) override; }; // ------------------------------------------------------------------------- class RArrowClickLabel : public QWidget { Q_OBJECT public: RArrowClickLabel(QWidget* const parent = 0); ~RArrowClickLabel() override; void setArrowType(Qt::ArrowType arrowType); Qt::ArrowType arrowType() const; QSize sizeHint () const override; Q_SIGNALS: void leftClicked(); protected: void mousePressEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; void paintEvent(QPaintEvent* event) override; protected: Qt::ArrowType m_arrowType; int m_size; int m_margin; }; // ------------------------------------------------------------------------- class RLabelExpander : public QWidget { Q_OBJECT public: RLabelExpander(QWidget* const parent = 0); ~RLabelExpander() override; void setCheckBoxVisible(bool b); bool checkBoxIsVisible() const; void setChecked(bool b); bool isChecked() const; void setLineVisible(bool b); bool lineIsVisible() const; void setText(const QString& txt); QString text() const; void setIcon(const QIcon &icon); QIcon icon() const; void setWidget(QWidget* const widget); QWidget* widget() const; void setExpanded(bool b); bool isExpanded() const; void setExpandByDefault(bool b); bool isExpandByDefault() const; Q_SIGNALS: void signalExpanded(bool); void signalToggled(bool); private Q_SLOTS: void slotToggleContainer(); private: bool eventFilter(QObject* obj, QEvent* ev) override; private: class Private; Private* const d; }; // ------------------------------------------------------------------------- class RExpanderBox : public QScrollArea { Q_OBJECT public: RExpanderBox(QWidget* const parent = 0); ~RExpanderBox() override; /** Add RLabelExpander item at end of box layout with these settings : 'w' : the widget hosted by RLabelExpander. 'pix' : pixmap used as icon to item title. 'txt' : text used as item title. 'objName' : item object name used to read/save expanded settings to rc file. 'expandBydefault' : item state by default (expanded or not). */ void addItem(QWidget* const w, const QIcon &icon, const QString& txt, const QString& objName, bool expandBydefault); void addItem(QWidget* const w, const QString& txt, const QString& objName, bool expandBydefault); /** Insert RLabelExpander item at box layout index with these settings : 'w' : the widget hosted by RLabelExpander. 'pix' : pixmap used as icon to item title. 'txt' : text used as item title. 'objName' : item object name used to read/save expanded settings to rc file. 'expandBydefault' : item state by default (expanded or not). */ void insertItem(int index, QWidget* const w, const QIcon &icon, const QString& txt, const QString& objName, bool expandBydefault); void insertItem(int index, QWidget* const w, const QString& txt, const QString& objName, bool expandBydefault); void removeItem(int index); void setCheckBoxVisible(int index, bool b); bool checkBoxIsVisible(int index) const; void setChecked(int index, bool b); bool isChecked(int index) const; void setItemText(int index, const QString& txt); QString itemText (int index) const; void setItemIcon(int index, const QIcon &icon); QIcon itemIcon(int index) const; void setItemToolTip(int index, const QString& tip); QString itemToolTip(int index) const; void setItemEnabled(int index, bool enabled); bool isItemEnabled(int index) const; void addStretch(); void insertStretch(int index); void setItemExpanded(int index, bool b); bool isItemExpanded(int index) const; int count() const; RLabelExpander* widget(int index) const; int indexOf(RLabelExpander* const widget) const; virtual void readSettings(KConfigGroup& group); virtual void writeSettings(KConfigGroup& group); Q_SIGNALS: void signalItemExpanded(int index, bool b); void signalItemToggled(int index, bool b); private Q_SLOTS: void slotItemExpanded(bool b); void slotItemToggled(bool b); private: class Private; Private* const d; }; // ------------------------------------------------------------------------- class RExpanderBoxExclusive : public RExpanderBox { Q_OBJECT public: RExpanderBoxExclusive(QWidget* const parent = 0); ~RExpanderBoxExclusive() override; /** Show one expander open at most */ void setIsToolBox(bool b); bool isToolBox() const; private Q_SLOTS: void slotItemExpanded(bool b); private: bool m_toolbox; }; } // namespace KDcrawIface #endif // REXPANDERBOX_H diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/rnuminput.cpp b/plugins/impex/raw/3rdparty/libkdcraw/src/rnuminput.cpp index adcb5f66ea..63425d2fb0 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/rnuminput.cpp +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/rnuminput.cpp @@ -1,257 +1,257 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2008-08-16 * @brief Integer and double num input widget * re-implemented with a reset button to switch to * a default value * * @author Copyright (C) 2008-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "rnuminput.h" // C++ includes #include // Qt includes #include #include #include #include #include // KDE includes #include // Local includes #include "rsliderspinbox.h" #include namespace KDcrawIface { class Q_DECL_HIDDEN RIntNumInput::Private { public: Private() { defaultValue = 0; resetButton = 0; input = 0; } int defaultValue; QToolButton* resetButton; RSliderSpinBox* input; }; RIntNumInput::RIntNumInput(QWidget* const parent) : QWidget(parent), d(new Private) { QHBoxLayout* const hlay = new QHBoxLayout(this); d->input = new RSliderSpinBox(this); d->resetButton = new QToolButton(this); d->resetButton->setAutoRaise(true); d->resetButton->setFocusPolicy(Qt::NoFocus); d->resetButton->setIcon(KisIconUtils::loadIcon("document-revert").pixmap(16, 16)); d->resetButton->setToolTip(i18nc("@info:tooltip", "Reset to default value")); hlay->addWidget(d->input); hlay->addWidget(d->resetButton); hlay->setContentsMargins(QMargins()); hlay->setStretchFactor(d->input, 10); hlay->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); // ------------------------------------------------------------- connect(d->resetButton, &QToolButton::clicked, this, &RIntNumInput::slotReset); connect(d->input, &RSliderSpinBox::valueChanged, this, &RIntNumInput::slotValueChanged); } RIntNumInput::~RIntNumInput() { delete d; } void RIntNumInput::setRange(int min, int max, int step) { d->input->setRange(min, max); d->input->setSingleStep(step); } int RIntNumInput::value() const { return d->input->value(); } void RIntNumInput::setValue(int v) { d->input->setValue(v); } int RIntNumInput::defaultValue() const { return d->defaultValue; } void RIntNumInput::setDefaultValue(int v) { d->defaultValue = v; d->input->setValue(d->defaultValue); slotValueChanged(v); } void RIntNumInput::setSuffix(const QString& suffix) { d->input->setSuffix(suffix); } void RIntNumInput::slotReset() { d->input->setValue(d->defaultValue); d->resetButton->setEnabled(false); emit reset(); } void RIntNumInput::slotValueChanged(int v) { d->resetButton->setEnabled(v != d->defaultValue); emit valueChanged(v); } // ---------------------------------------------------- class Q_DECL_HIDDEN RDoubleNumInput::Private { public: Private() { defaultValue = 0.0; resetButton = 0; input = 0; } double defaultValue; QToolButton* resetButton; RDoubleSliderSpinBox* input; }; RDoubleNumInput::RDoubleNumInput(QWidget* const parent) : QWidget(parent), d(new Private) { QHBoxLayout* const hlay = new QHBoxLayout(this); d->input = new RDoubleSliderSpinBox(this); d->resetButton = new QToolButton(this); d->resetButton->setAutoRaise(true); d->resetButton->setFocusPolicy(Qt::NoFocus); d->resetButton->setIcon(KisIconUtils::loadIcon("document-revert").pixmap(16, 16)); d->resetButton->setToolTip(i18nc("@info:tooltip", "Reset to default value")); hlay->addWidget(d->input); hlay->addWidget(d->resetButton); hlay->setContentsMargins(QMargins()); hlay->setStretchFactor(d->input, 10); hlay->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); // ------------------------------------------------------------- connect(d->resetButton, &QToolButton::clicked, this, &RDoubleNumInput::slotReset); connect(d->input, &RDoubleSliderSpinBox::valueChanged, this, &RDoubleNumInput::slotValueChanged); } RDoubleNumInput::~RDoubleNumInput() { delete d; } void RDoubleNumInput::setDecimals(int p) { d->input->setRange(d->input->minimum(), d->input->maximum(), p); } void RDoubleNumInput::setRange(double min, double max, double step) { d->input->setRange(min, max, (int) -floor(log10(step))); d->input->setFastSliderStep(5 * step); d->input->setSingleStep(step); } double RDoubleNumInput::value() const { return d->input->value(); } void RDoubleNumInput::setValue(double v) { d->input->setValue(v); } double RDoubleNumInput::defaultValue() const { return d->defaultValue; } void RDoubleNumInput::setDefaultValue(double v) { d->defaultValue = v; d->input->setValue(d->defaultValue); slotValueChanged(v); } void RDoubleNumInput::setSuffix(const QString& suffix) { d->input->setSuffix(suffix); } void RDoubleNumInput::slotReset() { d->input->setValue(d->defaultValue); d->resetButton->setEnabled(false); emit reset(); } void RDoubleNumInput::slotValueChanged(double v) { d->resetButton->setEnabled(v != d->defaultValue); emit valueChanged(v); } } // namespace KDcrawIface diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/rnuminput.h b/plugins/impex/raw/3rdparty/libkdcraw/src/rnuminput.h index cb18e58339..2c8ff38607 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/rnuminput.h +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/rnuminput.h @@ -1,121 +1,121 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2008-08-16 * @brief Integer and double num input widget * re-implemented with a reset button to switch to * a default value * * @author Copyright (C) 2008-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, 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. * * ============================================================ */ #ifndef RNUMINPUT_H #define RNUMINPUT_H // Qt includes #include // Local includes namespace KDcrawIface { class RIntNumInput : public QWidget { Q_OBJECT public: RIntNumInput(QWidget* const parent=0); ~RIntNumInput() override; void setRange(int min, int max, int step); void setDefaultValue(int d); int defaultValue() const; int value() const; void setSuffix(const QString& suffix); Q_SIGNALS: void reset(); void valueChanged(int); public Q_SLOTS: void setValue(int d); void slotReset(); private Q_SLOTS: void slotValueChanged(int); private: class Private; Private* const d; }; // --------------------------------------------------------- class RDoubleNumInput : public QWidget { Q_OBJECT public: RDoubleNumInput(QWidget* const parent=0); ~RDoubleNumInput() override; void setDecimals(int p); void setRange(double min, double max, double step); void setDefaultValue(double d); double defaultValue() const; double value() const; void setSuffix(const QString& suffix); Q_SIGNALS: void reset(); void valueChanged(double); public Q_SLOTS: void setValue(double d); void slotReset(); private Q_SLOTS: void slotValueChanged(double); private: class Private; Private* const d; }; } // namespace KDcrawIface #endif /* RNUMINPUT_H */ diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/rsliderspinbox.cpp b/plugins/impex/raw/3rdparty/libkdcraw/src/rsliderspinbox.cpp index 0efcf9dbcb..b89263c04d 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/rsliderspinbox.cpp +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/rsliderspinbox.cpp @@ -1,773 +1,772 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2014-11-30 * @brief Save space slider widget * * @author Copyright (C) 2014 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2010 by Justin Noel * justin at ics dot com * @author Copyright (C) 2010 by Cyrille Berger * cberger at cberger dot net * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "rsliderspinbox.h" // C++ includes #include // Qt includes #include #include #include #include #include #include #include #include #include #include namespace KDcrawIface { class RAbstractSliderSpinBoxPrivate { public: RAbstractSliderSpinBoxPrivate() { edit = 0; validator = 0; dummySpinBox = 0; upButtonDown = false; downButtonDown = false; shiftMode = false; factor = 1.0; fastSliderStep = 5; slowFactor = 0.1; shiftPercent = 0.0; exponentRatio = 0.0; value = 0; maximum = 100; minimum = 0; singleStep = 1; } QLineEdit* edit; QDoubleValidator* validator; bool upButtonDown; bool downButtonDown; int factor; int fastSliderStep; double slowFactor; double shiftPercent; bool shiftMode; QString suffix; double exponentRatio; int value; int maximum; int minimum; int singleStep; QSpinBox* dummySpinBox; }; RAbstractSliderSpinBox::RAbstractSliderSpinBox(QWidget* const parent, RAbstractSliderSpinBoxPrivate* const q) : QWidget(parent), d_ptr(q) { Q_D(RAbstractSliderSpinBox); d->edit = new QLineEdit(this); d->edit->setFrame(false); d->edit->setAlignment(Qt::AlignCenter); d->edit->hide(); d->edit->installEventFilter(this); // Make edit transparent d->edit->setAutoFillBackground(false); QPalette pal = d->edit->palette(); pal.setColor(QPalette::Base, Qt::transparent); d->edit->setPalette(pal); connect(d->edit, SIGNAL(editingFinished()), this, SLOT(editLostFocus())); d->validator = new QDoubleValidator(d->edit); d->edit->setValidator(d->validator); setExponentRatio(1.0); // Set sane defaults setFocusPolicy(Qt::StrongFocus); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); // dummy needed to fix a bug in the polyester theme d->dummySpinBox = new QSpinBox(this); d->dummySpinBox->hide(); } RAbstractSliderSpinBox::~RAbstractSliderSpinBox() { Q_D(RAbstractSliderSpinBox); delete d; } void RAbstractSliderSpinBox::showEdit() { Q_D(RAbstractSliderSpinBox); if (d->edit->isVisible()) return; d->edit->setGeometry(editRect(spinBoxOptions())); d->edit->setText(valueString()); d->edit->selectAll(); d->edit->show(); d->edit->setFocus(Qt::OtherFocusReason); update(); } void RAbstractSliderSpinBox::hideEdit() { Q_D(RAbstractSliderSpinBox); d->edit->hide(); update(); } void RAbstractSliderSpinBox::paintEvent(QPaintEvent* e) { Q_D(RAbstractSliderSpinBox); Q_UNUSED(e) QPainter painter(this); // Create options to draw spin box parts QStyleOptionSpinBox spinOpts = spinBoxOptions(); // Draw "SpinBox".Clip off the area of the lineEdit to avoid double borders being drawn painter.save(); painter.setClipping(true); QRect eraseRect(QPoint(rect().x(), rect().y()), QPoint(editRect(spinOpts).right(), rect().bottom())); painter.setClipRegion(QRegion(rect()).subtracted(eraseRect)); style()->drawComplexControl(QStyle::CC_SpinBox, &spinOpts, &painter, d->dummySpinBox); painter.setClipping(false); painter.restore(); // Create options to draw progress bar parts QStyleOptionProgressBar progressOpts = progressBarOptions(); // Draw "ProgressBar" in SpinBox style()->drawControl(QStyle::CE_ProgressBar, &progressOpts, &painter, 0); // Draw focus if necessary if (hasFocus() && d->edit->hasFocus()) { QStyleOptionFocusRect focusOpts; focusOpts.initFrom(this); focusOpts.rect = progressOpts.rect; focusOpts.backgroundColor = palette().color(QPalette::Window); style()->drawPrimitive(QStyle::PE_FrameFocusRect, &focusOpts, &painter, this); } } void RAbstractSliderSpinBox::mousePressEvent(QMouseEvent* e) { Q_D(RAbstractSliderSpinBox); QStyleOptionSpinBox spinOpts = spinBoxOptions(); // Depress buttons or highlight slider. Also used to emulate mouse grab. if (e->buttons() & Qt::LeftButton) { if (upButtonRect(spinOpts).contains(e->pos())) { d->upButtonDown = true; } else if (downButtonRect(spinOpts).contains(e->pos())) { d->downButtonDown = true; } } else if (e->buttons() & Qt::RightButton) { showEdit(); } update(); } void RAbstractSliderSpinBox::mouseReleaseEvent(QMouseEvent* e) { Q_D(RAbstractSliderSpinBox); QStyleOptionSpinBox spinOpts = spinBoxOptions(); // Step up/down for buttons. Emulating mouse grab too. if (upButtonRect(spinOpts).contains(e->pos()) && d->upButtonDown) { setInternalValue(d->value + d->singleStep); } else if (downButtonRect(spinOpts).contains(e->pos()) && d->downButtonDown) { setInternalValue(d->value - d->singleStep); } else if (editRect(spinOpts).contains(e->pos()) && !(d->edit->isVisible()) && !(d->upButtonDown || d->downButtonDown)) { // Snap to percentage for progress area setInternalValue(valueForX(e->pos().x(),e->modifiers())); } d->upButtonDown = false; d->downButtonDown = false; update(); } void RAbstractSliderSpinBox::mouseMoveEvent(QMouseEvent* e) { Q_D(RAbstractSliderSpinBox); if( e->modifiers() & Qt::ShiftModifier ) { if( !d->shiftMode ) { d->shiftPercent = pow(double(d->value - d->minimum)/double(d->maximum - d->minimum), 1/double(d->exponentRatio)); d->shiftMode = true; } } else { d->shiftMode = false; } // Respect emulated mouse grab. if (e->buttons() & Qt::LeftButton && !(d->downButtonDown || d->upButtonDown)) { setInternalValue(valueForX(e->pos().x(),e->modifiers())); update(); } } void RAbstractSliderSpinBox::keyPressEvent(QKeyEvent* e) { Q_D(RAbstractSliderSpinBox); switch (e->key()) { case Qt::Key_Up: case Qt::Key_Right: setInternalValue(d->value + d->singleStep); break; case Qt::Key_Down: case Qt::Key_Left: setInternalValue(d->value - d->singleStep); break; case Qt::Key_Shift: d->shiftPercent = pow( double(d->value - d->minimum)/double(d->maximum - d->minimum), 1/double(d->exponentRatio) ); d->shiftMode = true; break; case Qt::Key_Enter: // Line edit isn't "accepting" key strokes... case Qt::Key_Return: case Qt::Key_Escape: case Qt::Key_Control: case Qt::Key_Alt: case Qt::Key_AltGr: case Qt::Key_Super_L: case Qt::Key_Super_R: break; default: showEdit(); d->edit->event(e); break; } } void RAbstractSliderSpinBox::wheelEvent(QWheelEvent *e) { Q_D(RAbstractSliderSpinBox); int step = d->fastSliderStep; if( e->modifiers() & Qt::ShiftModifier ) { step = d->singleStep; } if ( e->delta() > 0) { setInternalValue(d->value + step); } else { setInternalValue(d->value - step); } update(); e->accept(); } bool RAbstractSliderSpinBox::eventFilter(QObject* recv, QEvent* e) { Q_D(RAbstractSliderSpinBox); if (recv == static_cast(d->edit) && e->type() == QEvent::KeyRelease) { QKeyEvent* const keyEvent = static_cast(e); switch (keyEvent->key()) { case Qt::Key_Enter: case Qt::Key_Return: setInternalValue(QLocale::system().toDouble(d->edit->text()) * d->factor); hideEdit(); return true; case Qt::Key_Escape: hideEdit(); return true; default: break; } } return false; } QSize RAbstractSliderSpinBox::sizeHint() const { const Q_D(RAbstractSliderSpinBox); QStyleOptionSpinBox spinOpts = spinBoxOptions(); QFontMetrics fm(font()); // We need at least 50 pixels or things start to look bad int w = qMax(fm.width(QString::number(d->maximum)), 50); QSize hint(w, d->edit->sizeHint().height() + 3); // Getting the size of the buttons is a pain as the calcs require a rect // that is "big enough". We run the calc twice to get the "smallest" buttons // This code was inspired by QAbstractSpinBox. QSize extra(35, 6); spinOpts.rect.setSize(hint + extra); extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &spinOpts, QStyle::SC_SpinBoxEditField, this).size(); spinOpts.rect.setSize(hint + extra); extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &spinOpts, QStyle::SC_SpinBoxEditField, this).size(); hint += extra; spinOpts.rect = rect(); - return style()->sizeFromContents(QStyle::CT_SpinBox, &spinOpts, hint, 0) - .expandedTo(QApplication::globalStrut()); + return style()->sizeFromContents(QStyle::CT_SpinBox, &spinOpts, hint, 0); } QSize RAbstractSliderSpinBox::minimumSizeHint() const { return sizeHint(); } QStyleOptionSpinBox RAbstractSliderSpinBox::spinBoxOptions() const { const Q_D(RAbstractSliderSpinBox); QStyleOptionSpinBox opts; opts.initFrom(this); opts.frame = false; opts.buttonSymbols = QAbstractSpinBox::UpDownArrows; opts.subControls = QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown; // Disable non-logical buttons if (d->value == d->minimum) { opts.stepEnabled = QAbstractSpinBox::StepUpEnabled; } else if (d->value == d->maximum) { opts.stepEnabled = QAbstractSpinBox::StepDownEnabled; } else { opts.stepEnabled = QAbstractSpinBox::StepUpEnabled | QAbstractSpinBox::StepDownEnabled; } // Deal with depressed buttons if (d->upButtonDown) { opts.activeSubControls = QStyle::SC_SpinBoxUp; } else if (d->downButtonDown) { opts.activeSubControls = QStyle::SC_SpinBoxDown; } else { opts.activeSubControls = 0; } return opts; } QStyleOptionProgressBar RAbstractSliderSpinBox::progressBarOptions() const { const Q_D(RAbstractSliderSpinBox); QStyleOptionSpinBox spinOpts = spinBoxOptions(); // Create opts for drawing the progress portion QStyleOptionProgressBar progressOpts; progressOpts.initFrom(this); progressOpts.maximum = d->maximum; progressOpts.minimum = d->minimum; double minDbl = d->minimum; double dValues = (d->maximum - minDbl); progressOpts.progress = dValues * pow((d->value - minDbl) / dValues, 1.0 / d->exponentRatio) + minDbl; progressOpts.text = valueString() + d->suffix; progressOpts.textAlignment = Qt::AlignCenter; progressOpts.textVisible = !(d->edit->isVisible()); // Change opts rect to be only the ComboBox's text area progressOpts.rect = editRect(spinOpts); return progressOpts; } QRect RAbstractSliderSpinBox::editRect(const QStyleOptionSpinBox& spinBoxOptions) const { return style()->subControlRect(QStyle::CC_SpinBox, &spinBoxOptions, QStyle::SC_SpinBoxEditField); } QRect RAbstractSliderSpinBox::progressRect(const QStyleOptionProgressBar& progressBarOptions) const { return style()->subElementRect(QStyle::SE_ProgressBarGroove, &progressBarOptions); } QRect RAbstractSliderSpinBox::upButtonRect(const QStyleOptionSpinBox& spinBoxOptions) const { return style()->subControlRect(QStyle::CC_SpinBox, &spinBoxOptions, QStyle::SC_SpinBoxUp); } QRect RAbstractSliderSpinBox::downButtonRect(const QStyleOptionSpinBox& spinBoxOptions) const { return style()->subControlRect(QStyle::CC_SpinBox, &spinBoxOptions, QStyle::SC_SpinBoxDown); } int RAbstractSliderSpinBox::valueForX(int x, Qt::KeyboardModifiers modifiers) const { const Q_D(RAbstractSliderSpinBox); QStyleOptionSpinBox spinOpts = spinBoxOptions(); QStyleOptionProgressBar progressOpts = progressBarOptions(); // Adjust for magic number in style code (margins) QRect correctedProgRect = progressRect(progressOpts).adjusted(2, 2, -2, -2); // Compute the distance of the progress bar, in pixel double leftDbl = correctedProgRect.left(); double xDbl = x - leftDbl; // Compute the ration of the progress bar used, linearly (ignoring the exponent) double rightDbl = correctedProgRect.right(); double minDbl = d->minimum; double maxDbl = d->maximum; double dValues = (maxDbl - minDbl); double percent = (xDbl / (rightDbl - leftDbl)); // If SHIFT is pressed, movement should be slowed. if ( modifiers & Qt::ShiftModifier ) { percent = d->shiftPercent + (percent - d->shiftPercent) * d->slowFactor; } // Final value double realvalue = ((dValues * pow(percent, d->exponentRatio)) + minDbl); // If key CTRL is pressed, round to the closest step. if ( modifiers & Qt::ControlModifier ) { double fstep = d->fastSliderStep; if( modifiers & Qt::ShiftModifier ) { fstep *= d->slowFactor; } realvalue = floor((realvalue + fstep / 2) / fstep) * fstep; } // Return the value return int(realvalue); } void RAbstractSliderSpinBox::setSuffix(const QString& suffix) { Q_D(RAbstractSliderSpinBox); d->suffix = suffix; } void RAbstractSliderSpinBox::setExponentRatio(double dbl) { Q_D(RAbstractSliderSpinBox); Q_ASSERT(dbl > 0); d->exponentRatio = dbl; } void RAbstractSliderSpinBox::contextMenuEvent(QContextMenuEvent* event) { event->accept(); } void RAbstractSliderSpinBox::editLostFocus() { // only hide on focus lost, if editing is finished that will be handled in eventFilter Q_D(RAbstractSliderSpinBox); if (!d->edit->hasFocus()) { hideEdit(); } } // --------------------------------------------------------------------------------------------- class RSliderSpinBoxPrivate : public RAbstractSliderSpinBoxPrivate { }; RSliderSpinBox::RSliderSpinBox(QWidget* const parent) : RAbstractSliderSpinBox(parent, new RSliderSpinBoxPrivate) { setRange(0,99); } RSliderSpinBox::~RSliderSpinBox() { } void RSliderSpinBox::setRange(int minimum, int maximum) { Q_D(RSliderSpinBox); d->minimum = minimum; d->maximum = maximum; d->fastSliderStep = (maximum-minimum+1)/20; d->validator->setRange(minimum, maximum, 0); update(); } int RSliderSpinBox::minimum() const { const Q_D(RSliderSpinBox); return d->minimum; } void RSliderSpinBox::setMinimum(int minimum) { Q_D(RSliderSpinBox); setRange(minimum, d->maximum); } int RSliderSpinBox::maximum() const { const Q_D(RSliderSpinBox); return d->maximum; } void RSliderSpinBox::setMaximum(int maximum) { Q_D(RSliderSpinBox); setRange(d->minimum, maximum); } int RSliderSpinBox::fastSliderStep() const { const Q_D(RSliderSpinBox); return d->fastSliderStep; } void RSliderSpinBox::setFastSliderStep(int step) { Q_D(RSliderSpinBox); d->fastSliderStep = step; } int RSliderSpinBox::value() const { const Q_D(RSliderSpinBox); return d->value; } void RSliderSpinBox::setValue(int value) { setInternalValue(value); update(); } QString RSliderSpinBox::valueString() const { const Q_D(RSliderSpinBox); return QLocale::system().toString(d->value); } void RSliderSpinBox::setSingleStep(int value) { Q_D(RSliderSpinBox); d->singleStep = value; } void RSliderSpinBox::setPageStep(int value) { Q_UNUSED(value); } void RSliderSpinBox::setInternalValue(int _value) { Q_D(RAbstractSliderSpinBox); d->value = qBound(d->minimum, _value, d->maximum); emit(valueChanged(value())); } // --------------------------------------------------------------------------------------------- class RDoubleSliderSpinBoxPrivate : public RAbstractSliderSpinBoxPrivate { }; RDoubleSliderSpinBox::RDoubleSliderSpinBox(QWidget* const parent) : RAbstractSliderSpinBox(parent, new RDoubleSliderSpinBoxPrivate) { } RDoubleSliderSpinBox::~RDoubleSliderSpinBox() { } void RDoubleSliderSpinBox::setRange(double minimum, double maximum, int decimals) { Q_D(RDoubleSliderSpinBox); d->factor = pow(10.0, decimals); d->minimum = minimum * d->factor; d->maximum = maximum * d->factor; // This code auto-compute a new step when pressing control. // A flag defaulting to "do not change the fast step" should be added, but it implies changing every call if (maximum - minimum >= 2.0 || decimals <= 0) { //Quick step on integers d->fastSliderStep = int(pow(10.0, decimals)); } else if(decimals == 1) { d->fastSliderStep = (maximum-minimum)*d->factor/10; } else { d->fastSliderStep = (maximum-minimum)*d->factor/20; } d->validator->setRange(minimum, maximum, decimals); update(); setValue(value()); } double RDoubleSliderSpinBox::minimum() const { const Q_D(RAbstractSliderSpinBox); return d->minimum / d->factor; } void RDoubleSliderSpinBox::setMinimum(double minimum) { Q_D(RAbstractSliderSpinBox); setRange(minimum, d->maximum); } double RDoubleSliderSpinBox::maximum() const { const Q_D(RAbstractSliderSpinBox); return d->maximum / d->factor; } void RDoubleSliderSpinBox::setMaximum(double maximum) { Q_D(RAbstractSliderSpinBox); setRange(d->minimum, maximum); } double RDoubleSliderSpinBox::fastSliderStep() const { const Q_D(RAbstractSliderSpinBox); return d->fastSliderStep; } void RDoubleSliderSpinBox::setFastSliderStep(double step) { Q_D(RAbstractSliderSpinBox); d->fastSliderStep = step * d->factor; } double RDoubleSliderSpinBox::value() const { const Q_D(RAbstractSliderSpinBox); return (double)d->value / d->factor; } void RDoubleSliderSpinBox::setValue(double value) { Q_D(RAbstractSliderSpinBox); setInternalValue(d->value = qRound(value * d->factor)); update(); } void RDoubleSliderSpinBox::setSingleStep(double value) { Q_D(RAbstractSliderSpinBox); d->singleStep = value * d->factor; } QString RDoubleSliderSpinBox::valueString() const { const Q_D(RAbstractSliderSpinBox); return QLocale::system().toString((double)d->value / d->factor, 'f', d->validator->decimals()); } void RDoubleSliderSpinBox::setInternalValue(int val) { Q_D(RAbstractSliderSpinBox); d->value = qBound(d->minimum, val, d->maximum); emit(valueChanged(value())); } } // namespace KDcrawIface diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/rsliderspinbox.h b/plugins/impex/raw/3rdparty/libkdcraw/src/rsliderspinbox.h index d1a1377647..0e79f3fce9 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/rsliderspinbox.h +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/rsliderspinbox.h @@ -1,183 +1,183 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2014-11-30 * @brief Save space slider widget * * @author Copyright (C) 2014 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2010 by Justin Noel * justin at ics dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, 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. * * ============================================================ */ #ifndef RSLIDERSPINBOX_H #define RSLIDERSPINBOX_H // Qt includes #include #include #include #include #include namespace KDcrawIface { class RAbstractSliderSpinBoxPrivate; class RSliderSpinBoxPrivate; class RDoubleSliderSpinBoxPrivate; /** * TODO: when inactive, also show the progress bar part as inactive! */ class RAbstractSliderSpinBox : public QWidget { Q_OBJECT Q_DISABLE_COPY(RAbstractSliderSpinBox) Q_DECLARE_PRIVATE(RAbstractSliderSpinBox) protected: explicit RAbstractSliderSpinBox(QWidget* const parent, RAbstractSliderSpinBoxPrivate* const q); public: ~RAbstractSliderSpinBox() override; void showEdit(); void hideEdit(); void setSuffix(const QString& suffix); void setExponentRatio(double dbl); protected: void paintEvent(QPaintEvent* e) override; void mousePressEvent(QMouseEvent* e) override; void mouseReleaseEvent(QMouseEvent* e) override; void mouseMoveEvent(QMouseEvent* e) override; void keyPressEvent(QKeyEvent* e) override; void wheelEvent(QWheelEvent *) override; bool eventFilter(QObject* recv, QEvent* e) override; QSize sizeHint() const override; QSize minimumSizeHint() const override; QStyleOptionSpinBox spinBoxOptions() const; QStyleOptionProgressBar progressBarOptions() const; QRect editRect(const QStyleOptionSpinBox& spinBoxOptions) const; QRect progressRect(const QStyleOptionProgressBar& progressBarOptions) const; QRect upButtonRect(const QStyleOptionSpinBox& spinBoxOptions) const; QRect downButtonRect(const QStyleOptionSpinBox& spinBoxOptions) const; int valueForX(int x, Qt::KeyboardModifiers modifiers = Qt::NoModifier) const; virtual QString valueString() const = 0; virtual void setInternalValue(int value) = 0; protected Q_SLOTS: void contextMenuEvent(QContextMenuEvent* event) override; void editLostFocus(); protected: RAbstractSliderSpinBoxPrivate* const d_ptr; }; // --------------------------------------------------------------------------------- class RSliderSpinBox : public RAbstractSliderSpinBox { Q_OBJECT Q_DECLARE_PRIVATE(RSliderSpinBox) Q_PROPERTY( int minimum READ minimum WRITE setMinimum ) Q_PROPERTY( int maximum READ maximum WRITE setMaximum ) public: RSliderSpinBox(QWidget* const parent = 0); ~RSliderSpinBox() override; void setRange(int minimum, int maximum); int minimum() const; void setMinimum(int minimum); int maximum() const; void setMaximum(int maximum); int fastSliderStep() const; void setFastSliderStep(int step); int value() const; void setValue(int value); void setSingleStep(int value); void setPageStep(int value); Q_SIGNALS: void valueChanged(int value); protected: QString valueString() const override; void setInternalValue(int value) override; }; // --------------------------------------------------------------------------------- class RDoubleSliderSpinBox : public RAbstractSliderSpinBox { Q_OBJECT Q_DECLARE_PRIVATE(RDoubleSliderSpinBox) public: RDoubleSliderSpinBox(QWidget* const parent = 0); ~RDoubleSliderSpinBox() override; void setRange(double minimum, double maximum, int decimals = 0); double minimum() const; void setMinimum(double minimum); double maximum() const; void setMaximum(double maximum); double fastSliderStep() const; void setFastSliderStep(double step); double value() const; void setValue(double value); void setSingleStep(double value); Q_SIGNALS: void valueChanged(double value); protected: QString valueString() const override; void setInternalValue(int val) override; }; } // namespace KDcrawIface #endif // RSLIDERSPINBOX_H diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/rwidgetutils.cpp b/plugins/impex/raw/3rdparty/libkdcraw/src/rwidgetutils.cpp index 35e1cc1266..67c2daf946 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/rwidgetutils.cpp +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/rwidgetutils.cpp @@ -1,618 +1,618 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2014-09-12 * @brief Simple helper widgets collection * * @author Copyright (C) 2014-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "rwidgetutils.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "libkdcraw_debug.h" namespace KDcrawIface { RActiveLabel::RActiveLabel(const QUrl& url, const QString& imgPath, QWidget* const parent) : QLabel(parent) { setMargin(0); setScaledContents(false); setOpenExternalLinks(true); setTextFormat(Qt::RichText); setFocusPolicy(Qt::NoFocus); setTextInteractionFlags(Qt::LinksAccessibleByMouse); setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); QImage img = QImage(imgPath); updateData(url, img); } RActiveLabel::~RActiveLabel() { } void RActiveLabel::updateData(const QUrl& url, const QImage& img) { QByteArray byteArray; QBuffer buffer(&byteArray); img.save(&buffer, "PNG"); setText(QString::fromLatin1("%2") .arg(url.url()) .arg(QString::fromLatin1("") .arg(QString::fromLatin1(byteArray.toBase64().data())))); } // ------------------------------------------------------------------------------------ RLineWidget::RLineWidget(Qt::Orientation orientation, QWidget* const parent) : QFrame(parent) { setLineWidth(1); setMidLineWidth(0); if (orientation == Qt::Vertical) { setFrameShape(QFrame::VLine); setFrameShadow(QFrame::Sunken); setMinimumSize(2, 0); } else { setFrameShape(QFrame::HLine); setFrameShadow(QFrame::Sunken); setMinimumSize(0, 2); } updateGeometry(); } RLineWidget::~RLineWidget() { } // ------------------------------------------------------------------------------------ RHBox::RHBox(QWidget* const parent) : QFrame(parent) { QHBoxLayout* const layout = new QHBoxLayout(this); layout->setSpacing(0); layout->setMargin(0); setLayout(layout); } RHBox::RHBox(bool /*vertical*/, QWidget* const parent) : QFrame(parent) { QVBoxLayout* const layout = new QVBoxLayout(this); layout->setSpacing(0); layout->setMargin(0); setLayout(layout); } RHBox::~RHBox() { } void RHBox::childEvent(QChildEvent* e) { switch (e->type()) { case QEvent::ChildAdded: { QChildEvent* const ce = static_cast(e); if (ce->child()->isWidgetType()) { QWidget* const w = static_cast(ce->child()); static_cast(layout())->addWidget(w); } break; } case QEvent::ChildRemoved: { QChildEvent* const ce = static_cast(e); if (ce->child()->isWidgetType()) { QWidget* const w = static_cast(ce->child()); static_cast(layout())->removeWidget(w); } break; } default: break; } QFrame::childEvent(e); } QSize RHBox::sizeHint() const { RHBox* const b = const_cast(this); QApplication::sendPostedEvents(b, QEvent::ChildAdded); return QFrame::sizeHint(); } QSize RHBox::minimumSizeHint() const { RHBox* const b = const_cast(this); QApplication::sendPostedEvents(b, QEvent::ChildAdded ); return QFrame::minimumSizeHint(); } void RHBox::setSpacing(int spacing) { layout()->setSpacing(spacing); } void RHBox::setMargin(int margin) { layout()->setMargin(margin); } void RHBox::setStretchFactor(QWidget* const widget, int stretch) { static_cast(layout())->setStretchFactor(widget, stretch); } // ------------------------------------------------------------------------------------ RVBox::RVBox(QWidget* const parent) : RHBox(true, parent) { } RVBox::~RVBox() { } // ------------------------------------------------------------------------------------ class Q_DECL_HIDDEN RAdjustableLabel::Private { public: Private() { emode = Qt::ElideMiddle; } QString ajdText; Qt::TextElideMode emode; }; RAdjustableLabel::RAdjustableLabel(QWidget* const parent) : QLabel(parent), d(new Private) { setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); } RAdjustableLabel::~RAdjustableLabel() { delete d; } void RAdjustableLabel::resizeEvent(QResizeEvent*) { adjustTextToLabel(); } QSize RAdjustableLabel::minimumSizeHint() const { QSize sh = QLabel::minimumSizeHint(); sh.setWidth(-1); return sh; } QSize RAdjustableLabel::sizeHint() const { QFontMetrics fm(fontMetrics()); int maxW = QApplication::desktop()->screenGeometry(this).width() * 3 / 4; int currentW = fm.width(d->ajdText); return (QSize(currentW > maxW ? maxW : currentW, QLabel::sizeHint().height())); } void RAdjustableLabel::setAdjustedText(const QString& text) { d->ajdText = text; if (d->ajdText.isNull()) QLabel::clear(); adjustTextToLabel(); } QString RAdjustableLabel::adjustedText() const { return d->ajdText; } void RAdjustableLabel::setAlignment(Qt::Alignment alignment) { QString tmp(d->ajdText); QLabel::setAlignment(alignment); d->ajdText = tmp; } void RAdjustableLabel::setElideMode(Qt::TextElideMode mode) { d->emode = mode; adjustTextToLabel(); } void RAdjustableLabel::adjustTextToLabel() { QFontMetrics fm(fontMetrics()); QStringList adjustedLines; int lblW = size().width(); bool adjusted = false; Q_FOREACH(const QString& line, d->ajdText.split(QLatin1Char('\n'))) { int lineW = fm.width(line); if (lineW > lblW) { adjusted = true; adjustedLines << fm.elidedText(line, d->emode, lblW); } else { adjustedLines << line; } } if (adjusted) { QLabel::setText(adjustedLines.join(QStringLiteral("\n"))); setToolTip(d->ajdText); } else { QLabel::setText(d->ajdText); setToolTip(QString()); } } // ------------------------------------------------------------------------------------ class Q_DECL_HIDDEN RFileSelector::Private { public: Private() { edit = 0; btn = 0; fdMode = QFileDialog::ExistingFile; fdOptions = QFileDialog::DontUseNativeDialog; } QLineEdit* edit; QPushButton* btn; QFileDialog::FileMode fdMode; QString fdFilter; QString fdTitle; QFileDialog::Options fdOptions; }; RFileSelector::RFileSelector(QWidget* const parent) : RHBox(parent), d(new Private) { d->edit = new QLineEdit(this); d->btn = new QPushButton(i18n("Browse..."), this); setStretchFactor(d->edit, 10); connect(d->btn, SIGNAL(clicked()), this, SLOT(slotBtnClicked())); } RFileSelector::~RFileSelector() { delete d; } QLineEdit* RFileSelector::lineEdit() const { return d->edit; } void RFileSelector::setFileDlgMode(QFileDialog::FileMode mode) { d->fdMode = mode; } void RFileSelector::setFileDlgFilter(const QString& filter) { d->fdFilter = filter; } void RFileSelector::setFileDlgTitle(const QString& title) { d->fdTitle = title; } void RFileSelector::setFileDlgOptions(QFileDialog::Options opts) { d->fdOptions = opts; } void RFileSelector::slotBtnClicked() { if (d->fdMode == QFileDialog::ExistingFiles) { qCDebug(LIBKDCRAW_LOG) << "Multiple selection is not supported"; return; } QFileDialog* const fileDlg = new QFileDialog(this); fileDlg->setOptions(d->fdOptions); fileDlg->setDirectory(QFileInfo(d->edit->text()).dir()); fileDlg->setFileMode(d->fdMode); if (!d->fdFilter.isNull()) fileDlg->setNameFilter(d->fdFilter); if (!d->fdTitle.isNull()) fileDlg->setWindowTitle(d->fdTitle); connect(fileDlg, SIGNAL(urlSelected(QUrl)), this, SIGNAL(signalUrlSelected(QUrl))); emit signalOpenFileDialog(); if (fileDlg->exec() == QDialog::Accepted) { QStringList sel = fileDlg->selectedFiles(); if (!sel.isEmpty()) { d->edit->setText(sel.first()); } } delete fileDlg; } // --------------------------------------------------------------------------------------- WorkingPixmap::WorkingPixmap() { QPixmap pix(QStandardPaths::locate(QStandardPaths::AppDataLocation, QLatin1String("libkdcraw/pics/process-working.png"))); QSize size(22, 22); if (pix.isNull()) { qCWarning(LIBKDCRAW_LOG) << "Invalid pixmap specified."; return; } if (!size.isValid()) { size = QSize(pix.width(), pix.width()); } if (pix.width() % size.width() || pix.height() % size.height()) { qCWarning(LIBKDCRAW_LOG) << "Invalid framesize."; return; } const int rowCount = pix.height() / size.height(); const int colCount = pix.width() / size.width(); m_frames.resize(rowCount * colCount); int pos = 0; for (int row = 0; row < rowCount; ++row) { for (int col = 0; col < colCount; ++col) { QPixmap frm = pix.copy(col * size.width(), row * size.height(), size.width(), size.height()); m_frames[pos++] = frm; } } } WorkingPixmap::~WorkingPixmap() { } bool WorkingPixmap::isEmpty() const { return m_frames.isEmpty(); } QSize WorkingPixmap::frameSize() const { if (isEmpty()) { qCWarning(LIBKDCRAW_LOG) << "No frame loaded."; return QSize(); } return m_frames[0].size(); } int WorkingPixmap::frameCount() const { return m_frames.size(); } QPixmap WorkingPixmap::frameAt(int index) const { if (isEmpty()) { qCWarning(LIBKDCRAW_LOG) << "No frame loaded."; return QPixmap(); } return m_frames.at(index); } // ------------------------------------------------------------------------------------ class Q_DECL_HIDDEN RColorSelector::Private { public: Private() { } QColor color; }; RColorSelector::RColorSelector(QWidget* const parent) : QPushButton(parent), d(new Private) { connect(this, SIGNAL(clicked()), this, SLOT(slotBtnClicked())); } RColorSelector::~RColorSelector() { delete d; } void RColorSelector::setColor(const QColor& color) { if (color.isValid()) { d->color = color; update(); } } QColor RColorSelector::color() const { return d->color; } void RColorSelector::slotBtnClicked() { QColor color = QColorDialog::getColor(d->color); if (color.isValid()) { setColor(color); emit signalColorSelected(color); } } void RColorSelector::paintEvent(QPaintEvent*) { QPainter painter(this); QStyle* const style = QWidget::style(); QStyleOptionButton opt; opt.initFrom(this); opt.state |= isDown() ? QStyle::State_Sunken : QStyle::State_Raised; opt.features = QStyleOptionButton::None; opt.icon = QIcon(); opt.text.clear(); style->drawControl(QStyle::CE_PushButtonBevel, &opt, &painter, this); QRect labelRect = style->subElementRect(QStyle::SE_PushButtonContents, &opt, this); int shift = style->pixelMetric(QStyle::PM_ButtonMargin, &opt, this) / 2; labelRect.adjust(shift, shift, -shift, -shift); int x, y, w, h; labelRect.getRect(&x, &y, &w, &h); if (isChecked() || isDown()) { x += style->pixelMetric(QStyle::PM_ButtonShiftHorizontal, &opt, this); y += style->pixelMetric(QStyle::PM_ButtonShiftVertical, &opt, this); } QColor fillCol = isEnabled() ? d->color : palette().color(backgroundRole()); qDrawShadePanel(&painter, x, y, w, h, palette(), true, 1, 0); if (fillCol.isValid()) { const QRect rect(x + 1, y + 1, w - 2, h - 2); if (fillCol.alpha() < 255) { QPixmap chessboardPattern(16, 16); QPainter patternPainter(&chessboardPattern); patternPainter.fillRect(0, 0, 8, 8, Qt::black); patternPainter.fillRect(8, 8, 8, 8, Qt::black); patternPainter.fillRect(0, 8, 8, 8, Qt::white); patternPainter.fillRect(8, 0, 8, 8, Qt::white); patternPainter.end(); painter.fillRect(rect, QBrush(chessboardPattern)); } painter.fillRect(rect, fillCol); } if (hasFocus()) { QRect focusRect = style->subElementRect(QStyle::SE_PushButtonFocusRect, &opt, this); QStyleOptionFocusRect focusOpt; focusOpt.init(this); focusOpt.rect = focusRect; focusOpt.backgroundColor = palette().background().color(); style->drawPrimitive(QStyle::PE_FrameFocusRect, &focusOpt, &painter, this); } } } // namespace KDcrawIface diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/rwidgetutils.h b/plugins/impex/raw/3rdparty/libkdcraw/src/rwidgetutils.h index d17b0690e5..14f8861350 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/rwidgetutils.h +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/rwidgetutils.h @@ -1,256 +1,256 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2014-09-12 * @brief Simple helpher widgets collection * * @author Copyright (C) 2014-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, 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. * * ============================================================ */ #ifndef RWIDGETUTILS_H #define RWIDGETUTILS_H // Qt includes #include #include #include #include #include #include #include #include #include #include // Local includes namespace KDcrawIface { /** A widget to host an image into a label with an active url which can be * open to default web browser using simple mouse click. */ class RActiveLabel : public QLabel { Q_OBJECT public: explicit RActiveLabel(const QUrl& url=QUrl(), const QString& imgPath=QString(), QWidget* const parent=0); ~RActiveLabel() override; void updateData(const QUrl& url, const QImage& img); }; // ------------------------------------------------------------------------------------ /** * A widget to show an horizontal or vertical line separator **/ class RLineWidget : public QFrame { Q_OBJECT public: explicit RLineWidget(Qt::Orientation orientation, QWidget* const parent=0); ~RLineWidget() override; }; // ------------------------------------------------------------------------------------ /** An Horizontal widget to host children widgets */ class RHBox : public QFrame { Q_OBJECT Q_DISABLE_COPY(RHBox) public: explicit RHBox(QWidget* const parent=0); ~RHBox() override; void setMargin(int margin); void setSpacing(int space); void setStretchFactor(QWidget* const widget, int stretch); QSize sizeHint() const override; QSize minimumSizeHint() const override; protected: RHBox(bool vertical, QWidget* const parent); void childEvent(QChildEvent* e) override; }; // ------------------------------------------------------------------------------------ /** A Vertical widget to host children widgets */ class RVBox : public RHBox { Q_OBJECT Q_DISABLE_COPY(RVBox) public: explicit RVBox(QWidget* const parent=0); ~RVBox() override; }; // ------------------------------------------------------------------------------------ /** A label to show text adjusted to widget size */ class RAdjustableLabel : public QLabel { Q_OBJECT public: explicit RAdjustableLabel(QWidget* const parent=0); ~RAdjustableLabel() override; QSize minimumSizeHint() const override; QSize sizeHint() const override; void setAlignment(Qt::Alignment align); void setElideMode(Qt::TextElideMode mode); QString adjustedText() const; public Q_SLOTS: void setAdjustedText(const QString& text=QString()); private: void resizeEvent(QResizeEvent*) override; void adjustTextToLabel(); // Disabled methods from QLabel QString text() const { return QString(); }; // Use adjustedText() instead. void setText(const QString&) {}; // Use setAdjustedText(text) instead. void clear() {}; // Use setdjustedText(QString()) instead. private: class Private; Private* const d; }; // ------------------------------------------------------------------------------------ /** A widget to chosse a single local file or path. * Use line edit and file dialog properties to customize operation modes. */ class RFileSelector : public RHBox { Q_OBJECT public: explicit RFileSelector(QWidget* const parent=0); ~RFileSelector() override; QLineEdit* lineEdit() const; void setFileDlgMode(QFileDialog::FileMode mode); void setFileDlgFilter(const QString& filter); void setFileDlgTitle(const QString& title); void setFileDlgOptions(QFileDialog::Options opts); Q_SIGNALS: void signalOpenFileDialog(); void signalUrlSelected(const QUrl&); private Q_SLOTS: void slotBtnClicked(); private: class Private; Private* const d; }; // -------------------------------------------------------------------------------------- /** A widget to draw progress wheel indicator over thumbnails. */ class WorkingPixmap { public: explicit WorkingPixmap(); ~WorkingPixmap(); bool isEmpty() const; QSize frameSize() const; int frameCount() const; QPixmap frameAt(int index) const; private: QVector m_frames; }; // ------------------------------------------------------------------------------------ /** A widget to chosse a color from a palette. */ class RColorSelector : public QPushButton { Q_OBJECT public: explicit RColorSelector(QWidget* const parent=0); ~RColorSelector() override; void setColor(const QColor& color); QColor color() const; Q_SIGNALS: void signalColorSelected(const QColor&); private Q_SLOTS: void slotBtnClicked(); private: void paintEvent(QPaintEvent*) override; private: class Private; Private* const d; }; } // namespace KDcrawIface #endif // RWIDGETUTILS_H diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/squeezedcombobox.cpp b/plugins/impex/raw/3rdparty/libkdcraw/src/squeezedcombobox.cpp index 2ff49314fc..ffa40dee49 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/squeezedcombobox.cpp +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/squeezedcombobox.cpp @@ -1,196 +1,195 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2008-08-21 * @brief a combo box with a width not depending of text * content size * * @author Copyright (C) 2006-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2008 by Andi Clemens * andi dot clemens at googlemail dot com * @author Copyright (C) 2005 by Tom Albers * tomalbers at kde dot nl * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "squeezedcombobox.h" // Qt includes #include #include #include #include #include namespace KDcrawIface { class Q_DECL_HIDDEN SqueezedComboBox::Private { public: Private() { timer = 0; } QMap originalItems; QTimer* timer; }; SqueezedComboBox::SqueezedComboBox(QWidget* const parent, const char* name) : QComboBox(parent), d(new Private) { setObjectName(name); setMinimumWidth(100); d->timer = new QTimer(this); d->timer->setSingleShot(true); connect(d->timer, &QTimer::timeout, this, &SqueezedComboBox::slotTimeOut); connect(this, static_cast(&SqueezedComboBox::activated), this, &SqueezedComboBox::slotUpdateToolTip); } SqueezedComboBox::~SqueezedComboBox() { d->originalItems.clear(); delete d->timer; delete d; } bool SqueezedComboBox::contains(const QString& text) const { if (text.isEmpty()) return false; for (QMap::const_iterator it = d->originalItems.constBegin() ; it != d->originalItems.constEnd(); ++it) { if (it.value() == text) return true; } return false; } QSize SqueezedComboBox::sizeHint() const { ensurePolished(); QFontMetrics fm = fontMetrics(); int maxW = count() ? 18 : 7 * fm.width(QChar('x')) + 18; int maxH = qMax( fm.lineSpacing(), 14 ) + 2; QStyleOptionComboBox options; options.initFrom(this); - return style()->sizeFromContents(QStyle::CT_ComboBox, &options, - QSize(maxW, maxH), this).expandedTo(QApplication::globalStrut()); + return style()->sizeFromContents(QStyle::CT_ComboBox, &options, QSize(maxW, maxH), this); } void SqueezedComboBox::insertSqueezedItem(const QString& newItem, int index, const QVariant& userData) { d->originalItems[index] = newItem; QComboBox::insertItem(index, squeezeText(newItem), userData); // if this is the first item, set the tooltip. if (index == 0) slotUpdateToolTip(0); } void SqueezedComboBox::insertSqueezedList(const QStringList& newItems, int index) { for(QStringList::const_iterator it = newItems.constBegin() ; it != newItems.constEnd() ; ++it) { insertSqueezedItem(*it, index); index++; } } void SqueezedComboBox::addSqueezedItem(const QString& newItem, const QVariant& userData) { insertSqueezedItem(newItem, count(), userData); } void SqueezedComboBox::setCurrent(const QString& itemText) { QString squeezedText = squeezeText(itemText); qint32 itemIndex = findText(squeezedText); if (itemIndex >= 0) setCurrentIndex(itemIndex); } void SqueezedComboBox::resizeEvent(QResizeEvent *) { d->timer->start(200); } void SqueezedComboBox::slotTimeOut() { for (QMap::iterator it = d->originalItems.begin() ; it != d->originalItems.end(); ++it) { setItemText( it.key(), squeezeText( it.value() ) ); } } QString SqueezedComboBox::squeezeText(const QString& original) const { // not the complete widgetSize is usable. Need to compensate for that. int widgetSize = width()-30; QFontMetrics fm( fontMetrics() ); // If we can fit the full text, return that. if (fm.width(original) < widgetSize) return(original); // We need to squeeze. QString sqItem = original; // prevent empty return value; widgetSize = widgetSize-fm.width("..."); for (int i = 0 ; i != original.length(); ++i) { if ((int)fm.width(original.right(i)) > widgetSize) { sqItem = QString(original.left(i) + "..."); break; } } return sqItem; } void SqueezedComboBox::slotUpdateToolTip(int index) { setToolTip(d->originalItems[index]); } QString SqueezedComboBox::currentUnsqueezedText() const { int curItem = currentIndex(); return d->originalItems[curItem]; } QString SqueezedComboBox::item(int index) const { return d->originalItems[index]; } } // namespace KDcrawIface diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/squeezedcombobox.h b/plugins/impex/raw/3rdparty/libkdcraw/src/squeezedcombobox.h index 7a482e52c2..906b32b664 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/squeezedcombobox.h +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/squeezedcombobox.h @@ -1,165 +1,165 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2008-08-21 * @brief a combo box with a width not depending of text * content size * * @author Copyright (C) 2006-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2008 by Andi Clemens * andi dot clemens at googlemail dot com * @author Copyright (C) 2005 by Tom Albers * tomalbers at kde dot nl * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef SQUEEZEDCOMBOBOX_H #define SQUEEZEDCOMBOBOX_H // Qt includes #include // Local includes namespace KDcrawIface { /** @class SqueezedComboBox * * This widget is a QComboBox, but then a little bit * different. It only shows the right part of the items * depending on de size of the widget. When it is not * possible to show the complete item, it will be shortened * and "..." will be prepended. */ class SqueezedComboBox : public QComboBox { Q_OBJECT public: /** * Constructor * @param parent parent widget * @param name name to give to the widget */ explicit SqueezedComboBox(QWidget* const parent = 0, const char* name = 0 ); /** * destructor */ ~SqueezedComboBox() override; /** * * Returns true if the combobox contains the original (not-squeezed) * version of text. * @param text the original (not-squeezed) text to check for */ bool contains(const QString& text) const; /** * This inserts a item to the list. See QComboBox::insertItem() * for details. Please do not use QComboBox::insertItem() to this * widget, as that will fail. * @param newItem the original (long version) of the item which needs * to be added to the combobox * @param index the position in the widget. * @param userData custom meta-data assigned to new item. */ void insertSqueezedItem(const QString& newItem, int index, const QVariant& userData=QVariant()); /** * This inserts items to the list. See QComboBox::insertItems() * for details. Please do not use QComboBox:: insertItems() to this * widget, as that will fail. * @param newItems the originals (long version) of the items which needs * to be added to the combobox * @param index the position in the widget. */ void insertSqueezedList(const QStringList& newItems, int index); /** * Append an item. * @param newItem the original (long version) of the item which needs * to be added to the combobox * @param userData custom meta-data assigned to new item. */ void addSqueezedItem(const QString& newItem, const QVariant& userData=QVariant()); /** * Set the current item to the one matching the given text. * * @param itemText the original (long version) of the item text */ void setCurrent(const QString& itemText); /** * This method returns the full text (not squeezed) of the currently * highlighted item. * @return full text of the highlighted item */ QString currentUnsqueezedText() const; /** * This method returns the full text (not squeezed) for the index. * @param index the position in the widget. * @return full text of the item */ QString item(int index) const; /** * Sets the sizeHint() of this widget. */ QSize sizeHint() const override; private Q_SLOTS: void slotTimeOut(); void slotUpdateToolTip(int index); private: void resizeEvent(QResizeEvent*) override; QString squeezeText(const QString& original) const; // Prevent these from being used. QString currentText() const; void setCurrentText(const QString& itemText); void insertItem(const QString& text); void insertItem(qint32 index, const QString& text); void insertItem(int index, const QIcon& icon, const QString& text, const QVariant& userData=QVariant()); void insertItems(int index, const QStringList& list); void addItem(const QString& text); void addItem(const QIcon& icon, const QString& text, const QVariant& userData=QVariant()); void addItems(const QStringList& texts); QString itemText(int index) const; private: class Private; Private* const d; }; } // namespace KDcrawIface #endif // SQUEEZEDCOMBOBOX_H diff --git a/plugins/impex/raw/3rdparty/libkdcraw/tests/libinfo.cpp b/plugins/impex/raw/3rdparty/libkdcraw/tests/libinfo.cpp index 84c194143a..65d4267c19 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/tests/libinfo.cpp +++ b/plugins/impex/raw/3rdparty/libkdcraw/tests/libinfo.cpp @@ -1,49 +1,49 @@ /** =========================================================== * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2013-09-07 * @brief a command line tool to show libkdcraw info * * @author Copyright (C) 2013 by Gilles Caulier * caulier dot gilles at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, 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. * * ============================================================ */ // Qt includes #include #include // Local includes #include using namespace KDcrawIface; int main(int /*argc*/, char** /*argv*/) { qDebug() << "Libkdcraw version : " << KDcraw::version(), qDebug() << "Libraw version : " << KDcraw::librawVersion(); qDebug() << "Use OpenMP : " << KDcraw::librawUseGomp(); qDebug() << "Use RawSpeed : " << KDcraw::librawUseRawSpeed(); qDebug() << "Use GPL2 Pack : " << KDcraw::librawUseGPL2DemosaicPack(); qDebug() << "Use GPL3 Pack : " << KDcraw::librawUseGPL3DemosaicPack(); qDebug() << "Raw files list : " << KDcraw::rawFilesList(); qDebug() << "Raw files version : " << KDcraw::rawFilesVersion(); qDebug() << "Supported camera : " << KDcraw::supportedCamera(); return 0; } diff --git a/plugins/impex/raw/3rdparty/libkdcraw/tests/multithreading/actionthread.cpp b/plugins/impex/raw/3rdparty/libkdcraw/tests/multithreading/actionthread.cpp index 48fd250504..6a6e1327f9 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/tests/multithreading/actionthread.cpp +++ b/plugins/impex/raw/3rdparty/libkdcraw/tests/multithreading/actionthread.cpp @@ -1,171 +1,171 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date : 2014-10-17 * @brief : a class to manage Raw to Png conversion using threads * * @author Copyright (C) 2011-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2014 by Veaceslav Munteanu * veaceslav dot munteanu90 at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "actionthread.h" // Qt includes #include #include #include // Local includes #include "kdcraw.h" #include "ractionjob.h" class Task : public RActionJob { public: Task() : RActionJob() { } RawDecodingSettings settings; QString errString; QUrl fileUrl; protected: void run() { emit signalStarted(); QImage image; if (m_cancel) return; emit signalProgress(20); KDcraw rawProcessor; if (m_cancel) return; emit signalProgress(30); QFileInfo input(fileUrl.toLocalFile()); QString fullFilePath(input.baseName() + QString::fromLatin1(".full.png")); QFileInfo fullOutput(fullFilePath); if (m_cancel) return; emit signalProgress(40); if (!rawProcessor.loadFullImage(image, fileUrl.toLocalFile(), settings)) { errString = QString::fromLatin1("raw2png: Loading full RAW image failed. Aborted..."); return; } if (m_cancel) return; emit signalProgress(60); qDebug() << "raw2png: Saving full RAW image to " << fullOutput.fileName() << " size (" << image.width() << "x" << image.height() << ")"; if (m_cancel) return; emit signalProgress(80); image.save(fullFilePath, "PNG"); emit signalDone(); } }; // ---------------------------------------------------------------------------------------------------- ActionThread::ActionThread(QObject* const parent) : RActionThreadBase(parent) { } ActionThread::~ActionThread() { } void ActionThread::convertRAWtoPNG(const QList& list, const RawDecodingSettings& settings, int priority) { RJobCollection collection; Q_FOREACH (const QUrl& url, list) { Task* const job = new Task(); job->fileUrl = url; job->settings = settings; connect(job, SIGNAL(signalStarted()), this, SLOT(slotJobStarted())); connect(job, SIGNAL(signalProgress(int)), this, SLOT(slotJobProgress(int))); connect(job, SIGNAL(signalDone()), this, SLOT(slotJobDone())); collection.insert(job, priority); qDebug() << "Appending file to process " << url; } appendJobs(collection); } void ActionThread::slotJobDone() { Task* const task = dynamic_cast(sender()); if (!task) return; if (task->errString.isEmpty()) { emit finished(task->fileUrl); } else { emit failed(task->fileUrl, task->errString); } } void ActionThread::slotJobProgress(int p) { Task* const task = dynamic_cast(sender()); if (!task) return; emit progress(task->fileUrl, p); } void ActionThread::slotJobStarted() { Task* const task = dynamic_cast(sender()); if (!task) return; emit starting(task->fileUrl); -} \ No newline at end of file +} diff --git a/plugins/impex/raw/3rdparty/libkdcraw/tests/multithreading/actionthread.h b/plugins/impex/raw/3rdparty/libkdcraw/tests/multithreading/actionthread.h index 526815c2e9..f0a7de9654 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/tests/multithreading/actionthread.h +++ b/plugins/impex/raw/3rdparty/libkdcraw/tests/multithreading/actionthread.h @@ -1,66 +1,66 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date : 2014-10-17 * @brief : a class to manage Raw to Png conversion using threads * * @author Copyright (C) 2011-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2014 by Veaceslav Munteanu * veaceslav dot munteanu90 at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, 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. * * ============================================================ */ #ifndef ACTIONTHREAD_H #define ACTIONTHREAD_H // Qt includes #include // Libkdcraw includes #include "ractionthreadbase.h" #include "rawdecodingsettings.h" using namespace KDcrawIface; class ActionThread : public RActionThreadBase { Q_OBJECT public: ActionThread(QObject* const parent); ~ActionThread(); void convertRAWtoPNG(const QList& list, const RawDecodingSettings& settings, int priority=0); Q_SIGNALS: void starting(const QUrl& url); void finished(const QUrl& url); void failed(const QUrl& url, const QString& err); void progress(const QUrl& url, int percent); private Q_SLOTS: void slotJobDone(); void slotJobProgress(int); void slotJobStarted(); }; #endif /* ACTIONTHREAD_H */ diff --git a/plugins/impex/raw/3rdparty/libkdcraw/tests/multithreading/main.cpp b/plugins/impex/raw/3rdparty/libkdcraw/tests/multithreading/main.cpp index 38d777a0e1..2b44cef00f 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/tests/multithreading/main.cpp +++ b/plugins/impex/raw/3rdparty/libkdcraw/tests/multithreading/main.cpp @@ -1,78 +1,78 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date : 2011-12-28 * @brief : test for implementation of threadWeaver api * * @author Copyright (C) 2011-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2014 by Veaceslav Munteanu * veaceslav dot munteanu90 at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, 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. * * ============================================================ */ // Qt includes #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "rawfiles.h" #include "processordlg.h" int main(int argc, char* argv[]) { QApplication app(argc, argv); QList list; if (argc <= 1) { QString filter = i18n("Raw Files") + QString::fromLatin1(" (%1)").arg(QString::fromLatin1(raw_file_extentions)); qDebug() << filter; QStringList files = QFileDialog::getOpenFileNames(0, i18n("Select RAW files to process"), QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).first(), filter); Q_FOREACH (QString f, files) list.append(QUrl::fromLocalFile(f)); } else { for (int i = 1 ; i < argc ; i++) list.append(QUrl::fromLocalFile(QString::fromLocal8Bit(argv[i]))); } if (!list.isEmpty()) { ProcessorDlg* const dlg = new ProcessorDlg(list); dlg->show(); app.exec(); } return 0; } diff --git a/plugins/impex/raw/3rdparty/libkdcraw/tests/multithreading/processordlg.cpp b/plugins/impex/raw/3rdparty/libkdcraw/tests/multithreading/processordlg.cpp index dcbb7fbb79..57fa8471e9 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/tests/multithreading/processordlg.cpp +++ b/plugins/impex/raw/3rdparty/libkdcraw/tests/multithreading/processordlg.cpp @@ -1,280 +1,280 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date : 2014-10-17 * @brief : test for implementation of threadWeaver api * * @author Copyright (C) 2011-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2014 by Veaceslav Munteanu * veaceslav dot munteanu90 at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "processordlg.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "actionthread.h" #include "dcrawsettingswidget.h" #include "rnuminput.h" class ProcessorDlg::Private { public: Private() { count = 0; page = 0; items = 0; buttons = 0; progressView = 0; usedCore = 0; thread = 0; settings = 0; } int count; QWidget* page; QLabel* items; QDialogButtonBox* buttons; QScrollArea* progressView; QList list; DcrawSettingsWidget* settings; RIntNumInput* usedCore; ActionThread* thread; }; ProcessorDlg::ProcessorDlg(const QList& list) : QDialog(0), d(new Private) { setModal(false); setWindowTitle(i18n("Convert RAW files To PNG")); d->buttons = new QDialogButtonBox(QDialogButtonBox::Apply | QDialogButtonBox::Close, this); d->thread = new ActionThread(this); d->list = list; d->count = d->list.count(); qDebug() << d->list; d->page = new QWidget(this); QVBoxLayout* const vbx = new QVBoxLayout(this); vbx->addWidget(d->page); vbx->addWidget(d->buttons); setLayout(vbx); int cpu = d->thread->maximumNumberOfThreads(); QGridLayout* const grid = new QGridLayout(d->page); QLabel* const pid = new QLabel(i18n("PID: %1", QCoreApplication::applicationPid()), this); QLabel* const core = new QLabel(i18n("CPU Cores: %1", cpu), this); QWidget* const hbox = new QWidget(this); d->items = new QLabel(this); QHBoxLayout* const hlay = new QHBoxLayout(hbox); QLabel* const coresLabel = new QLabel(i18n("Cores to use: "), this); d->usedCore = new RIntNumInput(this); d->usedCore->setRange(1, cpu, 1); d->usedCore->setDefaultValue(cpu); hlay->addWidget(coresLabel); hlay->addWidget(d->usedCore); hlay->setContentsMargins(QMargins()); d->progressView = new QScrollArea(this); QWidget* const progressbox = new QWidget(d->progressView->viewport()); QVBoxLayout* const progressLay = new QVBoxLayout(progressbox); d->progressView->setWidget(progressbox); d->progressView->setWidgetResizable(true); d->settings = new DcrawSettingsWidget(this); grid->addWidget(pid, 0, 0, 1, 1); grid->addWidget(core, 1, 0, 1, 1); grid->addWidget(hbox, 2, 0, 1, 1); grid->addWidget(d->items, 3, 0, 1, 1); grid->addWidget(d->progressView, 4, 0, 1, 1); grid->addWidget(d->settings, 0, 1, 5, 1); Q_FOREACH (const QUrl& url, d->list) { QProgressBar* const bar = new QProgressBar(progressbox); QString file = url.toLocalFile(); QFileInfo fi(file); bar->setMaximum(100); bar->setMinimum(0); bar->setValue(100); bar->setObjectName(file); bar->setFormat(fi.fileName()); progressLay->addWidget(bar); } progressLay->addStretch(); QPushButton* const applyBtn = d->buttons->button(QDialogButtonBox::Apply); QPushButton* const cancelBtn = d->buttons->button(QDialogButtonBox::Close); connect(applyBtn, SIGNAL(clicked()), this, SLOT(slotStart())); connect(cancelBtn, SIGNAL(clicked()), this, SLOT(slotStop())); connect(d->thread, SIGNAL(starting(QUrl)), this, SLOT(slotStarting(QUrl))); connect(d->thread, SIGNAL(finished(QUrl)), this, SLOT(slotFinished(QUrl))); connect(d->thread, SIGNAL(failed(QUrl,QString)), this, SLOT(slotFailed(QUrl,QString))); connect(d->thread, SIGNAL(progress(QUrl,int)), this, SLOT(slotProgress(QUrl,int))); updateCount(); resize(500, 400); } ProcessorDlg::~ProcessorDlg() { delete d; } void ProcessorDlg::updateCount() { d->items->setText(i18n("Files to process : %1", d->count)); } void ProcessorDlg::slotStart() { if (d->list.isEmpty()) return; d->buttons->button(QDialogButtonBox::Apply)->setDisabled(true); d->usedCore->setDisabled(true); d->settings->setDisabled(true); d->thread->setMaximumNumberOfThreads(d->usedCore->value()); d->thread->convertRAWtoPNG(d->list, d->settings->settings()); d->thread->start(); } void ProcessorDlg::slotStop() { d->thread->cancel(); reject(); } QProgressBar* ProcessorDlg::findProgressBar(const QUrl& url) const { QList bars = findChildren(); Q_FOREACH (QProgressBar* const b, bars) { if (b->objectName() == url.toLocalFile()) { return b; } } qWarning() << "Cannot found relevant progress bar for " << url.toLocalFile(); return 0; } void ProcessorDlg::slotStarting(const QUrl& url) { qDebug() << "Start to process item " << url.toLocalFile(); QProgressBar* const b = findProgressBar(url); if (b) { d->progressView->ensureWidgetVisible(b); b->setMinimum(0); b->setMaximum(100); b->setValue(0); } } void ProcessorDlg::slotProgress(const QUrl& url,int p) { qDebug() << "Processing item " << url.toLocalFile() << " : " << p << " %";; QProgressBar* const b = findProgressBar(url); if (b) { b->setMinimum(0); b->setMaximum(100); b->setValue(p); } } void ProcessorDlg::slotFinished(const QUrl& url) { qDebug() << "Completed item " << url.toLocalFile(); QProgressBar* const b = findProgressBar(url); if (b) { b->setMinimum(0); b->setMaximum(100); b->setValue(100); b->setFormat(i18n("Done")); d->count--; updateCount(); } } void ProcessorDlg::slotFailed(const QUrl& url, const QString& err) { qDebug() << "Failed to complete item " << url.toLocalFile(); QProgressBar* const b = findProgressBar(url); if (b) { b->setMinimum(0); b->setMaximum(100); b->setValue(100); b->setFormat(err); d->count--; updateCount(); } } diff --git a/plugins/impex/raw/3rdparty/libkdcraw/tests/multithreading/processordlg.h b/plugins/impex/raw/3rdparty/libkdcraw/tests/multithreading/processordlg.h index eca8578134..ae37ef5049 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/tests/multithreading/processordlg.h +++ b/plugins/impex/raw/3rdparty/libkdcraw/tests/multithreading/processordlg.h @@ -1,67 +1,67 @@ /** =========================================================== * @file * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date : 2014-10-17 * @brief : test for implementation of threadWeaver api * * @author Copyright (C) 2011-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2014 by Veaceslav Munteanu * veaceslav dot munteanu90 at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, 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. * * ============================================================ */ #ifndef PROCESSOR_DLG_H #define PROCESSOR_DLG_H // KDE includes #include #include #include class QProgressBar; class ProcessorDlg : public QDialog { Q_OBJECT public: ProcessorDlg(const QList &list); ~ProcessorDlg(); private : QProgressBar* findProgressBar(const QUrl& url) const; void updateCount(); private Q_SLOTS: void slotStart(); void slotStop(); void slotStarting(const QUrl&); void slotProgress(const QUrl&, int); void slotFinished(const QUrl&); void slotFailed(const QUrl&, const QString&); private: class Private; Private* const d; }; #endif // PROCESSOR_DLG_H diff --git a/plugins/impex/raw/3rdparty/libkdcraw/tests/raw2png.cpp b/plugins/impex/raw/3rdparty/libkdcraw/tests/raw2png.cpp index a182950681..827f43d35a 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/tests/raw2png.cpp +++ b/plugins/impex/raw/3rdparty/libkdcraw/tests/raw2png.cpp @@ -1,138 +1,138 @@ /** =========================================================== * * This file is a part of digiKam project - * http://www.digikam.org + * https://www.digikam.org * * @date 2008-15-09 * @brief a command line tool to convert RAW file to PNG * * @author Copyright (C) 2008-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, 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. * * ============================================================ */ // Qt includes #include #include #include #include // Local includes #include #include using namespace KDcrawIface; int main(int argc, char** argv) { if(argc != 2) { qDebug() << "raw2png - RAW Camera Image to PNG Converter"; qDebug() << "Usage: "; return -1; } QString filePath = QString::fromLatin1(argv[1]); QFileInfo input(filePath); QString previewFilePath(input.baseName() + QString::QString::fromLatin1(".preview.png")); QFileInfo previewOutput(previewFilePath); QString halfFilePath(input.baseName() + QString::fromLatin1(".half.png")); QFileInfo halfOutput(halfFilePath); QString fullFilePath(input.baseName() + QString::fromLatin1(".full.png")); QFileInfo fullOutput(fullFilePath); QImage image; DcrawInfoContainer identify; // ----------------------------------------------------------- qDebug() << "raw2png: Identify RAW image from " << input.fileName(); KDcraw rawProcessor; if (!rawProcessor.rawFileIdentify(identify, filePath)) { qDebug() << "raw2png: Idendify RAW image failed. Aborted..."; return -1; } int width = identify.imageSize.width(); int height = identify.imageSize.height(); qDebug() << "raw2png: Raw image info:"; qDebug() << "--- Date: " << identify.dateTime.toString(Qt::ISODate); qDebug() << "--- Make: " << identify.make; qDebug() << "--- Model: " << identify.model; qDebug() << "--- Size: " << width << "x" << height; qDebug() << "--- Filter: " << identify.filterPattern; qDebug() << "--- Colors: " << identify.rawColors; // ----------------------------------------------------------- qDebug() << "raw2png: Loading RAW image preview"; if (!rawProcessor.loadRawPreview(image, filePath)) { qDebug() << "raw2png: Loading RAW image preview failed. Aborted..."; return -1; } qDebug() << "raw2png: Saving preview image to " << previewOutput.fileName() << " size (" << image.width() << "x" << image.height() << ")"; image.save(previewFilePath, "PNG"); // ----------------------------------------------------------- qDebug() << "raw2png: Loading half RAW image"; image = QImage(); if (!rawProcessor.loadHalfPreview(image, filePath)) { qDebug() << "raw2png: Loading half RAW image failed. Aborted..."; return -1; } qDebug() << "raw2png: Saving half image to " << halfOutput.fileName() << " size (" << image.width() << "x" << image.height() << ")"; image.save(halfFilePath, "PNG"); // ----------------------------------------------------------- qDebug() << "raw2png: Loading full RAW image"; image = QImage(); RawDecodingSettings settings; settings.halfSizeColorImage = false; settings.sixteenBitsImage = false; settings.RGBInterpolate4Colors = false; settings.RAWQuality = RawDecodingSettings::BILINEAR; if (!rawProcessor.loadFullImage(image, filePath, settings)) { qDebug() << "raw2png: Loading full RAW image failed. Aborted..."; return -1; } qDebug() << "raw2png: Saving full RAW image to " << fullOutput.fileName() << " size (" << image.width() << "x" << image.height() << ")"; image.save(fullFilePath, "PNG"); return 0; } diff --git a/plugins/impex/xcf/3rdparty/xcftools/utils.c b/plugins/impex/xcf/3rdparty/xcftools/utils.c index aa8ba263f9..a905b5bbb1 100644 --- a/plugins/impex/xcf/3rdparty/xcftools/utils.c +++ b/plugins/impex/xcf/3rdparty/xcftools/utils.c @@ -1,178 +1,178 @@ /* Generic support functions for Xcftools * * This file was written by Henning Makholm * It is hereby in the public domain. * * In jurisdictions that do not recognise grants of copyright to the * public domain: I, the author and (presumably, in those jurisdictions) * copyright holder, hereby permit anyone to distribute and use this code, * in source code or binary form, with or without modifications. This * permission is world-wide and irrevocable. * * Of course, I will not be liable for any errors or shortcomings in the * code, since I give it away without asking any compenstations. * * If you use or distribute this code, I would appreciate receiving * credit for writing it, in whichever way you find proper and customary. */ #include "xcftools.h" #include #include #include #include const char *progname = "$0" ; int verboseFlag = 0 ; void vFatalGeneric(int status,const char *format, va_list args) { (void) status; /* mark as unused */ if( format ) { if( *format == '!' ) { vfprintf(stderr,format+1,args); fprintf(stderr,": %s\n",strerror(errno)); } else { vfprintf(stderr,format,args); fputc('\n',stderr); } } /* don't exit here - Krita can't handle errors otherwise */ /* exit(status); */ } void FatalGeneric(int status,const char* format,...) { va_list v; va_start(v,format); if( format ) fprintf(stderr,"%s: ",progname); vFatalGeneric(status,format,v); va_end(v); } void FatalUnexpected(const char* format,...) { va_list v; va_start(v, format); fprintf(stderr,"%s: ",progname); vFatalGeneric(127, format, v); va_end(v); } void FatalBadXCF(const char* format,...) { va_list v; va_start(v,format); fprintf(stderr,"%s: %s:\n ",progname,_("Corrupted or malformed XCF file")); vFatalGeneric(125,format,v) ; va_end(v); } int xcfCheckspace(uint32_t addr,int spaceafter,const char *format,...) { if( xcf_length < spaceafter || addr > xcf_length - spaceafter ) { va_list v; va_start(v,format); fprintf(stderr,"%s: %s\n ",progname,_("Corrupted or truncated XCF file")); fprintf(stderr,"(0x%" PRIXPTR " bytes): ",(uintptr_t)xcf_length); vFatalGeneric(125,format,v) ; va_end(v); return XCF_ERROR; } return XCF_OK; } void FatalUnsupportedXCF(const char* format,...) { va_list v; va_start(v,format); fprintf(stderr,"%s: %s\n ",progname, _("The image contains features not understood by this program:")); vFatalGeneric(123,format,v) ; va_end(v); } void gpl_blurb(void) { fprintf(stderr,PACKAGE_STRING "\n"); fprintf(stderr, _("Type \"%s -h\" to get an option summary.\n"),progname); /* don't exit here - Krita will close otherwise */ /* exit(1) ; */ } /* ******************************************************* */ void * xcfmalloc(size_t size) { void *ptr = malloc(size); if( !ptr ) { FatalUnexpected(_("Out of memory")); return XCF_PTR_EMPTY; } return ptr ; } void xcffree(void *block) { if( xcf_file && (uint8_t*)block >= xcf_file && (uint8_t*)block < xcf_file + xcf_length ) ; else free(block); } /* ******************************************************* */ FILE * openout(const char *name) { FILE *newfile ; if( strcmp(name,"-") == 0 ) return stdout ; newfile = fopen(name,"wb") ; if( newfile == NULL ) { FatalUnexpected(_("!Cannot create file %s"),name); return XCF_PTR_EMPTY; } return newfile ; } int closeout(FILE *f,const char *name) { if( f == NULL ) return XCF_OK; if( fflush(f) == 0 ) { errno = 0 ; if( !ferror(f) ) { if( fclose(f) == 0 ) return XCF_OK; } else if( errno == 0 ) { /* Attempt to coax a valid errno out of the standard library, * following an idea by Bruno Haible - * http://lists.gnu.org/archive/html/bug-gnulib/2003-09/msg00157.html + * https://lists.gnu.org/archive/html/bug-gnulib/2003-09/msg00157.html */ if( fputc('\0', f) != EOF && fflush(f) == 0 ) errno = EIO ; /* Argh, everything succedes. Just call it an I/O error */ } } FatalUnexpected(_("!Error writing file %s"),name); return XCF_ERROR; } diff --git a/plugins/paintops/hatching/kis_hatching_paintop_settings_widget.cpp b/plugins/paintops/hatching/kis_hatching_paintop_settings_widget.cpp index 856ec17169..a1b67a70af 100644 --- a/plugins/paintops/hatching/kis_hatching_paintop_settings_widget.cpp +++ b/plugins/paintops/hatching/kis_hatching_paintop_settings_widget.cpp @@ -1,138 +1,138 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2010 José Luis Vergara * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_hatching_paintop_settings_widget.h" #include "kis_hatching_options.h" #include "kis_hatching_preferences.h" #include "kis_hatching_paintop_settings.h" #include "kis_hatching_pressure_angle_option.h" #include "kis_hatching_pressure_crosshatching_option.h" #include "kis_hatching_pressure_separation_option.h" #include "kis_hatching_pressure_thickness_option.h" #include #include #include #include #include #include #include #include "kis_texture_option.h" #include #include "kis_pressure_texture_strength_option.h" #include #include KisHatchingPaintOpSettingsWidget:: KisHatchingPaintOpSettingsWidget(QWidget* parent) : KisBrushBasedPaintopOptionWidget(parent) { setPrecisionEnabled(true); //-------Adding widgets to the screen------------ addPaintOpOption(new KisHatchingOptions(), i18n("Hatching options")); addPaintOpOption(new KisHatchingPreferences(), i18n("Hatching preferences")); addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode")); addPaintOpOption(new KisCurveOptionWidget(new KisHatchingPressureSeparationOption(), i18n("0.0"), i18n("1.0")), i18n("Separation")); addPaintOpOption(new KisCurveOptionWidget(new KisHatchingPressureThicknessOption(), i18n("0.0"), i18n("1.0")), i18n("Thickness")); addPaintOpOption(new KisCurveOptionWidget(new KisHatchingPressureAngleOption(), i18n("0.0"), i18n("1.0")), i18n("Angle")); addPaintOpOption(new KisCurveOptionWidget(new KisHatchingPressureCrosshatchingOption(), i18n("0.0"), i18n("1.0")), i18n("Crosshatching")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size")); addPaintOpOption(new KisPressureMirrorOptionWidget(), i18n("Mirror")); addPaintOpOption(new KisPaintActionTypeOption(), i18n("Painting Mode")); addPaintOpOption(new KisTextureOption(), i18n("Pattern")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureTextureStrengthOption(), i18n("Weak"), i18n("Strong")), i18n("Strength")); //-----Useful to read first:------ /* Below you will encounter a reasonably correct solution to the problem of changing the default presets of the "BrushTip" popup configuration dialogue. In my (Pentalis) opinion, the best solution is code refactoring (simpler ways to change the defaults). On the meanwhile, copypasting this code won't give your class a charisma penalty. In kis_hatching_paintop_settings.cpp you will find a snippet of code to discover the structure of your XML config tree if you need to edit it at build time like here. */ //---------START ALTERING DEFAULT VALUES----------- //As the name implies, reconfigurationCourier is the KisPropertiesConfigurationSP //we'll use as an intermediary to edit the default settings KisPropertiesConfigurationSP reconfigurationCourier = configuration(); /*xMLAnalyzer is an empty document we'll use to analyze and edit the config string part by part I know the important string is "brush_definition" because I read the tree with the snippet in kis_hatching_paintop_settings.cpp */ QDomDocument xMLAnalyzer; xMLAnalyzer.setContent(reconfigurationCourier->getString("brush_definition")); /*More things I know by reading the XML tree. At this point you can just read it with: dbgKrita << xMLAnalyzer.toString() ; those QDomElements are the way to navigate the XML tree, read - http://doc.qt.nokia.com/latest/qdomdocument.html for more information */ + https://doc.qt.io/qt-5/qdomdocument.html for more information */ QDomElement firstTag = xMLAnalyzer.documentElement(); QDomElement firstTagsChild = firstTag.elementsByTagName("MaskGenerator").item(0).toElement(); // SET THE DEFAULT VALUES firstTag.attributeNode("spacing").setValue("0.4"); firstTagsChild.attributeNode("diameter").setValue("30"); //Write them into the intermediary config file reconfigurationCourier->setProperty("brush_definition", xMLAnalyzer.toString()); KisCubicCurve CurveSize; CurveSize.fromString("0,1;1,0.1;"); //dbgKrita << "\n\n\n" << CurveSize.toString() << "\n\n\n"; QVariant QVCurveSize = QVariant::fromValue(CurveSize); reconfigurationCourier->setProperty("CurveSize", QVCurveSize); setConfiguration(reconfigurationCourier); // Finished. /* Debugging block QMap rofl = QMap(reconfigurationCourier->getProperties()); QMap::const_iterator i; for (i = rofl.constBegin(); i != rofl.constEnd(); ++i) dbgKrita << i.key() << ":" << i.value(); */ } KisHatchingPaintOpSettingsWidget::~ KisHatchingPaintOpSettingsWidget() { } KisPropertiesConfigurationSP KisHatchingPaintOpSettingsWidget::configuration() const { KisHatchingPaintOpSettingsSP config = new KisHatchingPaintOpSettings(); config->setOptionsWidget(const_cast(this)); config->setProperty("paintop", "hatchingbrush"); // XXX: make this a const id string writeConfiguration(config); return config; } diff --git a/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.cpp b/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.cpp index b7dc342b13..b5e87819f8 100644 --- a/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.cpp +++ b/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.cpp @@ -1,336 +1,339 @@ /* * Copyright (c) 2010 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_brush_based_paintop_settings.h" #include #include #include "kis_brush_based_paintop_options_widget.h" #include #include "kis_brush_server.h" #include #include "kis_signals_blocker.h" #include "kis_brush_option.h" #include +#include struct BrushReader { BrushReader(const KisBrushBasedPaintOpSettings *parent) : m_parent(parent) { m_option.readOptionSetting(m_parent); } KisBrushSP brush() { return m_option.brush(); } const KisBrushBasedPaintOpSettings *m_parent; KisBrushOptionProperties m_option; }; struct BrushWriter { BrushWriter(KisBrushBasedPaintOpSettings *parent) : m_parent(parent) { m_option.readOptionSetting(m_parent); } ~BrushWriter() { m_option.writeOptionSetting(m_parent); } KisBrushSP brush() { return m_option.brush(); } KisBrushBasedPaintOpSettings *m_parent; KisBrushOptionProperties m_option; }; KisBrushBasedPaintOpSettings::KisBrushBasedPaintOpSettings() : KisOutlineGenerationPolicy(KisCurrentOutlineFetcher::SIZE_OPTION | KisCurrentOutlineFetcher::ROTATION_OPTION | - KisCurrentOutlineFetcher::MIRROR_OPTION) + KisCurrentOutlineFetcher::MIRROR_OPTION | + KisCurrentOutlineFetcher::SHARPNESS_OPTION) + { } bool KisBrushBasedPaintOpSettings::paintIncremental() { if (hasProperty("PaintOpAction")) { return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP; } return true; } KisPaintOpSettingsSP KisBrushBasedPaintOpSettings::clone() const { KisPaintOpSettingsSP _settings = KisOutlineGenerationPolicy::clone(); KisBrushBasedPaintOpSettingsSP settings = dynamic_cast(_settings.data()); settings->m_savedBrush = 0; return settings; } KisBrushSP KisBrushBasedPaintOpSettings::brush() const { KisBrushSP brush = m_savedBrush; if (!brush) { BrushReader w(this); brush = w.brush(); m_savedBrush = brush; } return brush; } QPainterPath KisBrushBasedPaintOpSettings::brushOutlineImpl(const KisPaintInformation &info, const OutlineMode &mode, qreal additionalScale) { QPainterPath path; if (mode.isVisible) { KisBrushSP brush = this->brush(); if (!brush) return path; qreal finalScale = brush->scale() * additionalScale; QPainterPath realOutline = brush->outline(); if (mode.forceCircle) { QPainterPath ellipse; ellipse.addEllipse(realOutline.boundingRect()); realOutline = ellipse; } path = outlineFetcher()->fetchOutline(info, this, realOutline, mode, finalScale, brush->angle()); if (mode.showTiltDecoration) { const QPainterPath tiltLine = makeTiltIndicator(info, realOutline.boundingRect().center(), realOutline.boundingRect().width() * 0.5, 3.0); path.addPath(outlineFetcher()->fetchOutline(info, this, tiltLine, mode, finalScale, 0.0, true, realOutline.boundingRect().center().x(), realOutline.boundingRect().center().y())); } } return path; } QPainterPath KisBrushBasedPaintOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode) { return brushOutlineImpl(info, mode, 1.0); } bool KisBrushBasedPaintOpSettings::isValid() const { QStringList files = getStringList(KisPaintOpUtils::RequiredBrushFilesListTag); files << getString(KisPaintOpUtils::RequiredBrushFileTag); Q_FOREACH (const QString &file, files) { if (!file.isEmpty()) { KisBrushSP brush = KisBrushServer::instance()->brushServer()->resourceByFilename(file); if (!brush) { return false; } } } return true; } bool KisBrushBasedPaintOpSettings::isLoadable() { return (KisBrushServer::instance()->brushServer()->resources().count() > 0); } void KisBrushBasedPaintOpSettings::setAngle(qreal value) { BrushWriter w(this); if (!w.brush()) return; w.brush()->setAngle(value); } qreal KisBrushBasedPaintOpSettings::angle() { return this->brush()->angle(); } void KisBrushBasedPaintOpSettings::setSpacing(qreal value) { BrushWriter w(this); if (!w.brush()) return; w.brush()->setSpacing(value); } qreal KisBrushBasedPaintOpSettings::spacing() { return this->brush()->spacing(); } void KisBrushBasedPaintOpSettings::setAutoSpacing(bool active, qreal coeff) { BrushWriter w(this); if (!w.brush()) return; w.brush()->setAutoSpacing(active, coeff); } bool KisBrushBasedPaintOpSettings::autoSpacingActive() { return this->brush()->autoSpacingActive(); } qreal KisBrushBasedPaintOpSettings::autoSpacingCoeff() { return this->brush()->autoSpacingCoeff(); } void KisBrushBasedPaintOpSettings::setPaintOpSize(qreal value) { BrushWriter w(this); if (!w.brush()) return; w.brush()->setUserEffectiveSize(value); } qreal KisBrushBasedPaintOpSettings::paintOpSize() const { return this->brush()->userEffectiveSize(); } #include #include "kis_paintop_preset.h" #include "kis_paintop_settings_update_proxy.h" QList KisBrushBasedPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props = listWeakToStrong(m_uniformProperties); if (props.isEmpty()) { { KisIntSliderBasedPaintOpPropertyCallback *prop = new KisIntSliderBasedPaintOpPropertyCallback( KisIntSliderBasedPaintOpPropertyCallback::Int, "angle", "Angle", settings, 0); prop->setRange(0, 360); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { KisBrushBasedPaintOpSettings *s = dynamic_cast(prop->settings().data()); const qreal angleResult = kisRadiansToDegrees(s->angle()); prop->setValue(angleResult); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { KisBrushBasedPaintOpSettings *s = dynamic_cast(prop->settings().data()); s->setAngle(kisDegreesToRadians(prop->value().toReal())); }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisUniformPaintOpPropertyCallback *prop = new KisUniformPaintOpPropertyCallback( KisUniformPaintOpPropertyCallback::Bool, "auto_spacing", "Auto Spacing", settings, 0); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { KisBrushBasedPaintOpSettings *s = dynamic_cast(prop->settings().data()); prop->setValue(s->autoSpacingActive()); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { KisBrushBasedPaintOpSettings *s = dynamic_cast(prop->settings().data()); s->setAutoSpacing(prop->value().toBool(), s->autoSpacingCoeff()); }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisDoubleSliderBasedPaintOpPropertyCallback *prop = new KisDoubleSliderBasedPaintOpPropertyCallback( KisDoubleSliderBasedPaintOpPropertyCallback::Double, "spacing", "Spacing", settings, 0); prop->setRange(0.01, 10); prop->setSingleStep(0.01); prop->setExponentRatio(3.0); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { KisBrushBasedPaintOpSettings *s = dynamic_cast(prop->settings().data()); if (s) { const qreal value = s->autoSpacingActive() ? s->autoSpacingCoeff() : s->spacing(); prop->setValue(value); } }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { KisBrushBasedPaintOpSettings *s = dynamic_cast(prop->settings().data()); if (s) { if (s->autoSpacingActive()) { s->setAutoSpacing(true, prop->value().toReal()); } else { s->setSpacing(prop->value().toReal()); } } }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } } return KisPaintOpSettings::uniformProperties(settings) + props; } void KisBrushBasedPaintOpSettings::onPropertyChanged() { m_savedBrush.clear(); KisOutlineGenerationPolicy::onPropertyChanged(); } diff --git a/plugins/paintops/libpaintop/kis_current_outline_fetcher.cpp b/plugins/paintops/libpaintop/kis_current_outline_fetcher.cpp index fee3928f66..0bdbe9dc43 100644 --- a/plugins/paintops/libpaintop/kis_current_outline_fetcher.cpp +++ b/plugins/paintops/libpaintop/kis_current_outline_fetcher.cpp @@ -1,162 +1,181 @@ /* * Copyright (c) 2013 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_current_outline_fetcher.h" #include "kis_pressure_size_option.h" #include "kis_pressure_rotation_option.h" #include "kis_pressure_mirror_option.h" +#include "kis_pressure_sharpness_option.h" #include #include #include "kis_paintop_settings.h" #include #define NOISY_UPDATE_SPEED 50 // Time in ms for outline updates to noisy brushes struct KisCurrentOutlineFetcher::Private { Private(Options optionsAvailable) : isDirty(true) { if (optionsAvailable & SIZE_OPTION) { sizeOption.reset(new KisPressureSizeOption()); } if (optionsAvailable & ROTATION_OPTION) { rotationOption.reset(new KisPressureRotationOption()); } if (optionsAvailable & MIRROR_OPTION) { mirrorOption.reset(new KisPressureMirrorOption()); } + + if (optionsAvailable & SHARPNESS_OPTION) { + sharpnessOption.reset(new KisPressureSharpnessOption()); + } } QScopedPointer sizeOption; QScopedPointer rotationOption; QScopedPointer mirrorOption; + QScopedPointer sharpnessOption; bool isDirty; QElapsedTimer lastUpdateTime; qreal lastRotationApplied; qreal lastSizeApplied; MirrorProperties lastMirrorApplied; }; KisCurrentOutlineFetcher::KisCurrentOutlineFetcher(Options optionsAvailable) : d(new Private(optionsAvailable)) { d->lastUpdateTime.start(); } KisCurrentOutlineFetcher::~KisCurrentOutlineFetcher() { } void KisCurrentOutlineFetcher::setDirty() { d->isDirty = true; } QPainterPath KisCurrentOutlineFetcher::fetchOutline(const KisPaintInformation &info, const KisPaintOpSettingsSP settings, const QPainterPath &originalOutline, const KisPaintOpSettings::OutlineMode &mode, qreal additionalScale, qreal additionalRotation, bool tilt, qreal tiltcenterx, qreal tiltcentery) const { if (d->isDirty) { if (d->sizeOption) { d->sizeOption->readOptionSetting(settings); d->sizeOption->resetAllSensors(); } if (d->rotationOption) { d->rotationOption->readOptionSetting(settings); d->rotationOption->resetAllSensors(); } if (d->mirrorOption) { d->mirrorOption->readOptionSetting(settings); d->mirrorOption->resetAllSensors(); } + if (d->sharpnessOption) { + d->sharpnessOption->readOptionSetting(settings); + d->sharpnessOption->resetAllSensors(); + } + d->isDirty = false; } qreal scale = additionalScale; qreal rotation = additionalRotation; bool needsUpdate = false; // Randomized rotation at full speed looks noisy, so slow it down if (d->lastUpdateTime.elapsed() > NOISY_UPDATE_SPEED) { needsUpdate = true; d->lastUpdateTime.restart(); } if (d->sizeOption && !tilt && !mode.forceFullSize) { if (!d->sizeOption->isRandom() || needsUpdate) { d->lastSizeApplied = d->sizeOption->apply(info); } scale *= d->lastSizeApplied; } if (d->rotationOption && !tilt) { if (!d->rotationOption->isRandom() || needsUpdate) { d->lastRotationApplied = d->rotationOption->apply(info); } rotation += d->lastRotationApplied; } else if (d->rotationOption && tilt) { rotation += info.canvasRotation() * M_PI / 180.0; } qreal xFlip = 1.0; qreal yFlip = 1.0; if (d->mirrorOption) { if (!d->mirrorOption->isRandom() || needsUpdate) { d->lastMirrorApplied = d->mirrorOption->apply(info); } if (d->lastMirrorApplied.coordinateSystemFlipped) { rotation = 2 * M_PI - rotation; } if (d->lastMirrorApplied.horizontalMirror) { xFlip = -1.0; } if (d->lastMirrorApplied.verticalMirror) { yFlip = -1.0; } } - QTransform rot; rot.rotateRadians(-rotation); QPointF hotSpot = originalOutline.boundingRect().center(); if (tilt) { hotSpot.setX(tiltcenterx); hotSpot.setY(tiltcentery); } + + QPointF pos = info.pos(); + if (d->sharpnessOption) { + qint32 x = 0, y = 0; + qreal subPixelX = 0.0, subPixelY = 0.0; + d->sharpnessOption->apply(info, pos - hotSpot, x, y, subPixelX, subPixelY); + pos = QPointF(x + subPixelX, y + subPixelY) + hotSpot; + } + QTransform T1 = QTransform::fromTranslate(-hotSpot.x(), -hotSpot.y()); - QTransform T2 = QTransform::fromTranslate(info.pos().x(), info.pos().y()); + QTransform T2 = QTransform::fromTranslate(pos.x(), pos.y()); QTransform S = QTransform::fromScale(xFlip * scale, yFlip * scale); return (T1 * rot * S * T2).map(originalOutline); } diff --git a/plugins/paintops/libpaintop/kis_current_outline_fetcher.h b/plugins/paintops/libpaintop/kis_current_outline_fetcher.h index 9877db6d46..412b65a328 100644 --- a/plugins/paintops/libpaintop/kis_current_outline_fetcher.h +++ b/plugins/paintops/libpaintop/kis_current_outline_fetcher.h @@ -1,69 +1,70 @@ /* * Copyright (c) 2013 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_CURRENT_OUTLINE_FETCHER_H #define __KIS_CURRENT_OUTLINE_FETCHER_H #include #include #include #include #include #include class KisPaintInformation; class PAINTOP_EXPORT KisCurrentOutlineFetcher { public: enum Option { NO_OPTION, SIZE_OPTION, ROTATION_OPTION, - MIRROR_OPTION + MIRROR_OPTION, + SHARPNESS_OPTION }; Q_DECLARE_FLAGS(Options, Option); public: KisCurrentOutlineFetcher(Options optionsAvailable); ~KisCurrentOutlineFetcher(); void setDirty(); QPainterPath fetchOutline(const KisPaintInformation &info, const KisPaintOpSettingsSP settings, const QPainterPath &originalOutline, const KisPaintOpSettings::OutlineMode &mode, qreal additionalScale = 1.0, qreal additionalRotation = 0.0, bool tilt = false, qreal tiltcenterx = 1.0, qreal tiltcentery = 1.0) const; private: Q_DISABLE_COPY(KisCurrentOutlineFetcher); struct Private; const QScopedPointer d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(KisCurrentOutlineFetcher::Options); #endif /* __KIS_CURRENT_OUTLINE_FETCHER_H */ diff --git a/plugins/paintops/sketch/kis_sketch_paintop.cpp b/plugins/paintops/sketch/kis_sketch_paintop.cpp index 1671edff80..fb20f0f335 100644 --- a/plugins/paintops/sketch/kis_sketch_paintop.cpp +++ b/plugins/paintops/sketch/kis_sketch_paintop.cpp @@ -1,325 +1,325 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2010 Ricardo Cabello * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_sketch_paintop.h" #include "kis_sketch_paintop_settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_lod_transform.h" #include /* -* Based on Harmony project http://github.com/mrdoob/harmony/ +* Based on Harmony project https://github.com/mrdoob/harmony/ */ // chrome : diff 0.2, sketchy : 0.3, fur: 0.5 // fur : distance / thresholdDistance // shaded: opacity per line :/ // ((1 - (d / 1000)) * 0.1 * BRUSH_PRESSURE), offset == 0 // chrome: color per line :/ //this.context.strokeStyle = "rgba(" + Math.floor(Math.random() * COLOR[0]) + ", " + Math.floor(Math.random() * COLOR[1]) + ", " + Math.floor(Math.random() * COLOR[2]) + ", " + 0.1 * BRUSH_PRESSURE + " )"; // long fur // from: count + offset * -random // to: i point - (offset * -random) + random * 2 // probability distance / thresholdDistnace // shaded: probability : paint always - 0.0 density KisSketchPaintOp::KisSketchPaintOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image) : KisPaintOp(painter) { Q_UNUSED(image); Q_UNUSED(node); m_airbrushOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); m_rotationOption.readOptionSetting(settings); m_rateOption.readOptionSetting(settings); m_sketchProperties.readOptionSetting(settings); m_brushOption.readOptionSetting(settings); m_densityOption.readOptionSetting(settings); m_lineWidthOption.readOptionSetting(settings); m_offsetScaleOption.readOptionSetting(settings); m_brush = m_brushOption.brush(); m_dabCache = new KisDabCache(m_brush); m_opacityOption.resetAllSensors(); m_sizeOption.resetAllSensors(); m_rotationOption.resetAllSensors(); m_rateOption.resetAllSensors(); m_painter = 0; m_count = 0; } KisSketchPaintOp::~KisSketchPaintOp() { delete m_painter; delete m_dabCache; } void KisSketchPaintOp::drawConnection(const QPointF& start, const QPointF& end, double lineWidth) { if (lineWidth == 1.0) { m_painter->drawThickLine(start, end, lineWidth, lineWidth); } else { m_painter->drawLine(start, end, lineWidth, true); } } void KisSketchPaintOp::updateBrushMask(const KisPaintInformation& info, qreal scale, qreal rotation) { QRect dstRect; m_maskDab = m_dabCache->fetchDab(m_dab->colorSpace(), painter()->paintColor(), info.pos(), KisDabShape(scale, 1.0, rotation), info, 1.0, &dstRect); m_brushBoundingBox = dstRect; m_hotSpot = QPointF(0.5 * m_brushBoundingBox.width(), 0.5 * m_brushBoundingBox.height()); } void KisSketchPaintOp::paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) { // Use superclass behavior for lines of zero length. Otherwise, airbrushing can happen faster // than it is supposed to. if (pi1.pos() == pi2.pos()) { KisPaintOp::paintLine(pi1, pi2, currentDistance); } else { doPaintLine(pi1, pi2); } } void KisSketchPaintOp::doPaintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2) { if (!m_brush || !painter()) return; if (!m_dab) { m_dab = source()->createCompositionSourceDevice(); m_painter = new KisPainter(m_dab); m_painter->setPaintColor(painter()->paintColor()); } else { m_dab->clear(); } QPointF prevMouse = pi1.pos(); QPointF mousePosition = pi2.pos(); m_points.append(mousePosition); const qreal lodAdditionalScale = KisLodTransform::lodToScale(painter()->device()); const qreal scale = lodAdditionalScale * m_sizeOption.apply(pi2); if ((scale * m_brush->width()) <= 0.01 || (scale * m_brush->height()) <= 0.01) return; const qreal currentLineWidth = qMax(0.9, lodAdditionalScale * m_lineWidthOption.apply(pi2, m_sketchProperties.lineWidth)); const qreal currentOffsetScale = m_offsetScaleOption.apply(pi2, m_sketchProperties.offset); const double rotation = m_rotationOption.apply(pi2); const double currentProbability = m_densityOption.apply(pi2, m_sketchProperties.probability); // shaded: does not draw this line, chrome does, fur does if (m_sketchProperties.makeConnection) { drawConnection(prevMouse, mousePosition, currentLineWidth); } qreal thresholdDistance = 0.0; // update the mask for simple mode only once // determine the radius if (m_count == 0 && m_sketchProperties.simpleMode) { updateBrushMask(pi2, 1.0, 0.0); //m_radius = qMax(m_maskDab->bounds().width(),m_maskDab->bounds().height()) * 0.5; m_radius = 0.5 * qMax(m_brush->width(), m_brush->height()); } if (!m_sketchProperties.simpleMode) { updateBrushMask(pi2, scale, rotation); m_radius = qMax(m_maskDab->bounds().width(), m_maskDab->bounds().height()) * 0.5; thresholdDistance = pow(m_radius, 2); } if (m_sketchProperties.simpleMode) { // update the radius according scale in simple mode thresholdDistance = pow(m_radius * scale, 2); } // determine density const qreal density = thresholdDistance * currentProbability; // probability behaviour qreal probability = 1.0 - currentProbability; QColor painterColor = painter()->paintColor().toQColor(); QColor randomColor; KoColor color(m_dab->colorSpace()); int w = m_maskDab->bounds().width(); quint8 opacityU8 = 0; quint8 * pixel; qreal distance; QPoint positionInMask; QPointF diff; int size = m_points.size(); // MAIN LOOP for (int i = 0; i < size; i++) { diff = m_points.at(i) - mousePosition; distance = diff.x() * diff.x() + diff.y() * diff.y(); // circle test bool makeConnection = false; if (m_sketchProperties.simpleMode) { if (distance < thresholdDistance) { makeConnection = true; } // mask test } else { if (m_brushBoundingBox.contains(m_points.at(i))) { positionInMask = (diff + m_hotSpot).toPoint(); uint pos = ((positionInMask.y() * w + positionInMask.x()) * m_maskDab->pixelSize()); if (pos < m_maskDab->allocatedPixels() * m_maskDab->pixelSize()) { pixel = m_maskDab->data() + pos; opacityU8 = m_maskDab->colorSpace()->opacityU8(pixel); if (opacityU8 != 0) { makeConnection = true; } } } } if (!makeConnection) { // check next point continue; } if (m_sketchProperties.distanceDensity) { probability = distance / density; } KisRandomSourceSP randomSource = pi2.randomSource(); // density check if (randomSource->generateNormalized() >= probability) { QPointF offsetPt = diff * currentOffsetScale; if (m_sketchProperties.randomRGB) { /** * Since the order of calculation of function * parameters is not defined by C++ standard, we * should generate values in an external code snippet * which has a definite order of execution. */ qreal r1 = randomSource->generateNormalized(); qreal r2 = randomSource->generateNormalized(); qreal r3 = randomSource->generateNormalized(); // some color transformation per line goes here randomColor.setRgbF(r1 * painterColor.redF(), r2 * painterColor.greenF(), r3 * painterColor.blueF()); color.fromQColor(randomColor); m_painter->setPaintColor(color); } // distance based opacity quint8 opacity = OPACITY_OPAQUE_U8; if (m_sketchProperties.distanceOpacity) { opacity *= qRound((1.0 - (distance / thresholdDistance))); } if (m_sketchProperties.randomOpacity) { opacity *= randomSource->generateNormalized(); } m_painter->setOpacity(opacity); if (m_sketchProperties.magnetify) { drawConnection(mousePosition + offsetPt, m_points.at(i) - offsetPt, currentLineWidth); } else { drawConnection(mousePosition + offsetPt, mousePosition - offsetPt, currentLineWidth); } } }// end of MAIN LOOP m_count++; QRect rc = m_dab->extent(); quint8 origOpacity = m_opacityOption.apply(painter(), pi2); painter()->bitBlt(rc.x(), rc.y(), m_dab, rc.x(), rc.y(), rc.width(), rc.height()); painter()->renderMirrorMask(rc, m_dab); painter()->setOpacity(origOpacity); } KisSpacingInformation KisSketchPaintOp::paintAt(const KisPaintInformation& info) { doPaintLine(info, info); return updateSpacingImpl(info); } KisSpacingInformation KisSketchPaintOp::updateSpacingImpl(const KisPaintInformation &info) const { return KisPaintOpPluginUtils::effectiveSpacing(0.0, 0.0, true, 0.0, false, 0.0, false, 0.0, KisLodTransform::lodToScale(painter()->device()), &m_airbrushOption, nullptr, info); } KisTimingInformation KisSketchPaintOp::updateTimingImpl(const KisPaintInformation &info) const { return KisPaintOpPluginUtils::effectiveTiming(&m_airbrushOption, &m_rateOption, info); } diff --git a/plugins/paintops/tangentnormal/kis_tangent_tilt_option.cpp b/plugins/paintops/tangentnormal/kis_tangent_tilt_option.cpp index 4e8f3ebbfb..7390fc598d 100644 --- a/plugins/paintops/tangentnormal/kis_tangent_tilt_option.cpp +++ b/plugins/paintops/tangentnormal/kis_tangent_tilt_option.cpp @@ -1,258 +1,258 @@ /* This file is part of the KDE project * * Copyright (C) 2015 Wolthera van Hövell tot Westerflier * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_tangent_tilt_option.h" #include #include #include #include "ui_wdgtangenttiltoption.h" #include "kis_global.h" class KisTangentTiltOptionWidget: public QWidget, public Ui::WdgTangentTiltOptions { public: KisTangentTiltOptionWidget(QWidget *parent = 0) : QWidget(parent) { setupUi(this); } }; KisTangentTiltOption::KisTangentTiltOption() : KisPaintOpOption(KisPaintOpOption::GENERAL, false) { m_checkable = false; m_options = new KisTangentTiltOptionWidget(); //Setup tangent tilt. m_options->comboRed->setCurrentIndex(0); m_options->comboGreen->setCurrentIndex(2); m_options->comboBlue->setCurrentIndex(4); m_options->sliderElevationSensitivity->setRange(0, 100, 0); m_options->sliderElevationSensitivity->setValue(100); m_options->sliderElevationSensitivity->setSuffix(i18n("%")); m_options->sliderMixValue->setRange(0, 100, 0); m_options->sliderMixValue->setValue(50); m_options->sliderMixValue->setSuffix(i18n("%")); connect(m_options->comboRed, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged())); connect(m_options->comboGreen, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged())); connect(m_options->comboBlue, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged())); connect(m_options->comboRed, SIGNAL(currentIndexChanged(int)), m_options->TangentTiltPreview, SLOT(setRedChannel(int))); connect(m_options->comboGreen, SIGNAL(currentIndexChanged(int)), m_options->TangentTiltPreview, SLOT(setGreenChannel(int))); connect(m_options->comboBlue, SIGNAL(currentIndexChanged(int)), m_options->TangentTiltPreview, SLOT(setBlueChannel(int))); connect(m_options->optionTilt, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_options->optionDirection, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_options->optionRotation, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_options->optionMix, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_options->sliderElevationSensitivity, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->sliderMixValue, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); m_options->sliderMixValue->setVisible(false); setConfigurationPage(m_options); } KisTangentTiltOption::~KisTangentTiltOption() { delete m_options; } //options int KisTangentTiltOption::redChannel() const { return m_options->comboRed->currentIndex(); } int KisTangentTiltOption::greenChannel() const { return m_options->comboGreen->currentIndex(); } int KisTangentTiltOption::blueChannel() const { return m_options->comboBlue->currentIndex(); } int KisTangentTiltOption::directionType() const { int type=0; if (m_options->optionTilt->isChecked()==true) { type=0; } else if (m_options->optionDirection->isChecked()==true) { type=1; } else if (m_options->optionRotation->isChecked()==true) { type=2; } else if (m_options->optionMix->isChecked()==true) { type=3; } else { warnKrita<<"There's something odd with the radio buttons. We'll use Tilt"; } return type; } double KisTangentTiltOption::elevationSensitivity() const { return m_options->sliderElevationSensitivity->value(); } double KisTangentTiltOption::mixValue() const { return m_options->sliderMixValue->value(); } void KisTangentTiltOption::swizzleAssign(qreal const horizontal, qreal const vertical, qreal const depth, qreal *component, int index, qreal maxvalue) { switch(index) { case 0: *component = horizontal; break; case 1: *component = maxvalue-horizontal; break; case 2: *component = vertical; break; case 3: *component = maxvalue-vertical; break; case 4: *component = depth; break; case 5: *component = maxvalue-depth; break; } } void KisTangentTiltOption::apply(const KisPaintInformation& info,qreal *r,qreal *g,qreal *b) { - //formula based on http://www.cerebralmeltdown.com/programming_projects/Altitude%20and%20Azimuth%20to%20Vector/index.html + //formula based on https://www.cerebralmeltdown.com/programming_projects/Altitude%20and%20Azimuth%20to%20Vector/index.html /* It doesn't make sense of have higher than 8bit color depth. * Instead we make sure in the paintAt function of kis_tangent_normal_paintop to pick an 8bit space of the current * color space if the space is an RGB space. If not, it'll pick sRGB 8bit. */ qreal halfvalue = 0.5; qreal maxvalue = 1.0; //have the azimuth and altitude in degrees. qreal direction = KisPaintInformation::tiltDirection(info, true)*360.0; qreal elevation= (info.tiltElevation(info, 60.0, 60.0, true)*90.0); if (directionType() == 0) { direction = KisPaintInformation::tiltDirection(info, true)*360.0; elevation= (info.tiltElevation(info, 60.0, 60.0, true)*90.0); } else if (directionType() == 1) { direction = (0.75 + info.drawingAngle() / (2.0 * M_PI))*360.0; elevation= 0;//turns out that tablets that don't support tilt just return 90 degrees for elevation. } else if (directionType() == 2) { direction = info.rotation(); elevation= (info.tiltElevation(info, 60.0, 60.0, true)*90.0);//artpens have tilt-recognition, so this should work. } else if (directionType() == 3) {//mix of tilt+direction qreal mixamount = mixValue()/100.0; direction = (KisPaintInformation::tiltDirection(info, true)*360.0*(1.0-mixamount))+((0.75 + info.drawingAngle() / (2.0 * M_PI))*360.0*(mixamount)); elevation= (info.tiltElevation(info, 60.0, 60.0, true)*90.0); } //subtract/add the rotation of the canvas. if (directionType() != 1) { direction = normalizeAngleDegrees(direction - info.canvasRotation()); } //limit the direction/elevation //qreal elevationMax = (elevationSensitivity()*90.0)/100.0; qreal elevationT = elevation*(elevationSensitivity()/100.0)+(90-(elevationSensitivity()*90.0)/100.0); elevation = static_cast(elevationT); //convert to radians. // Convert this to kis_global's radian function. direction = kisDegreesToRadians(direction); elevation = kisDegreesToRadians(elevation); //make variables for axes for easy switching later on. qreal horizontal, vertical, depth; //spherical coordinates always center themselves around the origin, leading to values. We need to work around those... horizontal = cos(elevation)*sin(direction); if (horizontal>0.0) { horizontal= halfvalue+(fabs(horizontal)*halfvalue); } else { horizontal= halfvalue-(fabs(horizontal)*halfvalue); } vertical = cos(elevation)*cos(direction); if (vertical>0.0) { vertical = halfvalue+(fabs(vertical)*halfvalue); } else { vertical = halfvalue-(fabs(vertical)*halfvalue); } if (info.canvasMirroredH()) {horizontal = maxvalue - horizontal;} if (info.canvasMirroredV()) {vertical = maxvalue - vertical;} depth = sin(elevation)*maxvalue; //assign right components to correct axes. swizzleAssign(horizontal, vertical, depth, r, redChannel(), maxvalue); swizzleAssign(horizontal, vertical, depth, g, greenChannel(), maxvalue); swizzleAssign(horizontal, vertical, depth, b, blueChannel(), maxvalue); } /*settings*/ void KisTangentTiltOption::writeOptionSetting(KisPropertiesConfigurationSP setting) const { setting->setProperty(TANGENT_RED, redChannel()); setting->setProperty(TANGENT_GREEN, greenChannel()); setting->setProperty(TANGENT_BLUE, blueChannel()); setting->setProperty(TANGENT_TYPE, directionType()); setting->setProperty(TANGENT_EV_SEN, elevationSensitivity()); setting->setProperty(TANGENT_MIX_VAL, mixValue()); } void KisTangentTiltOption::readOptionSetting(const KisPropertiesConfigurationSP setting) { m_options->comboRed->setCurrentIndex(setting->getInt(TANGENT_RED, 0)); m_options->comboGreen->setCurrentIndex(setting->getInt(TANGENT_GREEN, 2)); m_options->comboBlue->setCurrentIndex(setting->getInt(TANGENT_BLUE, 4)); //The comboboxes are connected to the TangentTiltPreview, so that gets automatically updated by them. if (setting->getInt(TANGENT_TYPE)== 0){ m_options->optionTilt->setChecked(true); m_options->sliderMixValue->setVisible(false); } else if (setting->getInt(TANGENT_TYPE)== 1) { m_options->optionDirection->setChecked(true); m_options->sliderMixValue->setVisible(false); } else if (setting->getInt(TANGENT_TYPE)== 2) { m_options->optionRotation->setChecked(true); m_options->sliderMixValue->setVisible(false); } else if (setting->getInt(TANGENT_TYPE)== 3) { m_options->optionMix->setChecked(true); m_options->sliderMixValue->setVisible(true); } m_options->sliderElevationSensitivity->setValue(setting->getDouble(TANGENT_EV_SEN, 100)); m_options->sliderMixValue->setValue(setting->getDouble(TANGENT_MIX_VAL, 50)); } diff --git a/plugins/python/assignprofiledialog/kritapykrita_assignprofiledialog.desktop b/plugins/python/assignprofiledialog/kritapykrita_assignprofiledialog.desktop index 544eda162d..fb4a784a97 100644 --- a/plugins/python/assignprofiledialog/kritapykrita_assignprofiledialog.desktop +++ b/plugins/python/assignprofiledialog/kritapykrita_assignprofiledialog.desktop @@ -1,57 +1,59 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=assignprofiledialog X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Assign Profile to Image Name[ar]=إسناد اللاحات إلى الصور Name[ca]=Assigna un perfil a una imatge Name[ca@valencia]=Assigna un perfil a una imatge Name[cs]=Přiřadit obrázku profil Name[el]=Αντιστοίχιση προφίλ σε εικόνα Name[en_GB]=Assign Profile to Image Name[es]=Asignar perfil a imagen +Name[et]=Pildile profiili omistamine Name[eu]=Esleitu profila irudiari Name[fi]=Liitä kuvaan profiili Name[fr]=Attribuer un profil à l'image Name[gl]=Asignar un perfil á imaxe Name[is]=Úthluta litasniði á myndina Name[it]=Assegna profilo a immagine Name[ko]=이미지에 프로필 할당 Name[nl]=Profiel aan afbeelding toewijzen Name[nn]=Tildel profil til bilete Name[pl]=Przypisz profil do obrazu Name[pt]=Atribuir um Perfil à Imagem Name[pt_BR]=Atribuir perfil a imagem Name[sv]=Tilldela profil till bild Name[tr]=Görüntüye Profil Ata Name[uk]=Призначити профіль до зображення Name[x-test]=xxAssign Profile to Imagexx Name[zh_CN]=为图像指定特性文件 Name[zh_TW]=指定設定檔到圖像 Comment=Assign a profile to an image without converting it. Comment[ar]=أسنِد لاحة إلى صورة دون تحويلها. Comment[ca]=Assigna un perfil a una imatge sense convertir-la. Comment[ca@valencia]=Assigna un perfil a una imatge sense convertir-la. Comment[el]=Αντιστοιχίζει ένα προφίλ σε μια εικόνα χωρίς μετατροπή. Comment[en_GB]=Assign a profile to an image without converting it. Comment[es]=Asignar un perfil a una imagen sin convertirla. +Comment[et]=Pildile profiili omistamine ilma seda teisendamata. Comment[eu]=Esleitu profil bat irudi bati hura bihurtu gabe. Comment[fi]=Liitä kuvaan profiili muuntamatta kuvaa Comment[fr]=Attribuer un profil à une image sans la convertir. Comment[gl]=Asignar un perfil a unha imaxe sen convertela. Comment[is]=Úthluta litasniði á myndina án þess að umbreyta henni. Comment[it]=Assegna un profilo a un'immagine senza convertirla. Comment[ko]=프로필을 변환하지 않고 이미지에 할당합니다. Comment[nl]=Een profiel aan een afbeelding toewijzen zonder het te converteren. Comment[nn]=Tildel fargeprofil til eit bilete utan å konvertera det til profilen Comment[pl]=Przypisz profil do obrazu bez jego przekształcania. Comment[pt]=Atribui um perfil à imagem sem a converter. Comment[pt_BR]=Atribui um perfil para uma imagem sem convertê-la. Comment[sv]=Tilldela en profil till en bild utan att konvertera den. Comment[tr]=Bir görüntüye, görüntüyü değiştirmeden bir profil ata. Comment[uk]=Призначити профіль до зображення без його перетворення. Comment[x-test]=xxAssign a profile to an image without converting it.xx Comment[zh_CN]=仅为图像指定特性文件,不转换其色彩空间 Comment[zh_TW]=將設定檔指定給圖像,而不進行轉換。 diff --git a/plugins/python/colorspace/kritapykrita_colorspace.desktop b/plugins/python/colorspace/kritapykrita_colorspace.desktop index 11c1c3415c..229e5afa66 100644 --- a/plugins/python/colorspace/kritapykrita_colorspace.desktop +++ b/plugins/python/colorspace/kritapykrita_colorspace.desktop @@ -1,59 +1,61 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=colorspace X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Color Space Name[ar]=الفضاء اللوني Name[ca]=Espai de color Name[ca@valencia]=Espai de color Name[cs]=Barevný prostor Name[de]=Farbraum Name[el]=Χρωματικός χώρος Name[en_GB]=Colour Space Name[es]=Espacio de color +Name[et]=Värviruum Name[eu]=Kolore-espazioa Name[fi]=Väriavaruus Name[fr]=Espace colorimétrique Name[gl]=Espazo de cores Name[is]=Litrýmd Name[it]=Spazio dei colori Name[ko]=색상 공간 Name[nl]=Kleurruimte Name[nn]=Fargerom Name[pl]=Przestrzeń barw Name[pt]=Espaço de Cores Name[pt_BR]=Espaço de cores Name[sk]=Farebný priestor Name[sv]=Färgrymd Name[tr]=Renk Aralığı Name[uk]=Простір кольорів Name[x-test]=xxColor Spacexx Name[zh_CN]=色彩空间 Name[zh_TW]=色彩空間 Comment=Plugin to change color space to selected documents Comment[ar]=ملحقة لتغيير الفضاء اللوني في المستندات المحددة Comment[ca]=Un connector per a canviar l'espai de color dels documents seleccionats Comment[ca@valencia]=Un connector per a canviar l'espai de color dels documents seleccionats Comment[cs]=Modul pro změnu rozsahu barvy pro vybrané dokumenty Comment[el]=Πρόσθετο αλλαγής χρωματικού χώρου σε επιλεγμένα έγγραφα Comment[en_GB]=Plugin to change colour space to selected documents Comment[es]=Complemento para cambiar el espacio de color de los documentos seleccionados +Comment[et]=Plugin valitud dokumentide värviruumi muutmiseks Comment[eu]=Hautatutako dokumentuei kolore-espazioa aldatzeko plugina Comment[fi]=Liitännäinen valittujen tiedostojen väriavaruuden muuttamiseksi Comment[fr]=Module externe pour l'espace de couleurs des documents sélectionnés Comment[gl]=Complemento para cambiar o espazo de cores dos documentos seleccionados. Comment[it]=Estensione per cambiare lo spazio dei colori ai documenti selezionati Comment[ko]=선택한 문서로 색상 공간을 변경하는 플러그인 Comment[nl]=Plug-in om kleurruimte in geselecteerde documenten te wijzigen Comment[nn]=Programtillegg for å byta fargerom på utvalde dokument Comment[pl]=Wtyczka do zmiany przestrzeni barw wybranych dokumentów Comment[pt]='Plugin' para mudar o espaço de cores do documento seleccionado Comment[pt_BR]=Plugin para alterar o espaço de cores em documentos selecionados Comment[sv]=Insticksprogram för att ändra färgrymd för valda dokument Comment[tr]=Seçili belgede renk aralığını değiştirmek için eklenti Comment[uk]=Додаток для зміни простору кольорів у позначених документах Comment[x-test]=xxPlugin to change color space to selected documentsxx Comment[zh_CN]=用于更改选定文档色彩空间的插件 Comment[zh_TW]=用於變更色彩空間為選定檔案的外掛程式 diff --git a/plugins/python/comics_project_management_tools/kritapykrita_comics_project_management_tools.desktop b/plugins/python/comics_project_management_tools/kritapykrita_comics_project_management_tools.desktop index f72ea33d5a..4184aee956 100644 --- a/plugins/python/comics_project_management_tools/kritapykrita_comics_project_management_tools.desktop +++ b/plugins/python/comics_project_management_tools/kritapykrita_comics_project_management_tools.desktop @@ -1,58 +1,60 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=comics_project_management_tools X-Krita-Manual=README.html X-Python-2-Compatible=false Name=Comics Project Management Tools Name[ar]=أدوات إدارة المشاريع الهزليّة Name[ca]=Eines per a la gestió dels projectes de còmics Name[ca@valencia]=Eines per a la gestió dels projectes de còmics Name[cs]=Nástroje pro správu projektů komixů Name[el]=Εργαλεία διαχείρισης έργων ιστοριών σε εικόνες Name[en_GB]=Comics Project Management Tools Name[es]=Herramientas de gestión de proyectos de cómics +Name[et]=Koomiksite projektihalduse tööriistad Name[eu]=Komikien proiektuak kudeatzeko tresnak Name[fi]=Sarjakuvaprojektien hallintatyökalut Name[fr]=Outils de gestion d'un projet de bande dessinée Name[gl]=Ferramentas de xestión de proxectos de cómics Name[is]=Verkefnisstjórn teiknimyndasögu Name[it]=Strumenti per la gestione dei progetti di fumetti Name[ko]=만화 프로젝트 관리 도구 Name[nl]=Hulpmiddelen voor projectbeheer van strips Name[nn]=Prosjekthandsaming for teikneseriar Name[pl]=Narzędzia do zarządzania projektami komiksów Name[pt]=Ferramentas de Gestão de Projectos de Banda Desenhada Name[pt_BR]=Ferramentas de gerenciamento de projeto de quadrinhos Name[sv]=Projekthanteringsverktyg för tecknade serier Name[tr]=Çizgi Roman Projesi Yönetimi Araçları Name[uk]=Інструменти для керування проєктами коміксів Name[x-test]=xxComics Project Management Toolsxx Name[zh_CN]=漫画项目管理工具 Name[zh_TW]=漫畫專案管理工具 Comment=Tools for managing comics. Comment[ar]=أدوات لإدارة الهزليّات. Comment[ca]=Eines per a gestionar els còmics. Comment[ca@valencia]=Eines per a gestionar els còmics. Comment[cs]=Nástroje pro správu komixů. Comment[el]=Εργαλεία για τη διαχείριση ιστοριών σε εικόνες. Comment[en_GB]=Tools for managing comics. Comment[es]=Herramientas para gestionar cómics. +Comment[et]=Koomiksite haldamise tööriistad. Comment[eu]=Komikiak kudeatzeko tresnak. Comment[fi]=Sarjakuvien hallintatyökalut. Comment[fr]=Outils pour gérer les bandes dessinées. Comment[gl]=Ferramentas para xestionar cómics. Comment[is]=Verkfæri til að stýra gerð teiknimyndasögu. Comment[it]=Strumenti per la gestione dei fumetti. Comment[ko]=만화 관리 도구입니다. Comment[nl]=Hulpmiddelen voor beheer van strips. Comment[nn]=Verktøy for handsaming av teikneseriar Comment[pl]=Narzędzie do zarządzania komiksami. Comment[pt]=Ferramentas para gerir bandas desenhadas. Comment[pt_BR]=Ferramentas para gerenciar quadrinhos. Comment[sv]=Verktyg för att hantera tecknade serier. Comment[tr]=Çizgi romanları yönetmek için araçlar. Comment[uk]=Інструменти для керування коміксами Comment[x-test]=xxTools for managing comics.xx Comment[zh_CN]=用于管理漫画的工具。 Comment[zh_TW]=管理漫畫的工具。 diff --git a/plugins/python/documenttools/kritapykrita_documenttools.desktop b/plugins/python/documenttools/kritapykrita_documenttools.desktop index c34c388ee2..650d03a0f3 100644 --- a/plugins/python/documenttools/kritapykrita_documenttools.desktop +++ b/plugins/python/documenttools/kritapykrita_documenttools.desktop @@ -1,56 +1,58 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=documenttools X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Document Tools Name[ar]=أدوات المستندات Name[ca]=Eines de document Name[ca@valencia]=Eines de document Name[cs]=Dokumentové nástroje Name[el]=Εργαλεία για έγγραφα Name[en_GB]=Document Tools Name[es]=Herramientas de documentos +Name[et]=Dokumenditööriistad Name[eu]=Dokumentuen tresnak Name[fi]=Tiedostotyökalut Name[fr]=Outil Document Name[gl]=Ferramentas de documentos Name[it]=Strumenti per i documenti Name[ko]=문서 도구 Name[nl]=Documenthulpmiddelen Name[nn]=Dokumentverktøy Name[pl]=Narzędzia dokumentu Name[pt]=Ferramentas de Documentos Name[pt_BR]=Ferramentas de documento Name[sv]=Dokumentverktyg Name[tr]=Belge Araçları Name[uk]=Засоби документа Name[x-test]=xxDocument Toolsxx Name[zh_CN]=文档工具 Name[zh_TW]=檔案工具 Comment=Plugin to manipulate properties of selected documents Comment[ar]=ملحقة لتعديل خصائص المستندات المحددة Comment[ca]=Un connector per a manipular les propietats dels documents seleccionats Comment[ca@valencia]=Un connector per a manipular les propietats dels documents seleccionats Comment[cs]=Modul pro správu vlastností vybraných dokumentů Comment[el]=Πρόσθετο χειρισμού ιδιοτήτων σε επιλεγμένα έγγραφα Comment[en_GB]=Plugin to manipulate properties of selected documents Comment[es]=Complemento para manipular las propiedades de los documentos seleccionados +Comment[et]=Plugin valitud dokumentide omaduste käitlemiseks Comment[eu]=Hautatutako dokumentuen propietateak manipulatzeko plugina Comment[fi]=Liitännäinen valittujen tiedostojen ominaisuuksien käsittelemiseksi Comment[fr]=Module externe de gestion des propriétés des documents sélectionnés Comment[gl]=Complemento para manipular as propiedades dos documentos seleccionados. Comment[it]=Estensione per manipolare le proprietà dei documenti selezionati Comment[ko]=선택한 문서의 속성을 변경하는 플러그인 Comment[nl]=Plug-in om eigenschappen van geselecteerde documenten te manipuleren Comment[nn]=Programtillegg for å endra eigenskapar på utvalde dokument Comment[pl]=Wtyczka do zmiany właściwości wybranych dokumentów Comment[pt]='Plugin' para manipular as propriedades dos documentos seleccionados Comment[pt_BR]=Plugin para manipular as propriedades de documentos selecionados Comment[sv]=Insticksprogram för att ändra egenskaper för valda dokument Comment[tr]=Seçili belgelerin özelliklerini değiştirmek için eklenti Comment[uk]=Додаток для керування властивостями позначених документів Comment[x-test]=xxPlugin to manipulate properties of selected documentsxx Comment[zh_CN]=用于编辑选定文档属性的插件 Comment[zh_TW]=用於修改所選檔案屬性的外掛程式 diff --git a/plugins/python/exportlayers/kritapykrita_exportlayers.desktop b/plugins/python/exportlayers/kritapykrita_exportlayers.desktop index 2469aeb41d..63e1511745 100644 --- a/plugins/python/exportlayers/kritapykrita_exportlayers.desktop +++ b/plugins/python/exportlayers/kritapykrita_exportlayers.desktop @@ -1,59 +1,61 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=exportlayers X-Krita-Manual=Manual.html X-Python-2-Compatible=true Name=Export Layers Name[ar]=تصدير الطبقات Name[ca]=Exportació de capes Name[ca@valencia]=Exportació de capes Name[cs]=Exportovat vrstvy Name[de]=Ebenen exportieren Name[el]=Εξαγωγή επιπέδων Name[en_GB]=Export Layers Name[es]=Exportar capas +Name[et]=Kihtide eksport Name[eu]=Esportatu geruzak Name[fi]=Vie tasoja Name[fr]=Exporter des calques Name[gl]=Exportar as capas Name[is]=Flytja út lög Name[it]=Esporta livelli Name[ko]=레이어 내보내기 Name[nl]=Lagen exporteren Name[nn]=Eksporter lag Name[pl]=Eksportuj warstwy Name[pt]=Exportar as Camadas Name[pt_BR]=Exportar camadas Name[sv]=Exportera lager Name[tr]=Katmanları Dışa Aktar Name[uk]=Експортувати шари Name[x-test]=xxExport Layersxx Name[zh_CN]=导出图层 Name[zh_TW]=匯出圖層 Comment=Plugin to export layers from a document Comment[ar]=ملحقة لتصدير الطبقات من مستند Comment[ca]=Un connector per exportar capes d'un document Comment[ca@valencia]=Un connector per exportar capes d'un document Comment[cs]=Modul pro export vrstev z dokumentu Comment[de]=Modul zum Exportieren von Ebenen aus einem Dokument Comment[el]=Πρόσθετο εξαγωγής επιπέδων από έγγραφο Comment[en_GB]=Plugin to export layers from a document Comment[es]=Complemento para exportar las capas de un documento +Comment[et]=Plugin dokumendi kihtide eksportimiseks Comment[eu]=Dokumentu batetik geruzak esportatzeko plugina Comment[fi]=Liitännäisen tiedoston tasojen viemiseksi Comment[fr]=Module externe d'export de calques d'un document Comment[gl]=Complemento para exportar as capas dun documento. Comment[it]=Estensione per esportare i livelli da un documento Comment[ko]=문서에서 레이어를 내보내는 플러그인 Comment[nl]=Plug-in om lagen uit een document te exporteren Comment[nn]=Programtillegg for eksportering av biletlag i dokument Comment[pl]=Wtyczka do eksportowania warstw z dokumentu Comment[pt]='Plugin' para exportar as camadas de um documento Comment[pt_BR]=Plugin para exportar as camadas de um documento Comment[sv]=Insticksprogram för att exportera lager från ett dokument Comment[tr]=Belgenin katmanlarını dışa aktarmak için eklenti Comment[uk]=Додаток для експортування шарів з документа Comment[x-test]=xxPlugin to export layers from a documentxx Comment[zh_CN]=用于从文档导出图层的插件 Comment[zh_TW]=用於從檔案匯出圖層的外掛程式 diff --git a/plugins/python/filtermanager/kritapykrita_filtermanager.desktop b/plugins/python/filtermanager/kritapykrita_filtermanager.desktop index 41a19bb39f..3be984b34b 100644 --- a/plugins/python/filtermanager/kritapykrita_filtermanager.desktop +++ b/plugins/python/filtermanager/kritapykrita_filtermanager.desktop @@ -1,58 +1,60 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=filtermanager X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Filter Manager Name[ar]=مدير المرشّحات Name[ca]=Gestor de filtres Name[ca@valencia]=Gestor de filtres Name[cs]=Správce filtrů Name[de]=Filterverwaltung Name[el]=Διαχειριστής φίλτρων Name[en_GB]=Filter Manager Name[es]=Gestor de filtros +Name[et]=Filtrihaldur Name[eu]=Iragazki-kudeatzailea Name[fi]=Suodatinhallinta Name[fr]=Gestionnaire de fichiers Name[gl]=Xestor de filtros Name[it]=Gestore dei filtri Name[ko]=필터 관리자 Name[nl]=Beheerder van filters Name[nn]=Filterhandsamar Name[pl]=Zarządzanie filtrami Name[pt]=Gestor de Filtros Name[pt_BR]=Gerenciador de filtros Name[sv]=Filterhantering Name[tr]=Süzgeç Yöneticisi Name[uk]=Керування фільтрами Name[x-test]=xxFilter Managerxx Name[zh_CN]=滤镜管理器 Name[zh_TW]=濾鏡管理器 Comment=Plugin to filters management Comment[ar]=ملحقة إدارة المرشّحات Comment[ca]=Un connector per a gestionar filtres Comment[ca@valencia]=Un connector per a gestionar filtres Comment[cs]=Modul pro správu filtrů Comment[de]=Modul zum Verwalten von Filtern Comment[el]=Πρόσθετο για τη διαχείριση φίλτρων Comment[en_GB]=Plugin to filters management Comment[es]=Complemento para la gestión de filtros +Comment[et]=Plugin filtrite haldamiseks Comment[eu]=Iragazkiak kudeatzeko plugina Comment[fi]=Liitännäinen suodatinten hallintaan Comment[fr]=Module externe de gestion des filtres Comment[gl]=Complemento para a xestión de filtros. Comment[it]=Estensione per la gestione dei filtri Comment[ko]=필터 관리에 대한 플러그인 Comment[nl]=Plug-in voor beheer van filters Comment[nn]=Programtillegg for filterhandsaming Comment[pl]=Wtyczka do zarządzania filtrami Comment[pt]='Plugin' para a gestão de filtros Comment[pt_BR]=Plugin para gerenciamento de filtros Comment[sv]=Insticksprogram för filterhantering Comment[tr]=Süzgeç yönetimi için eklenti Comment[uk]=Додаток для керування фільтрами Comment[x-test]=xxPlugin to filters managementxx Comment[zh_CN]=用于管理滤镜的插件。 Comment[zh_TW]=用於濾鏡管理的外掛程式 diff --git a/plugins/python/hello/kritapykrita_hello.desktop b/plugins/python/hello/kritapykrita_hello.desktop index 1ec86ba1df..c4a0c7ceae 100644 --- a/plugins/python/hello/kritapykrita_hello.desktop +++ b/plugins/python/hello/kritapykrita_hello.desktop @@ -1,61 +1,63 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=hello X-Krita-Manual=Manual.html X-Python-2-Compatible=true Name=Hello World Name[ar]=مرحبا يا عالم Name[ca]=Hola món Name[ca@valencia]=Hola món Name[cs]=Hello World Name[de]=Hallo Welt Name[el]=Hello World Name[en_GB]=Hello World Name[es]=Hola mundo +Name[et]=Tere, maailm Name[eu]=Kaixo mundua Name[fi]=Hei maailma Name[fr]=Bonjour tout le monde Name[gl]=Ola mundo Name[is]=Halló Heimur Name[it]=Ciao mondo Name[ko]=전 세계 여러분 안녕하세요 Name[nl]=Hallo wereld Name[nn]=Hei, verda Name[pl]=Witaj świecie Name[pt]=Olá Mundo Name[pt_BR]=Olá mundo Name[sk]=Ahoj svet Name[sv]=Hello World Name[tg]=Салом ҷаҳон Name[tr]=Merhaba Dünya Name[uk]=Привіт, світе Name[x-test]=xxHello Worldxx Name[zh_CN]=Hello World Name[zh_TW]=你好,世界 Comment=Basic plugin to test PyKrita Comment[ar]=ملحقة أساسية لاختبار PyKrita Comment[ca]=Connector bàsic per a provar el PyKrita Comment[ca@valencia]=Connector bàsic per a provar el PyKrita Comment[cs]=Základní modul pro testování PyKrita Comment[de]=Basismodul zum Testen von PyKrita Comment[el]=Βασικό πρόσθετο δοκιμής PyKrita Comment[en_GB]=Basic plugin to test PyKrita Comment[es]=Complemento básico para probar PyKrita +Comment[et]=Baasplugin PyKrita testimiseks Comment[eu]=PyKrita probatzeko plugina Comment[fi]=Perusliitännäinen PyKritan kokeilemiseksi Comment[fr]=Module externe élémentaire pour tester PyKrita Comment[gl]=Complemento básico para probar PyKrita. Comment[it]=Estensione di base per provare PyKrita Comment[ko]=PyKrita 테스트용 기본 플러그인 Comment[nl]=Basisplug-in om PyKrita te testen Comment[nn]=Enkelt programtillegg for testing av PyKrita Comment[pl]=Podstawowa wtyczka do wypróbowania PyKrity Comment[pt]='Plugin' básico para testar o PyKrita Comment[pt_BR]=Plugin básico para testar o PyKrita Comment[sv]=Enkelt insticksprogram för att utprova PyKrita Comment[tr]=PyKrita'yı test etmek için temel eklenti Comment[uk]=Базовий додаток для тестування PyKrita Comment[x-test]=xxBasic plugin to test PyKritaxx Comment[zh_CN]=用于测试 PyKrita 的简易插件 Comment[zh_TW]=測試 PyKrita 的基本外掛程式 diff --git a/plugins/python/highpass/kritapykrita_highpass.desktop b/plugins/python/highpass/kritapykrita_highpass.desktop index e40f7724c3..bb6471a1cc 100644 --- a/plugins/python/highpass/kritapykrita_highpass.desktop +++ b/plugins/python/highpass/kritapykrita_highpass.desktop @@ -1,53 +1,55 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=highpass X-Python-2-Compatible=false Name=Highpass Filter Name[ca]=Filtre passaalt Name[ca@valencia]=Filtre passaalt Name[cs]=Filtr s horní propustí Name[de]=Hochpassfilter Name[el]=Υψιπερατό φίλτρο Name[en_GB]=Highpass Filter Name[es]=Filtro paso alto +Name[et]=Kõrgpääsfilter Name[eu]=Goi-igaropeneko iragazkia Name[fr]=Filtre passe-haut Name[gl]=Filtro de paso alto Name[it]=Filtro di accentuazione passaggio Name[ko]=하이패스 필터 Name[nl]=Hoogdoorlaatfilter Name[nn]=Høgpass-filter Name[pl]=Filtr górnoprzepustowy Name[pt]=Filtro Passa-Alto Name[pt_BR]=Filtro passa alta Name[sv]=Högpassfilter Name[tr]=Yüksek Geçirgen Süzgeç Name[uk]=Високочастотний фільтр Name[x-test]=xxHighpass Filterxx Name[zh_CN]=高通滤镜 Name[zh_TW]=高通濾鏡 Comment=Highpass Filter, based on http://registry.gimp.org/node/7385 Comment[ca]=Filtre passaalt, basat en el http://registry.gimp.org/node/7385 Comment[ca@valencia]=Filtre passaalt, basat en el http://registry.gimp.org/node/7385 Comment[cs]=Filtr s horní propustí založený na http://registry.gimp.org/node/7385 Comment[de]=Hochpassfilter, Grundlage ist http://registry.gimp.org/node/7385 Comment[el]=Υψιπερατό φίλτρο, με βάση το http://registry.gimp.org/node/7385 Comment[en_GB]=Highpass Filter, based on http://registry.gimp.org/node/7385 Comment[es]=Filtro paso alto, basado en http://registry.gimp.org/node/7385 +Comment[et]=Kõrgpääsfilter, mille aluseks on http://registry.gimp.org/node/7385 Comment[eu]=Goi-igaropeneko iragazkia, honetan oinarritua http://registry.gimp.org/node/7385 Comment[fr]=Filtre passe-haut, fondé sur http://registry.gimp.org/node/7385 Comment[gl]=Filtro de paso alto, baseado en http://registry.gimp.org/node/7385. Comment[it]=Filtro di accentuazione passaggio, basato su http://registry.gimp.org/node/7385 Comment[ko]=http://registry.gimp.org/node/7385에 기반한 하이패스 필터 Comment[nl]=Hoogdoorlaatfilter, gebaseerd op http://registry.gimp.org/node/7385 Comment[nn]=Høgpass-filter, basert på http://registry.gimp.org/node/7385 Comment[pl]=Filtr górnoprzepustowy, oparty na http://registry.gimp.org/node/7385 Comment[pt]=Filtro passa-alto, baseado em http://registry.gimp.org/node/7385 Comment[pt_BR]=Filtro passa alta, baseado em http://registry.gimp.org/node/7385 Comment[sv]=Högpassfilter, baserat på http://registry.gimp.org/node/7385 Comment[tr]=http://registry.gimp.org/node/7385 tabanlı Yüksek Geçirgen Süzgeç Comment[uk]=Високочастотний фільтр, засновано на on http://registry.gimp.org/node/7385 Comment[x-test]=xxHighpass Filter, based on http://registry.gimp.org/node/7385xx Comment[zh_CN]=高通滤镜,基于 http://registry.gimp.org/node/7385 Comment[zh_TW]=高通濾鏡,基於 http://registry.gimp.org/node/7385 diff --git a/plugins/python/krita_script_starter/kritapykrita_krita_script_starter.desktop b/plugins/python/krita_script_starter/kritapykrita_krita_script_starter.desktop index 6a13606e33..aeddfef036 100644 --- a/plugins/python/krita_script_starter/kritapykrita_krita_script_starter.desktop +++ b/plugins/python/krita_script_starter/kritapykrita_krita_script_starter.desktop @@ -1,45 +1,47 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=krita_script_starter X-Python-2-Compatible=false X-Krita-Manual=Manual.html Name=Krita Script Starter Name[ca]=Iniciador de scripts del Krita Name[ca@valencia]=Iniciador de scripts del Krita Name[cs]=Spouštěč skriptů Krita Name[en_GB]=Krita Script Starter Name[es]=Iniciador de guiones de Krita +Name[et]=Krita skriptialustaja Name[eu]=Krita-ren script abiarazlea Name[gl]=Iniciador de scripts de Krita Name[it]=Iniziatore di script per Krita Name[ko]=Krita 스크립트 시작 도구 Name[nl]=Script-starter van Krita Name[nn]=Krita skriptbyggjar Name[pl]=Starter skryptów Krity Name[pt]=Inicialização do Programa do Krita Name[sv]=Krita skriptstart Name[tr]=Krita Betik Başlatıcı Name[uk]=Створення скрипту Krita Name[x-test]=xxKrita Script Starterxx Name[zh_CN]=Krita 空脚本生成器 Name[zh_TW]=Krita 指令啟動器 Comment=Create the metadata and file structure for a new Krita script Comment[ca]=Crea les metadades i l'estructura de fitxers d'un script nou del Krita Comment[ca@valencia]=Crea les metadades i l'estructura de fitxers d'un script nou del Krita Comment[en_GB]=Create the metadata and file structure for a new Krita script Comment[es]=Crear los metadatos y la estructura de archivos para un nuevo guion de Krita +Comment[et]=Uue Krita skripti metaandmete ja failistruktuuri loomine Comment[eu]=Sortu Krita-script berri baterako meta-datuak eta fitxategi egitura Comment[gl]=Crear os metadatos e a estrutura de ficheiros para un novo script de Krita. Comment[it]=Crea i metadati e la struttura dei file per un nuovo script di Krita Comment[ko]=새 Krita 스크립트에 대한 메타데이터 및 파일 구조 생성 Comment[nl]=Maak de metagegevens en bestandsstructuur voor een nieuw Krita-script Comment[nn]=Generer metadata og filstruktur for nye Krita-skript Comment[pl]=Utwórz metadane i strukturę plików dla nowego skryptu Krity Comment[pt]=Cria os meta-dados e a estrutura de ficheiros para um novo programa do Krita Comment[sv]=Skapa metadata och filstruktur för ett nytt Krita-skript Comment[tr]=Yeni Krita betiği için üstveri ve dosya yapısı oluştur Comment[uk]=Створення метаданих і структури файлів для нового скрипту Krita Comment[x-test]=xxCreate the metadata and file structure for a new Krita scriptxx Comment[zh_CN]=为新的 Krita 脚本创建元数据及文件结构 Comment[zh_TW]=為新的 Krita 指令稿建立中繼資料和檔案建構體 diff --git a/plugins/python/lastdocumentsdocker/kritapykrita_lastdocumentsdocker.desktop b/plugins/python/lastdocumentsdocker/kritapykrita_lastdocumentsdocker.desktop index 750d85fd0b..4821911758 100644 --- a/plugins/python/lastdocumentsdocker/kritapykrita_lastdocumentsdocker.desktop +++ b/plugins/python/lastdocumentsdocker/kritapykrita_lastdocumentsdocker.desktop @@ -1,52 +1,54 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=lastdocumentsdocker X-Python-2-Compatible=false X-Krita-Manual=Manual.html Name=Last Documents Docker Name[ar]=رصيف بآخر المستندات Name[ca]=Acoblador Darrers documents Name[ca@valencia]=Acoblador Darrers documents Name[el]=Προσάρτηση τελευταίων εγγράφοων Name[en_GB]=Last Documents Docker Name[es]=Panel de últimos documentos +Name[et]=Viimaste dokumentide dokk Name[eu]=Azken dokumentuen panela Name[fi]=Viimeisimpien tiedostojen telakka Name[fr]=Récemment ouverts Name[gl]=Doca dos últimos documentos Name[it]=Area di aggancio Ultimi documenti Name[ko]=마지막 문서 도킹 패널 Name[nl]=Laatste documenten verankering Name[nn]=Dokk for nyleg opna dokument Name[pl]=Dok ostatnich dokumentów Name[pt]=Área dos Últimos Documentos Name[sv]=Dockningsfönster för senaste dokument Name[tr]=Son Belgeler Doku Name[uk]=Бічна панель останніх документів Name[x-test]=xxLast Documents Dockerxx Name[zh_CN]=最近文档工具面板 Name[zh_TW]=「最後檔案」面板 Comment=A Python-based docker for show thumbnails to last ten documents Comment[ar]=رصيف بِ‍«پيثون» لعرض مصغّرات آخر ١٠ مستندات مفتوحة Comment[ca]=Un acoblador basat en Python per a mostrar miniatures dels darrers deu documents Comment[ca@valencia]=Un acoblador basat en Python per a mostrar miniatures dels darrers deu documents Comment[el]=Ένα εργαλείο προσάρτησης σε Python για την εμφάνιση επισκοπήσεων των δέκα τελευταίων εγγράφων Comment[en_GB]=A Python-based docker for show thumbnails to last ten documents Comment[es]=Un panel basado en Python para mostrar miniaturas de los últimos diez documentos +Comment[et]=Pythoni-põhine dokk viimase kümne dokumendi pisipildi näitamiseks Comment[eu]=Azken hamar dokumentuen koadro-txikiak erakusteko Python-oinarridun panel bat Comment[fi]=Python-pohjainen telakka viimeisimpien kymmenen tiedoston pienoiskuvien näyttämiseen Comment[fr]=Panneau en Python pour afficher les vignettes des dix derniers documents Comment[gl]=Unha doca baseada en Python para mostrar as miniaturas dos últimos dez documentos. Comment[it]=Un'area di aggancio basata su Python per mostrare miniature degli ultimi dieci documenti. Comment[ko]=10개의 문서를 표시할 수 있는 Python 기반 도킹 패널 Comment[nl]=Een op Python gebaseerde vastzetter om miniaturen te tonen naar de laatste tien documenten. Comment[nn]=Python-basert dokk for vising av miniatyrbilete av dei siste ti dokumenta Comment[pl]=Dok oparty na pythonie do wyświetlania miniatur ostatnich dziesięciu dokumentów Comment[pt]=Uma área acoplável, feita em Python, para mostrar as miniaturas dos últimos dez documentos Comment[sv]=Ett Python-baserat dockningsfönster för att visa miniatyrbilder för de tio senaste dokumenten Comment[tr]=Son on belgenin küçük resmini göstermek için Python-tabanlı bir dok Comment[uk]=Бічна панель на основі Python для показу мініатюр останніх десяти документів Comment[x-test]=xxA Python-based docker for show thumbnails to last ten documentsxx Comment[zh_CN]=这是一个基于 Python 的工具面板插件,它可以显示最近十个文档的缩略图 Comment[zh_TW]=基於 Python 的面板,用於顯示最後 10 個檔案縮圖 diff --git a/plugins/python/palette_docker/kritapykrita_palette_docker.desktop b/plugins/python/palette_docker/kritapykrita_palette_docker.desktop index 2603892208..4a6cc34a54 100644 --- a/plugins/python/palette_docker/kritapykrita_palette_docker.desktop +++ b/plugins/python/palette_docker/kritapykrita_palette_docker.desktop @@ -1,55 +1,57 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=palette_docker X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Palette docker Name[ar]=رصيف اللوحات Name[ca]=Acoblador Paleta Name[ca@valencia]=Acoblador Paleta Name[cs]=Dok palet Name[de]=Paletten-Docker Name[el]=Προσάρτηση παλέτας Name[en_GB]=Palette docker Name[es]=Panel de paleta +Name[et]=Paletidokk Name[eu]=Paleta-panela Name[fi]=Palettitelakka Name[fr]=Panneau de palette Name[gl]=Doca de paleta Name[is]=Tengikví fyrir litaspjald Name[it]=Area di aggancio della tavolozza Name[ko]=팔레트 도킹 패널 Name[nl]=Vastzetter van palet Name[nn]=Palettdokk Name[pl]=Dok palety Name[pt]=Área acoplável da paleta Name[sv]=Dockningsfönster för palett Name[tr]=Palet doku Name[uk]=Панель палітри Name[x-test]=xxPalette dockerxx Name[zh_CN]=调色板工具面板 Name[zh_TW]=「調色盤」面板 Comment=A Python-based docker to edit color palettes. Comment[ar]=رصيف بِ‍«پيثون» لتحرير لوحات الألوان. Comment[ca]=Un acoblador basat en Python per editar paletes de colors. Comment[ca@valencia]=Un acoblador basat en Python per editar paletes de colors. Comment[el]=Ένα εργαλείο προσάρτησης σε Python για την επεξεργασία παλετών χρώματος. Comment[en_GB]=A Python-based docker to edit colour palettes. Comment[es]=Un panel basado en Python para editar paletas de colores. +Comment[et]=Pythoni-põhine dokk värvipalettide muutmiseks. Comment[eu]=Kolore-paletak editatzeko Python-oinarridun paleta bat. Comment[fi]=Python-pohjainen telakka väripalettien muokkaamiseen. Comment[fr]=Panneau en Python pour éditer les palettes de couleurs. Comment[gl]=Unha doca baseada en Python para editar paletas de cores. Comment[it]=Un'area di aggancio per modificare le tavolozze di colori basata su Python. Comment[ko]=색상 팔레트를 편집할 수 있는 Python 기반 도킹 패널입니다. Comment[nl]=Een op Python gebaseerde vastzetter om kleurpaletten te bewerken. Comment[nn]=Python-basert dokk for redigering av fargepalettar Comment[pl]=Dok oparty na pythonie do edytowania palet barw. Comment[pt]=Uma área acoplável, feita em Python, para editar paletas de cores. Comment[sv]=Ett Python-baserat dockningsfönster för att redigera färgpaletter. Comment[tr]=Renk paletlerini düzenlemek için Python-tabanlı bir dok. Comment[uk]=Бічна панель для редагування палітр кольорів на основі Python. Comment[x-test]=xxA Python-based docker to edit color palettes.xx Comment[zh_CN]=基于 Python 的调色板编辑器工具面板 Comment[zh_TW]=基於 Python 的面板,用於編輯調色盤。 diff --git a/plugins/python/plugin_importer/kritapykrita_plugin_importer.desktop b/plugins/python/plugin_importer/kritapykrita_plugin_importer.desktop index 6dc82d0dda..845ed5c50b 100644 --- a/plugins/python/plugin_importer/kritapykrita_plugin_importer.desktop +++ b/plugins/python/plugin_importer/kritapykrita_plugin_importer.desktop @@ -1,44 +1,46 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=plugin_importer X-Python-2-Compatible=false X-Krita-Manual=manual.html Name=Python Plugin Importer Name[ca]=Importador de connectors en Python Name[ca@valencia]=Importador de connectors en Python Name[cs]=Importér modulů pro Python Name[en_GB]=Python Plugin Importer Name[es]=Importador de complementos de Python +Name[et]=Pythoni plugina importija Name[gl]=Importador de complementos de Python Name[it]=Importatore estensioni Python Name[ko]=Python 플러그인 가져오기 도구 Name[nl]=Importeur van Plugin voor Python Name[nn]=Importering av Python-tillegg Name[pl]=Import wtyczek Pythona Name[pt]=Importador de 'Plugins' do Python Name[sv]=Python-insticksimport Name[tr]=Python Eklenti İçe Aktarıcı Name[uk]=Засіб імпортування додатків Python Name[x-test]=xxPython Plugin Importerxx Name[zh_CN]=Python 插件导入器 Name[zh_TW]=Python 外掛程式匯入工具 Comment=Imports Python plugins from zip files. Comment[ca]=Importa connectors en Python a partir de fitxers «zip». Comment[ca@valencia]=Importa connectors en Python a partir de fitxers «zip». Comment[cs]=Importuje moduly Pythonu ze souborů zip. Comment[en_GB]=Imports Python plugins from zip files. Comment[es]=Importa complementos de Python desde archivos zip. +Comment[et]=Pythoni pluginate import zip-failidest. Comment[gl]=Importa complementos de Python de ficheiros zip. Comment[it]=Importa le estensioni Python dai file compressi. Comment[ko]=ZIP 파일에서 Python 플러그인을 가져옵니다. Comment[nl]=Importeert Python-plug-ins uit zip-bestanden. Comment[nn]=Importerer Python-baserte programtillegg frå .zip-filer Comment[pl]=Importuj wtyczki Pythona z plików zip. Comment[pt]=Importa os 'plugins' em Python a partir de ficheiros Zip. Comment[sv]=Importerar Python-insticksprogram från zip-filer. Comment[tr]=Python eklentilerini zip dosyasından içe aktarır. Comment[uk]=Імпортує додатки Python з файлів zip. Comment[x-test]=xxImports Python plugins from zip files.xx Comment[zh_CN]=从 zip 文件导入 Python 插件。 Comment[zh_TW]=匯入 Zip 檔案中的 Python 外掛程式。 diff --git a/plugins/python/quick_settings_docker/kritapykrita_quick_settings_docker.desktop b/plugins/python/quick_settings_docker/kritapykrita_quick_settings_docker.desktop index 86cce72c36..1602d4d0b7 100644 --- a/plugins/python/quick_settings_docker/kritapykrita_quick_settings_docker.desktop +++ b/plugins/python/quick_settings_docker/kritapykrita_quick_settings_docker.desktop @@ -1,53 +1,55 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=quick_settings_docker X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Quick Settings Docker Name[ar]=رصيف بإعدادات سريعة Name[ca]=Acoblador Arranjament ràpid Name[ca@valencia]=Acoblador Arranjament ràpid Name[cs]=Dok pro rychlé nastavení Name[el]=Προσάρτηση γρήγορων ρυθμίσεων Name[en_GB]=Quick Settings Docker Name[es]=Panel de ajustes rápidos +Name[et]=Kiirseadistuste dokk Name[eu]=Ezarpen azkarren panela Name[fi]=Pika-asetustelakka Name[fr]=Réglages rapides Name[gl]=Doca de configuración rápida Name[it]=Area di aggancio delle impostazioni rapide Name[ko]=빠른 설정 도킹 패널 Name[nl]=Verankering voor snelle instellingen Name[nn]=Snøgginnstillingar-dokk Name[pl]=Dok szybkich ustawień Name[pt]=Área de Configuração Rápida Name[sv]=Dockningspanel med snabbinställningar Name[tr]=Hızlı Ayarlar Doku Name[uk]=Панель швидких параметрів Name[x-test]=xxQuick Settings Dockerxx Name[zh_CN]=快速设置工具面板 Name[zh_TW]=「快速設定」面板 Comment=A Python-based docker for quickly changing brush size and opacity. Comment[ar]=رصيف بِ‍«پيثون» لتغيير حجم الفرشاة وشفافيّتها بسرعة. Comment[ca]=Un acoblador basat en Python per a canviar ràpidament la mida i l'opacitat del pinzell. Comment[ca@valencia]=Un acoblador basat en Python per a canviar ràpidament la mida i l'opacitat del pinzell. Comment[el]=Ένα εργαλείο προσάρτησης σε Python για γρήγορη αλλαγή του μεγέθους και της αδιαφάνειας του πινέλου. Comment[en_GB]=A Python-based docker for quickly changing brush size and opacity. Comment[es]=Un panel basado en Python para cambiar rápidamente el tamaño y la opacidad del pincel. +Comment[et]=Pythoni-põhine dokk pintsli suuruse ja läbipaistmatuse kiireks muutmiseks. Comment[eu]=Isipu-neurria eta -opakotasuna aldatzeko Python-oinarridun panel bat. Comment[fi]=Python-pohjainen telakka siveltimen koon ja läpikuultavuuden nopean muuttamiseen. Comment[fr]=Panneau en python pour modifier rapidement la taille et l'opacité des brosses. Comment[gl]=Unha doca baseada en Python para cambiar rapidamente a opacidade e o tamaño dos pinceis. Comment[it]=Un'area di aggancio basata su Python per cambiare rapidamente la dimensione del pennello e l'opacità. Comment[ko]=브러시 크기와 불투명도를 빠르게 변경할 수 있는 Python 기반 도킹 패널입니다. Comment[nl]=Een op Python gebaseerde docker voor snel wijzigen van penseelgrootte en dekking. Comment[nn]=Python-basert dokk for kjapp endring av penselstorleik/-tettleik Comment[pl]=Dok oparty na Pythonie do szybkiej zmiany rozmiaru i nieprzezroczystości pędzla. Comment[pt]=Uma área acoplável em Python para mudar rapidamente o tamanho e opacidade do pincel. Comment[sv]=En Python-baserad dockningspanel för att snabbt ändra penselstorlek och ogenomskinlighet. Comment[tr]=Fırça boyutunu ve matlığını hızlıca değiştirmek için Python-tabanlı bir dok. Comment[uk]=Панель на основі мови програмування Python для швидкої зміни розміру та непрозорості пензля. Comment[x-test]=xxA Python-based docker for quickly changing brush size and opacity.xx Comment[zh_CN]=这是一个基于 Python 的工具面板,用于快速更改笔刷尺寸和透明度。 Comment[zh_TW]=基於 Python 的面板,用於快速變更筆刷尺寸和不透明度。 diff --git a/plugins/python/scriptdocker/kritapykrita_scriptdocker.desktop b/plugins/python/scriptdocker/kritapykrita_scriptdocker.desktop index b37503456d..496b6a400b 100644 --- a/plugins/python/scriptdocker/kritapykrita_scriptdocker.desktop +++ b/plugins/python/scriptdocker/kritapykrita_scriptdocker.desktop @@ -1,50 +1,52 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=scriptdocker X-Python-2-Compatible=false Name=Script Docker Name[ar]=رصيف سكربتات Name[ca]=Acoblador Script Name[ca@valencia]=Acoblador Script Name[cs]=Dok skriptu Name[el]=Προσάρτηση σεναρίων Name[en_GB]=Script Docker Name[es]=Panel de guiones +Name[et]=Skriptidokk Name[eu]=Script-panela Name[fi]=Skriptitelakka Name[fr]=Panneau de script Name[gl]=Doca de scripts Name[it]=Area di aggancio degli script Name[ko]=스크립트 도킹 패널 Name[nl]=Verankering van scripts Name[nn]=Skriptdokk Name[pl]=Dok skryptów Name[pt]=Área de Programas Name[sv]=Dockningsfönster för skript Name[tr]=Betik Doku Name[uk]=Бічна панель скриптів Name[x-test]=xxScript Dockerxx Name[zh_CN]=脚本工具面板 Name[zh_TW]=「指令稿」面板 Comment=A Python-based docker for create actions and point to Python scripts Comment[ar]=رصيف بِ‍«پيثون» لإنشاء الإجراءات والإشارة إلى سكربتات «پيثون» Comment[ca]=Un acoblador basat en Python per a crear accions i apuntar a scripts en Python Comment[ca@valencia]=Un acoblador basat en Python per a crear accions i apuntar a scripts en Python Comment[el]=Ένα εργαλείο προσάρτησης σε Python για τη δημιουργία ενεργειών και τη δεικτοδότηση σεναρίων σε Python Comment[en_GB]=A Python-based docker for create actions and point to Python scripts Comment[es]=Un panel basado en Python para crear y apuntar a guiones de Python +Comment[et]=Pythoni-põhine dokk toimingute loomiseks ja viitamiseks Pythoni skriptidele Comment[eu]=Python-oinarridun panel bat. Comment[gl]=Unha doca escrita en Python para crear accións e apuntar a scripts escritos en Python. Comment[it]=Un'area di aggancio basata su Python per creare azioni e scegliere script Python Comment[ko]=작업 생성 및 Python 스크립트를 가리키는 Python 기반 도킹 패널 Comment[nl]=Een op Python gebaseerde vastzetter voor aanmaakacties en wijzen naar Python-scripts Comment[nn]=Python-basert dokk for å laga handlingar og peika til Python-skript Comment[pl]=Dok oparty na pythonie do tworzenia działań i punktów dla skryptów pythona Comment[pt]=Uma área acoplável, feita em Python, para criar acções e apontar para programas em Python Comment[sv]=Ett Python-baserat dockningsfönster för att skapa åtgärder och peka ut Python-skript Comment[tr]=Eylemler oluşturmak ve Python betiklerine yönlendirmek için Python-tabanlı bir dok Comment[uk]=Бічна панель для створення дій і керування скриптами на основі Python. Comment[x-test]=xxA Python-based docker for create actions and point to Python scriptsxx Comment[zh_CN]=这是一个基于 Python 的工具面板,用于创建操作,并将他们指向 Python 脚本。 Comment[zh_TW]=基於 Python 的面板,用於建立動作並指向 Python 指令稿 diff --git a/plugins/python/scripter/kritapykrita_scripter.desktop b/plugins/python/scripter/kritapykrita_scripter.desktop index e79dde80b6..e648f51e56 100644 --- a/plugins/python/scripter/kritapykrita_scripter.desktop +++ b/plugins/python/scripter/kritapykrita_scripter.desktop @@ -1,49 +1,51 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=scripter X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Scripter Name[ca]=Scripter Name[ca@valencia]=Scripter Name[de]=Scripter Name[el]=Σενάρια Name[en_GB]=Scripter Name[es]=Guionador +Name[et]=Skriptija Name[eu]=Script egilea Name[fr]=Scripter Name[gl]=Executor de scripts Name[it]=Scripter Name[ko]=스크립트 도구 Name[nl]=Scriptmaker Name[nn]=Skriptkøyrer Name[pl]=Skrypter Name[pt]=Programador Name[sv]=Skriptgenerator Name[tr]=Betik yazarı Name[uk]=Скриптер Name[x-test]=xxScripterxx Name[zh_CN]=脚本工具 Name[zh_TW]=指令稿編寫者 Comment=Plugin to execute ad-hoc Python code Comment[ca]=Connector per executar codi «ad hoc» en Python Comment[ca@valencia]=Connector per executar codi «ad hoc» en Python Comment[el]=Πρόσθετο για την εκτέλεση συγκεκριμένου κώδικα Python Comment[en_GB]=Plugin to execute ad-hoc Python code Comment[es]=Complemento para ejecutar código Python a medida +Comment[et]=Plugin kohapeal loodud Pythoni koodi täitmiseks Comment[eu]=Berariaz egindako Python kodea exekutatzeko plugina Comment[fi]=Liitännäinen satunnaisen Python-koodin suorittamiseksi Comment[gl]=Complemento para executar código de Python escrito no momento. Comment[it]=Estensione per eseguire ad-hoc codice Python Comment[ko]=즉석에서 Python 코드를 실행하는 플러그인 Comment[nl]=Plug-in om ad-hoc Python code uit te voeren Comment[nn]=Programtillegg for køyring av ad hoc Python-kode Comment[pl]=Wtyczka do wykonywania kodu Pythona ad-hoc Comment[pt]='Plugin' para executar código em Python arbitrário Comment[sv]=Insticksprogram för att köra godtycklig Python-kod Comment[tr]=Geçici Python kodu çalıştırmak için eklenti Comment[uk]=Додаток для виконання апріорного коду Python Comment[x-test]=xxPlugin to execute ad-hoc Python codexx Comment[zh_CN]=用于执行当场编写的 Python 代码的插件 Comment[zh_TW]=外掛程式,用於執行特定 Python 程式碼 diff --git a/plugins/python/selectionsbagdocker/kritapykrita_selectionsbagdocker.desktop b/plugins/python/selectionsbagdocker/kritapykrita_selectionsbagdocker.desktop index 1b463b43a6..aa23405c62 100644 --- a/plugins/python/selectionsbagdocker/kritapykrita_selectionsbagdocker.desktop +++ b/plugins/python/selectionsbagdocker/kritapykrita_selectionsbagdocker.desktop @@ -1,50 +1,52 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=selectionsbagdocker X-Python-2-Compatible=false Name=Selections Bag Docker Name[ca]=Acoblador Bossa de seleccions Name[ca@valencia]=Acoblador Bossa de seleccions Name[el]=Προσάρτηση σάκου επιλογών Name[en_GB]=Selections Bag Docker Name[es]=Panel de selecciones +Name[et]=Valikukarbi dokk Name[eu]=Hautapen-zakua panela Name[fr]=Outils de sélection Name[gl]=Doca de bolsa das seleccións Name[it]=Area di raccolta selezioni Name[ko]=선택 가방 도킹 패널 Name[nl]=Docker van zak met selecties Name[nn]=Utvalssamlingsdokk Name[pl]=Dok worka zaznaczeń Name[pt]=Área de Selecções Name[sv]=Dockningspanel med markeringspåse Name[tr]=Seçim Çantası Doku Name[uk]=Бічна панель позначеного Name[x-test]=xxSelections Bag Dockerxx Name[zh_CN]=选区列表工具面板 Name[zh_TW]=「選取範圍收藏」面板 Comment=A docker that allow to store a list of selections Comment[ar]=رصيف يتيح تخزين قائمة تحديدات Comment[ca]=Un acoblador que permet emmagatzemar una llista de seleccions Comment[ca@valencia]=Un acoblador que permet emmagatzemar una llista de seleccions Comment[cs]=Dok umožňující uložit seznam výběrů Comment[el]=Ένα εργαλείο προσάρτησης που επιτρέπει την αποθήκευση μιας λίστας επιλογών Comment[en_GB]=A docker that allow to store a list of selections Comment[es]=Un panel que permite guardar una lista de selecciones +Comment[et]=Dokk valikute salvestamiseks Comment[eu]=Hautapen zerrenda bat biltegiratzen uzten duen panel bat Comment[fi]=Telakka, joka sallii tallentaa valintaluettelon Comment[fr]=Panneau permettant de conserver une liste de sélections Comment[gl]=Unha doca que permite almacenar unha lista de seleccións. Comment[it]=Un'area di aggancio che consente di memorizzare un elenco di selezioni Comment[ko]=선택 목록을 저장할 수 있는 도킹 패널 Comment[nl]=Een docker die een lijst met selecties kan opslaan Comment[nn]=Dokk for lagring av ei liste med utval Comment[pl]=Dok, który umożliwia przechowywanie listy zaznaczeń Comment[pt]=Uma área acoplável que permite guardar uma lista de selecções Comment[sv]=En dockningspanel som gör det möjligt att lagra en lista över markeringar Comment[tr]=Seçimlerin bir listesini saklamayı sağlayan bir dok Comment[uk]=Бічна панель, на якій можна зберігати список позначеного Comment[x-test]=xxA docker that allow to store a list of selectionsxx Comment[zh_CN]=用于保存一组选区的工具面板 Comment[zh_TW]=允許儲存選取範圍列表的面板 diff --git a/plugins/python/tenbrushes/kritapykrita_tenbrushes.desktop b/plugins/python/tenbrushes/kritapykrita_tenbrushes.desktop index bb8dd0a6bd..e638386384 100644 --- a/plugins/python/tenbrushes/kritapykrita_tenbrushes.desktop +++ b/plugins/python/tenbrushes/kritapykrita_tenbrushes.desktop @@ -1,52 +1,54 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=tenbrushes X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Ten Brushes Name[ar]=عشرُ فُرش Name[ca]=Deu pinzells Name[ca@valencia]=Deu pinzells Name[cs]=Deset štětců Name[el]=Δέκα πινέλα Name[en_GB]=Ten Brushes Name[es]=Diez pinceles +Name[et]=Kümme pintslit Name[eu]=Hamar isipu Name[fi]=Kymmenen sivellintä Name[fr]=Raccourcis des préréglages de brosses Name[gl]=Dez pinceis Name[is]=Tíu penslar Name[it]=Dieci pennelli Name[ko]=10개의 브러시 Name[nl]=Tien penselen Name[nn]=Ti penslar Name[pl]=Dziesięć pędzli Name[pt]=Dez Pincéis Name[sv]=Tio penslar Name[tr]=On Fırça Name[uk]=Десять пензлів Name[x-test]=xxTen Brushesxx Name[zh_CN]=常用笔刷快捷键 Name[zh_TW]=10 個筆刷 Comment=Assign a preset to ctrl-1 to ctrl-0 Comment[ca]=Assigna una predefinició des de Ctrl-1 a Ctrl-0 Comment[ca@valencia]=Assigna una predefinició des de Ctrl-1 a Ctrl-0 Comment[el]=Αντιστοίχιση προκαθορισμένου από ctrl-1 στο ctrl-0 Comment[en_GB]=Assign a preset to ctrl-1 to ctrl-0 Comment[es]=Asignar una preselección a Ctrl-1 hasta Ctrl-0 +Comment[et]=Valmisvaliku omistamine Ctrl+1 kuni Ctrl+0 Comment[eu]=Aurrezarpena ezarri ktrl-1'etik ktrl-0'ra arte Comment[fi]=Kytke esiasetukset Ctrl-1:stä Ctrl-0:aan Comment[gl]=Asigne unha predefinición do Ctrl+1 ao Ctrl+0. Comment[it]=Assegna una preimpostazione per ctrl-1 a ctrl-0 Comment[ko]=Ctrl+1부터 Ctrl+0까지 프리셋 할당 Comment[nl]=Een voorinstelling toekennen aan Ctrl-1 tot Ctrl-0 Comment[nn]=Byt til ferdigpenslar med «Ctrl + 1» til «Ctrl + 0» Comment[pl]=Przypisz nastawę do ctrl-1 lub ctrl-0 Comment[pt]=Atribuir uma predefinição de Ctrl-1 a Ctrl-0 Comment[sv]=Tilldela en förinställning för Ctrl+1 till Ctrl+0 Comment[tr]= ctrl-1 ve ctrl-0 için ayar atama Comment[uk]=Прив’язування наборів налаштувань до скорочень від ctrl-1 до ctrl-0 Comment[x-test]=xxAssign a preset to ctrl-1 to ctrl-0xx Comment[zh_CN]=将预设分配给由 Ctrl+1 到 Ctrl+0 的快捷键 Comment[zh_TW]=將預設指定給 ctrl-1 至 ctrl-0 diff --git a/plugins/python/tenscripts/kritapykrita_tenscripts.desktop b/plugins/python/tenscripts/kritapykrita_tenscripts.desktop index 16a93d1691..6d3e124d88 100644 --- a/plugins/python/tenscripts/kritapykrita_tenscripts.desktop +++ b/plugins/python/tenscripts/kritapykrita_tenscripts.desktop @@ -1,51 +1,53 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=tenscripts X-Python-2-Compatible=true X-Krita-Manual=Manual.html Name=Ten Scripts Name[ar]=عشرُ سكربتات Name[ca]=Deu scripts Name[ca@valencia]=Deu scripts Name[en_GB]=Ten Scripts Name[es]=Diez guiones +Name[et]=Kümme skripti Name[eu]=Hamar script Name[fi]=Kymmenen skriptiä Name[fr]=Raccourcis des scripts Name[gl]=Dez scripts Name[is]=Tíu skriftur Name[it]=Dieci script Name[ko]=10개의 스크립트 Name[nl]=Tien scripts Name[nn]=Ti skript Name[pl]=Skrypty Ten Name[pt]=Dez Programas Name[sv]=Tio skript Name[tr]=On Betik Name[uk]=Десять скриптів Name[x-test]=xxTen Scriptsxx Name[zh_CN]=常用脚本快捷键 Name[zh_TW]=10 個指令稿 Comment=A Python-based plugin for creating ten actions and assign them to Python scripts Comment[ar]=ملحقة بِ‍«پيثون» لإنشاء ١٠ إجراءات وإسنادها إلى سكربتات «پيثون» Comment[ca]=Un connector basat en Python per a crear deu accions i assignar-les a scripts en Python Comment[ca@valencia]=Un connector basat en Python per a crear deu accions i assignar-les a scripts en Python Comment[en_GB]=A Python-based plugin for creating ten actions and assign them to Python scripts Comment[es]=Un complemento basado en Python para crear diez acciones y asignarlas a guiones de Python +Comment[et]=Pythoni-põhine plugin kümne toimingu loomiseks ja nende omistamiseks Pythoni skriptidele Comment[eu]=Hamar ekintza sortu eta haiek Python-scriptei esleitzeko Python-oinarridun plugin bat Comment[fi]=Python-pohjainen liitännäinen kymmenen toiminnon luomiseksi kytkemiseksi Python-skripteihin Comment[fr]=Module externe basé sur Python pour créer dix actions et les assigner à des scripts Python Comment[gl]=Un complemento escrito en Python para crear dez accións e asignalas a scripts escritos en Python. Comment[it]=Un'estensione basata su Python per creare dieci azioni e assegnarle a script Python Comment[ko]=10개의 작업을 생성하고 이를 Python 스크립트에 할당하는 Python 기반 플러그인 Comment[nl]=Een op Python gebaseerde plug-in voor aanmaken van tien acties en ze dan toewijzen aan Python-scripts Comment[nn]=Python-basert tillegg som legg til ti handlingar du kan tildela til Python-skript Comment[pl]=Wtyczka oparta na Pythonie do tworzenia działań i przypisywanie ich skryptom Pythona Comment[pt]=Um 'plugin' feito em Python para criar dez acções e atribuí-las a programas em Python Comment[sv]=Ett Python-baserat insticksprogram för att skapa tio åtgärder och tilldela dem till Python-skript Comment[tr]=On eylem oluşturmak ve Python betiklerine atamak için Python tabanlı bir eklenti Comment[uk]=Скрипт на основі Python для створення десяти дій і прив'язування до них скриптів Python Comment[x-test]=xxA Python-based plugin for creating ten actions and assign them to Python scriptsxx Comment[zh_CN]=这是一个基于 Python 编写的插件,它可以创建十种操作,并将它们指定到特定 Python 脚本。 Comment[zh_TW]=基於 Python 的外掛程式,用於建立 10 個動作並且將它們指定給 Python 指令稿 diff --git a/plugins/tools/basictools/kis_tool_colorpicker.cc b/plugins/tools/basictools/kis_tool_colorpicker.cc index 4795c1b243..19a090d52e 100644 --- a/plugins/tools/basictools/kis_tool_colorpicker.cc +++ b/plugins/tools/basictools/kis_tool_colorpicker.cc @@ -1,372 +1,421 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2002 Patrick Julien * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2018 Emmet & Eoin O'Neill * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tool_colorpicker.h" #include #include #include "kis_cursor.h" #include "KisDocument.h" #include "kis_canvas2.h" #include "KisReferenceImagesLayer.h" #include "KoCanvasBase.h" #include "kis_random_accessor_ng.h" #include "KoResourceServerProvider.h" #include #include "kis_wrapped_rect.h" #include "kis_tool_utils.h" namespace { // GUI ComboBox index constants const int SAMPLE_MERGED = 0; } KisToolColorPicker::KisToolColorPicker(KoCanvasBase *canvas) : KisTool(canvas, KisCursor::pickerCursor()), m_config(new KisToolUtils::ColorPickerConfig) { setObjectName("tool_colorpicker"); m_isActivated = false; m_optionsWidget = 0; m_pickedColor = KoColor(); + KoResourceServer *srv = KoResourceServerProvider::instance()->paletteServer(); + srv->addObserver(this); } KisToolColorPicker::~KisToolColorPicker() { if (m_isActivated) { m_config->save(m_toolActivationSource == KisTool::DefaultActivation); } + KoResourceServer *srv = KoResourceServerProvider::instance()->paletteServer(); + srv->removeObserver(this); } void KisToolColorPicker::paint(QPainter &gc, const KoViewConverter &converter) { Q_UNUSED(gc); Q_UNUSED(converter); } void KisToolColorPicker::activate(ToolActivation activation, const QSet &shapes) { m_isActivated = true; m_toolActivationSource = activation; m_config->load(m_toolActivationSource == KisTool::DefaultActivation); updateOptionWidget(); KisTool::activate(activation, shapes); } void KisToolColorPicker::deactivate() { m_config->save(m_toolActivationSource == KisTool::DefaultActivation); m_isActivated = false; KisTool::deactivate(); } bool KisToolColorPicker::pickColor(const QPointF &pos) { // Timer check. if (m_colorPickerDelayTimer.isActive()) { return false; } else { m_colorPickerDelayTimer.setSingleShot(true); m_colorPickerDelayTimer.start(100); } QScopedPointer> imageLocker; m_pickedColor.setOpacity(0.0); // Pick from reference images. if (m_optionsWidget->cmbSources->currentIndex() == SAMPLE_MERGED) { auto *kisCanvas = dynamic_cast(canvas()); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(kisCanvas, false); KisSharedPtr referenceImageLayer = kisCanvas->imageView()->document()->referenceImagesLayer(); if (referenceImageLayer && kisCanvas->referenceImagesDecoration()->visible()) { QColor color = referenceImageLayer->getPixel(pos); if (color.isValid()) { m_pickedColor.fromQColor(color); } } } if (m_pickedColor.opacityU8() == OPACITY_TRANSPARENT_U8) { if (!currentImage()->bounds().contains(pos.toPoint()) && !currentImage()->wrapAroundModePermitted()) { return false; } KisPaintDeviceSP dev; if (m_optionsWidget->cmbSources->currentIndex() != SAMPLE_MERGED && currentNode() && currentNode()->colorPickSourceDevice()) { dev = currentNode()->colorPickSourceDevice(); } else { imageLocker.reset(new boost::lock_guard(*currentImage())); dev = currentImage()->projection(); } KoColor previousColor = canvas()->resourceManager()->foregroundColor(); KisToolUtils::pickColor(m_pickedColor, dev, pos.toPoint(), &previousColor, m_config->radius, m_config->blend); } if (m_config->updateColor && m_pickedColor.opacityU8() != OPACITY_TRANSPARENT_U8) { KoColor publicColor = m_pickedColor; publicColor.setOpacity(OPACITY_OPAQUE_U8); // Alpha is unwanted for FG and BG colors. if (m_config->toForegroundColor) { canvas()->resourceManager()->setResource(KoCanvasResourceProvider::ForegroundColor, publicColor); } else { canvas()->resourceManager()->setResource(KoCanvasResourceProvider::BackgroundColor, publicColor); } } return true; } void KisToolColorPicker::beginPrimaryAction(KoPointerEvent *event) { bool sampleMerged = m_optionsWidget->cmbSources->currentIndex() == SAMPLE_MERGED; if (!sampleMerged) { if (!currentNode()) { QMessageBox::information(0, i18nc("@title:window", "Krita"), i18n("Cannot pick a color as no layer is active.")); event->ignore(); return; } if (!currentNode()->visible()) { QMessageBox::information(0, i18nc("@title:window", "Krita"), i18n("Cannot pick a color as the active layer is not visible.")); event->ignore(); return; } } QPoint pos = convertToImagePixelCoordFloored(event); setMode(KisTool::PAINT_MODE); bool picked = pickColor(pos); if (!picked) { // Color picking has to start in the visible part of the layer event->ignore(); return; } displayPickedColor(); } void KisToolColorPicker::continuePrimaryAction(KoPointerEvent *event) { CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); QPoint pos = convertToImagePixelCoordFloored(event); pickColor(pos); displayPickedColor(); } #include "kis_display_color_converter.h" void KisToolColorPicker::endPrimaryAction(KoPointerEvent *event) { Q_UNUSED(event); CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); if (m_config->addColorToCurrentPalette) { KisSwatch swatch; swatch.setColor(m_pickedColor); // We don't ask for a name, too intrusive here - KoColorSet *palette = m_palettes.at(m_optionsWidget->cmbPalette->currentIndex()); + KoColorSet *palette = m_palettes.at(m_optionsWidget->cmbPalette->currentIndex()); palette->add(swatch); if (!palette->save()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Cannot write to palette file %1. Maybe it is read-only.", palette->filename())); - } + } } } struct PickedChannel { QString name; QString valueText; }; void KisToolColorPicker::displayPickedColor() { if (m_pickedColor.data() && m_optionsWidget) { QList channels = m_pickedColor.colorSpace()->channels(); m_optionsWidget->listViewChannels->clear(); QVector pickedChannels; for (int i = 0; i < channels.count(); ++i) { pickedChannels.append(PickedChannel()); } for (int i = 0; i < channels.count(); ++i) { PickedChannel pc; pc.name = channels[i]->name(); if (m_config->normaliseValues) { pc.valueText = m_pickedColor.colorSpace()->normalisedChannelValueText(m_pickedColor.data(), i); } else { pc.valueText = m_pickedColor.colorSpace()->channelValueText(m_pickedColor.data(), i); } pickedChannels[channels[i]->displayPosition()] = pc; } Q_FOREACH (const PickedChannel &pc, pickedChannels) { QTreeWidgetItem *item = new QTreeWidgetItem(m_optionsWidget->listViewChannels); item->setText(0, pc.name); item->setText(1, pc.valueText); } KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); KoColor newColor = kritaCanvas->displayColorConverter()->applyDisplayFiltering(m_pickedColor, Float32BitsColorDepthID); QVector values(4); newColor.colorSpace()->normalisedChannelsValue(newColor.data(), values); for (int i = 0; i < values.size(); i++) { QTreeWidgetItem *item = new QTreeWidgetItem(m_optionsWidget->listViewChannels); item->setText(0, QString("DisplayCh%1").arg(i)); item->setText(1, QString::number(values[i])); } } } QWidget* KisToolColorPicker::createOptionWidget() { m_optionsWidget = new ColorPickerOptionsWidget(0); m_optionsWidget->setObjectName(toolId() + " option widget"); m_optionsWidget->listViewChannels->setSortingEnabled(false); // See https://bugs.kde.org/show_bug.cgi?id=316896 QWidget *specialSpacer = new QWidget(m_optionsWidget); specialSpacer->setObjectName("SpecialSpacer"); specialSpacer->setFixedSize(0, 0); m_optionsWidget->layout()->addWidget(specialSpacer); // Initialize blend KisSliderSpinBox m_optionsWidget->blend->setRange(0,100); m_optionsWidget->blend->setSuffix(i18n("%")); updateOptionWidget(); connect(m_optionsWidget->cbUpdateCurrentColor, SIGNAL(toggled(bool)), SLOT(slotSetUpdateColor(bool))); connect(m_optionsWidget->cbNormaliseValues, SIGNAL(toggled(bool)), SLOT(slotSetNormaliseValues(bool))); connect(m_optionsWidget->cbPalette, SIGNAL(toggled(bool)), SLOT(slotSetAddPalette(bool))); connect(m_optionsWidget->radius, SIGNAL(valueChanged(int)), SLOT(slotChangeRadius(int))); connect(m_optionsWidget->blend, SIGNAL(valueChanged(int)), SLOT(slotChangeBlend(int))); connect(m_optionsWidget->cmbSources, SIGNAL(currentIndexChanged(int)), SLOT(slotSetColorSource(int))); KoResourceServer *srv = KoResourceServerProvider::instance()->paletteServer(); if (!srv) { return m_optionsWidget; } QList palettes = srv->resources(); Q_FOREACH (KoColorSet *palette, palettes) { if (palette) { m_optionsWidget->cmbPalette->addSqueezedItem(palette->name()); m_palettes.append(palette); } } return m_optionsWidget; } void KisToolColorPicker::updateOptionWidget() { if (!m_optionsWidget) return; m_optionsWidget->cbNormaliseValues->setChecked(m_config->normaliseValues); m_optionsWidget->cbUpdateCurrentColor->setChecked(m_config->updateColor); m_optionsWidget->cmbSources->setCurrentIndex(SAMPLE_MERGED + !m_config->sampleMerged); m_optionsWidget->cbPalette->setChecked(m_config->addColorToCurrentPalette); m_optionsWidget->radius->setValue(m_config->radius); m_optionsWidget->blend->setValue(m_config->blend); } void KisToolColorPicker::setToForeground(bool newValue) { m_config->toForegroundColor = newValue; emit toForegroundChanged(); } bool KisToolColorPicker::toForeground() const { return m_config->toForegroundColor; } void KisToolColorPicker::slotSetUpdateColor(bool state) { m_config->updateColor = state; } void KisToolColorPicker::slotSetNormaliseValues(bool state) { m_config->normaliseValues = state; displayPickedColor(); } void KisToolColorPicker::slotSetAddPalette(bool state) { m_config->addColorToCurrentPalette = state; } void KisToolColorPicker::slotChangeRadius(int value) { m_config->radius = value; } void KisToolColorPicker::slotChangeBlend(int value) { m_config->blend = value; } void KisToolColorPicker::slotSetColorSource(int value) { m_config->sampleMerged = value == SAMPLE_MERGED; } -void KisToolColorPicker::slotAddPalette(KoResource *resource) +void KisToolColorPicker::unsetResourceServer() { - KoColorSet *palette = dynamic_cast(resource); - if (palette) { - m_optionsWidget->cmbPalette->addSqueezedItem(palette->name()); - m_palettes.append(palette); + KoResourceServer *srv = KoResourceServerProvider::instance()->paletteServer(); + srv->removeObserver(this); +} + +void KisToolColorPicker::resourceAdded(KoColorSet* resource){ + if(!resource || !m_optionsWidget) return; + if(m_palettes.contains(resource)) return; + + if(m_config->addColorToCurrentPalette){ + updateCmbPalette(); + } +} + +void KisToolColorPicker::removingResource(KoColorSet* resource){ + if(!resource || !m_optionsWidget) return; + if(!m_palettes.contains(resource)) return; + + if(m_config->addColorToCurrentPalette){ + updateCmbPalette(); + } +} + +void KisToolColorPicker::updateCmbPalette(){ + m_optionsWidget->cmbPalette->clear(); + m_palettes.clear(); + + KoResourceServer *srv = KoResourceServerProvider::instance()->paletteServer(); + + if (!srv) { + return ; } + + QList palettes = srv->resources(); + + Q_FOREACH (KoColorSet *palette, palettes) { + if (palette) { + m_optionsWidget->cmbPalette->addSqueezedItem(palette->name()); + m_palettes.append(palette); + } + } + } + +void KisToolColorPicker::resourceChanged(PointerType /*resource*/) +{} + +void KisToolColorPicker::syncTaggedResourceView() {} + +void KisToolColorPicker::syncTagAddition(const QString& /*tag*/) {} + +void KisToolColorPicker::syncTagRemoval(const QString& /*tag*/) {} \ No newline at end of file diff --git a/plugins/tools/basictools/kis_tool_colorpicker.h b/plugins/tools/basictools/kis_tool_colorpicker.h index ce485f1995..a7c078570e 100644 --- a/plugins/tools/basictools/kis_tool_colorpicker.h +++ b/plugins/tools/basictools/kis_tool_colorpicker.h @@ -1,142 +1,152 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2002 Patrick Julien * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2018 Emmet & Eoin O'Neill * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_TOOL_COLOR_PICKER_H_ #define KIS_TOOL_COLOR_PICKER_H_ #include #include "KoToolFactoryBase.h" #include "ui_wdgcolorpicker.h" #include "kis_tool.h" #include +#include + class KoResource; class KoColorSet; namespace KisToolUtils { struct ColorPickerConfig; } class ColorPickerOptionsWidget : public QWidget, public Ui::ColorPickerOptionsWidget { Q_OBJECT public: ColorPickerOptionsWidget(QWidget *parent) : QWidget(parent) { setupUi(this); } }; -class KisToolColorPicker : public KisTool +class KisToolColorPicker : public KisTool,public KoResourceServerObserver { Q_OBJECT Q_PROPERTY(bool toForeground READ toForeground WRITE setToForeground NOTIFY toForegroundChanged) public: KisToolColorPicker(KoCanvasBase *canvas); ~KisToolColorPicker() override; public: struct Configuration { Configuration(); bool toForegroundColor; bool updateColor; bool addPalette; bool normaliseValues; bool sampleMerged; int radius; int blend; void save(ToolActivation activation) const; void load(ToolActivation activation); }; public: QWidget* createOptionWidget() override; void beginPrimaryAction(KoPointerEvent *event) override; void continuePrimaryAction(KoPointerEvent *event) override; void endPrimaryAction(KoPointerEvent *event) override; void paint(QPainter &gc, const KoViewConverter &converter) override; bool toForeground() const; +public: //KoResourceServerObserver + void unsetResourceServer() override; + void resourceAdded(KoColorSet* resource) override; + void removingResource(KoColorSet* resource) override; + void resourceChanged(KoColorSet* resource) override; + void syncTaggedResourceView() override; + void syncTagAddition(const QString& tag) override; + void syncTagRemoval(const QString& tag) override; + Q_SIGNALS: void toForegroundChanged(); protected: void activate(ToolActivation activation, const QSet &) override; void deactivate() override; public Q_SLOTS: void setToForeground(bool newValue); void slotSetUpdateColor(bool); void slotSetNormaliseValues(bool); void slotSetAddPalette(bool); void slotChangeRadius(int); void slotChangeBlend(int); - void slotAddPalette(KoResource* resource); void slotSetColorSource(int value); private: void displayPickedColor(); bool pickColor(const QPointF& pos); void updateOptionWidget(); - + void updateCmbPalette(); // Configuration QScopedPointer m_config; ToolActivation m_toolActivationSource; bool m_isActivated; KoColor m_pickedColor; // Used to skip some tablet events and update color less often QTimer m_colorPickerDelayTimer; ColorPickerOptionsWidget *m_optionsWidget; QList m_palettes; }; class KisToolColorPickerFactory : public KoToolFactoryBase { public: KisToolColorPickerFactory() : KoToolFactoryBase("KritaSelected/KisToolColorPicker") { setToolTip(i18n("Color Selector Tool")); setSection(TOOL_TYPE_FILL); setPriority(2); setIconName(koIconNameCStr("krita_tool_color_picker")); setShortcut(QKeySequence(Qt::Key_P)); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); } ~KisToolColorPickerFactory() override {} KoToolBase *createTool(KoCanvasBase *canvas) override { return new KisToolColorPicker(canvas); } }; #endif // KIS_TOOL_COLOR_PICKER_H_ diff --git a/plugins/tools/basictools/kis_tool_line_helper.cpp b/plugins/tools/basictools/kis_tool_line_helper.cpp index 28a268a608..f823469b3e 100644 --- a/plugins/tools/basictools/kis_tool_line_helper.cpp +++ b/plugins/tools/basictools/kis_tool_line_helper.cpp @@ -1,190 +1,262 @@ /* * Copyright (c) 2014 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tool_line_helper.h" +#include + #include "kis_algebra_2d.h" #include "kis_painting_information_builder.h" #include "kis_image.h" +#include "kis_canvas_resource_provider.h" +#include + struct KisToolLineHelper::Private { Private(KisPaintingInformationBuilder *_infoBuilder) : infoBuilder(_infoBuilder), useSensors(true), enabled(true) { } QVector linePoints; KisPaintingInformationBuilder *infoBuilder; bool useSensors; bool enabled; }; KisToolLineHelper::KisToolLineHelper(KisPaintingInformationBuilder *infoBuilder, const KUndo2MagicString &transactionText) : KisToolFreehandHelper(infoBuilder, transactionText, new KisSmoothingOptions(false)), m_d(new Private(infoBuilder)) { } KisToolLineHelper::~KisToolLineHelper() { delete m_d; } void KisToolLineHelper::setEnabled(bool value) { m_d->enabled = value; } void KisToolLineHelper::setUseSensors(bool value) { m_d->useSensors = value; } void KisToolLineHelper::repaintLine(KoCanvasResourceProvider *resourceManager, KisImageWSP image, KisNodeSP node, KisStrokesFacade *strokesFacade) { if (!m_d->enabled) return; cancelPaint(); if (m_d->linePoints.isEmpty()) return; qreal startAngle = 0.0; if (m_d->linePoints.length() > 1) { startAngle = KisAlgebra2D::directionBetweenPoints(m_d->linePoints[0].pos(), m_d->linePoints[1].pos(), 0.0); } + + KisPaintOpPresetSP preset = resourceManager->resource(KisCanvasResourceProvider::CurrentPaintOpPreset) + .value(); + + if (preset->settings()->paintOpSize() <= 1) { + KisPaintInformation begin = m_d->linePoints.first(); + KisPaintInformation end = m_d->linePoints.last(); + m_d->linePoints.clear(); + m_d->linePoints.append(begin); + m_d->linePoints.append(end); + } + // Always adjust line sections to avoid jagged sections. + adjustPointsToDDA(m_d->linePoints); + QVector::const_iterator it = m_d->linePoints.constBegin(); QVector::const_iterator end = m_d->linePoints.constEnd(); initPaintImpl(startAngle, *it, resourceManager, image, node, strokesFacade); ++it; while (it != end) { paintLine(*(it - 1), *it); ++it; } } void KisToolLineHelper::start(KoPointerEvent *event, KoCanvasResourceProvider *resourceManager) { if (!m_d->enabled) return; // Ignore the elapsed stroke time, so that the line tool will behave as if the whole stroke is // drawn at once. This should prevent any possible spurious dabs caused by airbrushing features. KisPaintInformation pi = m_d->infoBuilder->startStroke(event, 0, resourceManager); if (!m_d->useSensors) { pi = KisPaintInformation(pi.pos()); } m_d->linePoints.append(pi); } void KisToolLineHelper::addPoint(KoPointerEvent *event, const QPointF &overridePos) { if (!m_d->enabled) return; // Ignore the elapsed stroke time, so that the line tool will behave as if the whole stroke is // drawn at once. This should prevent any possible spurious dabs caused by airbrushing features. KisPaintInformation pi = m_d->infoBuilder->continueStroke(event, 0); if (!m_d->useSensors) { pi = KisPaintInformation(pi.pos()); } if (!overridePos.isNull()) { pi.setPos(overridePos); } if (m_d->linePoints.size() > 1) { const QPointF startPos = m_d->linePoints.first().pos(); const QPointF endPos = pi.pos(); const qreal maxDistance = kisDistance(startPos, endPos); const QPointF unit = (endPos - startPos) / maxDistance; QVector::iterator it = m_d->linePoints.begin(); ++it; while (it != m_d->linePoints.end()) { qreal dist = kisDistance(startPos, it->pos()); if (dist < maxDistance) { QPointF pos = startPos + unit * dist; it->setPos(pos); ++it; } else { it = m_d->linePoints.erase(it); } } } m_d->linePoints.append(pi); } void KisToolLineHelper::translatePoints(const QPointF &offset) { if (!m_d->enabled) return; QVector::iterator it = m_d->linePoints.begin(); while (it != m_d->linePoints.end()) { it->setPos(it->pos() + offset); ++it; } } void KisToolLineHelper::end() { if (!m_d->enabled) return; KIS_ASSERT_RECOVER_RETURN(isRunning()); endPaint(); clearPoints(); } void KisToolLineHelper::cancel() { if (!m_d->enabled) return; KIS_ASSERT_RECOVER_RETURN(isRunning()); cancelPaint(); clearPoints(); } void KisToolLineHelper::clearPoints() { m_d->linePoints.clear(); } void KisToolLineHelper::clearPaint() { if (!m_d->enabled) return; cancelPaint(); } + +void KisToolLineHelper::adjustPointsToDDA(QVector &points) +{ + int x = qFloor(points.first().pos().x()); + int y = qFloor(points.first().pos().y()); + + int x2 = qFloor(points.last().pos().x()); + int y2 = qFloor(points.last().pos().y()); + + // Width and height of the line + int xd = x2 - x; + int yd = y2 - y; + + float m = 0; + bool lockAxis = true; + + if (xd == 0) { + m = 2.0; + } else if ( yd != 0) { + lockAxis = false; + m = (float)yd / (float)xd; + } + + float fx = x; + float fy = y; + + int inc; + int dist; + + if (fabs(m) > 1.0f) { + inc = (yd > 0) ? 1 : -1; + m = (lockAxis)? 0 : 1.0f / m; + m *= inc; + + for (int i = 0; i < points.size(); i++){ + dist = abs(qFloor(points.at(i).pos().y()) - y); + fy = y + (dist * inc); + fx = qRound(x + (dist * m)); + points[i].setPos(QPointF(fx,fy)); + } + + } else { + inc = (xd > 0) ? 1 : -1; + m *= inc; + + for (int i = 0; i < points.size(); i++){ + dist = abs(qFloor(points.at(i).pos().x()) - x); + fx = x + (dist * inc); + fy = qRound(y + (dist * m)); + points[i].setPos(QPointF(fx,fy)); + } + } +} diff --git a/plugins/tools/basictools/kis_tool_line_helper.h b/plugins/tools/basictools/kis_tool_line_helper.h index 13e3863e2c..c8cad7907a 100644 --- a/plugins/tools/basictools/kis_tool_line_helper.h +++ b/plugins/tools/basictools/kis_tool_line_helper.h @@ -1,56 +1,59 @@ /* * Copyright (c) 2014 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_TOOL_LINE_HELPER_H #define __KIS_TOOL_LINE_HELPER_H #include "kis_tool_freehand_helper.h" class KisToolLineHelper : private KisToolFreehandHelper { public: KisToolLineHelper(KisPaintingInformationBuilder *infoBuilder, const KUndo2MagicString &transactionText); ~KisToolLineHelper() override; void setEnabled(bool value); void setUseSensors(bool value); void repaintLine(KoCanvasResourceProvider *resourceManager, KisImageWSP image, KisNodeSP node, KisStrokesFacade *strokesFacade); void start(KoPointerEvent *event, KoCanvasResourceProvider *resourceManager); void addPoint(KoPointerEvent *event, const QPointF &overridePos = QPointF()); void translatePoints(const QPointF &offset); void end(); void cancel(); void clearPoints(); void clearPaint(); using KisToolFreehandHelper::isRunning; +private: + void adjustPointsToDDA(QVector &points); + private: struct Private; Private * const m_d; }; #endif /* __KIS_TOOL_LINE_HELPER_H */ diff --git a/plugins/tools/svgtexttool/kis_font_family_combo_box.cpp b/plugins/tools/svgtexttool/kis_font_family_combo_box.cpp index 9fc7aae48f..b789d9094a 100644 --- a/plugins/tools/svgtexttool/kis_font_family_combo_box.cpp +++ b/plugins/tools/svgtexttool/kis_font_family_combo_box.cpp @@ -1,303 +1,306 @@ /* This file is part of the KDE project * * Copyright 2017 Wolthera van Hövell tot Westerflier * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_font_family_combo_box.h" #include #include #include #include #include #include #include #include #include #include #include PinnedFontsSeparator::PinnedFontsSeparator(QAbstractItemDelegate *_default, QWidget *parent) : QStyledItemDelegate(parent), m_separatorIndex(0), m_separatorAdded(false), m_defaultDelegate(_default) { } void PinnedFontsSeparator::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.row() == m_separatorIndex && m_separatorAdded) { QRect viewRect = option.rect; painter->setPen(Qt::gray); painter->drawLine((viewRect.topLeft() + viewRect.bottomLeft()) / 2 + QPoint(5, 0), (viewRect.topRight() + viewRect.bottomRight()) / 2 - QPoint(5, 0)); } else { m_defaultDelegate->paint(painter, option, index); } } QSize PinnedFontsSeparator::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { return QStyledItemDelegate::sizeHint(option, index) * 1.25; } void PinnedFontsSeparator::setSeparatorIndex(int index) { m_separatorIndex = index; } void PinnedFontsSeparator::setSeparatorAdded() { m_separatorAdded = true; } KisFontFamilyComboBox::KisFontFamilyComboBox(QWidget *parent) : QComboBox(parent), m_initilized(false), m_initializeFromConfig(false) { setEditable(true); completer()->setCompletionMode(QCompleter::InlineCompletion); completer()->setCaseSensitivity(Qt::CaseInsensitive); // The following are all helper fonts for LaTeX that no one but LaTeX would use // but because many people use LaTeX, they do show up on quite a few systems. m_blacklistedFonts << "bbold10" << "cmbsy10" << "cmmib10" << "cmss10" << "cmex10" << "cmmi10" << "cmr10" << "cmsy10" << "eufb10" << "eufm10" << "eurb10" << "eurm10" << "esint10" << "eufm10" << "eusb10" << "eusm10" << "lasy10" << "lasyb10" << "msam10" << "msbm10" << "rsfs10" << "stmary10" << "wasy10" << "wasyb10"; refillComboBox(); - QFontComboBox *temp = new QFontComboBox(); + QFontComboBox *temp = new QFontComboBox(this); m_fontSeparator = new PinnedFontsSeparator(temp->itemDelegate(), this); temp->setEnabled(true); temp->hide(); m_separatorIndex = 0; m_pinnedFonts = KisConfig(true).readList("PinnedFonts", QStringList{}); } void KisFontFamilyComboBox::refillComboBox(QVector writingSystems) { QFontDatabase fonts = QFontDatabase(); int maxWidth = 0; this->clear(); QStringList duplicateFonts; QStringList filteredFonts; if (writingSystems.isEmpty()) { writingSystems.append(QFontDatabase::Any); } for (int i = 0; i < writingSystems.size(); i++) { Q_FOREACH (QString family, fonts.families(writingSystems.at(i))) { // if it's a private family it shouldn't be added. bool addFont = !fonts.isPrivateFamily(family); if (addFont && filteredFonts.contains(family)) { addFont = false; } if (addFont && duplicateFonts.contains(family)) { addFont = false; } if (addFont && m_blacklistedFonts.contains(family)) { addFont = false; } if (addFont && !fonts.isSmoothlyScalable(family)) { addFont = false; } if (addFont) { // now, check for all possible familyname+style name combinations, so we can avoid those. Q_FOREACH (const QString style, fonts.styles(family)) { duplicateFonts.append(family + " " + style); duplicateFonts.append(family + "_" + style); } filteredFonts.append(family); int width = 1.5 * view()->fontMetrics() .width(family + " " + fonts.writingSystemSample(QFontDatabase::Any)); if (width > maxWidth) { maxWidth = width; } } } } this->addItems(filteredFonts); if (this->count() > this->maxVisibleItems()) { maxWidth += view()->style()->pixelMetric(QStyle::PixelMetric::PM_ScrollBarExtent); } view()->setMinimumWidth(maxWidth); } // KisFontFamilyComboBox::refillComboBox void KisFontFamilyComboBox::setTopFont(const QString &family) { if (family.isEmpty() || !m_initilized || m_pinnedFonts.contains(family)) { return; } if (m_pinnedFonts.count() > 4) { this->removeItem(4); m_pinnedFonts.pop_back(); m_separatorIndex--; } if (m_pinnedFonts.isEmpty()) { this->insertSeparator(0); m_fontSeparator->setSeparatorAdded(); } m_pinnedFonts.push_front(family); this->insertItem(0, family); m_separatorIndex++; m_fontSeparator->setSeparatorIndex(m_separatorIndex); KisConfig(false).writeList("PinnedFonts", m_pinnedFonts); } void KisFontFamilyComboBox::setInitialized() { + if(m_initilized) + return; + m_initilized = true; for(int i=m_pinnedFonts.count()-1; i>=0; i--){ this->insertItem(0, m_pinnedFonts[i]); m_separatorIndex++; } if(m_pinnedFonts.count() > 0){ this->insertSeparator(m_separatorIndex); m_fontSeparator->setSeparatorIndex(m_separatorIndex); m_fontSeparator->setSeparatorAdded(); } this->setItemDelegate(m_fontSeparator); } KisFontComboBoxes::KisFontComboBoxes(QWidget *parent) : QWidget(parent) { QHBoxLayout *layout = new QHBoxLayout(); this->setLayout(layout); m_family = new KisFontFamilyComboBox(); m_family->setMinimumWidth(100); m_family->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); layout->addWidget(m_family); m_styles = new QComboBox(); layout->addWidget(m_styles); fontFamilyChanged(); m_family->setToolTip(i18n("Font Family")); m_styles->setToolTip(i18n("Font Style")); connect(m_family, SIGNAL(currentTextChanged(QString)), this, SLOT(fontFamilyChanged())); connect(m_family, SIGNAL(currentTextChanged(QString)), this, SLOT(fontChange())); connect(m_styles, SIGNAL(activated(int)), this, SLOT(fontChange())); } void KisFontComboBoxes::setCurrentFont(QFont font) { setCurrentFamily(font.family()); setCurrentStyle(QFontDatabase().styleString(font)); } void KisFontComboBoxes::setCurrentFamily(const QString family) { m_family->setCurrentText(family); fontFamilyChanged(); } void KisFontComboBoxes::setCurrentStyle(QString style) { QString properStyle = QString(); for (int i = 0; i < m_styles->count(); i++) { QString item = m_styles->itemText(i); if (item == style) { properStyle = style; } else if (item.contains(style, Qt::CaseInsensitive)) { properStyle = item; } else if (item.contains("regular", Qt::CaseInsensitive)) { properStyle = item; } } m_styles->setCurrentText(properStyle); } QString KisFontComboBoxes::currentFamily() const { return m_family->currentText(); } QString KisFontComboBoxes::currentStyle() const { return m_styles->currentText(); } QFont KisFontComboBoxes::currentFont(int pointSize) const { return QFontDatabase().font(m_family->currentText(), m_styles->currentText(), pointSize); } void KisFontComboBoxes::refillComboBox(QVector writingSystems) { KisFontFamilyComboBox *cmb = qobject_cast(m_family); cmb->refillComboBox(writingSystems); } void KisFontComboBoxes::fontFamilyChanged() { QString currentText = m_styles->currentText(); QFontDatabase fonts; const QString family = m_family->currentText(); int maxWidth = 0; m_styles->clear(); QStringList styles; KisFontFamilyComboBox *cmb = qobject_cast(m_family); cmb->setTopFont(family); if (fonts.styles(family).isEmpty()) { styles.append("Normal"); } Q_FOREACH (const QString style, fonts.styles(family)) { int b = fonts.weight(family, style); int bindex = 0; for (int i = 0; i < styles.size(); i++) { if (b > fonts.weight(family, styles.at(i))) { bindex = i; } } if (!styles.contains(style)) { styles.insert(bindex, style); maxWidth = qMax(m_styles->view()->fontMetrics().width(style + " "), maxWidth); } } m_styles->addItems(styles); if (m_styles->count() > m_styles->maxVisibleItems()) { maxWidth += m_styles->view()->style()->pixelMetric(QStyle::PixelMetric::PM_ScrollBarExtent); } m_styles->view()->setMinimumWidth(maxWidth); if (styles.contains(currentText)) { m_styles->setCurrentText(currentText); } } // KisFontComboBoxes::fontFamilyChanged void KisFontComboBoxes::fontChange() { emit fontChanged(currentFont(10).toString()); } void KisFontComboBoxes::setInitialized() { KisFontFamilyComboBox *cmb = qobject_cast(m_family); cmb->setInitialized(); } diff --git a/plugins/tools/tool_transform2/kis_tool_transform.cc b/plugins/tools/tool_transform2/kis_tool_transform.cc index 32ace87044..529bed98ef 100644 --- a/plugins/tools/tool_transform2/kis_tool_transform.cc +++ b/plugins/tools/tool_transform2/kis_tool_transform.cc @@ -1,1125 +1,1125 @@ /* * kis_tool_transform.cc -- part of Krita * * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2005 C. Boemann * Copyright (c) 2010 Marc Pegon * Copyright (c) 2013 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tool_transform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "widgets/kis_progress_widget.h" #include "kis_transform_utils.h" #include "kis_warp_transform_strategy.h" #include "kis_cage_transform_strategy.h" #include "kis_liquify_transform_strategy.h" #include "kis_free_transform_strategy.h" #include "kis_perspective_transform_strategy.h" #include "kis_transform_mask.h" #include "kis_transform_mask_adapter.h" #include "krita_container_utils.h" #include "kis_layer_utils.h" #include #include "strokes/transform_stroke_strategy.h" KisToolTransform::KisToolTransform(KoCanvasBase * canvas) : KisTool(canvas, KisCursor::rotateCursor()) , m_workRecursively(true) , m_warpStrategy( new KisWarpTransformStrategy( dynamic_cast(canvas)->coordinatesConverter(), m_currentArgs, m_transaction)) , m_cageStrategy( new KisCageTransformStrategy( dynamic_cast(canvas)->coordinatesConverter(), m_currentArgs, m_transaction)) , m_liquifyStrategy( new KisLiquifyTransformStrategy( dynamic_cast(canvas)->coordinatesConverter(), m_currentArgs, m_transaction, canvas->resourceManager())) , m_freeStrategy( new KisFreeTransformStrategy( dynamic_cast(canvas)->coordinatesConverter(), dynamic_cast(canvas)->snapGuide(), m_currentArgs, m_transaction)) , m_perspectiveStrategy( new KisPerspectiveTransformStrategy( dynamic_cast(canvas)->coordinatesConverter(), dynamic_cast(canvas)->snapGuide(), m_currentArgs, m_transaction)) { m_canvas = dynamic_cast(canvas); Q_ASSERT(m_canvas); setObjectName("tool_transform"); useCursor(KisCursor::selectCursor()); m_optionsWidget = 0; warpAction = new KisAction(i18n("Warp")); liquifyAction = new KisAction(i18n("Liquify")); cageAction = new KisAction(i18n("Cage")); freeTransformAction = new KisAction(i18n("Free")); perspectiveAction = new KisAction(i18n("Perspective")); // extra actions for free transform that are in the tool options mirrorHorizontalAction = new KisAction(i18n("Mirror Horizontal")); mirrorVericalAction = new KisAction(i18n("Mirror Vertical")); rotateNinteyCWAction = new KisAction(i18n("Rotate 90 degrees Clockwise")); rotateNinteyCCWAction = new KisAction(i18n("Rotate 90 degrees CounterClockwise")); applyTransformation = new KisAction(i18n("Apply")); resetTransformation = new KisAction(i18n("Reset")); m_contextMenu.reset(new QMenu()); connect(m_warpStrategy.data(), SIGNAL(requestCanvasUpdate()), SLOT(canvasUpdateRequested())); connect(m_cageStrategy.data(), SIGNAL(requestCanvasUpdate()), SLOT(canvasUpdateRequested())); connect(m_liquifyStrategy.data(), SIGNAL(requestCanvasUpdate()), SLOT(canvasUpdateRequested())); connect(m_liquifyStrategy.data(), SIGNAL(requestCursorOutlineUpdate(QPointF)), SLOT(cursorOutlineUpdateRequested(QPointF))); connect(m_liquifyStrategy.data(), SIGNAL(requestUpdateOptionWidget()), SLOT(updateOptionWidget())); connect(m_freeStrategy.data(), SIGNAL(requestCanvasUpdate()), SLOT(canvasUpdateRequested())); connect(m_freeStrategy.data(), SIGNAL(requestResetRotationCenterButtons()), SLOT(resetRotationCenterButtonsRequested())); connect(m_freeStrategy.data(), SIGNAL(requestShowImageTooBig(bool)), SLOT(imageTooBigRequested(bool))); connect(m_perspectiveStrategy.data(), SIGNAL(requestCanvasUpdate()), SLOT(canvasUpdateRequested())); connect(m_perspectiveStrategy.data(), SIGNAL(requestShowImageTooBig(bool)), SLOT(imageTooBigRequested(bool))); connect(&m_changesTracker, SIGNAL(sigConfigChanged(KisToolChangesTrackerDataSP)), this, SLOT(slotTrackerChangedConfig(KisToolChangesTrackerDataSP))); } KisToolTransform::~KisToolTransform() { cancelStroke(); } void KisToolTransform::outlineChanged() { emit freeTransformChanged(); m_canvas->updateCanvas(); } void KisToolTransform::canvasUpdateRequested() { m_canvas->updateCanvas(); } void KisToolTransform::resetCursorStyle() { setFunctionalCursor(); } void KisToolTransform::resetRotationCenterButtonsRequested() { if (!m_optionsWidget) return; m_optionsWidget->resetRotationCenterButtons(); } void KisToolTransform::imageTooBigRequested(bool value) { if (!m_optionsWidget) return; m_optionsWidget->setTooBigLabelVisible(value); } KisTransformStrategyBase* KisToolTransform::currentStrategy() const { if (m_currentArgs.mode() == ToolTransformArgs::FREE_TRANSFORM) { return m_freeStrategy.data(); } else if (m_currentArgs.mode() == ToolTransformArgs::WARP) { return m_warpStrategy.data(); } else if (m_currentArgs.mode() == ToolTransformArgs::CAGE) { return m_cageStrategy.data(); } else if (m_currentArgs.mode() == ToolTransformArgs::LIQUIFY) { return m_liquifyStrategy.data(); } else /* if (m_currentArgs.mode() == ToolTransformArgs::PERSPECTIVE_4POINT) */ { return m_perspectiveStrategy.data(); } } void KisToolTransform::paint(QPainter& gc, const KoViewConverter &converter) { Q_UNUSED(converter); if (!m_strokeId || !m_transaction.rootNode()) return; QRectF newRefRect = KisTransformUtils::imageToFlake(m_canvas->coordinatesConverter(), QRectF(0.0,0.0,1.0,1.0)); if (m_refRect != newRefRect) { m_refRect = newRefRect; currentStrategy()->externalConfigChanged(); } currentStrategy()->paint(gc); if (!m_cursorOutline.isEmpty()) { QPainterPath mappedOutline = KisTransformUtils::imageToFlakeTransform( m_canvas->coordinatesConverter()).map(m_cursorOutline); paintToolOutline(&gc, mappedOutline); } } void KisToolTransform::setFunctionalCursor() { if (overrideCursorIfNotEditable()) { return; } if (!m_strokeId) { useCursor(KisCursor::pointingHandCursor()); } else if (m_strokeId && !m_transaction.rootNode()) { // we are in the middle of stroke initialization useCursor(KisCursor::waitCursor()); } else { useCursor(currentStrategy()->getCurrentCursor()); } } void KisToolTransform::cursorOutlineUpdateRequested(const QPointF &imagePos) { QRect canvasUpdateRect; if (!m_cursorOutline.isEmpty()) { canvasUpdateRect = m_canvas->coordinatesConverter()-> imageToDocument(m_cursorOutline.boundingRect()).toAlignedRect(); } m_cursorOutline = currentStrategy()-> getCursorOutline().translated(imagePos); if (!m_cursorOutline.isEmpty()) { canvasUpdateRect |= m_canvas->coordinatesConverter()-> imageToDocument(m_cursorOutline.boundingRect()).toAlignedRect(); } if (!canvasUpdateRect.isEmpty()) { // grow rect a bit to follow interpolation fuzziness canvasUpdateRect = kisGrowRect(canvasUpdateRect, 2); m_canvas->updateCanvas(canvasUpdateRect); } } void KisToolTransform::beginActionImpl(KoPointerEvent *event, bool usePrimaryAction, KisTool::AlternateAction action) { if (!nodeEditable()) { event->ignore(); return; } if (!m_strokeId) { startStroke(m_currentArgs.mode(), false); } else if (m_transaction.rootNode()) { bool result = false; if (usePrimaryAction) { result = currentStrategy()->beginPrimaryAction(event); } else { result = currentStrategy()->beginAlternateAction(event, action); } if (result) { setMode(KisTool::PAINT_MODE); } } m_actuallyMoveWhileSelected = false; outlineChanged(); } void KisToolTransform::continueActionImpl(KoPointerEvent *event, bool usePrimaryAction, KisTool::AlternateAction action) { if (mode() != KisTool::PAINT_MODE) return; if (!m_transaction.rootNode()) return; m_actuallyMoveWhileSelected = true; if (usePrimaryAction) { currentStrategy()->continuePrimaryAction(event); } else { currentStrategy()->continueAlternateAction(event, action); } updateOptionWidget(); outlineChanged(); } void KisToolTransform::endActionImpl(KoPointerEvent *event, bool usePrimaryAction, KisTool::AlternateAction action) { if (mode() != KisTool::PAINT_MODE) return; setMode(KisTool::HOVER_MODE); if (m_actuallyMoveWhileSelected || currentStrategy()->acceptsClicks()) { bool result = false; if (usePrimaryAction) { result = currentStrategy()->endPrimaryAction(event); } else { result = currentStrategy()->endAlternateAction(event, action); } if (result) { commitChanges(); } outlineChanged(); } updateOptionWidget(); updateApplyResetAvailability(); } QMenu* KisToolTransform::popupActionsMenu() { if (m_contextMenu) { m_contextMenu->clear(); m_contextMenu->addSection(i18n("Transform Tool Actions")); m_contextMenu->addSeparator(); // add a quick switch to different transform types m_contextMenu->addAction(freeTransformAction); m_contextMenu->addAction(perspectiveAction); m_contextMenu->addAction(warpAction); m_contextMenu->addAction(cageAction); m_contextMenu->addAction(liquifyAction); // extra options if free transform is selected if (transformMode() == FreeTransformMode) { m_contextMenu->addSeparator(); m_contextMenu->addAction(mirrorHorizontalAction); m_contextMenu->addAction(mirrorVericalAction); m_contextMenu->addAction(rotateNinteyCWAction); m_contextMenu->addAction(rotateNinteyCCWAction); } m_contextMenu->addSeparator(); m_contextMenu->addAction(applyTransformation); m_contextMenu->addAction(resetTransformation); } return m_contextMenu.data(); } void KisToolTransform::beginPrimaryAction(KoPointerEvent *event) { beginActionImpl(event, true, KisTool::NONE); } void KisToolTransform::continuePrimaryAction(KoPointerEvent *event) { continueActionImpl(event, true, KisTool::NONE); } void KisToolTransform::endPrimaryAction(KoPointerEvent *event) { endActionImpl(event, true, KisTool::NONE); } void KisToolTransform::activatePrimaryAction() { currentStrategy()->activatePrimaryAction(); setFunctionalCursor(); } void KisToolTransform::deactivatePrimaryAction() { currentStrategy()->deactivatePrimaryAction(); } void KisToolTransform::activateAlternateAction(AlternateAction action) { currentStrategy()->activateAlternateAction(action); setFunctionalCursor(); } void KisToolTransform::deactivateAlternateAction(AlternateAction action) { currentStrategy()->deactivateAlternateAction(action); } void KisToolTransform::beginAlternateAction(KoPointerEvent *event, AlternateAction action) { beginActionImpl(event, false, action); } void KisToolTransform::continueAlternateAction(KoPointerEvent *event, AlternateAction action) { continueActionImpl(event, false, action); } void KisToolTransform::endAlternateAction(KoPointerEvent *event, AlternateAction action) { endActionImpl(event, false, action); } void KisToolTransform::mousePressEvent(KoPointerEvent *event) { KisTool::mousePressEvent(event); } void KisToolTransform::mouseMoveEvent(KoPointerEvent *event) { QPointF mousePos = m_canvas->coordinatesConverter()->documentToImage(event->point); cursorOutlineUpdateRequested(mousePos); if (this->mode() != KisTool::PAINT_MODE) { currentStrategy()->hoverActionCommon(event); setFunctionalCursor(); KisTool::mouseMoveEvent(event); return; } } void KisToolTransform::mouseReleaseEvent(KoPointerEvent *event) { KisTool::mouseReleaseEvent(event); } void KisToolTransform::applyTransform() { slotApplyTransform(); } KisToolTransform::TransformToolMode KisToolTransform::transformMode() const { TransformToolMode mode = FreeTransformMode; switch (m_currentArgs.mode()) { case ToolTransformArgs::FREE_TRANSFORM: mode = FreeTransformMode; break; case ToolTransformArgs::WARP: mode = WarpTransformMode; break; case ToolTransformArgs::CAGE: mode = CageTransformMode; break; case ToolTransformArgs::LIQUIFY: mode = LiquifyTransformMode; break; case ToolTransformArgs::PERSPECTIVE_4POINT: mode = PerspectiveTransformMode; break; default: KIS_ASSERT_RECOVER_NOOP(0 && "unexpected transform mode"); } return mode; } double KisToolTransform::translateX() const { return m_currentArgs.transformedCenter().x(); } double KisToolTransform::translateY() const { return m_currentArgs.transformedCenter().y(); } double KisToolTransform::rotateX() const { return m_currentArgs.aX(); } double KisToolTransform::rotateY() const { return m_currentArgs.aY(); } double KisToolTransform::rotateZ() const { return m_currentArgs.aZ(); } double KisToolTransform::scaleX() const { return m_currentArgs.scaleX(); } double KisToolTransform::scaleY() const { return m_currentArgs.scaleY(); } double KisToolTransform::shearX() const { return m_currentArgs.shearX(); } double KisToolTransform::shearY() const { return m_currentArgs.shearY(); } KisToolTransform::WarpType KisToolTransform::warpType() const { switch(m_currentArgs.warpType()) { case KisWarpTransformWorker::AFFINE_TRANSFORM: return AffineWarpType; case KisWarpTransformWorker::RIGID_TRANSFORM: return RigidWarpType; case KisWarpTransformWorker::SIMILITUDE_TRANSFORM: return SimilitudeWarpType; default: return RigidWarpType; } } double KisToolTransform::warpFlexibility() const { return m_currentArgs.alpha(); } int KisToolTransform::warpPointDensity() const { return m_currentArgs.numPoints(); } void KisToolTransform::setTransformMode(KisToolTransform::TransformToolMode newMode) { ToolTransformArgs::TransformMode mode = ToolTransformArgs::FREE_TRANSFORM; switch (newMode) { case FreeTransformMode: mode = ToolTransformArgs::FREE_TRANSFORM; break; case WarpTransformMode: mode = ToolTransformArgs::WARP; break; case CageTransformMode: mode = ToolTransformArgs::CAGE; break; case LiquifyTransformMode: mode = ToolTransformArgs::LIQUIFY; break; case PerspectiveTransformMode: mode = ToolTransformArgs::PERSPECTIVE_4POINT; break; default: KIS_ASSERT_RECOVER_NOOP(0 && "unexpected transform mode"); } if( mode != m_currentArgs.mode() ) { if( newMode == FreeTransformMode ) { m_optionsWidget->slotSetFreeTransformModeButtonClicked( true ); } else if( newMode == WarpTransformMode ) { m_optionsWidget->slotSetWarpModeButtonClicked( true ); } else if( newMode == CageTransformMode ) { m_optionsWidget->slotSetCageModeButtonClicked( true ); } else if( newMode == LiquifyTransformMode ) { m_optionsWidget->slotSetLiquifyModeButtonClicked( true ); } else if( newMode == PerspectiveTransformMode ) { m_optionsWidget->slotSetPerspectiveModeButtonClicked( true ); } emit transformModeChanged(); } } void KisToolTransform::setRotateX( double rotation ) { m_currentArgs.setAX( normalizeAngle(rotation) ); } void KisToolTransform::setRotateY( double rotation ) { m_currentArgs.setAY( normalizeAngle(rotation) ); } void KisToolTransform::setRotateZ( double rotation ) { m_currentArgs.setAZ( normalizeAngle(rotation) ); } void KisToolTransform::setWarpType( KisToolTransform::WarpType type ) { switch( type ) { case RigidWarpType: m_currentArgs.setWarpType(KisWarpTransformWorker::RIGID_TRANSFORM); break; case AffineWarpType: m_currentArgs.setWarpType(KisWarpTransformWorker::AFFINE_TRANSFORM); break; case SimilitudeWarpType: m_currentArgs.setWarpType(KisWarpTransformWorker::SIMILITUDE_TRANSFORM); break; default: break; } } void KisToolTransform::setWarpFlexibility( double flexibility ) { m_currentArgs.setAlpha( flexibility ); } void KisToolTransform::setWarpPointDensity( int density ) { m_optionsWidget->slotSetWarpDensity(density); } void KisToolTransform::initTransformMode(ToolTransformArgs::TransformMode mode) { m_currentArgs = KisTransformUtils::resetArgsForMode(mode, m_currentArgs.filterId(), m_transaction); initGuiAfterTransformMode(); } void KisToolTransform::initGuiAfterTransformMode() { currentStrategy()->externalConfigChanged(); outlineChanged(); updateOptionWidget(); updateApplyResetAvailability(); setFunctionalCursor(); } void KisToolTransform::initThumbnailImage(KisPaintDeviceSP previewDevice) { QImage origImg; m_selectedPortionCache = previewDevice; QTransform thumbToImageTransform; const int maxSize = 2000; QRect srcRect(m_transaction.originalRect().toAlignedRect()); int x, y, w, h; srcRect.getRect(&x, &y, &w, &h); if (m_selectedPortionCache) { if (w > maxSize || h > maxSize) { qreal scale = qreal(maxSize) / (w > h ? w : h); QTransform scaleTransform = QTransform::fromScale(scale, scale); QRect thumbRect = scaleTransform.mapRect(m_transaction.originalRect()).toAlignedRect(); origImg = m_selectedPortionCache-> createThumbnail(thumbRect.width(), thumbRect.height(), srcRect, 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); thumbToImageTransform = scaleTransform.inverted(); } else { origImg = m_selectedPortionCache->convertToQImage(0, x, y, w, h, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); thumbToImageTransform = QTransform(); } } // init both strokes since the thumbnail is initialized only once // during the stroke m_freeStrategy->setThumbnailImage(origImg, thumbToImageTransform); m_perspectiveStrategy->setThumbnailImage(origImg, thumbToImageTransform); m_warpStrategy->setThumbnailImage(origImg, thumbToImageTransform); m_cageStrategy->setThumbnailImage(origImg, thumbToImageTransform); m_liquifyStrategy->setThumbnailImage(origImg, thumbToImageTransform); } void KisToolTransform::activate(ToolActivation toolActivation, const QSet &shapes) { KisTool::activate(toolActivation, shapes); if (currentNode()) { m_transaction = TransformTransactionProperties(QRectF(), &m_currentArgs, KisNodeSP(), {}); } startStroke(ToolTransformArgs::FREE_TRANSFORM, false); } void KisToolTransform::deactivate() { endStroke(); m_canvas->updateCanvas(); KisTool::deactivate(); } void KisToolTransform::requestUndoDuringStroke() { if (!m_strokeId || !m_transaction.rootNode()) return; if (m_changesTracker.isEmpty()) { cancelStroke(); } else { m_changesTracker.requestUndo(); } } void KisToolTransform::requestStrokeEnd() { endStroke(); } void KisToolTransform::requestStrokeCancellation() { if (!m_transaction.rootNode() || m_currentArgs.isIdentity()) { cancelStroke(); } else { slotResetTransform(); } } void KisToolTransform::startStroke(ToolTransformArgs::TransformMode mode, bool forceReset) { Q_ASSERT(!m_strokeId); // set up and null checks before we do anything KisResourcesSnapshotSP resources = new KisResourcesSnapshot(image(), currentNode(), this->canvas()->resourceManager()); KisNodeSP currentNode = resources->currentNode(); if (!currentNode || !currentNode->isEditable()) return; m_transaction = TransformTransactionProperties(QRectF(), &m_currentArgs, KisNodeSP(), {}); m_currentArgs = ToolTransformArgs(); // some layer types cannot be transformed. Give a message and return if a user tries it if (currentNode->inherits("KisColorizeMask") || currentNode->inherits("KisFileLayer") || currentNode->inherits("KisCloneLayer")) { KisCanvas2 *kisCanvas = dynamic_cast(canvas()); kisCanvas->viewManager()-> showFloatingMessage( i18nc("floating message in transformation tool", "Layer type cannot use the transform tool"), koIcon("object-locked"), 4000, KisFloatingMessage::High); return; } if (m_optionsWidget) { m_workRecursively = m_optionsWidget->workRecursively() || !currentNode->paintDevice(); } KisSelectionSP selection = resources->activeSelection(); /** * When working with transform mask, selections are not * taken into account. */ if (selection && dynamic_cast(currentNode.data())) { KisCanvas2 *kisCanvas = dynamic_cast(canvas()); kisCanvas->viewManager()-> showFloatingMessage( i18nc("floating message in transformation tool", "Selections are not used when editing transform masks "), QIcon(), 4000, KisFloatingMessage::Low); selection = 0; } TransformStrokeStrategy *strategy = new TransformStrokeStrategy(mode, m_workRecursively, m_currentArgs.filterId(), forceReset, currentNode, selection, image().data(), image().data()); connect(strategy, SIGNAL(sigPreviewDeviceReady(KisPaintDeviceSP)), SLOT(slotPreviewDeviceGenerated(KisPaintDeviceSP))); connect(strategy, SIGNAL(sigTransactionGenerated(TransformTransactionProperties, ToolTransformArgs, void*)), SLOT(slotTransactionGenerated(TransformTransactionProperties, ToolTransformArgs, void*))); // save unique identifier of the stroke so we could // recognize it when sigTransactionGenerated() is - // recieved (theoretically, the user can start two + // received (theoretically, the user can start two // strokes at the same time, if he is quick enough) m_strokeStrategyCookie = strategy; m_strokeId = image()->startStroke(strategy); KIS_SAFE_ASSERT_RECOVER_NOOP(m_changesTracker.isEmpty()); slotPreviewDeviceGenerated(0); } void KisToolTransform::endStroke() { if (!m_strokeId) return; if (m_transaction.rootNode() && !m_currentArgs.isIdentity()) { image()->addJob(m_strokeId, new TransformStrokeStrategy::TransformAllData(m_currentArgs)); } image()->endStroke(m_strokeId); m_strokeStrategyCookie = 0; m_strokeId.clear(); m_changesTracker.reset(); m_transaction = TransformTransactionProperties(QRectF(), &m_currentArgs, KisNodeSP(), {}); outlineChanged(); } void KisToolTransform::slotTransactionGenerated(TransformTransactionProperties transaction, ToolTransformArgs args, void *strokeStrategyCookie) { if (!m_strokeId || strokeStrategyCookie != m_strokeStrategyCookie) return; if (transaction.transformedNodes().isEmpty()) { KisCanvas2 *kisCanvas = dynamic_cast(canvas()); kisCanvas->viewManager()-> showFloatingMessage( i18nc("floating message in transformation tool", "Cannot transform empty layer "), QIcon(), 1000, KisFloatingMessage::Medium); cancelStroke(); return; } m_transaction = transaction; m_currentArgs = args; m_transaction.setCurrentConfigLocation(&m_currentArgs); KIS_SAFE_ASSERT_RECOVER_NOOP(m_changesTracker.isEmpty()); commitChanges(); initGuiAfterTransformMode(); if (m_transaction.hasInvisibleNodes()) { KisCanvas2 *kisCanvas = dynamic_cast(canvas()); kisCanvas->viewManager()-> showFloatingMessage( i18nc("floating message in transformation tool", "Invisible sublayers will also be transformed. Lock layers if you do not want them to be transformed "), QIcon(), 4000, KisFloatingMessage::Low); } } void KisToolTransform::slotPreviewDeviceGenerated(KisPaintDeviceSP device) { if (device && device->exactBounds().isEmpty()) { KisCanvas2 *kisCanvas = dynamic_cast(canvas()); kisCanvas->viewManager()-> showFloatingMessage( i18nc("floating message in transformation tool", "Cannot transform empty layer "), QIcon(), 1000, KisFloatingMessage::Medium); cancelStroke(); } else { initThumbnailImage(device); initGuiAfterTransformMode(); } } void KisToolTransform::cancelStroke() { if (!m_strokeId) return; image()->cancelStroke(m_strokeId); m_strokeStrategyCookie = 0; m_strokeId.clear(); m_changesTracker.reset(); m_transaction = TransformTransactionProperties(QRectF(), &m_currentArgs, KisNodeSP(), {}); outlineChanged(); } void KisToolTransform::commitChanges() { if (!m_strokeId || !m_transaction.rootNode()) return; m_changesTracker.commitConfig(toQShared(m_currentArgs.clone())); } void KisToolTransform::slotTrackerChangedConfig(KisToolChangesTrackerDataSP status) { const ToolTransformArgs *newArgs = dynamic_cast(status.data()); KIS_SAFE_ASSERT_RECOVER_RETURN(newArgs); *m_transaction.currentConfig() = *newArgs; slotUiChangedConfig(); updateOptionWidget(); } QList KisToolTransform::fetchNodesList(ToolTransformArgs::TransformMode mode, KisNodeSP root, bool recursive) { QList result; auto fetchFunc = [&result, mode, root] (KisNodeSP node) { if (node->isEditable(node == root) && (!node->inherits("KisShapeLayer") || mode == ToolTransformArgs::FREE_TRANSFORM) && !node->inherits("KisFileLayer") && (!node->inherits("KisTransformMask") || node == root)) { result << node; } }; if (recursive) { KisLayerUtils::recursiveApplyNodes(root, fetchFunc); } else { fetchFunc(root); } return result; } QWidget* KisToolTransform::createOptionWidget() { if (!m_canvas) return 0; m_optionsWidget = new KisToolTransformConfigWidget(&m_transaction, m_canvas, m_workRecursively, 0); Q_CHECK_PTR(m_optionsWidget); m_optionsWidget->setObjectName(toolId() + " option widget"); // See https://bugs.kde.org/show_bug.cgi?id=316896 QWidget *specialSpacer = new QWidget(m_optionsWidget); specialSpacer->setObjectName("SpecialSpacer"); specialSpacer->setFixedSize(0, 0); m_optionsWidget->layout()->addWidget(specialSpacer); connect(m_optionsWidget, SIGNAL(sigConfigChanged()), this, SLOT(slotUiChangedConfig())); connect(m_optionsWidget, SIGNAL(sigApplyTransform()), this, SLOT(slotApplyTransform())); connect(m_optionsWidget, SIGNAL(sigResetTransform()), this, SLOT(slotResetTransform())); connect(m_optionsWidget, SIGNAL(sigRestartTransform()), this, SLOT(slotRestartTransform())); connect(m_optionsWidget, SIGNAL(sigEditingFinished()), this, SLOT(slotEditingFinished())); connect(mirrorHorizontalAction, SIGNAL(triggered(bool)), m_optionsWidget, SLOT(slotFlipX())); connect(mirrorVericalAction, SIGNAL(triggered(bool)), m_optionsWidget, SLOT(slotFlipY())); connect(rotateNinteyCWAction, SIGNAL(triggered(bool)), m_optionsWidget, SLOT(slotRotateCW())); connect(rotateNinteyCCWAction, SIGNAL(triggered(bool)), m_optionsWidget, SLOT(slotRotateCCW())); connect(warpAction, SIGNAL(triggered(bool)), this, SLOT(slotUpdateToWarpType())); connect(perspectiveAction, SIGNAL(triggered(bool)), this, SLOT(slotUpdateToPerspectiveType())); connect(freeTransformAction, SIGNAL(triggered(bool)), this, SLOT(slotUpdateToFreeTransformType())); connect(liquifyAction, SIGNAL(triggered(bool)), this, SLOT(slotUpdateToLiquifyType())); connect(cageAction, SIGNAL(triggered(bool)), this, SLOT(slotUpdateToCageType())); connect(applyTransformation, SIGNAL(triggered(bool)), this, SLOT(slotApplyTransform())); connect(resetTransformation, SIGNAL(triggered(bool)), this, SLOT(slotResetTransform())); updateOptionWidget(); return m_optionsWidget; } void KisToolTransform::updateOptionWidget() { if (!m_optionsWidget) return; if (!currentNode()) { m_optionsWidget->setEnabled(false); return; } else { m_optionsWidget->setEnabled(true); m_optionsWidget->updateConfig(m_currentArgs); } } void KisToolTransform::updateApplyResetAvailability() { if (m_optionsWidget) { m_optionsWidget->setApplyResetDisabled(m_currentArgs.isIdentity()); } } void KisToolTransform::slotUiChangedConfig() { if (mode() == KisTool::PAINT_MODE) return; currentStrategy()->externalConfigChanged(); if (m_currentArgs.mode() == ToolTransformArgs::LIQUIFY) { m_currentArgs.saveLiquifyTransformMode(); } outlineChanged(); updateApplyResetAvailability(); } void KisToolTransform::slotApplyTransform() { QApplication::setOverrideCursor(KisCursor::waitCursor()); endStroke(); QApplication::restoreOverrideCursor(); } void KisToolTransform::slotResetTransform() { if (!m_strokeId || !m_transaction.rootNode()) return; if (m_currentArgs.continuedTransform()) { ToolTransformArgs::TransformMode savedMode = m_currentArgs.mode(); /** * Our reset transform button can be used for two purposes: * * 1) Reset current transform to the initial one, which was * loaded from the previous user action. * * 2) Reset transform frame to infinity when the frame is unchanged */ const bool transformDiffers = !m_currentArgs.continuedTransform()->isSameMode(m_currentArgs); if (transformDiffers && m_currentArgs.continuedTransform()->mode() == savedMode) { m_currentArgs.restoreContinuedState(); initGuiAfterTransformMode(); slotEditingFinished(); } else { KisNodeSP root = m_transaction.rootNode() ? m_transaction.rootNode() : image()->root(); cancelStroke(); startStroke(savedMode, true); KIS_ASSERT_RECOVER_NOOP(!m_currentArgs.continuedTransform()); } } else { initTransformMode(m_currentArgs.mode()); slotEditingFinished(); } } void KisToolTransform::slotRestartTransform() { if (!m_strokeId || !m_transaction.rootNode()) return; KisNodeSP root = m_transaction.rootNode(); KIS_ASSERT_RECOVER_RETURN(root); // the stroke is guaranteed to be started by an 'if' above ToolTransformArgs savedArgs(m_currentArgs); cancelStroke(); startStroke(savedArgs.mode(), true); } void KisToolTransform::slotEditingFinished() { commitChanges(); } void KisToolTransform::slotUpdateToWarpType() { setTransformMode(KisToolTransform::TransformToolMode::WarpTransformMode); } void KisToolTransform::slotUpdateToPerspectiveType() { setTransformMode(KisToolTransform::TransformToolMode::PerspectiveTransformMode); } void KisToolTransform::slotUpdateToFreeTransformType() { setTransformMode(KisToolTransform::TransformToolMode::FreeTransformMode); } void KisToolTransform::slotUpdateToLiquifyType() { setTransformMode(KisToolTransform::TransformToolMode::LiquifyTransformMode); } void KisToolTransform::slotUpdateToCageType() { setTransformMode(KisToolTransform::TransformToolMode::CageTransformMode); } void KisToolTransform::setShearY(double shear) { m_optionsWidget->slotSetShearY(shear); } void KisToolTransform::setShearX(double shear) { m_optionsWidget->slotSetShearX(shear); } void KisToolTransform::setScaleY(double scale) { m_optionsWidget->slotSetScaleY(scale); } void KisToolTransform::setScaleX(double scale) { m_optionsWidget->slotSetScaleX(scale); } void KisToolTransform::setTranslateY(double translation) { m_optionsWidget->slotSetTranslateY(translation); } void KisToolTransform::setTranslateX(double translation) { m_optionsWidget->slotSetTranslateX(translation); } diff --git a/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp b/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp index 42d405feae..1b1318e82a 100644 --- a/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp +++ b/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp @@ -1,717 +1,710 @@ /* * Copyright (c) 2013 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "transform_stroke_strategy.h" #include #include "kundo2commandextradata.h" #include "kis_node_progress_proxy.h" #include #include #include #include #include #include #include #include #include "kis_transform_mask_adapter.h" #include "kis_transform_utils.h" #include "kis_abstract_projection_plane.h" #include "kis_recalculate_transform_mask_job.h" #include "kis_projection_leaf.h" #include "kis_modify_transform_mask_command.h" #include "kis_sequential_iterator.h" #include "kis_selection_mask.h" #include "kis_image_config.h" #include "kis_layer_utils.h" #include #include #include "transform_transaction_properties.h" #include "krita_container_utils.h" #include "commands_new/kis_saved_commands.h" #include "kis_command_ids.h" #include "KisRunnableStrokeJobUtils.h" #include "commands_new/KisHoldUIUpdatesCommand.h" #include "KisDecoratedNodeInterface.h" TransformStrokeStrategy::TransformStrokeStrategy(ToolTransformArgs::TransformMode mode, bool workRecursively, const QString &filterId, bool forceReset, KisNodeSP rootNode, KisSelectionSP selection, KisStrokeUndoFacade *undoFacade, KisUpdatesFacade *updatesFacade) : KisStrokeStrategyUndoCommandBased(kundo2_i18n("Transform"), false, undoFacade), m_updatesFacade(updatesFacade), m_mode(mode), m_workRecursively(workRecursively), m_filterId(filterId), m_forceReset(forceReset), m_selection(selection) { KIS_SAFE_ASSERT_RECOVER_NOOP(!selection || !dynamic_cast(rootNode.data())); m_rootNode = rootNode; setMacroId(KisCommandUtils::TransformToolId); } TransformStrokeStrategy::~TransformStrokeStrategy() { } KisPaintDeviceSP TransformStrokeStrategy::createDeviceCache(KisPaintDeviceSP dev) { KisPaintDeviceSP cache; if (m_selection) { QRect srcRect = m_selection->selectedExactRect(); cache = dev->createCompositionSourceDevice(); KisPainter gc(cache); gc.setSelection(m_selection); gc.bitBlt(srcRect.topLeft(), dev, srcRect); } else { cache = dev->createCompositionSourceDevice(dev); } return cache; } bool TransformStrokeStrategy::haveDeviceInCache(KisPaintDeviceSP src) { QMutexLocker l(&m_devicesCacheMutex); return m_devicesCacheHash.contains(src.data()); } void TransformStrokeStrategy::putDeviceCache(KisPaintDeviceSP src, KisPaintDeviceSP cache) { QMutexLocker l(&m_devicesCacheMutex); m_devicesCacheHash.insert(src.data(), cache); } KisPaintDeviceSP TransformStrokeStrategy::getDeviceCache(KisPaintDeviceSP src) { QMutexLocker l(&m_devicesCacheMutex); KisPaintDeviceSP cache = m_devicesCacheHash.value(src.data()); if (!cache) { warnKrita << "WARNING: Transform Stroke: the device is absent in cache!"; } return cache; } bool TransformStrokeStrategy::checkBelongsToSelection(KisPaintDeviceSP device) const { return m_selection && (device == m_selection->pixelSelection().data() || device == m_selection->projection().data()); } void TransformStrokeStrategy::doStrokeCallback(KisStrokeJobData *data) { TransformData *td = dynamic_cast(data); ClearSelectionData *csd = dynamic_cast(data); PreparePreviewData *ppd = dynamic_cast(data); TransformAllData *runAllData = dynamic_cast(data); if (runAllData) { // here we only save the passed args, actual // transformation will be performed during // finish job m_savedTransformArgs = runAllData->config; } else if (ppd) { KisNodeSP rootNode = m_rootNode; KisNodeList processedNodes = m_processedNodes; KisPaintDeviceSP previewDevice; if (rootNode->childCount() || !rootNode->paintDevice()) { if (KisTransformMask* tmask = dynamic_cast(rootNode.data())) { previewDevice = createDeviceCache(tmask->buildPreviewDevice()); KIS_SAFE_ASSERT_RECOVER(!m_selection) { m_selection = 0; } } else if (KisGroupLayer *group = dynamic_cast(rootNode.data())) { const QRect bounds = group->image()->bounds(); KisImageSP clonedImage = new KisImage(0, bounds.width(), bounds.height(), group->colorSpace(), "transformed_image"); KisGroupLayerSP clonedGroup = dynamic_cast(group->clone().data()); // In case the group is pass-through, it needs to be disabled for the preview, // otherwise it will crash (no parent for a preview leaf). // Also it needs to be done before setting the root layer for clonedImage. // Result: preview for pass-through group is the same as for standard group // (i.e. filter layers in the group won't affect the layer stack for a moment). clonedGroup->setPassThroughMode(false); clonedImage->setRootLayer(clonedGroup); QQueue linearizedSrcNodes; KisLayerUtils::recursiveApplyNodes(rootNode, [&linearizedSrcNodes] (KisNodeSP node) { linearizedSrcNodes.enqueue(node); }); KisLayerUtils::recursiveApplyNodes(KisNodeSP(clonedGroup), [&linearizedSrcNodes, processedNodes] (KisNodeSP node) { KisNodeSP srcNode = linearizedSrcNodes.dequeue(); if (!processedNodes.contains(srcNode)) { node->setVisible(false); } }); clonedImage->refreshGraph(); KisLayerUtils::refreshHiddenAreaAsync(clonedImage, clonedGroup, clonedImage->bounds()); KisLayerUtils::forceAllDelayedNodesUpdate(clonedGroup); clonedImage->waitForDone(); previewDevice = createDeviceCache(clonedImage->projection()); previewDevice->setDefaultBounds(group->projection()->defaultBounds()); // we delete the cloned image in GUI thread to ensure // no signals are still pending makeKisDeleteLaterWrapper(clonedImage)->deleteLater(); } else { rootNode->projectionLeaf()->explicitlyRegeneratePassThroughProjection(); previewDevice = createDeviceCache(rootNode->projection()); } } else { KisPaintDeviceSP cacheDevice = createDeviceCache(rootNode->paintDevice()); if (dynamic_cast(rootNode.data())) { KIS_SAFE_ASSERT_RECOVER (cacheDevice->colorSpace()->colorModelId() == GrayAColorModelID && cacheDevice->colorSpace()->colorDepthId() == Integer8BitsColorDepthID) { cacheDevice->convertTo(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id())); } previewDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); const QRect srcRect = cacheDevice->exactBounds(); KisSequentialConstIterator srcIt(cacheDevice, srcRect); KisSequentialIterator dstIt(previewDevice, srcRect); const int pixelSize = previewDevice->colorSpace()->pixelSize(); KisImageConfig cfg(true); KoColor pixel(cfg.selectionOverlayMaskColor(), previewDevice->colorSpace()); const qreal coeff = 1.0 / 255.0; const qreal baseOpacity = 0.5; while (srcIt.nextPixel() && dstIt.nextPixel()) { qreal gray = srcIt.rawDataConst()[0]; qreal alpha = srcIt.rawDataConst()[1]; pixel.setOpacity(quint8(gray * alpha * baseOpacity * coeff)); memcpy(dstIt.rawData(), pixel.data(), pixelSize); } } else { previewDevice = cacheDevice; } putDeviceCache(rootNode->paintDevice(), cacheDevice); } emit sigPreviewDeviceReady(previewDevice); } else if(td) { if (td->destination == TransformData::PAINT_DEVICE) { QRect oldExtent = td->node->extent(); KisPaintDeviceSP device = td->node->paintDevice(); if (device && !checkBelongsToSelection(device)) { KisPaintDeviceSP cachedPortion = getDeviceCache(device); Q_ASSERT(cachedPortion); KisTransaction transaction(device); KisProcessingVisitor::ProgressHelper helper(td->node); transformAndMergeDevice(td->config, cachedPortion, device, &helper); runAndSaveCommand(KUndo2CommandSP(transaction.endAndTake()), KisStrokeJobData::CONCURRENT, KisStrokeJobData::NORMAL); td->node->setDirty(oldExtent | td->node->extent()); } else if (KisExternalLayer *extLayer = dynamic_cast(td->node.data())) { if (td->config.mode() == ToolTransformArgs::FREE_TRANSFORM || (td->config.mode() == ToolTransformArgs::PERSPECTIVE_4POINT && extLayer->supportsPerspectiveTransform())) { QVector3D transformedCenter; KisTransformWorker w = KisTransformUtils::createTransformWorker(td->config, 0, 0, &transformedCenter); QTransform t = w.transform(); runAndSaveCommand(KUndo2CommandSP(extLayer->transform(t)), KisStrokeJobData::CONCURRENT, KisStrokeJobData::NORMAL); } } else if (KisTransformMask *transformMask = dynamic_cast(td->node.data())) { runAndSaveCommand(KUndo2CommandSP( new KisModifyTransformMaskCommand(transformMask, KisTransformMaskParamsInterfaceSP( new KisTransformMaskAdapter(td->config)))), KisStrokeJobData::CONCURRENT, KisStrokeJobData::NORMAL); } } else if (m_selection) { /** * We use usual transaction here, because we cannot calsulate * transformation for perspective and warp workers. */ KisTransaction transaction(m_selection->pixelSelection()); KisProcessingVisitor::ProgressHelper helper(td->node); KisTransformUtils::transformDevice(td->config, m_selection->pixelSelection(), &helper); runAndSaveCommand(KUndo2CommandSP(transaction.endAndTake()), KisStrokeJobData::CONCURRENT, KisStrokeJobData::NORMAL); } } else if (csd) { KisPaintDeviceSP device = csd->node->paintDevice(); if (device && !checkBelongsToSelection(device)) { if (!haveDeviceInCache(device)) { putDeviceCache(device, createDeviceCache(device)); } clearSelection(device); /** * Selection masks might have an overlay enabled, we should disable that */ if (KisSelectionMask *mask = dynamic_cast(csd->node.data())) { KisSelectionSP selection = mask->selection(); if (selection) { selection->setVisible(false); m_deactivatedSelections.append(selection); mask->setDirty(); } } } else if (KisExternalLayer *externalLayer = dynamic_cast(csd->node.data())) { externalLayer->projectionLeaf()->setTemporaryHiddenFromRendering(true); externalLayer->setDirty(); m_hiddenProjectionLeaves.append(csd->node); } else if (KisTransformMask *transformMask = dynamic_cast(csd->node.data())) { runAndSaveCommand(KUndo2CommandSP( new KisModifyTransformMaskCommand(transformMask, KisTransformMaskParamsInterfaceSP( new KisDumbTransformMaskParams(true)))), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL); } } else { KisStrokeStrategyUndoCommandBased::doStrokeCallback(data); } } void TransformStrokeStrategy::clearSelection(KisPaintDeviceSP device) { KisTransaction transaction(device); if (m_selection) { device->clearSelection(m_selection); } else { QRect oldExtent = device->extent(); device->clear(); device->setDirty(oldExtent); } runAndSaveCommand(KUndo2CommandSP(transaction.endAndTake()), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL); } void TransformStrokeStrategy::transformAndMergeDevice(const ToolTransformArgs &config, KisPaintDeviceSP src, KisPaintDeviceSP dst, KisProcessingVisitor::ProgressHelper *helper) { KoUpdaterPtr mergeUpdater = src != dst ? helper->updater() : 0; KisTransformUtils::transformDevice(config, src, helper); if (src != dst) { QRect mergeRect = src->extent(); KisPainter painter(dst); painter.setProgress(mergeUpdater); painter.bitBlt(mergeRect.topLeft(), src, mergeRect); painter.end(); } } struct TransformExtraData : public KUndo2CommandExtraData { ToolTransformArgs savedTransformArgs; KisNodeSP rootNode; KisNodeList transformedNodes; KUndo2CommandExtraData* clone() const override { return new TransformExtraData(*this); } }; void TransformStrokeStrategy::postProcessToplevelCommand(KUndo2Command *command) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_savedTransformArgs); TransformExtraData *data = new TransformExtraData(); data->savedTransformArgs = *m_savedTransformArgs; data->rootNode = m_rootNode; data->transformedNodes = m_processedNodes; command->setExtraData(data); KisSavedMacroCommand *macroCommand = dynamic_cast(command); KIS_SAFE_ASSERT_RECOVER_NOOP(macroCommand); if (m_overriddenCommand && macroCommand) { macroCommand->setOverrideInfo(m_overriddenCommand, m_skippedWhileMergeCommands); } KisStrokeStrategyUndoCommandBased::postProcessToplevelCommand(command); } bool TransformStrokeStrategy::fetchArgsFromCommand(const KUndo2Command *command, ToolTransformArgs *args, KisNodeSP *rootNode, KisNodeList *transformedNodes) { const TransformExtraData *data = dynamic_cast(command->extraData()); if (data) { *args = data->savedTransformArgs; *rootNode = data->rootNode; *transformedNodes = data->transformedNodes; } return bool(data); } QList TransformStrokeStrategy::fetchNodesList(ToolTransformArgs::TransformMode mode, KisNodeSP root, bool recursive) { QList result; auto fetchFunc = [&result, mode, root] (KisNodeSP node) { if (node->isEditable(node == root) && (!node->inherits("KisShapeLayer") || mode == ToolTransformArgs::FREE_TRANSFORM) && !node->inherits("KisFileLayer") && (!node->inherits("KisTransformMask") || node == root)) { result << node; } }; if (recursive) { KisLayerUtils::recursiveApplyNodes(root, fetchFunc); } else { fetchFunc(root); } return result; } bool TransformStrokeStrategy::tryInitArgsFromNode(KisNodeSP node, ToolTransformArgs *args) { bool result = false; if (KisTransformMaskSP mask = dynamic_cast(node.data())) { KisTransformMaskParamsInterfaceSP savedParams = mask->transformParams(); KisTransformMaskAdapter *adapter = dynamic_cast(savedParams.data()); if (adapter) { *args = adapter->transformArgs(); result = true; } } return result; } bool TransformStrokeStrategy::tryFetchArgsFromCommandAndUndo(ToolTransformArgs *outArgs, ToolTransformArgs::TransformMode mode, KisNodeSP currentNode, KisNodeList selectedNodes, QVector *undoJobs) { bool result = false; const KUndo2Command *lastCommand = undoFacade()->lastExecutedCommand(); KisNodeSP oldRootNode; KisNodeList oldTransformedNodes; ToolTransformArgs args; if (lastCommand && TransformStrokeStrategy::fetchArgsFromCommand(lastCommand, &args, &oldRootNode, &oldTransformedNodes) && args.mode() == mode && oldRootNode == currentNode) { if (KritaUtils::compareListsUnordered(oldTransformedNodes, selectedNodes)) { args.saveContinuedState(); *outArgs = args; const KisSavedMacroCommand *command = dynamic_cast(lastCommand); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(command, false); // the jobs are fetched as !shouldGoToHistory, // so there is no need to put them into // m_skippedWhileMergeCommands command->getCommandExecutionJobs(undoJobs, true, false); m_overriddenCommand = command; result = true; } } return result; } void TransformStrokeStrategy::initStrokeCallback() { KisStrokeStrategyUndoCommandBased::initStrokeCallback(); if (m_selection) { m_selection->setVisible(false); m_deactivatedSelections.append(m_selection); } KisSelectionMaskSP overlaySelectionMask = dynamic_cast(m_rootNode->graphListener()->graphOverlayNode()); if (overlaySelectionMask) { overlaySelectionMask->setDecorationsVisible(false); m_deactivatedOverlaySelectionMask = overlaySelectionMask; } ToolTransformArgs initialTransformArgs; m_processedNodes = fetchNodesList(m_mode, m_rootNode, m_workRecursively); bool argsAreInitialized = false; QVector lastCommandUndoJobs; if (!m_forceReset && tryFetchArgsFromCommandAndUndo(&initialTransformArgs, m_mode, m_rootNode, m_processedNodes, &lastCommandUndoJobs)) { argsAreInitialized = true; } else if (!m_forceReset && tryInitArgsFromNode(m_rootNode, &initialTransformArgs)) { argsAreInitialized = true; } QVector extraInitJobs; extraInitJobs << new Data(new KisHoldUIUpdatesCommand(m_updatesFacade, KisCommandUtils::FlipFlopCommand::INITIALIZING), false, KisStrokeJobData::BARRIER); extraInitJobs << lastCommandUndoJobs; KritaUtils::addJobSequential(extraInitJobs, [this]() { /** * We must request shape layers to rerender areas outside image bounds */ KisLayerUtils::forceAllHiddenOriginalsUpdate(m_rootNode); }); KritaUtils::addJobBarrier(extraInitJobs, [this]() { /** * We must ensure that the currently selected subtree * has finished all its updates. */ KisLayerUtils::forceAllDelayedNodesUpdate(m_rootNode); }); /// Disable all decorated nodes to generate outline /// and preview correctly. We will enable them back /// as soon as preview generation is finished. KritaUtils::addJobBarrier(extraInitJobs, [this]() { Q_FOREACH (KisNodeSP node, m_processedNodes) { KisDecoratedNodeInterface *decoratedNode = dynamic_cast(node.data()); if (decoratedNode && decoratedNode->decorationsVisible()) { decoratedNode->setDecorationsVisible(false); m_disabledDecoratedNodes << decoratedNode; } } }); KritaUtils::addJobBarrier(extraInitJobs, [this, initialTransformArgs, argsAreInitialized]() mutable { QRect srcRect; if (m_selection) { srcRect = m_selection->selectedExactRect(); } else { srcRect = QRect(); Q_FOREACH (KisNodeSP node, m_processedNodes) { // group layers may have a projection of layers // that are locked and will not be transformed if (node->inherits("KisGroupLayer")) continue; if (const KisTransformMask *mask = dynamic_cast(node.data())) { srcRect |= mask->sourceDataBounds(); } else if (const KisSelectionMask *mask = dynamic_cast(node.data())) { srcRect |= mask->selection()->selectedExactRect(); } else { srcRect |= node->exactBounds(); } } } TransformTransactionProperties transaction(srcRect, &initialTransformArgs, m_rootNode, m_processedNodes); if (!argsAreInitialized) { initialTransformArgs = KisTransformUtils::resetArgsForMode(m_mode, m_filterId, transaction); } this->m_initialTransformArgs = initialTransformArgs; emit this->sigTransactionGenerated(transaction, initialTransformArgs, this); }); extraInitJobs << new PreparePreviewData(); Q_FOREACH (KisNodeSP node, m_processedNodes) { extraInitJobs << new ClearSelectionData(node); } /// recover back visibility of decorated nodes KritaUtils::addJobBarrier(extraInitJobs, [this]() { Q_FOREACH (KisDecoratedNodeInterface *decoratedNode, m_disabledDecoratedNodes) { decoratedNode->setDecorationsVisible(true); } m_disabledDecoratedNodes.clear(); }); extraInitJobs << new Data(toQShared(new KisHoldUIUpdatesCommand(m_updatesFacade, KisCommandUtils::FlipFlopCommand::FINALIZING)), false, KisStrokeJobData::BARRIER); if (!lastCommandUndoJobs.isEmpty()) { KIS_SAFE_ASSERT_RECOVER_NOOP(m_overriddenCommand); for (auto it = extraInitJobs.begin(); it != extraInitJobs.end(); ++it) { (*it)->setCancellable(false); } } addMutatedJobs(extraInitJobs); } void TransformStrokeStrategy::finishStrokeImpl(bool applyTransform, const ToolTransformArgs &args) { /** * Since our finishStrokeCallback() initiates new jobs, * cancellation request may come even after * finishStrokeCallback() (cancellations may be called * until there are no jobs left in the stroke's queue). * * Therefore we should check for double-entry here and * make sure the finilizing jobs are no cancellable. */ if (m_finalizingActionsStarted) return; m_finalizingActionsStarted = true; QVector mutatedJobs; if (applyTransform) { Q_FOREACH (KisNodeSP node, m_processedNodes) { mutatedJobs << new TransformData(TransformData::PAINT_DEVICE, args, node); } mutatedJobs << new TransformData(TransformData::SELECTION, args, m_rootNode); } KritaUtils::addJobBarrier(mutatedJobs, [this, applyTransform]() { Q_FOREACH (KisSelectionSP selection, m_deactivatedSelections) { selection->setVisible(true); } Q_FOREACH (KisNodeSP node, m_hiddenProjectionLeaves) { node->projectionLeaf()->setTemporaryHiddenFromRendering(false); } if (m_deactivatedOverlaySelectionMask) { m_deactivatedOverlaySelectionMask->selection()->setVisible(true); m_deactivatedOverlaySelectionMask->setDirty(); } if (applyTransform) { KisStrokeStrategyUndoCommandBased::finishStrokeCallback(); } else { KisStrokeStrategyUndoCommandBased::cancelStrokeCallback(); } }); for (auto it = mutatedJobs.begin(); it != mutatedJobs.end(); ++it) { (*it)->setCancellable(false); } addMutatedJobs(mutatedJobs); } void TransformStrokeStrategy::finishStrokeCallback() { if (!m_savedTransformArgs || m_savedTransformArgs->isIdentity()) { cancelStrokeCallback(); return; } finishStrokeImpl(true, *m_savedTransformArgs); } void TransformStrokeStrategy::cancelStrokeCallback() { - const bool shouldRecoverSavedInitialState = - !m_initialTransformArgs.isIdentity(); - - if (shouldRecoverSavedInitialState) { - m_savedTransformArgs = m_initialTransformArgs; - } - - finishStrokeImpl(shouldRecoverSavedInitialState, *m_savedTransformArgs); + finishStrokeImpl(!m_initialTransformArgs.isIdentity(), m_initialTransformArgs); }