diff --git a/3rdparty/README.md b/3rdparty/README.md index 6025bb7bb9..0567b67876 100644 --- a/3rdparty/README.md +++ b/3rdparty/README.md @@ -1,305 +1,305 @@ = CMake external projects to build krita's dependencies on Linux, Windows or OSX = If you need to build Krita's dependencies for the following reasons: * you develop on Windows and aren't using Emerge * you develop on OSX and aren't using Homebrew * you want to build a generic, distro-agnostic version of Krita for Linux * you develop on Linux, but some dependencies aren't available for your distribution and you know what you're doing, you can use the following guide to build the dependencies that Krita needs. If you develop on Linux and your distribution has the dependencies available, YOU DO NOT NEED THIS GUIDE AND YOU SHOULD STOP READING NOW Otherwise you risk major confusion. == Prerequisites == Note: on all operating systems the entire procedure is done in a terminal window. 1. git: https://git-scm.com/downloads. Make sure git is in your path 2. cmake 3.3.2: https://cmake.org/download/. Make sure cmake is in your path. 3. Make sure you have a compiler: * Linux: gcc, minimum version 4.8 * OSX: clang, you need to install xcode for this * Windows: MSVC 2015 Community Edition: https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx 4. If you compile Qt on Windows, you will also need Python 2.7: https://www.python.org/download/releases/2.7/. Make sure to have python.exe in you path. == Setup your environment == Windows Only: When launching the commands from the console, run first "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64.bat" in it (include the quotes). This way all the environment variables for the compiler will be ready. == Prepare your directory layout == -1. Make a toplevel build directory, say $HOME/dev or c:\dev. We'll refer to this directory as BUILDROOT. You can use a variable for this, on WINDOWS %BUILDROOT%, on OSX and Linux $BUILDROOT. +1. Make a toplevel build directory, say $HOME/dev or c:\dev. We'll refer to this directory as BUILDROOT. You can use a variable for this, on WINDOWS %BUILDROOT%, on OSX and Linux $BUILDROOT. You will have to replace BUILDROOT with $BUILDROOT or %BUILDROOT whenever you copy and paste a command, depending on your operating system. 2. Checkout krita in BUILDROOT cd BUILDROOT git clone git://anongit.kde.org/krita.git 3. Create the build directory mkdir BUILDROOT/b 4. Create the downloads directory mkdir BUILDROOT/d 5. Create the install directory mkdir BUILDROOT/i == Qt == Install Qt. You have three options: * build from source yourself * install with the qt.io installer. * build using the krita/3rdparty/ext_qt project as detailed in the next section. Either way, make sure qmake is in your environment path. If you are using Qt 5.6 for example, this is the default location for Windows: C:\Qt\Qt5.6.0\5.6\msvc2015_64\bin, when using the qt.io installer. When building from source, you need the following modules: * qtbase * qtdeclarative * qtimageformats * qtscript * qtscript * qtsvg * qttools * qttranslations On Windows, you also need qtwinextras, on Linux qtx11extras, on OSX qtmacextras. When installing from source, you can use these example configure commands: * Linux: ./configure \ -skip qt3d \ -skip qtactiveqt \ -skip qtcanvas3d \ -skip qtconnectivity \ -skip qtdoc \ -skip qtenginio \ -skip qtgraphicaleffects \ -skip qtlocation \ -skip qtmultimedia \ -skip qtsensors \ -skip qtserialport \ -skip qtwayland \ -skip qtwebchannel \ -skip qtwebengine \ -skip qtwebkit \ -skip qtwebkit-examples \ -skip qtwebsockets \ -skip qtxmlpatterns \ -opensource -confirm-license -release \ -no-qml-debug -no-mtdev -no-journald \ -no-openssl -no-libproxy \ -no-pulseaudio -no-alsa -no-nis \ -no-cups -no-tslib -no-pch \ -no-dbus -no-gstreamer -no-system-proxies \ -no-openssl -no-libproxy -no-pulseaudio \ -qt-xcb -xcb -qt-freetype -qt-harfbuzz \ -qt-pcre -qt-xkbcommon-x11 -xcb-xlib \ -prefix BUILDROOT/i make -j8 * OSX ./configure \ -skip qt3d \ -skip qtactiveqt \ -skip qtcanvas3d \ -skip qtconnectivity \ -skip qtdoc \ -skip qtenginio \ -skip qtgraphicaleffects \ -skip qtlocation \ -skip qtmultimedia \ -skip qtsensors \ -skip qtserialport \ -skip qtwayland \ -skip qtwebchannel \ -skip qtwebengine \ -skip qtwebsockets \ -skip qtxmlpatterns \ -opensource -confirm-license -release \ -no-qml-debug -no-mtdev -no-journald \ -no-openssl -no-libproxy \ -no-pulseaudio -no-alsa -no-nis \ -no-cups -no-tslib -no-pch \ -no-dbus -no-gstreamer -no-system-proxies \ -no-openssl -no-libproxy -no-pulseaudio \ -nomake examples -nomake tests \ -prefix $BUILDROOT/i * Windows To do a normal release build use: configure -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtdoc -skip qtenginio -skip qtgraphicaleffects -skip qtlocation -skip qtmultimedia -skip qtsensors -skip qtserialport -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtxmlpatterns -no-sql-sqlite -nomake examples -nomake tools -no-compile-examples -no-dbus -no-iconv -no-angle -no-ssl -no-openssl -no-wmf-backend -no-qml-debug -no-libproxy -no-system-proxies -no-nis -no-icu -no-mtdev -opensource -confirm-license -release -opengl desktop -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -prefix %BUILDROOT%\i If a release with debug info for developing purposes is needed, add the -force-debug-info to the configure options above, so that the pdb files will be generated. N.B.: if you don't have the Qt 5.6 pre-built binaries already installed, you will need to copy Qt5Core.dll, coming from those binaries inside the Qt build directory, under the path qtbase\bin, otherwise you'll get an error about it missing when using jom and a more cryptic error with nmake. The same has to be done with Qt5Xml.dll, to be copied under qttools\bin. Don't use the dlls coming from the QtCreator folder, they won't work. == Prepare the externals build == 1. enter the BUILDROOT/b directory 2. run cmake: * Linux: cmake ../krita/3rdparty -DINSTALL_ROOT=BUILDROOT/i -DEXTERNALS_DOWNLOAD_DIR=BUILDROOT/d -DCMAKE_INSTALL_PREFIX=BUILDROOT/i : * OSX: * Windows 32 bits: * Windows 64 bits: Note that the cmake command needs to point to your BUILDROOT like /dev/d, not c:\dev\d. set PATH=BUILDROOT\i\bin\;BUILDROOT\i\lib;%PATH% cmake ..\krita\3rdparty -DEXTERNALS_DOWNLOAD_DIR=/dev/d -DINSTALL_ROOT=/dev/i -G "Visual Studio 14 Win64" 3. build the packages: With a judicious application of DEPENDS statements, it's possible to build it all in one go, but in my experience that fails anyway, so it's better to build the dependencies indepdendently. On Windows: cmake --build . --config RelWithDebInfo --target ext_patch cmake --build . --config RelWithDebInfo --target ext_png2ico cmake --build . --config RelWithDebInfo --target ext_pthreads On all operation systems, if you have decided to build Qt using the ext_qt project: cmake --build . --config RelWithDebInfo --target ext_qt On all operating systems: cmake --build . --config RelWithDebInfo --target ext_boost cmake --build . --config RelWithDebInfo --target ext_eigen3 cmake --build . --config RelWithDebInfo --target ext_exiv2 cmake --build . --config RelWithDebInfo --target ext_fftw3 cmake --build . --config RelWithDebInfo --target ext_ilmbase cmake --build . --config RelWithDebInfo --target ext_jpeg cmake --build . --config RelWithDebInfo --target ext_lcms2 cmake --build . --config RelWithDebInfo --target ext_ocio cmake --build . --config RelWithDebInfo --target ext_openexr Note for OSX: On OSX, you need to first build openexr; that will fail; then you need to set the rpath for the two utilities correctly, then try to build openexr again. install_name_tool -add_rpath $BUILD_ROOT/i/lib $BUILD_ROOT/b/ext_openexr/ext_openexr-prefix/src/ext_openexr-build/IlmImf/./b44ExpLogTable install_name_tool -add_rpath $BUILD_ROOT/i/lib $BUILD_ROOT/b/ext_openexr/ext_openexr-prefix/src/ext_openexr-build/IlmImf/./dwaLookups On All operating systems: cmake --build . --config RelWithDebInfo --target ext_png cmake --build . --config RelWithDebInfo --target ext_tiff cmake --build . --config RelWithDebInfo --target ext_gsl cmake --build . --config RelWithDebInfo --target ext_vc cmake --build . --config RelWithDebInfo --target ext_libraw cmake --build . --config RelWithDebInfo --target ext_openjpeg On Windows and OSX cmake --build . --config RelWithDebInfo --target ext_kwindowsystem On Windows (Note: skip this for now if you're using msvc 2015, poppler isn't compatible with that compiler yet) cmake --build . --config RelWithDebInfo --target ext_freetype cmake --build . --config RelWithDebInfo --target ext_poppler On Linux cmake --build . --config RelWithDebInfo --target ext_kcrash Note: poppler should be buildable on Linux as well with a home-built freetype and fontconfig, but I don't know how to make fontconfig find freetype, and on Linux, fontconfig is needed for poppler. Poppler is needed for PDF import. Note 2: libcurl still isn't available. == Build Krita == 1. Make a krita build directory: mkdir BUILDROOT/build 2. Enter the BUILDROOT/build 3. Run On Windows Depending on what you want to use, run this command for MSBuild: cmake ..\krita -G "Visual Studio 14 Win64" -DBoost_DEBUG=OFF -DBOOST_INCLUDEDIR=c:\dev\i\include -DBOOST_DEBUG=ON -DBOOST_ROOT=c:\dev\i -DBOOST_LIBRARYDIR=c:\dev\i\lib -DCMAKE_INSTALL_PREFIX=c:\dev\i -DCMAKE_PREFIX_PATH=c:\dev\i -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_TESTING=OFF -DKDE4_BUILD_TESTS=OFF -DHAVE_MEMORY_LEAK_TRACKER=OFF -DPACKAGERS_BUILD=ON -Wno-dev -DDEFINE_NO_DEPRECATED=1 Or this to later use jom (faster compiling, uses all cores, ships with QtCreator/pre-built Qt binaries): cmake ..\krita -G "NMake Makefiles" -DBoost_DEBUG=OFF -DBOOST_INCLUDEDIR=c:\dev\i\include -DBOOST_DEBUG=ON -DBOOST_ROOT=c:\dev\i -DBOOST_LIBRARYDIR=c:\dev\i\lib -DCMAKE_INSTALL_PREFIX=c:\dev\i -DCMAKE_PREFIX_PATH=c:\dev\i -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_TESTING=OFF -DKDE4_BUILD_TESTS=OFF -DHAVE_MEMORY_LEAK_TRACKER=OFF -DPACKAGERS_BUILD=ON -Wno-dev -DDEFINE_NO_DEPRECATED=1 On Linux cmake ../krita -DCMAKE_INSTALL_PREFIX=BUILDROOT/i -DDEFINE_NO_DEPRECATED=1 -DPACKAGERS_BUILD=ON -DBUILD_TESTING=OFF -DKDE4_BUILD_TESTS=OFF -DCMAKE_BUILD_TYPE=RelWithDebInfo On OSX cmake ../krita -DCMAKE_INSTALL_PREFIX=/Users/boud/dev/i -DDEFINE_NO_DEPRECATED=1 -DBUILD_TESTING=OFF -DKDE4_BUILD_TESTS=OFF -DPACKAGERS_BUILD=ON -DBUNDLE_INSTALL_DIR=$HOME/dev/i/bin -DCMAKE_BUILD_TYPE=RelWithDebInfo 4. Run On Linux and OSX make make install On Windows Either use MSBuild to build (-- /m tells msbuild to use all your cores): cmake --build . --config RelWithDebInfo --target INSTALL -- /m Or use jom which should be in a path similar to C:\Qt\Qt5.6.0\Tools\QtCreator\bin\jom.exe. So, from the same folder, instead of running cmake run: "C:\Qt\Qt5.6.0\Tools\QtCreator\bin\jom.exe" install 6. Run krita: On Linux BUILDROOT/i/bin/krita On Windows BUILDROOT\i\bin\krita.exe On OSX BUILDROOT/i/bin/krita.app/Contents/MacOS/krita == Packaging a Windows Build == If you want to create a stripped down version of Krita to distribute, after building everything just copy the makepkg.bat file from the "windows" folder inside krita root source folder to BUILDROOT and run it. That will copy the Krita necessary files into the specified folder and leave behind developer related files, so the resulting folder will be a smaller install folder. == Common Issues == - On Windows, if you get a 'mspdb140.dll' missing alert window, it means you did not run the bat file. Make sure to include the quotes in the command: "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64.bat" - On Windows, if you get an error about Qt5Core.dll missing/not found or nmake exit with an error that mention QT_PLUGIN_PATH, you have to copy a couple of dlls in the Qt build directory, look for the N.B. in the Qt instructions at the start of the Readme. - If you receive an error while compiling about "missing QtCore5.cmake", or something similar, check to make sure qmake is in your PATH. Restart your command line after any changes are made. diff --git a/3rdparty/ext_vc/CMakeLists.txt b/3rdparty/ext_vc/CMakeLists.txt index 421c171f91..d6680211a3 100755 --- a/3rdparty/ext_vc/CMakeLists.txt +++ b/3rdparty/ext_vc/CMakeLists.txt @@ -1,13 +1,13 @@ SET(PREFIX_ext_vc "${EXTPREFIX}" ) ExternalProject_Add( ext_vc DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://files.kde.org/krita/build/dependencies/Vc-0.7.5.tar.gz - URL_MD5 59854e2381b78d602b4a00e459e3d333 + URL http://files.kde.org/krita/build/dependencies/Vc-1.2.0.tar.gz + URL_MD5 f2a213ae4bad0dcf4ec6469e4dad41c1 INSTALL_DIR ${PREFIX_ext_vc} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${PREFIX_ext_vc} -DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} UPDATE_COMMAND "" ALWAYS 0 ) diff --git a/CMakeLists.txt b/CMakeLists.txt index 29bbd5a876..e392b3f5a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,685 +1,667 @@ project(krita) message(STATUS "Using CMake version: ${CMAKE_VERSION}") cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) set(MIN_QT_VERSION 5.4.0) set(MIN_FRAMEWORKS_VERSION 5.7.0) if (POLICY CMP0002) cmake_policy(SET CMP0002 OLD) endif() if (POLICY CMP0017) cmake_policy(SET CMP0017 NEW) endif () if (POLICY CMP0022) cmake_policy(SET CMP0022 OLD) endif () if (POLICY CMP0026) cmake_policy(SET CMP0026 OLD) endif() if (POLICY CMP0042) cmake_policy(SET CMP0042 NEW) endif() if (POLICY CMP0046) cmake_policy(SET CMP0046 OLD) endif () if (POLICY CMP0059) cmake_policy(SET CMP0059 OLD) endif() if (POLICY CMP0063) cmake_policy(SET CMP0063 OLD) endif() if (POLICY CMP0054) cmake_policy(SET CMP0054 OLD) endif() if (POLICY CMP0064) cmake_policy(SET CMP0064 OLD) endif() if (APPLE) set(APPLE_SUPPRESS_X11_WARNING TRUE) set(KDE_SKIP_RPATH_SETTINGS TRUE) set(CMAKE_MACOSX_RPATH 1) set(BUILD_WITH_INSTALL_RPATH 1) add_definitions(-Wno-deprecated-register) endif() # QT5TODO: remove KDE4_BUILD_TESTS once all kde4_add_unit_test have been converted # transitional forward compatibility: # BUILD_TESTING is cmake standard, KDE4_BUILD_TESTS not used by ECM/KF5, but only # macros in cmake/transitional. Just, Macros from cmake/transitional, # incl. kde4_add_unit_test, are only picked up if no macros from kdelibs4 are installed, # because that transitional path is appended. Prepending instead might possibly unwantedly # mask ECM/KF5 macros. Not tested. # So have BUILD_TESTING define KDE4_BUILD_TESTS. if (BUILD_TESTING) set(KDE4_BUILD_TESTS TRUE) else() set(KDE4_BUILD_TESTS FALSE) endif() ###################### ####################### ## Constants defines ## ####################### ###################### # define common versions of Krita applications, used to generate kritaversion.h # update these version for every release: -set(KRITA_VERSION_STRING "3.0 Beta") +set(KRITA_VERSION_STRING "3.1 Alpha") set(KRITA_STABLE_VERSION_MAJOR 3) # 3 for 3.x, 4 for 4.x, etc. -set(KRITA_STABLE_VERSION_MINOR 0) # 0 for 3.0, 1 for 3.1, etc. -set(KRITA_VERSION_RELEASE 90) # 89 for Alpha, increase for next test releases, set 0 for first Stable, etc. -#set(KRITA_ALPHA 1) # uncomment only for Alpha -set(KRITA_BETA 1) # uncomment only for Beta +set(KRITA_STABLE_VERSION_MINOR 1) # 0 for 3.0, 1 for 3.1, etc. +set(KRITA_VERSION_RELEASE 89) # 89 for Alpha, increase for next test releases, set 0 for first Stable, etc. +set(KRITA_ALPHA 1) # uncomment only for Alpha +#set(KRITA_BETA 1) # uncomment only for Beta #set(KRITA_RC 1) # uncomment only for RC set(KRITA_YEAR 2016) # update every year if(NOT DEFINED KRITA_ALPHA AND NOT DEFINED KRITA_BETA AND NOT DEFINED KRITA_RC) set(KRITA_STABLE 1) # do not edit endif() message(STATUS "Krita version: ${KRITA_VERSION_STRING}") # Define the generic version of the Krita libraries here # This makes it easy to advance it when the next Krita release comes. # 14 was the last GENERIC_KRITA_LIB_VERSION_MAJOR of the previous Krita series # (2.x) so we're starting with 15 in 3.x series. if(KRITA_STABLE_VERSION_MAJOR EQUAL 3) math(EXPR GENERIC_KRITA_LIB_VERSION_MAJOR "${KRITA_STABLE_VERSION_MINOR} + 15") else() # let's make sure we won't forget to update the "15" message(FATAL_ERROR "Reminder: please update offset == 15 used to compute GENERIC_KRITA_LIB_VERSION_MAJOR to something bigger") endif() set(GENERIC_KRITA_LIB_VERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}.0.0") set(GENERIC_KRITA_LIB_SOVERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules") LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/kde_macro") message("Module path:" ${CMAKE_MODULE_PATH}) # fetch git revision for the current build set(KRITA_GIT_SHA1_STRING "") set(KRITA_GIT_BRANCH_STRING "") include(GetGitRevisionDescription) get_git_head_revision(GIT_REFSPEC GIT_SHA1) get_git_branch(GIT_BRANCH) if(GIT_SHA1 AND GIT_BRANCH) string(SUBSTRING ${GIT_SHA1} 0 7 GIT_SHA1) set(KRITA_GIT_SHA1_STRING ${GIT_SHA1}) set(KRITA_GIT_BRANCH_STRING ${GIT_BRANCH}) endif() if(NOT DEFINED RELEASE_BUILD) # estimate mode by CMAKE_BUILD_TYPE content if not set on cmdline string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_TOLOWER) set(RELEASE_BUILD_TYPES "release" "relwithdebinfo" "minsizerel") list(FIND RELEASE_BUILD_TYPES "${CMAKE_BUILD_TYPE_TOLOWER}" INDEX) if (INDEX EQUAL -1) set(RELEASE_BUILD FALSE) else() set(RELEASE_BUILD TRUE) endif() endif() message(STATUS "Release build: ${RELEASE_BUILD}") ############ ############# ## Options ## ############# ############ option(PACKAGERS_BUILD "Build support of multiple CPU architectures in one binary. Should be used by packagers only or Krita developers. Only switch off when you're an artist optimizing a build for your very own machine." ON) if (WIN32) option(USE_BREAKPAD "Build the crash handler for Krita (only on windows)" OFF) 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) ####################### ######################## ## Productset setting ## ######################## ####################### # For predefined productsets see the definitions in KritaProducts.cmake and # in the files in the folder cmake/productsets. # Finding out the products & features to build is done in 5 steps: # 1. have the user define the products/features wanted, by giving a productset # 2. estimate all additional required products/features # 3. estimate which of the products/features can be build by external deps # 4. find which products/features have been temporarily disabled due to problems # 5. estimate which of the products/features can be build by internal deps # get the special macros include(CalligraProductSetMacros) include(MacroJPEG) include(GenerateTestExportHeader) # get the definitions of products, features and product sets include(KritaProducts.cmake) set(PRODUCTSET_DEFAULT "ALL") # temporary migration support if (CREATIVEONLY) set(WARN_ABOUT_CREATIVEONLY TRUE) set(PRODUCTSET_DEFAULT "CREATIVE") endif () if(NOT PRODUCTSET) set(PRODUCTSET ${PRODUCTSET_DEFAULT} CACHE STRING "Set of products/features to build" FORCE) endif() if (RELEASE_BUILD) set(CALLIGRA_SHOULD_BUILD_STAGING FALSE) else () set(CALLIGRA_SHOULD_BUILD_STAGING TRUE) endif () # finally choose products/features to build calligra_set_productset(${PRODUCTSET}) ######################## ######################### ## Look for KDE and Qt ## ######################### ######################## find_package(ECM 1.7.0 REQUIRED NOMODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(ECMOptionalAddSubdirectory) include(ECMAddAppIcon) include(ECMSetupVersion) include(ECMMarkNonGuiExecutable) include(ECMGenerateHeaders) include(GenerateExportHeader) include(ECMMarkAsTest) include(ECMInstallIcons) include(CMakePackageConfigHelpers) include(WriteBasicConfigVersionFile) include(CheckFunctionExists) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings) include(FeatureSummary) include(KDE4Macros) # do not reorder to be alphabetical: this is the order in which the frameworks # depend on each other. find_package(KF5 ${MIN_FRAMEWORKS_VERSION} REQUIRED COMPONENTS Archive Config WidgetsAddons Completion CoreAddons GuiAddons I18n ItemModels ItemViews WindowSystem ) find_package(Qt5 ${MIN_QT_VERSION} REQUIRED COMPONENTS Core Gui Widgets Xml Network PrintSupport Svg Test Concurrent ) set(QT_QTTEST_LIBRARY Qt5::Test) include (MacroLibrary) include (MacroAdditionalCleanFiles) include (MacroAddFileDependencies) 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) if (NOT WIN32 AND NOT APPLE) find_package(Qt5 ${MIN_QT_VERSION} REQUIRED X11Extras) find_package(Qt5DBus ${MIN_QT_VERSION} QUIET) set(HAVE_DBUS ${Qt5DBus_FOUND}) macro_log_feature(${Qt5DBus_FOUND} "dbus" "Qt DBUS integration" "http://www.qt.io/" FALSE "" "Optionally used to provide a dbus api on Linux") find_package(KF5KIO ${MIN_FRAMEWORKS_VERSION} QUIET) macro_bool_to_01(KF5KIO_FOUND HAVE_KIO) macro_log_feature(${KF5KIO_FOUND} "KIO" "KDE's KIO Framework" "http://api.kde.org/frameworks-api/frameworks5-apidocs/kio/html/index.html" FALSE "" "Optionally used for recent document handling") find_package(KF5Crash ${MIN_FRAMEWORKS_VERSION} QUIET) macro_bool_to_01(KF5Crash_FOUND HAVE_KCRASH) macro_log_feature(${KF5Crash_FOUND} "kcrash" "KDE's Crash Handler" "http://api.kde.org/frameworks-api/frameworks5-apidocs/kcrash/html/index.html" FALSE "" "Optionally used to provide crash reporting on Linux") find_package(X11) if(X11_FOUND) find_package(Qt5 ${MIN_QT_VERSION} REQUIRED NO_MODULE COMPONENTS X11Extras) set(HAVE_X11 TRUE) add_definitions(-DHAVE_X11) else() set(HAVE_X11 FALSE) endif() find_package(XCB COMPONENTS XCB ATOM) if(XCB_FOUND) set(HAVE_XCB TRUE) else() set(HAVE_XCB FALSE) endif() else() set(HAVE_DBUS FALSE) set(HAVE_X11 FALSE) set(HAVE_XCB FALSE) endif() add_definitions( -DTRANSLATION_DOMAIN=\"krita\" -DQT_USE_QSTRINGBUILDER -DQT_STRICT_ITERATORS -DQT_NO_SIGNALS_SLOTS_KEYWORDS -DQT_USE_FAST_OPERATOR_PLUS -DQT_USE_FAST_CONCATENATION -DQT_NO_URL_CAST_FROM_STRING -DQT_DISABLE_DEPRECATED_BEFORE=0 ) # # The reason for this mode is that the Debug mode disable inlining # if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS_KRITADEVS "-O3 -g" CACHE STRING "" FORCE) endif() if(UNIX) set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES};m") endif() if(WIN32) if(MSVC) # C4522: 'class' : multiple assignment operators specified set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4522") endif() endif() # enable exceptions globally kde_enable_exceptions() # only with this definition will all the FOO_TEST_EXPORT macro do something # TODO: check if this can be moved to only those places which make use of it, # to reduce global compiler definitions that would trigger a recompile of # everything on a change (like adding/removing tests to/from the build) if(BUILD_TESTING) add_definitions(-DCOMPILING_TESTS) endif() set(KRITA_DEFAULT_TEST_DATA_DIR ${CMAKE_SOURCE_DIR}/sdk/tests/data/) macro(macro_add_unittest_definitions) add_definitions(-DFILES_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data/") add_definitions(-DFILES_OUTPUT_DIR="${CMAKE_CURRENT_BINARY_DIR}") add_definitions(-DFILES_DEFAULT_DATA_DIR="${KRITA_DEFAULT_TEST_DATA_DIR}") endmacro() # overcome some platform incompatibilities if(WIN32) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/winquirks) add_definitions(-D_USE_MATH_DEFINES) add_definitions(-DNOMINMAX) set(WIN32_PLATFORM_NET_LIBS ws2_32.lib netapi32.lib) endif() # set custom krita plugin installdir set(KRITA_PLUGIN_INSTALL_DIR ${LIB_INSTALL_DIR}/kritaplugins) ########################### ############################ ## Required dependencies ## ############################ ########################### find_package(PNG REQUIRED) if (APPLE) # this is not added correctly on OSX -- see http://forum.kde.org/viewtopic.php?f=139&t=101867&p=221242#p221242 include_directories(SYSTEM ${PNG_INCLUDE_DIR}) endif() add_definitions(-DBOOST_ALL_NO_LIB) find_package(Boost REQUIRED COMPONENTS system) # for pigment and stage ## ## Test for GNU Scientific Library ## macro_optional_find_package(GSL) macro_log_feature(GSL_FOUND "GSL" "GNU Scientific Library" "http://www.gnu.org/software/gsl" FALSE "1.7" "Required by Krita's Transform tool.") macro_bool_to_01(GSL_FOUND HAVE_GSL) configure_file(config-gsl.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-gsl.h ) ########################### ############################ ## Optional dependencies ## ############################ ########################### ## ## Check for OpenEXR ## macro_optional_find_package(ZLIB) macro_log_feature(ZLIB_FOUND "zlib" "Compression library" "http://www.zlib.net/" FALSE "" "Optionally used by the G'Mic and the PSD plugins") macro_bool_to_01(ZLIB_FOUND HAVE_ZLIB) macro_optional_find_package(OpenEXR) macro_log_feature(OPENEXR_FOUND "OpenEXR" "High dynamic-range (HDR) image file format" "http://www.openexr.com" FALSE "" "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() macro_optional_find_package(TIFF) macro_log_feature(TIFF_FOUND "tiff" "TIFF Library and Utilities" "http://www.remotesensing.org/libtiff" FALSE "" "Required by the Krita TIFF filter") macro_optional_find_package(JPEG) macro_log_feature(JPEG_FOUND "jpeg" "Free library for JPEG image compression. Note: libjpeg8 is NOT supported." "http://www.libjpeg-turbo.org" FALSE "" "Required by the Krita JPEG filter") macro_optional_find_package(OpenJPEG) macro_log_feature(OPENJPEG_FOUND "openjpeg" "Free library for JPEG 2000 image compression" "http://www.openjpeg.org" FALSE "" "Required by the Krita JPEG 2000 filter") set(LIBRAW_MIN_VERSION "0.16") macro_optional_find_package(LibRaw ${LIBRAW_MIN_VERSION}) macro_log_feature(LIBRAW_FOUND "LibRaw" "Library to decode RAW images" "http://www.libraw.org" FALSE "" "Required to build the raw import plugin") macro_optional_find_package(FFTW3) macro_log_feature(FFTW3_FOUND "FFTW3" "A fast, free C FFT library" "http://www.fftw.org/" FALSE "" "Required by the Krita for fast convolution operators and some G'Mic features") macro_bool_to_01(FFTW3_FOUND HAVE_FFTW3) macro_optional_find_package(OCIO) macro_log_feature(OCIO_FOUND "OCIO" "The OpenColorIO Library" "http://www.opencolorio.org" FALSE "" "Required by the Krita LUT docker") macro_bool_to_01(OCIO_FOUND HAVE_OCIO) ## ## Look for OpenGL ## # TODO: see if there is a better check for QtGui being built with opengl support (and thus the QOpenGL* classes) if(Qt5Gui_OPENGL_IMPLEMENTATION) message(STATUS "Found QtGui OpenGL support") else() message(FATAL_ERROR "Did NOT find QtGui OpenGL support. Check your Qt configuration. You cannot build Krita without Qt OpenGL support.") endif() ## ## Test for eigen3 ## find_package(Eigen3 REQUIRED) macro_log_feature(EIGEN3_FOUND "Eigen" "C++ template library for linear algebra" "http://eigen.tuxfamily.org" FALSE "3.0" "Required by Krita") ## ## Test for exiv2 ## set(EXIV2_MIN_VERSION "0.16") find_package(Exiv2 REQUIRED) macro_log_feature(EXIV2_FOUND "Exiv2" "Image metadata library and tools" "http://www.exiv2.org" FALSE "0.16" "Required by Krita") ## ## Test for lcms ## find_package(LCMS2 REQUIRED) macro_log_feature(LCMS2_FOUND "LittleCMS" "Color management engine" "http://www.littlecms.com" FALSE "2.4" "Will be used for color management and is necessary for Krita") if(LCMS2_FOUND) if(NOT ${LCMS2_VERSION} VERSION_LESS 2040 ) set(HAVE_LCMS24 TRUE) endif() set(HAVE_REQUIRED_LCMS_VERSION TRUE) set(HAVE_LCMS2 TRUE) endif() ## ## Test for Vc ## set(OLD_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ) -macro_optional_find_package(Vc 0.6.70) -macro_log_feature(Vc_FOUND "Vc" "Portable, zero-overhead SIMD library for C++" "https://github.com/VcDevel/Vc" FALSE "" "Required by the Krita for vectorization") -macro_bool_to_01(Vc_FOUND HAVE_VC) -macro_bool_to_01(PACKAGERS_BUILD DO_PACKAGERS_BUILD) - +set(HAVE_VC FALSE) +if( NOT MSVC) + macro_optional_find_package(Vc 1.1.0) + macro_log_feature(Vc_FOUND "Vc" "Portable, zero-overhead SIMD library for C++" "https://github.com/VcDevel/Vc" FALSE "" "Required by the Krita for vectorization") + macro_bool_to_01(Vc_FOUND HAVE_VC) + macro_bool_to_01(PACKAGERS_BUILD DO_PACKAGERS_BUILD) +endif() configure_file(config-vc.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-vc.h ) if(HAVE_VC) message(STATUS "Vc found!") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/vc") include (VcMacros) if(Vc_COMPILER_IS_CLANG) set(ADDITIONAL_VC_FLAGS "-Wabi -ffp-contract=fast -fPIC") elseif (NOT MSVC) set(ADDITIONAL_VC_FLAGS "-Wabi -fabi-version=0 -ffp-contract=fast -fPIC") endif() #Handle Vc master - if(Vc_VERSION_MAJOR GREATER 0 OR Vc_VERSION_MINOR GREATER 7) - message(STATUS "Vc version is greater than 0.7, enabling AVX2 support") - - if(Vc_COMPILER_IS_GCC OR Vc_COMPILER_IS_CLANG) - AddCompilerFlag("-std=c++11" _ok) - if(NOT _ok) - AddCompilerFlag("-std=c++0x" _ok) - endif() + if(Vc_COMPILER_IS_GCC OR Vc_COMPILER_IS_CLANG) + AddCompilerFlag("-std=c++11" _ok) + if(NOT _ok) + AddCompilerFlag("-std=c++0x" _ok) endif() + endif() - macro(ko_compile_for_all_implementations_no_scalar _objs _src) - if(PACKAGERS_BUILD) - vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY SSE2 SSSE3 SSE4_1 AVX AVX2) - else() - set(${_objs} ${_src}) - endif() - endmacro() - - macro(ko_compile_for_all_implementations _objs _src) - if(PACKAGERS_BUILD) - vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY Scalar SSE2 SSSE3 SSE4_1 AVX AVX2) - else() - set(${_objs} ${_src}) - endif() - endmacro() + macro(ko_compile_for_all_implementations_no_scalar _objs _src) + if(PACKAGERS_BUILD) + vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) else() - macro(ko_compile_for_all_implementations_no_scalar _objs _src) - if(PACKAGERS_BUILD) - vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY SSE2 SSSE3 SSE4_1 AVX) - else() - set(${_objs} ${_src}) - endif() - endmacro() + set(${_objs} ${_src}) + endif() + endmacro() - macro(ko_compile_for_all_implementations _objs _src) - if(PACKAGERS_BUILD) - vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY Scalar SSE2 SSSE3 SSE4_1 AVX) - else() - set(${_objs} ${_src}) - endif() - endmacro() + macro(ko_compile_for_all_implementations _objs _src) + if(PACKAGERS_BUILD) + vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY Scalar SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) + else() + set(${_objs} ${_src}) endif() + endmacro() if (NOT PACKAGERS_BUILD) # Optimize everything for the current architecture set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Vc_DEFINITIONS}") endif () endif() set(CMAKE_MODULE_PATH ${OLD_CMAKE_MODULE_PATH} ) ## ## Test for Xinput ## if(NOT WIN32 AND NOT APPLE) set(REQUIRED_Xinput_FOUND ${X11_Xinput_FOUND}) else() set(REQUIRED_Xinput_FOUND TRUE) endif() add_definitions(${QT_DEFINITIONS} ${KDE4_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS}) if(WIN32) set(LIB_INSTALL_DIR ${LIB_INSTALL_DIR} RUNTIME DESTINATION ${BIN_INSTALL_DIR} LIBRARY ${INSTALL_TARGETS_DEFAULT_ARGS} ARCHIVE ${INSTALL_TARGETS_DEFAULT_ARGS} ) endif() ## ## Test endianess ## include (TestBigEndian) test_big_endian(CMAKE_WORDS_BIGENDIAN) ## ## Test for qt-poppler ## macro_optional_find_package(Poppler) macro_log_feature( POPPLER_FOUND "Poppler-Qt5" "A PDF rendering library" "http://poppler.freedesktop.org" FALSE "" "Required by the Krita PDF filter.") ## ## Test for pthreads (for G'Mic) ## macro_optional_find_package(Threads) macro_log_feature(Threads_FOUND "PThreads" "A low-level threading library" "" FALSE "" "Optionally used by the G'Mic plugin") ## ## Test for OpenMP (for G'Mic) ## macro_optional_find_package(OpenMP) macro_log_feature(OPENMP_FOUND "OpenMP" "A low-level parallel execution library" "http://openmp.org/wp/" FALSE "" "Optionally used by the G'Mic plugin") ## ## Test for Curl (for G'Mic) ## macro_optional_find_package(CURL) macro_log_feature(CURL_FOUND "CURL" "A tool to fetch remote data" "http://curl.haxx.se/" FALSE "" "Optionally used by the G'Mic plugin") ############################ ############################# ## Add Krita helper macros ## ############################# ############################ include(MacroKritaAddBenchmark) #################### ##################### ## Define includes ## ##################### #################### # for config.h and includes (if any?) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/interfaces ) include_directories( ${CMAKE_SOURCE_DIR}/libs/version ${CMAKE_BINARY_DIR}/libs/version ) ################################################### #################################################### ## Detect which products/features can be compiled ## #################################################### ################################################### calligra_drop_product_on_bad_condition( APP_KRITA EIGEN3_FOUND "Eigen devel not found" EXIV2_FOUND "libexiv2 devel not found" HAVE_REQUIRED_LCMS_VERSION "lcms devel not found" Boost_SYSTEM_FOUND "boost-system devel not found" REQUIRED_Xinput_FOUND "Xinput devel not found " ) ############################################# #### Backward compatibility BUILD_x=off #### ############################################# # workaround: disable directly all products which might be activated by internal # dependencies, but belong to scope of old flag calligra_drop_products_on_old_flag(krita APP_KRITA) ############################################# #### Temporarily broken products #### ############################################# # If a product does not build due to some temporary brokeness disable it here, # by calling calligra_disable_product with the product id and the reason, # e.g.: # calligra_disable_product(APP_KEXI "isn't buildable at the moment") ############################################# #### Calculate buildable products #### ############################################# calligra_drop_unbuildable_products() ################### #################### ## Subdirectories ## #################### ################### if(SHOULD_BUILD_APP_KRITA) add_subdirectory(krita) endif() # non-app directories are moved here because they can depend on SHOULD_BUILD_{appname} variables set above add_subdirectory(libs) add_subdirectory(plugins) add_subdirectory( benchmarks ) macro_display_feature_log() calligra_product_deps_report("product_deps") calligra_log_should_build() 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) diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 2ea18fff86..a92a669cc3 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -1,77 +1,79 @@ set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) include_directories( - ${CMAKE_SOURCE_DIR}/krita/image/tiles3 ${CMAKE_SOURCE_DIR}/sdk/tests ${CMAKE_SOURCE_DIR}/libs/pigment ${CMAKE_SOURCE_DIR}/libs/pigment/compositeops ) include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR} ${Boost_INCLUDE_DIRS} ) set(LINK_VC_LIB) if(HAVE_VC) include_directories(${Vc_INCLUDE_DIR}) # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Vc_DEFINITIONS}") set(LINK_VC_LIB ${Vc_LIBRARIES}) endif() macro_add_unittest_definitions() ########### next target ############### set(kis_datamanager_benchmark_SRCS kis_datamanager_benchmark.cpp) set(kis_hiterator_benchmark_SRCS kis_hline_iterator_benchmark.cpp) set(kis_viterator_benchmark_SRCS kis_vline_iterator_benchmark.cpp) set(kis_random_iterator_benchmark_SRCS kis_random_iterator_benchmark.cpp) set(kis_projection_benchmark_SRCS kis_projection_benchmark.cpp) set(kis_bcontrast_benchmark_SRCS kis_bcontrast_benchmark.cpp) set(kis_blur_benchmark_SRCS kis_blur_benchmark.cpp) set(kis_level_filter_benchmark_SRCS kis_level_filter_benchmark.cpp) set(kis_painter_benchmark_SRCS kis_painter_benchmark.cpp) set(kis_stroke_benchmark_SRCS kis_stroke_benchmark.cpp) set(kis_fast_math_benchmark_SRCS kis_fast_math_benchmark.cpp) set(kis_floodfill_benchmark_SRCS kis_floodfill_benchmark.cpp) set(kis_gradient_benchmark_SRCS kis_gradient_benchmark.cpp) set(kis_mask_generator_benchmark_SRCS kis_mask_generator_benchmark.cpp) set(kis_low_memory_benchmark_SRCS kis_low_memory_benchmark.cpp) set(kis_filter_selections_benchmark_SRCS kis_filter_selections_benchmark.cpp) set(kis_composition_benchmark_SRCS kis_composition_benchmark.cpp) krita_add_benchmark(KisDatamanagerBenchmark TESTNAME krita-benchmarks-KisDataManager ${kis_datamanager_benchmark_SRCS}) krita_add_benchmark(KisHLineIteratorBenchmark TESTNAME krita-benchmarks-KisHLineIterator ${kis_hiterator_benchmark_SRCS}) krita_add_benchmark(KisVLineIteratorBenchmark TESTNAME krita-benchmarks-KisVLineIterator ${kis_viterator_benchmark_SRCS}) krita_add_benchmark(KisRandomIteratorBenchmark TESTNAME krita-benchmarks-KisRandomIterator ${kis_random_iterator_benchmark_SRCS}) krita_add_benchmark(KisProjectionBenchmark TESTNAME krita-benchmarks-KisProjectionBenchmark ${kis_projection_benchmark_SRCS}) krita_add_benchmark(KisBContrastBenchmark TESTNAME krita-benchmarks-KisBContrastBenchmark ${kis_bcontrast_benchmark_SRCS}) krita_add_benchmark(KisBlurBenchmark TESTNAME krita-benchmarks-KisBlurBenchmark ${kis_blur_benchmark_SRCS}) krita_add_benchmark(KisLevelFilterBenchmark TESTNAME krita-benchmarks-KisLevelFilterBenchmark ${kis_level_filter_benchmark_SRCS}) krita_add_benchmark(KisPainterBenchmark TESTNAME krita-benchmarks-KisPainterBenchmark ${kis_painter_benchmark_SRCS}) krita_add_benchmark(KisStrokeBenchmark TESTNAME krita-benchmarks-KisStrokeBenchmark ${kis_stroke_benchmark_SRCS}) krita_add_benchmark(KisFastMathBenchmark TESTNAME krita-benchmarks-KisFastMath ${kis_fast_math_benchmark_SRCS}) krita_add_benchmark(KisFloodfillBenchmark TESTNAME krita-benchmarks-KisFloodFill ${kis_floodfill_benchmark_SRCS}) krita_add_benchmark(KisGradientBenchmark TESTNAME krita-benchmarks-KisGradientFill ${kis_gradient_benchmark_SRCS}) krita_add_benchmark(KisMaskGeneratorBenchmark TESTNAME krita-benchmarks-KisMaskGenerator ${kis_mask_generator_benchmark_SRCS}) krita_add_benchmark(KisLowMemoryBenchmark TESTNAME krita-benchmarks-KisLowMemory ${kis_low_memory_benchmark_SRCS}) krita_add_benchmark(KisFilterSelectionsBenchmark TESTNAME krita-image-KisFilterSelectionsBenchmark ${kis_filter_selections_benchmark_SRCS}) krita_add_benchmark(KisCompositionBenchmark TESTNAME krita-benchmarks-KisComposition ${kis_composition_benchmark_SRCS}) target_link_libraries(KisDatamanagerBenchmark kritaimage Qt5::Test) target_link_libraries(KisHLineIteratorBenchmark kritaimage Qt5::Test) target_link_libraries(KisVLineIteratorBenchmark kritaimage Qt5::Test) target_link_libraries(KisRandomIteratorBenchmark kritaimage Qt5::Test) target_link_libraries(KisProjectionBenchmark kritaimage kritaui Qt5::Test) target_link_libraries(KisBContrastBenchmark kritaimage Qt5::Test) target_link_libraries(KisBlurBenchmark kritaimage Qt5::Test) target_link_libraries(KisLevelFilterBenchmark kritaimage Qt5::Test) target_link_libraries(KisPainterBenchmark kritaimage Qt5::Test) target_link_libraries(KisStrokeBenchmark kritaimage Qt5::Test) target_link_libraries(KisFastMathBenchmark kritaimage Qt5::Test) target_link_libraries(KisFloodfillBenchmark kritaimage Qt5::Test) target_link_libraries(KisGradientBenchmark kritaimage Qt5::Test) target_link_libraries(KisLowMemoryBenchmark kritaimage Qt5::Test) target_link_libraries(KisFilterSelectionsBenchmark kritaimage Qt5::Test) target_link_libraries(KisCompositionBenchmark kritaimage Qt5::Test ${LINK_VC_LIB}) +if(HAVE_VC) + set_property(TARGET KisCompositionBenchmark APPEND PROPERTY COMPILE_OPTIONS "${Vc_ARCHITECTURE_FLAGS}") +endif() target_link_libraries(KisMaskGeneratorBenchmark kritaimage Qt5::Test) diff --git a/benchmarks/kis_composition_benchmark.cpp b/benchmarks/kis_composition_benchmark.cpp index 75f70ff0fe..3925ee1d25 100644 --- a/benchmarks/kis_composition_benchmark.cpp +++ b/benchmarks/kis_composition_benchmark.cpp @@ -1,940 +1,950 @@ /* * Copyright (c) 2012 Dmitry Kazakov * Copyright (c) 2015 Thorsten Zachmann * * This program is free software; you can redistribute 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. */ // for calculation of the needed alignment #include #ifdef HAVE_VC #if defined _MSC_VER // Lets shut up the "possible loss of data" and "forcing value to bool 'true' or 'false' #pragma warning ( push ) #pragma warning ( disable : 4244 ) #pragma warning ( disable : 4800 ) #endif #include #include #if defined _MSC_VER #pragma warning ( pop ) #endif #include #include #include #endif #include "kis_composition_benchmark.h" #include #include #include #include #include #include #include #include "KoOptimizedCompositeOpFactory.h" // for posix_memalign() #include #include #if defined _MSC_VER #define MEMALIGN_ALLOC(p, a, s) ((*(p)) = _aligned_malloc((s), (a)), *(p) ? 0 : errno) #define MEMALIGN_FREE(p) _aligned_free((p)) #else #define MEMALIGN_ALLOC(p, a, s) posix_memalign((p), (a), (s)) #define MEMALIGN_FREE(p) free((p)) #endif const int alpha_pos = 3; enum AlphaRange { ALPHA_ZERO, ALPHA_UNIT, ALPHA_RANDOM }; template inline channel_type generateAlphaValue(AlphaRange range, RandomGenerator &rnd) { channel_type value = 0; switch (range) { case ALPHA_ZERO: break; case ALPHA_UNIT: value = rnd.unit(); break; case ALPHA_RANDOM: value = rnd(); break; } return value; } #include #include #include template struct RandomGenerator { channel_type operator() () { qFatal("Wrong template instantiation"); return channel_type(0); } channel_type unit() { qFatal("Wrong template instantiation"); return channel_type(0); } }; template <> struct RandomGenerator { RandomGenerator(int seed) : m_smallint(0,255), m_rnd(seed) { } quint8 operator() () { return m_smallint(m_rnd); } quint8 unit() { return KoColorSpaceMathsTraits::unitValue; } boost::uniform_smallint m_smallint; boost::mt11213b m_rnd; }; template <> struct RandomGenerator { RandomGenerator(int seed) : m_rnd(seed) { } float operator() () { //return float(m_rnd()) / float(m_rnd.max()); return m_smallfloat(m_rnd); } float unit() { return KoColorSpaceMathsTraits::unitValue; } boost::uniform_real m_smallfloat; boost::mt11213b m_rnd; }; template <> struct RandomGenerator : RandomGenerator { RandomGenerator(int seed) : RandomGenerator(seed) { } }; template void generateDataLine(uint seed, int numPixels, quint8 *srcPixels, quint8 *dstPixels, quint8 *mask, AlphaRange srcAlphaRange, AlphaRange dstAlphaRange) { Q_ASSERT(numPixels >= 4); RandomGenerator rnd(seed); RandomGenerator maskRnd(seed + 1); channel_type *srcArray = reinterpret_cast(srcPixels); channel_type *dstArray = reinterpret_cast(dstPixels); for (int i = 0; i < numPixels; i++) { for (int j = 0; j < 3; j++) { channel_type s = rnd(); channel_type d = rnd(); *(srcArray++) = s; *(dstArray++) = d; } channel_type sa = generateAlphaValue(srcAlphaRange, rnd); channel_type da = generateAlphaValue(dstAlphaRange, rnd); *(srcArray++) = sa; *(dstArray++) = da; *(mask++) = maskRnd(); } } void printData(int numPixels, quint8 *srcPixels, quint8 *dstPixels, quint8 *mask) { for (int i = 0; i < numPixels; i++) { dbgKrita << "Src: " << srcPixels[i*4] << "\t" << srcPixels[i*4+1] << "\t" << srcPixels[i*4+2] << "\t" << srcPixels[i*4+3] << "\t" << "Msk:" << mask[i]; dbgKrita << "Dst: " << dstPixels[i*4] << "\t" << dstPixels[i*4+1] << "\t" << dstPixels[i*4+2] << "\t" << dstPixels[i*4+3]; } } const int rowStride = 64; const int totalRows = 64; const QRect processRect(0,0,64,64); const int numPixels = rowStride * totalRows; const int numTiles = 1024; struct Tile { quint8 *src; quint8 *dst; quint8 *mask; }; #include QVector generateTiles(int size, const int srcAlignmentShift, const int dstAlignmentShift, AlphaRange srcAlphaRange, AlphaRange dstAlphaRange, const quint32 pixelSize) { QVector tiles(size); #ifdef HAVE_VC - const int vecSize = Vc::float_v::Size; + const int vecSize = Vc::float_v::size(); #else const int vecSize = 1; #endif // the 256 are used to make sure that we have a good alignment no matter what build options are used. const size_t pixelAlignment = qMax(size_t(vecSize * sizeof(float)), size_t(256)); const size_t maskAlignment = qMax(size_t(vecSize), size_t(256)); for (int i = 0; i < size; i++) { void *ptr = 0; int error = MEMALIGN_ALLOC(&ptr, pixelAlignment, numPixels * pixelSize + srcAlignmentShift); if (error) { qFatal("posix_memalign failed: %d", error); } tiles[i].src = (quint8*)ptr + srcAlignmentShift; error = MEMALIGN_ALLOC(&ptr, pixelAlignment, numPixels * pixelSize + dstAlignmentShift); if (error) { qFatal("posix_memalign failed: %d", error); } tiles[i].dst = (quint8*)ptr + dstAlignmentShift; error = MEMALIGN_ALLOC(&ptr, maskAlignment, numPixels); if (error) { qFatal("posix_memalign failed: %d", error); } tiles[i].mask = (quint8*)ptr; if (pixelSize == 4) { generateDataLine(1, numPixels, tiles[i].src, tiles[i].dst, tiles[i].mask, srcAlphaRange, dstAlphaRange); } else if (pixelSize == 16) { generateDataLine(1, numPixels, tiles[i].src, tiles[i].dst, tiles[i].mask, srcAlphaRange, dstAlphaRange); } else { qFatal("Pixel size %i is not implemented", pixelSize); } } return tiles; } void freeTiles(QVector tiles, const int srcAlignmentShift, const int dstAlignmentShift) { Q_FOREACH (const Tile &tile, tiles) { MEMALIGN_FREE(tile.src - srcAlignmentShift); MEMALIGN_FREE(tile.dst - dstAlignmentShift); MEMALIGN_FREE(tile.mask); } } template inline bool fuzzyCompare(channel_type a, channel_type b, channel_type prec) { return qAbs(a - b) <= prec; } template inline bool comparePixels(channel_type *p1, channel_type *p2, channel_type prec) { return (p1[3] == p2[3] && p1[3] == 0) || (fuzzyCompare(p1[0], p2[0], prec) && fuzzyCompare(p1[1], p2[1], prec) && fuzzyCompare(p1[2], p2[2], prec) && fuzzyCompare(p1[3], p2[3], prec)); } template bool compareTwoOpsPixels(QVector &tiles, channel_type prec) { channel_type *dst1 = reinterpret_cast(tiles[0].dst); channel_type *dst2 = reinterpret_cast(tiles[1].dst); channel_type *src1 = reinterpret_cast(tiles[0].src); channel_type *src2 = reinterpret_cast(tiles[1].src); for (int i = 0; i < numPixels; i++) { if (!comparePixels(dst1, dst2, prec)) { dbgKrita << "Wrong result:" << i; dbgKrita << "Act: " << dst1[0] << dst1[1] << dst1[2] << dst1[3]; dbgKrita << "Exp: " << dst2[0] << dst2[1] << dst2[2] << dst2[3]; dbgKrita << "Dif: " << dst1[0] - dst2[0] << dst1[1] - dst2[1] << dst1[2] - dst2[2] << dst1[3] - dst2[3]; channel_type *s1 = src1 + 4 * i; channel_type *s2 = src2 + 4 * i; dbgKrita << "SrcA:" << s1[0] << s1[1] << s1[2] << s1[3]; dbgKrita << "SrcE:" << s2[0] << s2[1] << s2[2] << s2[3]; dbgKrita << "MskA:" << tiles[0].mask[i]; dbgKrita << "MskE:" << tiles[1].mask[i]; return false; } dst1 += 4; dst2 += 4; } return true; } bool compareTwoOps(bool haveMask, const KoCompositeOp *op1, const KoCompositeOp *op2) { Q_ASSERT(op1->colorSpace()->pixelSize() == op2->colorSpace()->pixelSize()); const quint32 pixelSize = op1->colorSpace()->pixelSize(); const int alignment = 16; QVector tiles = generateTiles(2, alignment, alignment, ALPHA_RANDOM, ALPHA_RANDOM, op1->colorSpace()->pixelSize()); KoCompositeOp::ParameterInfo params; params.dstRowStride = 4 * rowStride; params.srcRowStride = 4 * rowStride; params.maskRowStride = rowStride; params.rows = processRect.height(); params.cols = processRect.width(); // This is a hack as in the old version we get a rounding of opacity to this value params.opacity = float(Arithmetic::scale(0.5*1.0f))/255.0; params.flow = 0.3*1.0f; params.channelFlags = QBitArray(); params.dstRowStart = tiles[0].dst; params.srcRowStart = tiles[0].src; params.maskRowStart = haveMask ? tiles[0].mask : 0; op1->composite(params); params.dstRowStart = tiles[1].dst; params.srcRowStart = tiles[1].src; params.maskRowStart = haveMask ? tiles[1].mask : 0; op2->composite(params); bool compareResult = true; if (pixelSize == 4) { compareResult = compareTwoOpsPixels(tiles, 10); } else if (pixelSize == 16) { compareResult = compareTwoOpsPixels(tiles, 2e-7); } else { qFatal("Pixel size %i is not implemented", pixelSize); } freeTiles(tiles, alignment, alignment); return compareResult; } QString getTestName(bool haveMask, const int srcAlignmentShift, const int dstAlignmentShift, AlphaRange srcAlphaRange, AlphaRange dstAlphaRange) { QString testName; testName += !srcAlignmentShift && !dstAlignmentShift ? "Aligned " : !srcAlignmentShift && dstAlignmentShift ? "SrcUnalig " : srcAlignmentShift && !dstAlignmentShift ? "DstUnalig " : srcAlignmentShift && dstAlignmentShift ? "Unaligned " : "###"; testName += haveMask ? "Mask " : "NoMask "; testName += srcAlphaRange == ALPHA_RANDOM ? "SrcRand " : srcAlphaRange == ALPHA_ZERO ? "SrcZero " : srcAlphaRange == ALPHA_UNIT ? "SrcUnit " : "###"; testName += dstAlphaRange == ALPHA_RANDOM ? "DstRand" : dstAlphaRange == ALPHA_ZERO ? "DstZero" : dstAlphaRange == ALPHA_UNIT ? "DstUnit" : "###"; return testName; } void benchmarkCompositeOp(const KoCompositeOp *op, bool haveMask, qreal opacity, qreal flow, const int srcAlignmentShift, const int dstAlignmentShift, AlphaRange srcAlphaRange, AlphaRange dstAlphaRange) { QString testName = getTestName(haveMask, srcAlignmentShift, dstAlignmentShift, srcAlphaRange, dstAlphaRange); QVector tiles = generateTiles(numTiles, srcAlignmentShift, dstAlignmentShift, srcAlphaRange, dstAlphaRange, op->colorSpace()->pixelSize()); const int tileOffset = 4 * (processRect.y() * rowStride + processRect.x()); KoCompositeOp::ParameterInfo params; params.dstRowStride = 4 * rowStride; params.srcRowStride = 4 * rowStride; params.maskRowStride = rowStride; params.rows = processRect.height(); params.cols = processRect.width(); params.opacity = opacity; params.flow = flow; params.channelFlags = QBitArray(); QTime timer; timer.start(); Q_FOREACH (const Tile &tile, tiles) { params.dstRowStart = tile.dst + tileOffset; params.srcRowStart = tile.src + tileOffset; params.maskRowStart = haveMask ? tile.mask : 0; op->composite(params); } dbgKrita << testName << "RESULT:" << timer.elapsed() << "msec"; freeTiles(tiles, srcAlignmentShift, dstAlignmentShift); } void benchmarkCompositeOp(const KoCompositeOp *op, const QString &postfix) { dbgKrita << "Testing Composite Op:" << op->id() << "(" << postfix << ")"; benchmarkCompositeOp(op, true, 0.5, 0.3, 0, 0, ALPHA_RANDOM, ALPHA_RANDOM); benchmarkCompositeOp(op, true, 0.5, 0.3, 8, 0, ALPHA_RANDOM, ALPHA_RANDOM); benchmarkCompositeOp(op, true, 0.5, 0.3, 0, 8, ALPHA_RANDOM, ALPHA_RANDOM); benchmarkCompositeOp(op, true, 0.5, 0.3, 4, 8, ALPHA_RANDOM, ALPHA_RANDOM); /// --- Vary the content of the source and destination benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_RANDOM, ALPHA_RANDOM); benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_ZERO, ALPHA_RANDOM); benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_UNIT, ALPHA_RANDOM); /// --- benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_RANDOM, ALPHA_ZERO); benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_ZERO, ALPHA_ZERO); benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_UNIT, ALPHA_ZERO); /// --- benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_RANDOM, ALPHA_UNIT); benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_ZERO, ALPHA_UNIT); benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_UNIT, ALPHA_UNIT); } #ifdef HAVE_VC template void checkRounding(qreal opacity, qreal flow, qreal averageOpacity = -1, quint32 pixelSize = 4) { QVector tiles = generateTiles(2, 0, 0, ALPHA_RANDOM, ALPHA_RANDOM, pixelSize); - const int vecSize = Vc::float_v::Size; + const int vecSize = Vc::float_v::size(); const int numBlocks = numPixels / vecSize; quint8 *src1 = tiles[0].src; quint8 *dst1 = tiles[0].dst; quint8 *msk1 = tiles[0].mask; quint8 *src2 = tiles[1].src; quint8 *dst2 = tiles[1].dst; quint8 *msk2 = tiles[1].mask; KoCompositeOp::ParameterInfo params; params.opacity = opacity; params.flow = flow; if (averageOpacity >= 0.0) { params._lastOpacityData = averageOpacity; params.lastOpacity = ¶ms._lastOpacityData; } params.channelFlags = QBitArray(); typename Compositor::OptionalParams optionalParams(params); // The error count is needed as 38.5 gets rounded to 38 instead of 39 in the vc version. int errorcount = 0; for (int i = 0; i < numBlocks; i++) { - Compositor::template compositeVector(src1, dst1, msk1, params.opacity, optionalParams); + Compositor::template compositeVector(src1, dst1, msk1, params.opacity, optionalParams); for (int j = 0; j < vecSize; j++) { //if (8 * i + j == 7080) { // dbgKrita << "src: " << src2[0] << src2[1] << src2[2] << src2[3]; // dbgKrita << "dst: " << dst2[0] << dst2[1] << dst2[2] << dst2[3]; // dbgKrita << "msk:" << msk2[0]; //} - Compositor::template compositeOnePixelScalar(src2, dst2, msk2, params.opacity, optionalParams); + Compositor::template compositeOnePixelScalar(src2, dst2, msk2, params.opacity, optionalParams); bool compareResult = true; if (pixelSize == 4) { compareResult = comparePixels(dst1, dst2, 0); if (!compareResult) { ++errorcount; compareResult = comparePixels(dst1, dst2, 1); if (!compareResult) { ++errorcount; } } } else if (pixelSize == 16) { compareResult = comparePixels(reinterpret_cast(dst1), reinterpret_cast(dst2), 0); } else { qFatal("Pixel size %i is not implemented", pixelSize); } if(!compareResult || errorcount > 1) { dbgKrita << "Wrong rounding in pixel:" << 8 * i + j; dbgKrita << "Vector version: " << dst1[0] << dst1[1] << dst1[2] << dst1[3]; dbgKrita << "Scalar version: " << dst2[0] << dst2[1] << dst2[2] << dst2[3]; dbgKrita << "src:" << src1[0] << src1[1] << src1[2] << src1[3]; dbgKrita << "msk:" << msk1[0]; QFAIL("Wrong rounding"); } src1 += pixelSize; dst1 += pixelSize; src2 += pixelSize; dst2 += pixelSize; msk1++; msk2++; } } freeTiles(tiles, 0, 0); } #endif void KisCompositionBenchmark::checkRoundingAlphaDarken_05_03() { #ifdef HAVE_VC checkRounding >(0.5,0.3); #endif } void KisCompositionBenchmark::checkRoundingAlphaDarken_05_05() { #ifdef HAVE_VC checkRounding >(0.5,0.5); #endif } void KisCompositionBenchmark::checkRoundingAlphaDarken_05_07() { #ifdef HAVE_VC checkRounding >(0.5,0.7); #endif } void KisCompositionBenchmark::checkRoundingAlphaDarken_05_10() { #ifdef HAVE_VC checkRounding >(0.5,1.0); #endif } void KisCompositionBenchmark::checkRoundingAlphaDarken_05_10_08() { #ifdef HAVE_VC checkRounding >(0.5,1.0,0.8); #endif } void KisCompositionBenchmark::checkRoundingAlphaDarkenF32_05_03() { #ifdef HAVE_VC checkRounding >(0.5, 0.3, -1, 16); #endif } void KisCompositionBenchmark::checkRoundingAlphaDarkenF32_05_05() { #ifdef HAVE_VC checkRounding >(0.5, 0.5, -1, 16); #endif } void KisCompositionBenchmark::checkRoundingAlphaDarkenF32_05_07() { #ifdef HAVE_VC checkRounding >(0.5, 0.7, -1, 16); #endif } void KisCompositionBenchmark::checkRoundingAlphaDarkenF32_05_10() { #ifdef HAVE_VC checkRounding >(0.5, 1.0, -1, 16); #endif } void KisCompositionBenchmark::checkRoundingAlphaDarkenF32_05_10_08() { #ifdef HAVE_VC checkRounding >(0.5, 1.0, 0.8, 16); #endif } void KisCompositionBenchmark::checkRoundingOver() { #ifdef HAVE_VC checkRounding >(0.5, 0.3); #endif } void KisCompositionBenchmark::checkRoundingOverRgbaF32() { #ifdef HAVE_VC checkRounding >(0.5, 0.3, -1, 16); #endif } void KisCompositionBenchmark::compareAlphaDarkenOps() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KoCompositeOp *opAct = KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs); KoCompositeOp *opExp = new KoCompositeOpAlphaDarken(cs); QVERIFY(compareTwoOps(true, opAct, opExp)); delete opExp; delete opAct; } void KisCompositionBenchmark::compareRgbF32AlphaDarkenOps() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace("RGBA", "F32", ""); KoCompositeOp *opAct = KoOptimizedCompositeOpFactory::createAlphaDarkenOp128(cs); KoCompositeOp *opExp = new KoCompositeOpAlphaDarken(cs); QVERIFY(compareTwoOps(true, opAct, opExp)); delete opExp; delete opAct; } void KisCompositionBenchmark::compareAlphaDarkenOpsNoMask() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KoCompositeOp *opAct = KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs); KoCompositeOp *opExp = new KoCompositeOpAlphaDarken(cs); QVERIFY(compareTwoOps(false, opAct, opExp)); delete opExp; delete opAct; } void KisCompositionBenchmark::compareOverOps() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KoCompositeOp *opAct = KoOptimizedCompositeOpFactory::createOverOp32(cs); KoCompositeOp *opExp = new KoCompositeOpOver(cs); QVERIFY(compareTwoOps(true, opAct, opExp)); delete opExp; delete opAct; } void KisCompositionBenchmark::compareOverOpsNoMask() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KoCompositeOp *opAct = KoOptimizedCompositeOpFactory::createOverOp32(cs); KoCompositeOp *opExp = new KoCompositeOpOver(cs); QVERIFY(compareTwoOps(false, opAct, opExp)); delete opExp; delete opAct; } void KisCompositionBenchmark::compareRgbF32OverOps() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace("RGBA", "F32", ""); KoCompositeOp *opAct = KoOptimizedCompositeOpFactory::createOverOp128(cs); KoCompositeOp *opExp = new KoCompositeOpOver(cs); QVERIFY(compareTwoOps(false, opAct, opExp)); delete opExp; delete opAct; } void KisCompositionBenchmark::testRgb8CompositeAlphaDarkenLegacy() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KoCompositeOp *op = new KoCompositeOpAlphaDarken(cs); benchmarkCompositeOp(op, "Legacy"); delete op; } void KisCompositionBenchmark::testRgb8CompositeAlphaDarkenOptimized() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KoCompositeOp *op = KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs); benchmarkCompositeOp(op, "Optimized"); delete op; } void KisCompositionBenchmark::testRgb8CompositeOverLegacy() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KoCompositeOp *op = new KoCompositeOpOver(cs); benchmarkCompositeOp(op, "Legacy"); delete op; } void KisCompositionBenchmark::testRgb8CompositeOverOptimized() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KoCompositeOp *op = KoOptimizedCompositeOpFactory::createOverOp32(cs); benchmarkCompositeOp(op, "Optimized"); delete op; } void KisCompositionBenchmark::testRgbF32CompositeAlphaDarkenLegacy() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace("RGBA", "F32", ""); KoCompositeOp *op = new KoCompositeOpAlphaDarken(cs); benchmarkCompositeOp(op, "Legacy"); delete op; } void KisCompositionBenchmark::testRgbF32CompositeAlphaDarkenOptimized() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace("RGBA", "F32", ""); KoCompositeOp *op = KoOptimizedCompositeOpFactory::createAlphaDarkenOp128(cs); benchmarkCompositeOp(op, "Optimized"); delete op; } void KisCompositionBenchmark::testRgbF32CompositeOverLegacy() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace("RGBA", "F32", ""); KoCompositeOp *op = new KoCompositeOpOver(cs); benchmarkCompositeOp(op, "RGBF32 Legacy"); delete op; } void KisCompositionBenchmark::testRgbF32CompositeOverOptimized() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace("RGBA", "F32", ""); KoCompositeOp *op = KoOptimizedCompositeOpFactory::createOverOp128(cs); benchmarkCompositeOp(op, "RGBF32 Optimized"); delete op; } void KisCompositionBenchmark::testRgb8CompositeAlphaDarkenReal_Aligned() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); const KoCompositeOp *op = cs->compositeOp(COMPOSITE_ALPHA_DARKEN); benchmarkCompositeOp(op, true, 0.5, 0.3, 0, 0, ALPHA_RANDOM, ALPHA_RANDOM); } void KisCompositionBenchmark::testRgb8CompositeOverReal_Aligned() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); const KoCompositeOp *op = cs->compositeOp(COMPOSITE_OVER); benchmarkCompositeOp(op, true, 0.5, 0.3, 0, 0, ALPHA_RANDOM, ALPHA_RANDOM); } void KisCompositionBenchmark::testRgb8CompositeCopyLegacy() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); const KoCompositeOp *op = cs->compositeOp(COMPOSITE_COPY); benchmarkCompositeOp(op, "Copy"); } void KisCompositionBenchmark::benchmarkMemcpy() { QVector tiles = generateTiles(numTiles, 0, 0, ALPHA_UNIT, ALPHA_UNIT, 4); QBENCHMARK_ONCE { Q_FOREACH (const Tile &tile, tiles) { memcpy(tile.dst, tile.src, 4 * numPixels); } } freeTiles(tiles, 0, 0); } #ifdef HAVE_VC - const int vecSize = Vc::float_v::Size; + const int vecSize = Vc::float_v::size(); const size_t uint8VecAlignment = qMax(vecSize * sizeof(quint8), sizeof(void*)); const size_t uint32VecAlignment = qMax(vecSize * sizeof(quint32), sizeof(void*)); const size_t floatVecAlignment = qMax(vecSize * sizeof(float), sizeof(void*)); #endif void KisCompositionBenchmark::benchmarkUintFloat() { #ifdef HAVE_VC + using uint_v = Vc::SimdArray; + const int dataSize = 4096; void *ptr = 0; int error = MEMALIGN_ALLOC(&ptr, uint8VecAlignment, dataSize); if (error) { qFatal("posix_memalign failed: %d", error); } quint8 *iData = (quint8*)ptr; error = MEMALIGN_ALLOC(&ptr, floatVecAlignment, dataSize * sizeof(float)); if (error) { qFatal("posix_memalign failed: %d", error); } float *fData = (float*)ptr; QBENCHMARK { - for (int i = 0; i < dataSize; i += Vc::float_v::Size) { + for (int i = 0; i < dataSize; i += Vc::float_v::size()) { // convert uint -> float directly, this causes // static_cast helper be called - Vc::float_v b(Vc::uint_v(iData + i)); + Vc::float_v b(uint_v(iData + i)); b.store(fData + i); } } MEMALIGN_FREE(iData); MEMALIGN_FREE(fData); #endif } void KisCompositionBenchmark::benchmarkUintIntFloat() { #ifdef HAVE_VC + using int_v = Vc::SimdArray; + using uint_v = Vc::SimdArray; + const int dataSize = 4096; void *ptr = 0; int error = MEMALIGN_ALLOC(&ptr, uint8VecAlignment, dataSize); if (error) { qFatal("posix_memalign failed: %d", error); } quint8 *iData = (quint8*)ptr; error = MEMALIGN_ALLOC(&ptr, floatVecAlignment, dataSize * sizeof(float)); if (error) { qFatal("posix_memalign failed: %d", error); } float *fData = (float*)ptr; QBENCHMARK { - for (int i = 0; i < dataSize; i += Vc::float_v::Size) { + for (int i = 0; i < dataSize; i += Vc::float_v::size()) { // convert uint->int->float, that avoids special sign // treating, and gives 2.6 times speedup - Vc::float_v b(Vc::int_v(Vc::uint_v(iData + i))); + Vc::float_v b(int_v(uint_v(iData + i))); b.store(fData + i); } } MEMALIGN_FREE(iData); MEMALIGN_FREE(fData); #endif } void KisCompositionBenchmark::benchmarkFloatUint() { #ifdef HAVE_VC + using uint_v = Vc::SimdArray; + const int dataSize = 4096; void *ptr = 0; int error = MEMALIGN_ALLOC(&ptr, uint32VecAlignment, dataSize * sizeof(quint32)); if (error) { qFatal("posix_memalign failed: %d", error); } quint32 *iData = (quint32*)ptr; error = MEMALIGN_ALLOC(&ptr, floatVecAlignment, dataSize * sizeof(float)); if (error) { qFatal("posix_memalign failed: %d", error); } float *fData = (float*)ptr; QBENCHMARK { - for (int i = 0; i < dataSize; i += Vc::float_v::Size) { + for (int i = 0; i < dataSize; i += Vc::float_v::size()) { // conversion float -> uint - Vc::uint_v b(Vc::float_v(fData + i)); + uint_v b(Vc::float_v(fData + i)); b.store(iData + i); } } MEMALIGN_FREE(iData); MEMALIGN_FREE(fData); #endif } void KisCompositionBenchmark::benchmarkFloatIntUint() { #ifdef HAVE_VC + using int_v = Vc::SimdArray; + using uint_v = Vc::SimdArray; + const int dataSize = 4096; void *ptr = 0; int error = MEMALIGN_ALLOC(&ptr, uint32VecAlignment, dataSize * sizeof(quint32)); if (error) { qFatal("posix_memalign failed: %d", error); } quint32 *iData = (quint32*)ptr; error = MEMALIGN_ALLOC(&ptr, floatVecAlignment, dataSize * sizeof(float)); if (error) { qFatal("posix_memalign failed: %d", error); } float *fData = (float*)ptr; QBENCHMARK { - for (int i = 0; i < dataSize; i += Vc::float_v::Size) { + for (int i = 0; i < dataSize; i += Vc::float_v::size()) { // conversion float -> int -> uint - Vc::uint_v b(Vc::int_v(Vc::float_v(fData + i))); + uint_v b(int_v(Vc::float_v(fData + i))); b.store(iData + i); } } MEMALIGN_FREE(iData); MEMALIGN_FREE(fData); #endif } QTEST_MAIN(KisCompositionBenchmark) diff --git a/benchmarks/kis_mask_generator_benchmark.cpp b/benchmarks/kis_mask_generator_benchmark.cpp index d8999c16c8..da3709a01e 100644 --- a/benchmarks/kis_mask_generator_benchmark.cpp +++ b/benchmarks/kis_mask_generator_benchmark.cpp @@ -1,86 +1,111 @@ /* * Copyright (c) 2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #ifdef HAVE_VC #if defined _MSC_VER // Lets shut up the "possible loss of data" and "forcing value to bool 'true' or 'false' #pragma warning ( push ) #pragma warning ( disable : 4244 ) #pragma warning ( disable : 4800 ) #endif #include #include #if defined _MSC_VER #pragma warning ( pop ) #endif #endif #include #include "kis_mask_generator_benchmark.h" #include "kis_circle_mask_generator.h" #include "kis_rect_mask_generator.h" void KisMaskGeneratorBenchmark::benchmarkCircle() { KisCircleMaskGenerator gen(1000, 0.5, 0.5, 0.5, 3, true); QBENCHMARK{ for(int i = -600; i < 600; ++i) { for(int j = -600; j < 600; ++j) { gen.valueAt(i, j); } } } } -void KisMaskGeneratorBenchmark::benchmarkSIMD() -{ -#ifdef HAVE_VC - int width = 1000; - float *buffer = Vc::malloc(width); +#include +#include +#include "kis_fixed_paint_device.h" +#include "kis_types.h" +#include "kis_brush_mask_applicator_base.h" +#include "krita_utils.h" + + +void benchmarkSIMD(qreal fade) { + const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); + KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); + dev->setRect(QRect(0, 0, 1000, 1000)); + dev->initialize(); + + MaskProcessingData data(dev, cs, + 0.0, 1.0, + 500, 500, 0); + + KisCircleMaskGenerator gen(1000, 1.0, fade, fade, 2, false); + + KisBrushMaskApplicatorBase *applicator = gen.applicator(); + applicator->initializeData(&data); + + QVector rects = KritaUtils::splitRectIntoPatches(dev->bounds(), QSize(63, 63)); - KisCircleMaskGenerator gen(1000, 0.5, 0.5, 0.5, 2, true); QBENCHMARK{ - for(int y = 0; y < 1000; ++y) - { -// gen.processRowFast(buffer, width, y, 0.0f, 1.0f, 500.0f, 500.0f, 0.5f, 0.5f); + Q_FOREACH (const QRect &rc, rects) { + applicator->process(rc); } } - Vc::free(buffer); -#endif +} + +void KisMaskGeneratorBenchmark::benchmarkSIMD_SharpBrush() +{ + benchmarkSIMD(1.0); +} + +void KisMaskGeneratorBenchmark::benchmarkSIMD_FadedBrush() +{ + benchmarkSIMD(0.5); } void KisMaskGeneratorBenchmark::benchmarkSquare() { KisRectangleMaskGenerator gen(1000, 0.5, 0.5, 0.5, 3, true); QBENCHMARK{ for(int i = -600; i < 600; ++i) { for(int j = -600; j < 600; ++j) { gen.valueAt(i, j); } } } } QTEST_MAIN(KisMaskGeneratorBenchmark) diff --git a/benchmarks/kis_mask_generator_benchmark.h b/benchmarks/kis_mask_generator_benchmark.h index a7fa369551..a70bfe5dc3 100644 --- a/benchmarks/kis_mask_generator_benchmark.h +++ b/benchmarks/kis_mask_generator_benchmark.h @@ -1,34 +1,35 @@ /* * Copyright (c) 2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_MASK_GENERATOR_BENCHMARK_H #define KIS_MASK_GENERATOR_BENCHMARK_H #include class KisMaskGeneratorBenchmark : public QObject { Q_OBJECT private Q_SLOTS: void benchmarkCircle(); - void benchmarkSIMD(); + void benchmarkSIMD_SharpBrush(); + void benchmarkSIMD_FadedBrush(); void benchmarkSquare(); - + }; #endif diff --git a/cmake/modules/FindVc.cmake b/cmake/modules/FindVc.cmake index bb9823fb4f..f9315d4490 100644 --- a/cmake/modules/FindVc.cmake +++ b/cmake/modules/FindVc.cmake @@ -1,32 +1,32 @@ # Locate the Vc template library. Vc can be found at http://gitorious.org/Vc/ # # Copyright 2009-2012 Matthias Kretz # # This file is meant to be copied into projects that want to use Vc. It will # search for VcConfig.cmake, which ships with Vc and will provide up-to-date # buildsystem changes. Thus there should not be any need to update FindVc.cmake # again after you integrated it into your project. # # This module defines the following variables: # Vc_FOUND # Vc_INCLUDE_DIR # Vc_LIBRARIES # Vc_DEFINITIONS # Vc_VERSION_MAJOR # Vc_VERSION_MINOR # Vc_VERSION_PATCH # Vc_VERSION # Vc_VERSION_STRING # Vc_INSTALL_DIR # Vc_LIB_DIR # Vc_CMAKE_MODULES_DIR # # The following two variables are set according to the compiler used. Feel free # to use them to skip whole compilation units. # Vc_SSE_INTRINSICS_BROKEN # Vc_AVX_INTRINSICS_BROKEN find_package(Vc ${Vc_FIND_VERSION} QUIET NO_MODULE PATHS $ENV{HOME} /opt/Vc) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Vc CONFIG_MODE) +find_package_handle_standard_args(Vc CONFIG_MODE REQUIRED_VARS Vc_LIBRARIES) diff --git a/cmake/vc/AddCompilerFlag.cmake b/cmake/vc/AddCompilerFlag.cmake deleted file mode 100644 index 801cdd849f..0000000000 --- a/cmake/vc/AddCompilerFlag.cmake +++ /dev/null @@ -1,162 +0,0 @@ -# - Add a given compiler flag to flags variables. -# AddCompilerFlag( []) -# or -# AddCompilerFlag( [C_FLAGS ] [CXX_FLAGS ] [C_RESULT ] -# [CXX_RESULT ]) - -#============================================================================= -# Copyright 2010-2013 Matthias Kretz -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# * 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. -# -# * The names of Kitware, Inc., the Insight Consortium, or the names of -# any consortium members, or of any contributors, may not be used to -# endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#============================================================================= - -get_filename_component(_currentDir "${CMAKE_CURRENT_LIST_FILE}" PATH) -include("${_currentDir}/CheckCCompilerFlag.cmake") -include("${_currentDir}/CheckCXXCompilerFlag.cmake") -include("${_currentDir}/CheckMicCCompilerFlag.cmake") -include("${_currentDir}/CheckMicCXXCompilerFlag.cmake") - -macro(AddCompilerFlag _flag) - string(REGEX REPLACE "[-.+/:= ]" "_" _flag_esc "${_flag}") - - set(_c_flags "CMAKE_C_FLAGS") - set(_cxx_flags "CMAKE_CXX_FLAGS") - set(_mic_c_flags "CMAKE_MIC_C_FLAGS") - set(_mic_cxx_flags "CMAKE_MIC_CXX_FLAGS") - set(_c_result tmp) - set(_cxx_result tmp) - set(_mic_c_result) - set(_mic_cxx_result) - if(${ARGC} EQUAL 2) - message(WARNING "Deprecated use of the AddCompilerFlag macro.") - unset(_c_result) - set(_cxx_result ${ARGV1}) - elseif(${ARGC} GREATER 2) - set(state 0) - unset(_c_flags) - unset(_cxx_flags) - unset(_mic_c_flags) - unset(_mic_cxx_flags) - unset(_c_result) - unset(_cxx_result) - unset(_mic_c_result) - unset(_mic_cxx_result) - foreach(_arg ${ARGN}) - if(_arg STREQUAL "C_FLAGS") - set(state 1) - if(NOT DEFINED _c_result) - set(_c_result tmp) - endif() - elseif(_arg STREQUAL "CXX_FLAGS") - set(state 2) - if(NOT DEFINED _cxx_result) - set(_cxx_result tmp) - endif() - elseif(_arg STREQUAL "C_RESULT") - set(state 3) - elseif(_arg STREQUAL "CXX_RESULT") - set(state 4) - elseif(_arg STREQUAL "MIC_C_RESULT") - set(state 5) - elseif(_arg STREQUAL "MIC_CXX_RESULT") - set(state 6) - elseif(_arg STREQUAL "MIC_C_FLAGS") - set(state 7) - elseif(_arg STREQUAL "MIC_CXX_FLAGS") - set(state 8) - elseif(state EQUAL 1) - set(_c_flags "${_arg}") - elseif(state EQUAL 2) - set(_cxx_flags "${_arg}") - elseif(state EQUAL 3) - set(_c_result "${_arg}") - elseif(state EQUAL 4) - set(_cxx_result "${_arg}") - elseif(state EQUAL 5) - set(_mic_c_result "${_arg}") - elseif(state EQUAL 6) - set(_mic_cxx_result "${_arg}") - elseif(state EQUAL 7) - set(_mic_c_flags "${_arg}") - elseif(state EQUAL 8) - set(_mic_cxx_flags "${_arg}") - else() - message(FATAL_ERROR "Syntax error for AddCompilerFlag") - endif() - endforeach() - endif() - - if("${_flag}" STREQUAL "-mfma") - # Compiling with FMA3 support may fail only at the assembler level. - # In that case we need to have such an instruction in the test code - set(_code "#include - __m128 foo(__m128 x) { return _mm_fmadd_ps(x, x, x); } - int main() { return 0; }") - elseif("${_flag}" STREQUAL "-stdlib=libc++") - # Compiling with libc++ not only requires a compiler that understands it, but also - # the libc++ headers itself - set(_code "#include - int main() { return 0; }") - else() - set(_code "int main() { return 0; }") - endif() - - if(DEFINED _c_result) - check_c_compiler_flag("${_flag}" check_c_compiler_flag_${_flag_esc} "${_code}") - set(${_c_result} ${check_c_compiler_flag_${_flag_esc}}) - endif() - if(DEFINED _cxx_result) - check_cxx_compiler_flag("${_flag}" check_cxx_compiler_flag_${_flag_esc} "${_code}") - set(${_cxx_result} ${check_cxx_compiler_flag_${_flag_esc}}) - endif() - - if(check_c_compiler_flag_${_flag_esc} AND DEFINED _c_flags) - set(${_c_flags} "${${_c_flags}} ${_flag}") - endif() - if(check_cxx_compiler_flag_${_flag_esc} AND DEFINED _cxx_flags) - set(${_cxx_flags} "${${_cxx_flags}} ${_flag}") - endif() - - if(MIC_NATIVE_FOUND) - if(DEFINED _mic_c_result) - check_mic_c_compiler_flag("${_flag}" check_mic_c_compiler_flag_${_flag_esc} "${_code}") - set(${_mic_c_result} ${check_mic_c_compiler_flag_${_flag_esc}}) - endif() - if(DEFINED _mic_cxx_result) - check_mic_cxx_compiler_flag("${_flag}" check_mic_cxx_compiler_flag_${_flag_esc} "${_code}") - set(${_mic_cxx_result} ${check_mic_cxx_compiler_flag_${_flag_esc}}) - endif() - - if(check_mic_c_compiler_flag_${_flag_esc} AND DEFINED _mic_c_flags) - set(${_mic_c_flags} "${${_mic_c_flags}} ${_flag}") - endif() - if(check_mic_cxx_compiler_flag_${_flag_esc} AND DEFINED _mic_cxx_flags) - set(${_mic_cxx_flags} "${${_mic_cxx_flags}} ${_flag}") - endif() - endif() -endmacro(AddCompilerFlag) diff --git a/cmake/vc/AddTargetProperty.cmake b/cmake/vc/AddTargetProperty.cmake deleted file mode 100644 index c410135ea8..0000000000 --- a/cmake/vc/AddTargetProperty.cmake +++ /dev/null @@ -1,39 +0,0 @@ -#============================================================================= -# Copyright 2010-2013 Matthias Kretz -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# * 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. -# -# * The names of Kitware, Inc., the Insight Consortium, or the names of -# any consortium members, or of any contributors, may not be used to -# endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#============================================================================= - -macro(add_target_property _target _prop _value) - get_target_property(_oldprop "${_target}" ${_prop}) - if(NOT _oldprop) - set_target_properties("${_target}" PROPERTIES ${_prop} "${_value}") - else(NOT _oldprop) - set_target_properties("${_target}" PROPERTIES ${_prop} "${_oldprop} ${_value}") - endif(NOT _oldprop) -endmacro(add_target_property) diff --git a/cmake/vc/CheckCCompilerFlag.cmake b/cmake/vc/CheckCCompilerFlag.cmake deleted file mode 100644 index c9680c5fa0..0000000000 --- a/cmake/vc/CheckCCompilerFlag.cmake +++ /dev/null @@ -1,70 +0,0 @@ -# - Check whether the C compiler supports a given flag. -# CHECK_C_COMPILER_FLAG( ) -# - the compiler flag -# - variable to store the result -# This internally calls the check_c_source_compiles macro. -# See help for CheckCSourceCompiles for a listing of variables -# that can modify the build. - -#============================================================================= -# Copyright 2006-2009 Kitware, Inc. -# Copyright 2006 Alexander Neundorf -# Copyright 2011-2013 Matthias Kretz -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# * 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. -# -# * The names of Kitware, Inc., the Insight Consortium, or the names of -# any consortium members, or of any contributors, may not be used to -# endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#============================================================================= - -INCLUDE(CheckCSourceCompiles) - -MACRO (CHECK_C_COMPILER_FLAG _FLAG _RESULT) - SET(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}") - SET(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}") - if(${ARGC} GREATER 2) - SET(TEST_SOURCE "${ARGV2}") - else() - SET(TEST_SOURCE "int main() { return 0;}") - endif() - CHECK_C_SOURCE_COMPILES("${TEST_SOURCE}" ${_RESULT} - # Some compilers do not fail with a bad flag - FAIL_REGEX "error: bad value (.*) for .* switch" # GNU - FAIL_REGEX "argument unused during compilation" # clang - FAIL_REGEX "is valid for .* but not for C" # GNU - FAIL_REGEX "unrecognized .*option" # GNU - FAIL_REGEX "ignored for target" # GNU - FAIL_REGEX "ignoring unknown option" # MSVC - FAIL_REGEX "[Uu]nknown option" # HP - FAIL_REGEX "[Ww]arning: [Oo]ption" # SunPro - FAIL_REGEX "command option .* is not recognized" # XL - FAIL_REGEX "WARNING: unknown flag:" # Open64 - FAIL_REGEX "command line error" # ICC - FAIL_REGEX "command line warning" # ICC - FAIL_REGEX "#10236:" # ICC: File not found - ) - SET (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}") -ENDMACRO (CHECK_C_COMPILER_FLAG) - diff --git a/cmake/vc/CheckCXXCompilerFlag.cmake b/cmake/vc/CheckCXXCompilerFlag.cmake deleted file mode 100644 index 72a6cc39fc..0000000000 --- a/cmake/vc/CheckCXXCompilerFlag.cmake +++ /dev/null @@ -1,70 +0,0 @@ -# - Check whether the CXX compiler supports a given flag. -# CHECK_CXX_COMPILER_FLAG( ) -# - the compiler flag -# - variable to store the result -# This internally calls the check_cxx_source_compiles macro. See help -# for CheckCXXSourceCompiles for a listing of variables that can -# modify the build. - -#============================================================================= -# Copyright 2006-2009 Kitware, Inc. -# Copyright 2006 Alexander Neundorf -# Copyright 2011-2013 Matthias Kretz -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# * 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. -# -# * The names of Kitware, Inc., the Insight Consortium, or the names of -# any consortium members, or of any contributors, may not be used to -# endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#============================================================================= - -INCLUDE(CheckCXXSourceCompiles) - -MACRO (CHECK_CXX_COMPILER_FLAG _FLAG _RESULT) - SET(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}") - SET(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}") - if(${ARGC} GREATER 2) - SET(TEST_SOURCE "${ARGV2}") - else() - SET(TEST_SOURCE "int main() { return 0;}") - endif() - CHECK_CXX_SOURCE_COMPILES("${TEST_SOURCE}" ${_RESULT} - # Some compilers do not fail with a bad flag - FAIL_REGEX "error: bad value (.*) for .* switch" # GNU - FAIL_REGEX "argument unused during compilation" # clang - FAIL_REGEX "is valid for .* but not for C\\\\+\\\\+" # GNU - FAIL_REGEX "unrecognized .*option" # GNU - FAIL_REGEX "ignored for target" # GNU - FAIL_REGEX "ignoring unknown option" # MSVC - FAIL_REGEX "[Uu]nknown option" # HP - FAIL_REGEX "[Ww]arning: [Oo]ption" # SunPro - FAIL_REGEX "command option .* is not recognized" # XL - FAIL_REGEX "WARNING: unknown flag:" # Open64 - FAIL_REGEX "command line error" # ICC - FAIL_REGEX "command line warning" # ICC - FAIL_REGEX "#10236:" # ICC: File not found - ) - SET (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}") -ENDMACRO (CHECK_CXX_COMPILER_FLAG) - diff --git a/cmake/vc/CheckMicCCompilerFlag.cmake b/cmake/vc/CheckMicCCompilerFlag.cmake deleted file mode 100644 index eae725b790..0000000000 --- a/cmake/vc/CheckMicCCompilerFlag.cmake +++ /dev/null @@ -1,101 +0,0 @@ -# - Check whether the MIC C compiler supports a given flag. -# CHECK_MIC_C_COMPILER_FLAG( ) -# - the compiler flag -# - variable to store the result -# This internally calls the check_c_source_compiles macro. See help -# for CheckCSourceCompiles for a listing of variables that can -# modify the build. - -#============================================================================= -# Copyright 2006-2009 Kitware, Inc. -# Copyright 2006 Alexander Neundorf -# Copyright 2011-2013 Matthias Kretz -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# * 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. -# -# * The names of Kitware, Inc., the Insight Consortium, or the names of -# any consortium members, or of any contributors, may not be used to -# endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#============================================================================= - -macro(check_mic_c_compiler_flag _FLAG _RESULT) - if("${_RESULT}" MATCHES "^${_RESULT}$") - set(_tmpdir "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp") - if(${ARGC} GREATER 2) - file(WRITE "${_tmpdir}/src.c" "${ARGV2}") - else() - file(WRITE "${_tmpdir}/src.c" "int main() { return 0; }") - endif() - - execute_process( - COMMAND "${MIC_CC}" -mmic -c -o "${_tmpdir}/src.o" - "${_FLAG}" "${_tmpdir}/src.c" - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - RESULT_VARIABLE ${_RESULT} - OUTPUT_VARIABLE OUTPUT - ERROR_VARIABLE OUTPUT - ) - - if(${_RESULT}) - foreach(_fail_regex - "error: bad value (.*) for .* switch" # GNU - "argument unused during compilation" # clang - "is valid for .* but not for C" # GNU - "unrecognized .*option" # GNU - "ignored for target" # GNU - "ignoring unknown option" # MSVC - "[Uu]nknown option" # HP - "[Ww]arning: [Oo]ption" # SunPro - "command option .* is not recognized" # XL - "WARNING: unknown flag:" # Open64 - "command line error" # ICC - "command line warning" # ICC - "#10236:" # ICC: File not found - ) - if("${OUTPUT}" MATCHES "${_fail_regex}") - set(${_RESULT} FALSE) - endif() - endforeach() - endif() - - if(${_RESULT}) - set(${_RESULT} 1 CACHE INTERNAL "Test ${_FLAG}") - message(STATUS "Performing Test Check MIC C Compiler flag ${_FLAG} - Success") - file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log - "Performing MIC C Compiler Flag Test ${_FLAG} succeded with the following output:\n" - "${OUTPUT}\n" - "COMMAND: ${MIC_CC} -mmic -c -o ${_tmpdir}/src.o ${_FLAG} ${_tmpdir}/src.cpp\n" - ) - else() - message(STATUS "Performing Test Check MIC C Compiler flag ${_FLAG} - Failed") - set(${_RESULT} "" CACHE INTERNAL "Test ${_FLAG}") - file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log - "Performing MIC C Compiler Flag Test ${_FLAG} failed with the following output:\n" - "${OUTPUT}\n" - "COMMAND: ${MIC_CC} -mmic -c -o ${_tmpdir}/src.o ${_FLAG} ${_tmpdir}/src.cpp\n" - ) - endif() - endif() -endmacro() - diff --git a/cmake/vc/CheckMicCXXCompilerFlag.cmake b/cmake/vc/CheckMicCXXCompilerFlag.cmake deleted file mode 100644 index c0444c4cc2..0000000000 --- a/cmake/vc/CheckMicCXXCompilerFlag.cmake +++ /dev/null @@ -1,101 +0,0 @@ -# - Check whether the MIC CXX compiler supports a given flag. -# CHECK_MIC_CXX_COMPILER_FLAG( ) -# - the compiler flag -# - variable to store the result -# This internally calls the check_cxx_source_compiles macro. See help -# for CheckCXXSourceCompiles for a listing of variables that can -# modify the build. - -#============================================================================= -# Copyright 2006-2009 Kitware, Inc. -# Copyright 2006 Alexander Neundorf -# Copyright 2011-2013 Matthias Kretz -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# * 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. -# -# * The names of Kitware, Inc., the Insight Consortium, or the names of -# any consortium members, or of any contributors, may not be used to -# endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#============================================================================= - -macro(check_mic_cxx_compiler_flag _FLAG _RESULT) - if("${_RESULT}" MATCHES "^${_RESULT}$") - set(_tmpdir "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp") - if(${ARGC} GREATER 2) - file(WRITE "${_tmpdir}/src.cpp" "${ARGV2}") - else() - file(WRITE "${_tmpdir}/src.cpp" "int main() { return 0; }") - endif() - - execute_process( - COMMAND "${MIC_CXX}" -mmic -c -o "${_tmpdir}/src.o" - "${_FLAG}" "${_tmpdir}/src.cpp" - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - RESULT_VARIABLE ${_RESULT} - OUTPUT_VARIABLE OUTPUT - ERROR_VARIABLE OUTPUT - ) - - if(${_RESULT} EQUAL 0) - foreach(_fail_regex - "error: bad value (.*) for .* switch" # GNU - "argument unused during compilation" # clang - "is valid for .* but not for C\\\\+\\\\+" # GNU - "unrecognized .*option" # GNU - "ignored for target" # GNU - "ignoring unknown option" # MSVC - "[Uu]nknown option" # HP - "[Ww]arning: [Oo]ption" # SunPro - "command option .* is not recognized" # XL - "WARNING: unknown flag:" # Open64 - "command line error" # ICC - "command line warning" # ICC - "#10236:" # ICC: File not found - ) - if("${OUTPUT}" MATCHES "${_fail_regex}") - set(${_RESULT} FALSE) - endif() - endforeach() - endif() - - if(${_RESULT} EQUAL 0) - set(${_RESULT} 1 CACHE INTERNAL "Test ${_FLAG}") - message(STATUS "Performing Test Check MIC C++ Compiler flag ${_FLAG} - Success") - file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log - "Performing MIC C++ Compiler Flag Test ${_FLAG} succeded with the following output:\n" - "${OUTPUT}\n" - "COMMAND: ${MIC_CXX} -mmic -c -o ${_tmpdir}/src.o ${_FLAG} ${_tmpdir}/src.cpp\n" - ) - else() - message(STATUS "Performing Test Check MIC C++ Compiler flag ${_FLAG} - Failed") - set(${_RESULT} "" CACHE INTERNAL "Test ${_FLAG}") - file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log - "Performing MIC C++ Compiler Flag Test ${_FLAG} failed with the following output:\n" - "${OUTPUT}\n" - "COMMAND: ${MIC_CXX} -mmic -c -o ${_tmpdir}/src.o ${_FLAG} ${_tmpdir}/src.cpp\n" - ) - endif() - endif() -endmacro() - diff --git a/cmake/vc/FindMIC.cmake b/cmake/vc/FindMIC.cmake deleted file mode 100644 index 830c1af3dc..0000000000 --- a/cmake/vc/FindMIC.cmake +++ /dev/null @@ -1,393 +0,0 @@ -# Copyright (C) 2010-2013 Matthias Kretz -# -# This program is free software: you can 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, see . - -# This check will search for a MIC compiler and check whether the C and C++ -# compilers are able to offload via offload pragma and target(mic) attribute. -# The project may choose to either build native MIC binaries, or offload -# binaries (hybrid code), or both. In the case where only native MIC binaries -# are built, the compiler does not need to support offloading -# -# MIC_NATIVE_FOUND is true if native MIC binaries can be built -# MIC_OFFLOAD_FOUND is true if hybrid host/MIC binaries via offload can be built -# MIC_FOUND is true if either MIC_NATIVE_FOUND or MIC_OFFLOAD_FOUND is true -# -# When MIC_NATIVE_FOUND is true you can use the macros -# mic_add_definitions -# mic_include_directories -# mic_set_link_libraries -# mic_add_library -# mic_add_executable -# for building native libraries/executables -# -# When MIC_OFFLOAD_FOUND is true you use the standard cmake macros to build -# libraries and executables but have to make sure manually that the necessary -# offload compiler switches are present. You might want to add something like: -# if(MIC_OFFLOAD_FOUND) -# AddCompilerFlag("-offload-build") -# AddCompilerFlag("-offload-copts=-vec-report=3 -H") -# AddCompilerFlag("-offload-ldopts=-lmylib") -# AddCompilerFlag("-opt-report-phase=offload") -# endif() - -set(MIC_FOUND false) -set(MIC_NATIVE_FOUND false) - -file(GLOB _intel_dirs "/opt/intel/composer_xe_*") -list(SORT _intel_dirs) -list(REVERSE _intel_dirs) -find_path(MIC_SDK_DIR bin/intel64_mic/icpc PATHS - "$ENV{MIC_SDK_DIR}" - ${_intel_dirs} - ) - -############################################################################## -# First check whether offload works - -if(NOT DEFINED c_compiler_can_offload OR NOT DEFINED cxx_compiler_can_offload) - set(c_compiler_can_offload FALSE) - set(cxx_compiler_can_offload FALSE) - - # For now offload is not supported so skip it -# include(CheckCSourceCompiles) -# include(CheckCXXSourceCompiles) -# -# #find_library(MIC_HOST_IMF_LIBRARY imf HINTS ENV LIBRARY_PATH) -# #find_library(MIC_HOST_SVML_LIBRARY svml HINTS ENV LIBRARY_PATH) -# #find_library(MIC_HOST_INTLC_LIBRARY intlc HINTS ENV LIBRARY_PATH) -# -# #set(MIC_HOST_LIBS ${MIC_HOST_IMF_LIBRARY} ${MIC_HOST_SVML_LIBRARY} ${MIC_HOST_INTLC_LIBRARY}) -# -# set(_mic_offload_test_source " -##ifdef __MIC__ -##include -##endif -#__attribute__((target(mic))) void test() -#{ -##ifdef __MIC__ -# __m512 v = _mm512_setzero_ps(); -# (void)v; -##endif -#} -# -#int main() -#{ -##pragma offload target(mic) -# test(); -# return 0; -#} -#") -# -# set(CMAKE_REQUIRED_FLAGS "-offload-build") -# check_c_source_compiles("${_mic_offload_test_source}" c_compiler_can_offload) -# check_cxx_source_compiles("${_mic_offload_test_source}" cxx_compiler_can_offload) -# set(CMAKE_REQUIRED_FLAGS) -endif() - -if(c_compiler_can_offload AND cxx_compiler_can_offload) - message(STATUS "C/C++ Compiler can offload to MIC.") - set(MIC_OFFLOAD_FOUND true) -else() - message(STATUS "C/C++ Compiler can NOT offload to MIC.") - set(MIC_OFFLOAD_FOUND false) -endif() - -############################################################################## -# Next check whether everything required for native builds is available - -find_path(MIC_TARGET_TOOLS_DIR bin/x86_64-k1om-linux-ar HINTS - "$ENV{MIC_TARGET_TOOLS_DIR}" - "${MIC_SDK_DIR}/target" - "/usr/linux-k1om-4.7" - ) -find_program(MIC_AR x86_64-k1om-linux-ar PATHS "${MIC_TARGET_TOOLS_DIR}/bin") -find_program(MIC_RANLIB x86_64-k1om-linux-ranlib PATHS "${MIC_TARGET_TOOLS_DIR}/bin") -find_program(MIC_NATIVELOAD micnativeloadex PATHS "/opt/intel/mic/bin") -mark_as_advanced(MIC_AR MIC_RANLIB MIC_NATIVELOAD) - -if(MIC_SDK_DIR AND MIC_AR AND MIC_RANLIB) - find_program(MIC_CC icc HINTS "${MIC_SDK_DIR}/bin" "${MIC_SDK_DIR}/bin/intel64") - find_program(MIC_CXX icpc HINTS "${MIC_SDK_DIR}/bin" "${MIC_SDK_DIR}/bin/intel64") - - find_library(MIC_IMF_LIBRARY imf HINTS "${MIC_SDK_DIR}/compiler/lib/mic") - find_library(MIC_SVML_LIBRARY svml HINTS "${MIC_SDK_DIR}/compiler/lib/mic") - find_library(MIC_INTLC_LIBRARY intlc HINTS "${MIC_SDK_DIR}/compiler/lib/mic") - mark_as_advanced(MIC_CC MIC_CXX MIC_IMF_LIBRARY MIC_SVML_LIBRARY MIC_INTLC_LIBRARY) - - set(MIC_LIBS ${MIC_IMF_LIBRARY} ${MIC_SVML_LIBRARY} ${MIC_INTLC_LIBRARY}) - set(MIC_CFLAGS "-O2 -vec") - - exec_program(${MIC_CXX} ARGS -V OUTPUT_VARIABLE _mic_icc_version_string) - string(REGEX MATCH "Version (Mainline)?[0-9. a-zA-Z]+" Vc_MIC_ICC_VERSION "${_mic_icc_version_string}") - string(SUBSTRING "${Vc_MIC_ICC_VERSION}" 8 -1 Vc_MIC_ICC_VERSION) - message(STATUS "MIC ICC Version: \"${Vc_MIC_ICC_VERSION}\"") - - if(MIC_CC AND MIC_CXX AND MIC_IMF_LIBRARY AND MIC_SVML_LIBRARY AND MIC_INTLC_LIBRARY) - set(MIC_NATIVE_FOUND true) - endif() -endif() - -if(MIC_NATIVE_FOUND OR MIC_OFFLOAD_FOUND) - set(MIC_FOUND true) - list(APPEND CMAKE_MIC_CXX_FLAGS "-diag-disable 2338") # this switch statement does not have a default clause - list(APPEND CMAKE_MIC_CXX_FLAGS "-diag-disable 193") # zero used for undefined preprocessing identifier "VC_GCC" - - macro(mic_add_definitions) - add_definitions(${ARGN}) - foreach(_def ${ARGN}) - set(_mic_cflags ${_mic_cflags} "${_def}") - endforeach() - endmacro() - macro(mic_include_directories) - foreach(_dir ${ARGN}) - set(_mic_cflags ${_mic_cflags} "-I${_dir}") - endforeach() - include_directories(${ARGN}) - endmacro() - if(NOT DEFINED MIC_C_FLAGS) - set(MIC_C_FLAGS) - endif() - if(NOT DEFINED MIC_CXX_FLAGS) - set(MIC_CXX_FLAGS) - endif() -else() - message(WARNING "MIC SDK was not found!") -endif() - -if(MIC_NATIVE_FOUND) - macro(_mic_add_object _target _source _output) - get_filename_component(_abs "${_source}" ABSOLUTE) - get_filename_component(_ext "${_source}" EXT) - get_filename_component(_tmp "${_source}" NAME_WE) - set(${_output} "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${_target}.dir/${_tmp}${_ext}.mic.o") - set(_lang CXX) - set(_compiler "${MIC_CXX}") - if(_ext STREQUAL "c") - set(_lang C) - set(_compiler "${MIC_CC}") - endif() - - string(TOUPPER "${CMAKE_BUILD_TYPE}" _tmp) - string(STRIP "${CMAKE_MIC_${_lang}_FLAGS} ${CMAKE_${_lang}_FLAGS_${_tmp}} ${_mic_cflags}" _flags) - string(REPLACE " " ";" _flags "${_flags} ${ARGN}") - get_directory_property(_inc INCLUDE_DIRECTORIES) - foreach(_i ${_inc}) - list(APPEND _flags "-I${_i}") - endforeach() - - add_custom_command(OUTPUT "${${_output}}" - COMMAND "${_compiler}" -mmic - -DVC_IMPL=MIC - ${_flags} -c -o "${${_output}}" "${_abs}" - DEPENDS "${_abs}" - IMPLICIT_DEPENDS ${_lang} "${_abs}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "Compiling (MIC) ${${_output}}" - VERBATIM - ) - endmacro() - macro(mic_set_link_libraries) - set(_mic_lflags) - foreach(_lib ${ARGN}) - get_filename_component(_lpath "${_lib}" PATH) - get_filename_component(_lname "${_lib}" NAME) - set(_mic_lflags ${_mic_lflags} "-L${_lpath}" "-l${_lname}") - endforeach() - endmacro() - macro(mic_add_library _target) - set(_state 0) - if(BUILD_SHARED_LIBS) - set(_type SHARED) - else() - set(_type STATIC) - endif() - set(_all ALL) - set(_srcs) - set(_cflags) - set(_libs) - foreach(_arg ${ARGN}) - if(_arg MATCHES "^(STATIC|SHARED|MODULE)$") - set(_type ${_arg}) - elseif(_arg STREQUAL "EXCLUDE_FROM_ALL") - set(_all) - elseif(_arg STREQUAL "COMPILE_FLAGS") - set(_state 1) - elseif(_arg STREQUAL "LINK_LIBRARIES") - set(_state 2) - elseif(_arg STREQUAL "SOURCES") - set(_state 0) - elseif(_state EQUAL 0) # SOURCES - set(_srcs ${_srcs} "${_arg}") - elseif(_state EQUAL 1) # COMPILE_FLAGS - list(APPEND _cflags "${_arg}") - elseif(_state EQUAL 2) # LINK_LIBRARIES - get_filename_component(_lpath "${_arg}" PATH) - get_filename_component(_lname "${_arg}" NAME) - set(_libs ${_libs} "-L${_lpath}" "-l${_lname}") - endif() - endforeach() - set(_objects) - set(_objectsStr) - foreach(_src ${_srcs}) - _mic_add_object("${_target}" "${_src}" _obj ${_cflags}) - list(APPEND _objects "${_obj}") - set(_objectsStr "${_objectsStr} \"${_obj}\"") - endforeach() - - set(_outdir "${CMAKE_CURRENT_BINARY_DIR}/x86_64-k1om-linux") - file(MAKE_DIRECTORY "${_outdir}") - - #TODO: handle STATIC/SHARED/MODULE differently - set(_output "lib${_target}.a") - set(_linkscript "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${_target}.dir/link.txt") - set(_cleanscript "CMakeFiles/${_target}.dir/cmake_clean_target.cmake") - file(WRITE "${_linkscript}" - "${MIC_AR} cr ${_output} ${_objectsStr} -${MIC_RANLIB} ${_output} -") - file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_cleanscript}" - "FILE(REMOVE_RECURSE \"${_output}\") -") - add_custom_command(OUTPUT "${_outdir}/${_output}" - COMMAND "${CMAKE_COMMAND}" -E cmake_link_script "${_linkscript}" --verbose=$(VERBOSE) - DEPENDS ${_objects} - WORKING_DIRECTORY "${_outdir}" - COMMENT "Linking (MIC) ${_output}" - VERBATIM - ) - add_custom_target("${_target}" ${_all} - DEPENDS "${_outdir}/${_output}" - COMMENT "" - SOURCES ${_srcs} - ) - set_target_properties("${_target}" PROPERTIES - OUTPUT_NAME "${_outdir}/${_output}" - ) - endmacro() - - macro(mic_add_executable _target) - set(_state 0) - set(_all ALL) - set(_srcs) - set(_cflags) - set(_libs) - set(_libTargets) - set(_dump_asm false) - set(_exec_output_name "${_target}") - foreach(_arg ${ARGN}) - if(_arg STREQUAL "EXCLUDE_FROM_ALL") - set(_all) - elseif(_arg STREQUAL "COMPILE_FLAGS") - set(_state 1) - elseif(_arg STREQUAL "LINK_LIBRARIES") - set(_state 2) - elseif(_arg STREQUAL "OUTPUT_NAME") - set(_state 3) - elseif(_arg STREQUAL "SOURCES") - set(_state 0) - elseif(_arg STREQUAL "DUMP_ASM") - set(_dump_asm true) - elseif(_state EQUAL 0) # SOURCES - set(_srcs ${_srcs} "${_arg}") - elseif(_state EQUAL 1) # COMPILE_FLAGS - set(_cflags ${_cflags} "${_arg}") - elseif(_state EQUAL 2) # LINK_LIBRARIES - get_target_property(_tmp "${_arg}" OUTPUT_NAME) - if(_tmp) - set(_libs ${_libs} "${_tmp}") - set(_libTargets ${_libTargets} "${_tmp}" "${_arg}") - else() - get_filename_component(_lpath "${_arg}" PATH) - get_filename_component(_lname "${_arg}" NAME) - set(_libs "${_libs} \"-L${_lpath}\" \"-l${_lname}\"") - set(_libTargets ${_libTargets} "${_arg}") - endif() - elseif(_state EQUAL 3) # OUTPUT_NAME - set(_exec_output_name "${_arg}") - endif() - endforeach() - set(_objects) - set(_objectsStr) - foreach(_src ${_srcs}) - _mic_add_object("${_target}" "${_src}" _obj ${_cflags}) - set(_objects ${_objects} "${_obj}") - set(_objectsStr "${_objectsStr} \"${_obj}\"") - endforeach() - - set(_exec_output "${CMAKE_CURRENT_BINARY_DIR}/${_exec_output_name}") - add_custom_command(OUTPUT "${_exec_output}" - COMMAND "${MIC_CXX}" -mmic - "-L${MIC_SDK_DIR}/compiler/lib/mic/" - ${_mic_lflags} ${_objects} -o "${_exec_output}" - ${_libs} - DEPENDS ${_objects} ${_libTargets} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "Linking (MIC) ${_exec_output}" - VERBATIM - ) - set(_dump_asm_output) - if(_dump_asm) - foreach(_src ${_srcs}) - get_filename_component(_abs "${_src}" ABSOLUTE) - get_filename_component(_name "${_src}" NAME) - add_custom_command(OUTPUT "${_name}.s" - COMMAND "${MIC_CXX}" -mmic - -DVC_IMPL=LRBni ${_mic_cflags} ${_cflags} - ${_abs} - -S -fsource-asm -fno-verbose-asm -o "${_name}.x" - COMMAND sh -c "grep -v ___tag_value '${_name}.x' | c++filt > '${_name}.s'" - COMMAND rm "${_name}.x" - DEPENDS ${_abs} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "Creating MIC Assembly ${_name}.s" - VERBATIM - ) - set(_dump_asm_output ${_dump_asm_output} "${CMAKE_CURRENT_BINARY_DIR}/${_name}.s") - endforeach() - endif() - add_custom_target("${_target}" ${_all} - DEPENDS "${_exec_output}" ${_dump_asm_output} - COMMENT "" - SOURCES ${_srcs} - ) - set_target_properties("${_target}" PROPERTIES OUTPUT_NAME "${_exec_output_name}") - endmacro() -endif() - -if(MIC_OFFLOAD_FOUND) - macro(mic_offload _target) - set(_mic_debug) - if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") - set(_mic_debug "-g") - endif() - add_target_property(${_target} COMPILE_FLAGS "-offload-build -DCAN_OFFLOAD ${_mic_debug}") - set(_offload_ldflags "${_mic_debug}") - set(_libTargets) - foreach(_lib ${ARGN}) - get_target_property(_tmp "${_lib}" OUTPUT_NAME) - if(_tmp) - set(_offload_ldflags "${_offload_ldflags} ${_tmp}") - set(_libTargets ${_libTargets} "${_arg}") - else() - get_filename_component(_lpath "${_arg}" PATH) - get_filename_component(_lname "${_arg}" NAME) - set(_offload_ldflags "${_offload_ldflags} -L${_lpath} -l${_lname}") - endif() - endforeach() - add_target_property(${_target} LINK_FLAGS "-offload-build -offload-ldopts=\"${_offload_ldflags}\" ${_mic_debug}") - if(_libTargets) - add_dependencies(${_target} ${_libTargets}) - endif() - endmacro() -endif() diff --git a/cmake/vc/FindSSE.cmake b/cmake/vc/FindSSE.cmake deleted file mode 100644 index 6ece876896..0000000000 --- a/cmake/vc/FindSSE.cmake +++ /dev/null @@ -1,104 +0,0 @@ -# Check if SSE instructions are available on the machine where -# the project is compiled. - -IF(CMAKE_SYSTEM_NAME MATCHES "Linux") - EXEC_PROGRAM(cat ARGS "/proc/cpuinfo" OUTPUT_VARIABLE CPUINFO) - - STRING(REGEX REPLACE "^.*(sse2).*$" "\\1" SSE_THERE ${CPUINFO}) - STRING(COMPARE EQUAL "sse2" "${SSE_THERE}" SSE2_TRUE) - IF (SSE2_TRUE) - set(SSE2_FOUND true CACHE BOOL "SSE2 available on host") - ELSE (SSE2_TRUE) - set(SSE2_FOUND false CACHE BOOL "SSE2 available on host") - ENDIF (SSE2_TRUE) - - # /proc/cpuinfo apparently omits sse3 :( - STRING(REGEX REPLACE "^.*[^s](sse3).*$" "\\1" SSE_THERE ${CPUINFO}) - STRING(COMPARE EQUAL "sse3" "${SSE_THERE}" SSE3_TRUE) - IF (NOT SSE3_TRUE) - STRING(REGEX REPLACE "^.*(T2300).*$" "\\1" SSE_THERE ${CPUINFO}) - STRING(COMPARE EQUAL "T2300" "${SSE_THERE}" SSE3_TRUE) - ENDIF (NOT SSE3_TRUE) - - STRING(REGEX REPLACE "^.*(ssse3).*$" "\\1" SSE_THERE ${CPUINFO}) - STRING(COMPARE EQUAL "ssse3" "${SSE_THERE}" SSSE3_TRUE) - IF (SSE3_TRUE OR SSSE3_TRUE) - set(SSE3_FOUND true CACHE BOOL "SSE3 available on host") - ELSE (SSE3_TRUE OR SSSE3_TRUE) - set(SSE3_FOUND false CACHE BOOL "SSE3 available on host") - ENDIF (SSE3_TRUE OR SSSE3_TRUE) - IF (SSSE3_TRUE) - set(SSSE3_FOUND true CACHE BOOL "SSSE3 available on host") - ELSE (SSSE3_TRUE) - set(SSSE3_FOUND false CACHE BOOL "SSSE3 available on host") - ENDIF (SSSE3_TRUE) - - STRING(REGEX REPLACE "^.*(sse4_1).*$" "\\1" SSE_THERE ${CPUINFO}) - STRING(COMPARE EQUAL "sse4_1" "${SSE_THERE}" SSE41_TRUE) - IF (SSE41_TRUE) - set(SSE4_1_FOUND true CACHE BOOL "SSE4.1 available on host") - ELSE (SSE41_TRUE) - set(SSE4_1_FOUND false CACHE BOOL "SSE4.1 available on host") - ENDIF (SSE41_TRUE) -ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Darwin") - EXEC_PROGRAM("/usr/sbin/sysctl -n machdep.cpu.features" OUTPUT_VARIABLE - CPUINFO) - - STRING(REGEX REPLACE "^.*[^S](SSE2).*$" "\\1" SSE_THERE ${CPUINFO}) - STRING(COMPARE EQUAL "SSE2" "${SSE_THERE}" SSE2_TRUE) - IF (SSE2_TRUE) - set(SSE2_FOUND true CACHE BOOL "SSE2 available on host") - ELSE (SSE2_TRUE) - set(SSE2_FOUND false CACHE BOOL "SSE2 available on host") - ENDIF (SSE2_TRUE) - - STRING(REGEX REPLACE "^.*[^S](SSE3).*$" "\\1" SSE_THERE ${CPUINFO}) - STRING(COMPARE EQUAL "SSE3" "${SSE_THERE}" SSE3_TRUE) - IF (SSE3_TRUE) - set(SSE3_FOUND true CACHE BOOL "SSE3 available on host") - ELSE (SSE3_TRUE) - set(SSE3_FOUND false CACHE BOOL "SSE3 available on host") - ENDIF (SSE3_TRUE) - - STRING(REGEX REPLACE "^.*(SSSE3).*$" "\\1" SSE_THERE ${CPUINFO}) - STRING(COMPARE EQUAL "SSSE3" "${SSE_THERE}" SSSE3_TRUE) - IF (SSSE3_TRUE) - set(SSSE3_FOUND true CACHE BOOL "SSSE3 available on host") - ELSE (SSSE3_TRUE) - set(SSSE3_FOUND false CACHE BOOL "SSSE3 available on host") - ENDIF (SSSE3_TRUE) - - STRING(REGEX REPLACE "^.*(SSE4.1).*$" "\\1" SSE_THERE ${CPUINFO}) - STRING(COMPARE EQUAL "SSE4.1" "${SSE_THERE}" SSE41_TRUE) - IF (SSE41_TRUE) - set(SSE4_1_FOUND true CACHE BOOL "SSE4.1 available on host") - ELSE (SSE41_TRUE) - set(SSE4_1_FOUND false CACHE BOOL "SSE4.1 available on host") - ENDIF (SSE41_TRUE) -ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Windows") - # TODO - set(SSE2_FOUND true CACHE BOOL "SSE2 available on host") - set(SSE3_FOUND false CACHE BOOL "SSE3 available on host") - set(SSSE3_FOUND false CACHE BOOL "SSSE3 available on host") - set(SSE4_1_FOUND false CACHE BOOL "SSE4.1 available on host") -ELSE(CMAKE_SYSTEM_NAME MATCHES "Linux") - set(SSE2_FOUND true CACHE BOOL "SSE2 available on host") - set(SSE3_FOUND false CACHE BOOL "SSE3 available on host") - set(SSSE3_FOUND false CACHE BOOL "SSSE3 available on host") - set(SSE4_1_FOUND false CACHE BOOL "SSE4.1 available on host") -ENDIF(CMAKE_SYSTEM_NAME MATCHES "Linux") - -if(NOT SSE2_FOUND) - MESSAGE(STATUS "Could not find hardware support for SSE2 on this machine.") -endif(NOT SSE2_FOUND) -if(NOT SSE3_FOUND) - MESSAGE(STATUS "Could not find hardware support for SSE3 on this machine.") -endif(NOT SSE3_FOUND) -if(NOT SSSE3_FOUND) - MESSAGE(STATUS "Could not find hardware support for SSSE3 on this machine.") -endif(NOT SSSE3_FOUND) -if(NOT SSE4_1_FOUND) - MESSAGE(STATUS "Could not find hardware support for SSE4.1 on this machine.") -endif(NOT SSE4_1_FOUND) - -mark_as_advanced(SSE2_FOUND SSE3_FOUND SSSE3_FOUND SSE4_1_FOUND) diff --git a/cmake/vc/FindVc.cmake b/cmake/vc/FindVc.cmake deleted file mode 100644 index 704bc08dc8..0000000000 --- a/cmake/vc/FindVc.cmake +++ /dev/null @@ -1,61 +0,0 @@ -# Locate the Vc template library. Vc can be found at http://gitorious.org/Vc/ -# -# This file is meant to be copied into projects that want to use Vc. It will -# search for VcConfig.cmake, which ships with Vc and will provide up-to-date -# buildsystem changes. Thus there should not be any need to update FindVc.cmake -# again after you integrated it into your project. -# -# This module defines the following variables: -# Vc_FOUND -# Vc_INCLUDE_DIR -# Vc_LIBRARIES -# Vc_DEFINITIONS -# Vc_VERSION_MAJOR -# Vc_VERSION_MINOR -# Vc_VERSION_PATCH -# Vc_VERSION -# Vc_VERSION_STRING -# Vc_INSTALL_DIR -# Vc_LIB_DIR -# Vc_CMAKE_MODULES_DIR -# -# The following two variables are set according to the compiler used. Feel free -# to use them to skip whole compilation units. -# Vc_SSE_INTRINSICS_BROKEN -# Vc_AVX_INTRINSICS_BROKEN -# -#============================================================================= -# Copyright 2009-2012 Matthias Kretz -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# * 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. -# -# * The names of Kitware, Inc., the Insight Consortium, or the names of -# any consortium members, or of any contributors, may not be used to -# endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#============================================================================= - -find_package(Vc ${Vc_FIND_VERSION} QUIET NO_MODULE PATHS $ENV{HOME} /opt/Vc) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Vc CONFIG_MODE) diff --git a/cmake/vc/OptimizeForArchitecture.cmake b/cmake/vc/OptimizeForArchitecture.cmake deleted file mode 100644 index 0bdcae3cfe..0000000000 --- a/cmake/vc/OptimizeForArchitecture.cmake +++ /dev/null @@ -1,468 +0,0 @@ -#============================================================================= -# Copyright 2010-2013 Matthias Kretz -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# * 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. -# -# * The names of Kitware, Inc., the Insight Consortium, or the names of -# any consortium members, or of any contributors, may not be used to -# endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#============================================================================= - -get_filename_component(_currentDir "${CMAKE_CURRENT_LIST_FILE}" PATH) -include("${_currentDir}/AddCompilerFlag.cmake") -include(CheckIncludeFile) - -macro(_my_find _list _value _ret) - list(FIND ${_list} "${_value}" _found) - if(_found EQUAL -1) - set(${_ret} FALSE) - else(_found EQUAL -1) - set(${_ret} TRUE) - endif(_found EQUAL -1) -endmacro(_my_find) - -macro(AutodetectHostArchitecture) - set(TARGET_ARCHITECTURE "generic") - set(Vc_ARCHITECTURE_FLAGS) - set(_vendor_id) - set(_cpu_family) - set(_cpu_model) - if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - file(READ "/proc/cpuinfo" _cpuinfo) - string(REGEX REPLACE ".*vendor_id[ \t]*:[ \t]+([a-zA-Z0-9_-]+).*" "\\1" _vendor_id "${_cpuinfo}") - string(REGEX REPLACE ".*cpu family[ \t]*:[ \t]+([a-zA-Z0-9_-]+).*" "\\1" _cpu_family "${_cpuinfo}") - string(REGEX REPLACE ".*model[ \t]*:[ \t]+([a-zA-Z0-9_-]+).*" "\\1" _cpu_model "${_cpuinfo}") - string(REGEX REPLACE ".*flags[ \t]*:[ \t]+([^\n]+).*" "\\1" _cpu_flags "${_cpuinfo}") - elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - exec_program("/usr/sbin/sysctl -n machdep.cpu.vendor" OUTPUT_VARIABLE _vendor_id) - exec_program("/usr/sbin/sysctl -n machdep.cpu.model" OUTPUT_VARIABLE _cpu_model) - exec_program("/usr/sbin/sysctl -n machdep.cpu.family" OUTPUT_VARIABLE _cpu_family) - exec_program("/usr/sbin/sysctl -n machdep.cpu.features" OUTPUT_VARIABLE _cpu_flags) - string(TOLOWER "${_cpu_flags}" _cpu_flags) - string(REPLACE "." "_" _cpu_flags "${_cpu_flags}") - elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") - get_filename_component(_vendor_id "[HKEY_LOCAL_MACHINE\\Hardware\\Description\\System\\CentralProcessor\\0;VendorIdentifier]" NAME CACHE) - get_filename_component(_cpu_id "[HKEY_LOCAL_MACHINE\\Hardware\\Description\\System\\CentralProcessor\\0;Identifier]" NAME CACHE) - mark_as_advanced(_vendor_id _cpu_id) - string(REGEX REPLACE ".* Family ([0-9]+) .*" "\\1" _cpu_family "${_cpu_id}") - string(REGEX REPLACE ".* Model ([0-9]+) .*" "\\1" _cpu_model "${_cpu_id}") - endif(CMAKE_SYSTEM_NAME STREQUAL "Linux") - if(_vendor_id STREQUAL "GenuineIntel") - if(_cpu_family EQUAL 6) - # Any recent Intel CPU except NetBurst - if(_cpu_model EQUAL 58) - set(TARGET_ARCHITECTURE "ivy-bridge") - elseif(_cpu_model EQUAL 47) # Xeon E7 4860 - set(TARGET_ARCHITECTURE "westmere") - elseif(_cpu_model EQUAL 46) # Xeon 7500 series - set(TARGET_ARCHITECTURE "westmere") - elseif(_cpu_model EQUAL 45) # Xeon TNG - set(TARGET_ARCHITECTURE "sandy-bridge") - elseif(_cpu_model EQUAL 44) # Xeon 5600 series - set(TARGET_ARCHITECTURE "westmere") - elseif(_cpu_model EQUAL 42) # Core TNG - set(TARGET_ARCHITECTURE "sandy-bridge") - elseif(_cpu_model EQUAL 37) # Core i7/i5/i3 - set(TARGET_ARCHITECTURE "westmere") - elseif(_cpu_model EQUAL 31) # Core i7/i5 - set(TARGET_ARCHITECTURE "westmere") - elseif(_cpu_model EQUAL 30) # Core i7/i5 - set(TARGET_ARCHITECTURE "westmere") - elseif(_cpu_model EQUAL 29) - set(TARGET_ARCHITECTURE "penryn") - elseif(_cpu_model EQUAL 28) - set(TARGET_ARCHITECTURE "atom") - elseif(_cpu_model EQUAL 26) - set(TARGET_ARCHITECTURE "nehalem") - elseif(_cpu_model EQUAL 23) - set(TARGET_ARCHITECTURE "penryn") - elseif(_cpu_model EQUAL 15) - set(TARGET_ARCHITECTURE "merom") - elseif(_cpu_model EQUAL 14) - set(TARGET_ARCHITECTURE "core") - elseif(_cpu_model LESS 14) - message(WARNING "Your CPU (family ${_cpu_family}, model ${_cpu_model}) is not known. Auto-detection of optimization flags failed and will use the generic CPU settings with SSE2.") - set(TARGET_ARCHITECTURE "generic") - else() - message(WARNING "Your CPU (family ${_cpu_family}, model ${_cpu_model}) is not known. Auto-detection of optimization flags failed and will use the 65nm Core 2 CPU settings.") - set(TARGET_ARCHITECTURE "merom") - endif() - elseif(_cpu_family EQUAL 7) # Itanium (not supported) - message(WARNING "Your CPU (Itanium: family ${_cpu_family}, model ${_cpu_model}) is not supported by OptimizeForArchitecture.cmake.") - elseif(_cpu_family EQUAL 15) # NetBurst - list(APPEND _available_vector_units_list "sse" "sse2") - if(_cpu_model GREATER 2) # Not sure whether this must be 3 or even 4 instead - list(APPEND _available_vector_units_list "sse" "sse2" "sse3") - endif(_cpu_model GREATER 2) - endif(_cpu_family EQUAL 6) - elseif(_vendor_id STREQUAL "AuthenticAMD") - if(_cpu_family EQUAL 21) # 15h - if(_cpu_model LESS 2) - set(TARGET_ARCHITECTURE "bulldozer") - else() - set(TARGET_ARCHITECTURE "piledriver") - endif() - elseif(_cpu_family EQUAL 20) # 14h - elseif(_cpu_family EQUAL 18) # 12h - elseif(_cpu_family EQUAL 16) # 10h - set(TARGET_ARCHITECTURE "barcelona") - elseif(_cpu_family EQUAL 15) - set(TARGET_ARCHITECTURE "k8") - if(_cpu_model GREATER 64) # I don't know the right number to put here. This is just a guess from the hardware I have access to - set(TARGET_ARCHITECTURE "k8-sse3") - endif(_cpu_model GREATER 64) - endif() - endif(_vendor_id STREQUAL "GenuineIntel") -endmacro() - -macro(OptimizeForArchitecture) - set(TARGET_ARCHITECTURE "auto" CACHE STRING "CPU architecture to optimize for. Using an incorrect setting here can result in crashes of the resulting binary because of invalid instructions used.\nSetting the value to \"auto\" will try to optimize for the architecture where cmake is called.\nOther supported values are: \"none\", \"generic\", \"core\", \"merom\" (65nm Core2), \"penryn\" (45nm Core2), \"nehalem\", \"westmere\", \"sandy-bridge\", \"ivy-bridge\", \"atom\", \"k8\", \"k8-sse3\", \"barcelona\", \"istanbul\", \"magny-cours\", \"bulldozer\", \"interlagos\", \"piledriver\".") - set(_force) - if(NOT _last_target_arch STREQUAL "${TARGET_ARCHITECTURE}") - message(STATUS "target changed from \"${_last_target_arch}\" to \"${TARGET_ARCHITECTURE}\"") - set(_force FORCE) - endif() - set(_last_target_arch "${TARGET_ARCHITECTURE}" CACHE STRING "" FORCE) - mark_as_advanced(_last_target_arch) - string(TOLOWER "${TARGET_ARCHITECTURE}" TARGET_ARCHITECTURE) - - set(_march_flag_list) - set(_available_vector_units_list) - - if(TARGET_ARCHITECTURE STREQUAL "auto") - AutodetectHostArchitecture() - message(STATUS "Detected CPU: ${TARGET_ARCHITECTURE}") - endif(TARGET_ARCHITECTURE STREQUAL "auto") - - if(TARGET_ARCHITECTURE STREQUAL "core") - list(APPEND _march_flag_list "core2") - list(APPEND _available_vector_units_list "sse" "sse2" "sse3") - elseif(TARGET_ARCHITECTURE STREQUAL "merom") - list(APPEND _march_flag_list "merom") - list(APPEND _march_flag_list "core2") - list(APPEND _available_vector_units_list "sse" "sse2" "sse3" "ssse3") - elseif(TARGET_ARCHITECTURE STREQUAL "penryn") - list(APPEND _march_flag_list "penryn") - list(APPEND _march_flag_list "core2") - list(APPEND _available_vector_units_list "sse" "sse2" "sse3" "ssse3") - message(STATUS "Sadly the Penryn architecture exists in variants with SSE4.1 and without SSE4.1.") - if(_cpu_flags MATCHES "sse4_1") - message(STATUS "SSE4.1: enabled (auto-detected from this computer's CPU flags)") - list(APPEND _available_vector_units_list "sse4.1") - else() - message(STATUS "SSE4.1: disabled (auto-detected from this computer's CPU flags)") - endif() - elseif(TARGET_ARCHITECTURE STREQUAL "nehalem") - list(APPEND _march_flag_list "nehalem") - list(APPEND _march_flag_list "corei7") - list(APPEND _march_flag_list "core2") - list(APPEND _available_vector_units_list "sse" "sse2" "sse3" "ssse3" "sse4.1" "sse4.2") - elseif(TARGET_ARCHITECTURE STREQUAL "westmere") - list(APPEND _march_flag_list "westmere") - list(APPEND _march_flag_list "corei7") - list(APPEND _march_flag_list "core2") - list(APPEND _available_vector_units_list "sse" "sse2" "sse3" "ssse3" "sse4.1" "sse4.2") - elseif(TARGET_ARCHITECTURE STREQUAL "ivy-bridge") - list(APPEND _march_flag_list "core-avx-i") - list(APPEND _march_flag_list "corei7-avx") - list(APPEND _march_flag_list "core2") - list(APPEND _available_vector_units_list "sse" "sse2" "sse3" "ssse3" "sse4.1" "sse4.2" "avx" "rdrnd" "f16c") - elseif(TARGET_ARCHITECTURE STREQUAL "sandy-bridge") - list(APPEND _march_flag_list "sandybridge") - list(APPEND _march_flag_list "corei7-avx") - list(APPEND _march_flag_list "core2") - list(APPEND _available_vector_units_list "sse" "sse2" "sse3" "ssse3" "sse4.1" "sse4.2" "avx") - elseif(TARGET_ARCHITECTURE STREQUAL "atom") - list(APPEND _march_flag_list "atom") - list(APPEND _march_flag_list "core2") - list(APPEND _available_vector_units_list "sse" "sse2" "sse3" "ssse3") - elseif(TARGET_ARCHITECTURE STREQUAL "k8") - list(APPEND _march_flag_list "k8") - list(APPEND _available_vector_units_list "sse" "sse2") - elseif(TARGET_ARCHITECTURE STREQUAL "k8-sse3") - list(APPEND _march_flag_list "k8-sse3") - list(APPEND _march_flag_list "k8") - list(APPEND _available_vector_units_list "sse" "sse2" "sse3") - elseif(TARGET_ARCHITECTURE STREQUAL "piledriver") - list(APPEND _march_flag_list "bdver2") - list(APPEND _march_flag_list "bdver1") - list(APPEND _march_flag_list "bulldozer") - list(APPEND _march_flag_list "barcelona") - list(APPEND _march_flag_list "core2") - list(APPEND _available_vector_units_list "sse" "sse2" "sse3" "ssse3" "sse4a" "sse4.1" "sse4.2" "avx" "xop" "fma4" "fma" "f16c") - elseif(TARGET_ARCHITECTURE STREQUAL "interlagos") - list(APPEND _march_flag_list "bdver1") - list(APPEND _march_flag_list "bulldozer") - list(APPEND _march_flag_list "barcelona") - list(APPEND _march_flag_list "core2") - list(APPEND _available_vector_units_list "sse" "sse2" "sse3" "ssse3" "sse4a" "sse4.1" "sse4.2" "avx" "xop" "fma4") - elseif(TARGET_ARCHITECTURE STREQUAL "bulldozer") - list(APPEND _march_flag_list "bdver1") - list(APPEND _march_flag_list "bulldozer") - list(APPEND _march_flag_list "barcelona") - list(APPEND _march_flag_list "core2") - list(APPEND _available_vector_units_list "sse" "sse2" "sse3" "ssse3" "sse4a" "sse4.1" "sse4.2" "avx" "xop" "fma4") - elseif(TARGET_ARCHITECTURE STREQUAL "barcelona") - list(APPEND _march_flag_list "barcelona") - list(APPEND _march_flag_list "core2") - list(APPEND _available_vector_units_list "sse" "sse2" "sse3" "sse4a") - elseif(TARGET_ARCHITECTURE STREQUAL "istanbul") - list(APPEND _march_flag_list "barcelona") - list(APPEND _march_flag_list "core2") - list(APPEND _available_vector_units_list "sse" "sse2" "sse3" "sse4a") - elseif(TARGET_ARCHITECTURE STREQUAL "magny-cours") - list(APPEND _march_flag_list "barcelona") - list(APPEND _march_flag_list "core2") - list(APPEND _available_vector_units_list "sse" "sse2" "sse3" "sse4a") - elseif(TARGET_ARCHITECTURE STREQUAL "generic") - list(APPEND _march_flag_list "generic") - elseif(TARGET_ARCHITECTURE STREQUAL "none") - # add this clause to remove it from the else clause - else(TARGET_ARCHITECTURE STREQUAL "core") - message(FATAL_ERROR "Unknown target architecture: \"${TARGET_ARCHITECTURE}\". Please set TARGET_ARCHITECTURE to a supported value.") - endif(TARGET_ARCHITECTURE STREQUAL "core") - - if(NOT TARGET_ARCHITECTURE STREQUAL "none") - set(_disable_vector_unit_list) - set(_enable_vector_unit_list) - _my_find(_available_vector_units_list "sse2" SSE2_FOUND) - _my_find(_available_vector_units_list "sse3" SSE3_FOUND) - _my_find(_available_vector_units_list "ssse3" SSSE3_FOUND) - _my_find(_available_vector_units_list "sse4.1" SSE4_1_FOUND) - _my_find(_available_vector_units_list "sse4.2" SSE4_2_FOUND) - _my_find(_available_vector_units_list "sse4a" SSE4a_FOUND) - if(DEFINED Vc_AVX_INTRINSICS_BROKEN AND Vc_AVX_INTRINSICS_BROKEN) - UserWarning("AVX disabled per default because of old/broken compiler") - set(AVX_FOUND false) - set(XOP_FOUND false) - set(FMA4_FOUND false) - set(AVX2_FOUND false) - else() - _my_find(_available_vector_units_list "avx" AVX_FOUND) - if(DEFINED Vc_FMA4_INTRINSICS_BROKEN AND Vc_FMA4_INTRINSICS_BROKEN) - UserWarning("FMA4 disabled per default because of old/broken compiler") - set(FMA4_FOUND false) - else() - _my_find(_available_vector_units_list "fma4" FMA4_FOUND) - endif() - if(DEFINED Vc_XOP_INTRINSICS_BROKEN AND Vc_XOP_INTRINSICS_BROKEN) - UserWarning("XOP disabled per default because of old/broken compiler") - set(XOP_FOUND false) - else() - _my_find(_available_vector_units_list "xop" XOP_FOUND) - endif() - if(DEFINED Vc_AVX2_INTRINSICS_BROKEN AND Vc_AVX2_INTRINSICS_BROKEN) - UserWarning("AVX2 disabled per default because of old/broken compiler") - set(AVX2_FOUND false) - else() - _my_find(_available_vector_units_list "avx2" AVX2_FOUND) - endif() - endif() - set(USE_SSE2 ${SSE2_FOUND} CACHE BOOL "Use SSE2. If SSE2 instructions are not enabled the SSE implementation will be disabled." ${_force}) - set(USE_SSE3 ${SSE3_FOUND} CACHE BOOL "Use SSE3. If SSE3 instructions are not enabled they will be emulated." ${_force}) - set(USE_SSSE3 ${SSSE3_FOUND} CACHE BOOL "Use SSSE3. If SSSE3 instructions are not enabled they will be emulated." ${_force}) - set(USE_SSE4_1 ${SSE4_1_FOUND} CACHE BOOL "Use SSE4.1. If SSE4.1 instructions are not enabled they will be emulated." ${_force}) - set(USE_SSE4_2 ${SSE4_2_FOUND} CACHE BOOL "Use SSE4.2. If SSE4.2 instructions are not enabled they will be emulated." ${_force}) - set(USE_SSE4a ${SSE4a_FOUND} CACHE BOOL "Use SSE4a. If SSE4a instructions are not enabled they will be emulated." ${_force}) - set(USE_AVX ${AVX_FOUND} CACHE BOOL "Use AVX. This will double some of the vector sizes relative to SSE." ${_force}) - set(USE_AVX2 ${AVX2_FOUND} CACHE BOOL "Use AVX2. This will double all of the vector sizes relative to SSE." ${_force}) - set(USE_XOP ${XOP_FOUND} CACHE BOOL "Use XOP." ${_force}) - set(USE_FMA4 ${FMA4_FOUND} CACHE BOOL "Use FMA4." ${_force}) - mark_as_advanced(USE_SSE2 USE_SSE3 USE_SSSE3 USE_SSE4_1 USE_SSE4_2 USE_SSE4a USE_AVX USE_AVX2 USE_XOP USE_FMA4) - if(USE_SSE2) - list(APPEND _enable_vector_unit_list "sse2") - else(USE_SSE2) - list(APPEND _disable_vector_unit_list "sse2") - endif(USE_SSE2) - if(USE_SSE3) - list(APPEND _enable_vector_unit_list "sse3") - else(USE_SSE3) - list(APPEND _disable_vector_unit_list "sse3") - endif(USE_SSE3) - if(USE_SSSE3) - list(APPEND _enable_vector_unit_list "ssse3") - else(USE_SSSE3) - list(APPEND _disable_vector_unit_list "ssse3") - endif(USE_SSSE3) - if(USE_SSE4_1) - list(APPEND _enable_vector_unit_list "sse4.1") - else(USE_SSE4_1) - list(APPEND _disable_vector_unit_list "sse4.1") - endif(USE_SSE4_1) - if(USE_SSE4_2) - list(APPEND _enable_vector_unit_list "sse4.2") - else(USE_SSE4_2) - list(APPEND _disable_vector_unit_list "sse4.2") - endif(USE_SSE4_2) - if(USE_SSE4a) - list(APPEND _enable_vector_unit_list "sse4a") - else(USE_SSE4a) - list(APPEND _disable_vector_unit_list "sse4a") - endif(USE_SSE4a) - if(USE_AVX) - list(APPEND _enable_vector_unit_list "avx") - # we want SSE intrinsics to result in instructions using the VEX prefix. - # Otherwise integer ops (which require the older SSE intrinsics) would - # always have a large penalty. - list(APPEND _enable_vector_unit_list "sse2avx") - else(USE_AVX) - list(APPEND _disable_vector_unit_list "avx") - endif(USE_AVX) - if(USE_XOP) - list(APPEND _enable_vector_unit_list "xop") - else() - list(APPEND _disable_vector_unit_list "xop") - endif() - if(USE_FMA4) - list(APPEND _enable_vector_unit_list "fma4") - else() - list(APPEND _disable_vector_unit_list "fma4") - endif() - if(USE_AVX2) - list(APPEND _enable_vector_unit_list "avx2") - else() - list(APPEND _disable_vector_unit_list "avx2") - endif() - if(MSVC) - # MSVC on 32 bit can select /arch:SSE2 (since 2010 also /arch:AVX) - # MSVC on 64 bit cannot select anything (should have changed with MSVC 2010) - _my_find(_enable_vector_unit_list "avx" _avx) - set(_avx_flag FALSE) - if(_avx) - AddCompilerFlag("/arch:AVX" CXX_FLAGS Vc_ARCHITECTURE_FLAGS CXX_RESULT _avx_flag) - endif() - if(NOT _avx_flag) - _my_find(_enable_vector_unit_list "sse2" _found) - if(_found) - AddCompilerFlag("/arch:SSE2" CXX_FLAGS Vc_ARCHITECTURE_FLAGS) - endif() - endif() - foreach(_flag ${_enable_vector_unit_list}) - string(TOUPPER "${_flag}" _flag) - string(REPLACE "." "_" _flag "__${_flag}__") - add_definitions("-D${_flag}") - endforeach(_flag) - elseif(CMAKE_CXX_COMPILER MATCHES "/(icpc|icc)$") # ICC (on Linux) - _my_find(_available_vector_units_list "avx2" _found) - if(_found) - AddCompilerFlag("-xCORE-AVX2" CXX_FLAGS Vc_ARCHITECTURE_FLAGS) - else(_found) - _my_find(_available_vector_units_list "f16c" _found) - if(_found) - AddCompilerFlag("-xCORE-AVX-I" CXX_FLAGS Vc_ARCHITECTURE_FLAGS) - else(_found) - _my_find(_available_vector_units_list "avx" _found) - if(_found) - AddCompilerFlag("-xAVX" CXX_FLAGS Vc_ARCHITECTURE_FLAGS) - else(_found) - _my_find(_available_vector_units_list "sse4.2" _found) - if(_found) - AddCompilerFlag("-xSSE4.2" CXX_FLAGS Vc_ARCHITECTURE_FLAGS) - else(_found) - _my_find(_available_vector_units_list "sse4.1" _found) - if(_found) - AddCompilerFlag("-xSSE4.1" CXX_FLAGS Vc_ARCHITECTURE_FLAGS) - else(_found) - _my_find(_available_vector_units_list "ssse3" _found) - if(_found) - AddCompilerFlag("-xSSSE3" CXX_FLAGS Vc_ARCHITECTURE_FLAGS) - else(_found) - _my_find(_available_vector_units_list "sse3" _found) - if(_found) - # If the target host is an AMD machine then we still want to use -xSSE2 because the binary would refuse to run at all otherwise - _my_find(_march_flag_list "barcelona" _found) - if(NOT _found) - _my_find(_march_flag_list "k8-sse3" _found) - endif(NOT _found) - if(_found) - AddCompilerFlag("-xSSE2" CXX_FLAGS Vc_ARCHITECTURE_FLAGS) - else(_found) - AddCompilerFlag("-xSSE3" CXX_FLAGS Vc_ARCHITECTURE_FLAGS) - endif(_found) - else(_found) - _my_find(_available_vector_units_list "sse2" _found) - if(_found) - AddCompilerFlag("-xSSE2" CXX_FLAGS Vc_ARCHITECTURE_FLAGS) - endif(_found) - endif(_found) - endif(_found) - endif(_found) - endif(_found) - endif(_found) - endif(_found) - endif(_found) - else() # not MSVC and not ICC => GCC, Clang, Open64 - foreach(_flag ${_march_flag_list}) - AddCompilerFlag("-march=${_flag}" CXX_RESULT _good CXX_FLAGS Vc_ARCHITECTURE_FLAGS) - if(_good) - break() - endif(_good) - endforeach(_flag) - foreach(_flag ${_enable_vector_unit_list}) - AddCompilerFlag("-m${_flag}" CXX_RESULT _result CXX_FLAGS Vc_ARCHITECTURE_FLAGS) - if(_result) - set(_header FALSE) - if(_flag STREQUAL "sse3") - set(_header "pmmintrin.h") - elseif(_flag STREQUAL "ssse3") - set(_header "tmmintrin.h") - elseif(_flag STREQUAL "sse4.1") - set(_header "smmintrin.h") - elseif(_flag STREQUAL "sse4.2") - set(_header "smmintrin.h") - elseif(_flag STREQUAL "sse4a") - set(_header "ammintrin.h") - elseif(_flag STREQUAL "avx") - set(_header "immintrin.h") - elseif(_flag STREQUAL "avx2") - set(_header "immintrin.h") - elseif(_flag STREQUAL "fma4") - set(_header "x86intrin.h") - elseif(_flag STREQUAL "xop") - set(_header "x86intrin.h") - endif() - set(_resultVar "HAVE_${_header}") - string(REPLACE "." "_" _resultVar "${_resultVar}") - if(_header) - CHECK_INCLUDE_FILE("${_header}" ${_resultVar} "-m${_flag}") - if(NOT ${_resultVar}) - set(_useVar "USE_${_flag}") - string(TOUPPER "${_useVar}" _useVar) - string(REPLACE "." "_" _useVar "${_useVar}") - message(STATUS "disabling ${_useVar} because ${_header} is missing") - set(${_useVar} FALSE) - list(APPEND _disable_vector_unit_list "${_flag}") - endif() - endif() - if(NOT _header OR ${_resultVar}) - set(Vc_ARCHITECTURE_FLAGS "${Vc_ARCHITECTURE_FLAGS} -m${_flag}") - endif() - endif() - endforeach(_flag) - foreach(_flag ${_disable_vector_unit_list}) - AddCompilerFlag("-mno-${_flag}" CXX_FLAGS Vc_ARCHITECTURE_FLAGS) - endforeach(_flag) - endif() - endif() -endmacro(OptimizeForArchitecture) diff --git a/cmake/vc/UserWarning.cmake b/cmake/vc/UserWarning.cmake deleted file mode 100644 index 0be6ad240d..0000000000 --- a/cmake/vc/UserWarning.cmake +++ /dev/null @@ -1,9 +0,0 @@ -macro(UserWarning _msg) - if("$ENV{DASHBOARD_TEST_FROM_CTEST}" STREQUAL "") - # developer (non-dashboard) build - message(WARNING "${_msg}") - else() - # dashboard build - message(STATUS "${_msg}") - endif() -endmacro() diff --git a/cmake/vc/VcConfig.cmake.in b/cmake/vc/VcConfig.cmake.in deleted file mode 100644 index dd38ae0c71..0000000000 --- a/cmake/vc/VcConfig.cmake.in +++ /dev/null @@ -1,18 +0,0 @@ -set(Vc_VERSION_MAJOR @Vc_VERSION_MAJOR@) -set(Vc_VERSION_MINOR @Vc_VERSION_MINOR@) -set(Vc_VERSION_PATCH @Vc_VERSION_PATCH@) -set(Vc_VERSION @Vc_VERSION_MAJOR@.@Vc_VERSION_MINOR@.@Vc_VERSION_PATCH@) -set(Vc_VERSION_STRING "@Vc_VERSION_MAJOR@.@Vc_VERSION_MINOR@.@Vc_VERSION_PATCH@") - -set(Vc_INSTALL_DIR "@CMAKE_INSTALL_PREFIX@") - -set(Vc_LIB_DIR "@CMAKE_INSTALL_PREFIX@/lib") -set(Vc_INCLUDE_DIR "@CMAKE_INSTALL_PREFIX@/include") -set(Vc_CMAKE_MODULES_DIR "@CMAKE_INSTALL_PREFIX@/lib/cmake/Vc") - -find_library(Vc_LIBRARIES Vc PATHS "${Vc_LIB_DIR}" NO_DEFAULT_PATH) - -include("${Vc_CMAKE_MODULES_DIR}/VcMacros.cmake") - -set(Vc_DEFINITIONS) -vc_set_preferred_compiler_flags() diff --git a/cmake/vc/VcConfigVersion.cmake.in b/cmake/vc/VcConfigVersion.cmake.in deleted file mode 100644 index 19f767296b..0000000000 --- a/cmake/vc/VcConfigVersion.cmake.in +++ /dev/null @@ -1,11 +0,0 @@ -set(PACKAGE_VERSION @Vc_VERSION_MAJOR@.@Vc_VERSION_MINOR@.@Vc_VERSION_PATCH@) - -if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") - set(PACKAGE_VERSION_COMPATIBLE FALSE) -else() - set(PACKAGE_VERSION_COMPATIBLE TRUE) - if("${PACKAGE_FIND_VERSION}" STREQUAL "${PACKAGE_VERSION}") - set(PACKAGE_VERSION_EXACT TRUE) - endif() -endif() - diff --git a/cmake/vc/VcMacros.cmake b/cmake/vc/VcMacros.cmake deleted file mode 100644 index 9d6f8514e6..0000000000 --- a/cmake/vc/VcMacros.cmake +++ /dev/null @@ -1,511 +0,0 @@ -# Macros for use with the Vc library. Vc can be found at http://code.compeng.uni-frankfurt.de/projects/vc -# -# The following macros are provided: -# vc_determine_compiler -# vc_set_preferred_compiler_flags -# -#============================================================================= -# Copyright 2009-2013 Matthias Kretz -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# * 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. -# -# * The names of Kitware, Inc., the Insight Consortium, or the names of -# any consortium members, or of any contributors, may not be used to -# endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#============================================================================= - -cmake_minimum_required(VERSION 2.8.3) - -get_filename_component(_currentDir "${CMAKE_CURRENT_LIST_FILE}" PATH) -include ("${_currentDir}/UserWarning.cmake") -include ("${_currentDir}/AddCompilerFlag.cmake") -include ("${_currentDir}/OptimizeForArchitecture.cmake") - -macro(vc_determine_compiler) - if(NOT DEFINED Vc_COMPILER_IS_INTEL) - execute_process(COMMAND "${CMAKE_CXX_COMPILER}" "--version" OUTPUT_VARIABLE _cxx_compiler_version ERROR_VARIABLE _cxx_compiler_version) - set(Vc_COMPILER_IS_INTEL false) - set(Vc_COMPILER_IS_OPEN64 false) - set(Vc_COMPILER_IS_CLANG false) - set(Vc_COMPILER_IS_MSVC false) - set(Vc_COMPILER_IS_GCC false) - if(CMAKE_CXX_COMPILER MATCHES "/(icpc|icc)$") - set(Vc_COMPILER_IS_INTEL true) - exec_program(${CMAKE_C_COMPILER} ARGS -dumpversion OUTPUT_VARIABLE Vc_ICC_VERSION) - message(STATUS "Detected Compiler: Intel ${Vc_ICC_VERSION}") - elseif(CMAKE_CXX_COMPILER MATCHES "(opencc|openCC)$") - set(Vc_COMPILER_IS_OPEN64 true) - message(STATUS "Detected Compiler: Open64") - elseif(CMAKE_CXX_COMPILER MATCHES "clang\\+\\+$" OR "${_cxx_compiler_version}" MATCHES "clang") - set(Vc_COMPILER_IS_CLANG true) - exec_program(${CMAKE_CXX_COMPILER} ARGS --version OUTPUT_VARIABLE Vc_CLANG_VERSION) - string(REGEX MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?" Vc_CLANG_VERSION "${Vc_CLANG_VERSION}") - message(STATUS "Detected Compiler: Clang ${Vc_CLANG_VERSION}") - elseif(MSVC) - set(Vc_COMPILER_IS_MSVC true) - message(STATUS "Detected Compiler: MSVC ${MSVC_VERSION}") - elseif(CMAKE_COMPILER_IS_GNUCXX) - set(Vc_COMPILER_IS_GCC true) - exec_program(${CMAKE_C_COMPILER} ARGS -dumpversion OUTPUT_VARIABLE Vc_GCC_VERSION) - message(STATUS "Detected Compiler: GCC ${Vc_GCC_VERSION}") - - # some distributions patch their GCC to return nothing or only major and minor version on -dumpversion. - # In that case we must extract the version number from --version. - if(NOT Vc_GCC_VERSION OR Vc_GCC_VERSION MATCHES "^[0-9]\\.[0-9]+$") - exec_program(${CMAKE_C_COMPILER} ARGS --version OUTPUT_VARIABLE Vc_GCC_VERSION) - string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" Vc_GCC_VERSION "${Vc_GCC_VERSION}") - message(STATUS "GCC Version from --version: ${Vc_GCC_VERSION}") - endif() - - # some distributions patch their GCC to be API incompatible to what the FSF released. In - # those cases we require a macro to identify the distribution version - find_program(_lsb_release lsb_release) - mark_as_advanced(_lsb_release) - if(_lsb_release) - execute_process(COMMAND ${_lsb_release} -is OUTPUT_VARIABLE _distributor_id OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process(COMMAND ${_lsb_release} -rs OUTPUT_VARIABLE _distributor_release OUTPUT_STRIP_TRAILING_WHITESPACE) - string(TOUPPER "${_distributor_id}" _distributor_id) - if(_distributor_id STREQUAL "UBUNTU") - execute_process(COMMAND ${CMAKE_C_COMPILER} --version OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE _gcc_version) - string(REGEX MATCH "\\(.* ${Vc_GCC_VERSION}-([0-9]+).*\\)" _tmp "${_gcc_version}") - if(_tmp) - set(_patch ${CMAKE_MATCH_1}) - string(REGEX MATCH "^([0-9]+)\\.([0-9]+)$" _tmp "${_distributor_release}") - execute_process(COMMAND printf 0x%x%02x%02x ${CMAKE_MATCH_1} ${CMAKE_MATCH_2} ${_patch} OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE _tmp) - set(Vc_DEFINITIONS "${Vc_DEFINITIONS} -D__GNUC_UBUNTU_VERSION__=${_tmp}") - endif() - endif() - endif() - else() - message(WARNING "Untested/-supported Compiler (${CMAKE_CXX_COMPILER}) for use with Vc.\nPlease fill out the missing parts in the CMake scripts and submit a patch to http://code.compeng.uni-frankfurt.de/projects/vc") - endif() - endif() -endmacro() - -macro(vc_set_gnu_buildtype_flags) - set(CMAKE_CXX_FLAGS_DEBUG "-g3" CACHE STRING "Flags used by the compiler during debug builds." FORCE) - set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG" CACHE STRING "Flags used by the compiler during release minsize builds." FORCE) - set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG" CACHE STRING "Flags used by the compiler during release builds (/MD /Ob1 /Oi /Ot /Oy /Gs will produce slightly less optimized but smaller files)." FORCE) - set(CMAKE_CXX_FLAGS_RELWITHDEBUG "-O3" CACHE STRING "Flags used by the compiler during release builds containing runtime checks." FORCE) - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBUG} -g" CACHE STRING "Flags used by the compiler during Release with Debug Info builds." FORCE) - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}" CACHE STRING "Flags used by the compiler during debug builds." FORCE) - set(CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL}" CACHE STRING "Flags used by the compiler during release minsize builds." FORCE) - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}" CACHE STRING "Flags used by the compiler during release builds (/MD /Ob1 /Oi /Ot /Oy /Gs will produce slightly less optimized but smaller files)." FORCE) - set(CMAKE_C_FLAGS_RELWITHDEBUG "${CMAKE_CXX_FLAGS_RELWITHDEBUG}" CACHE STRING "Flags used by the compiler during release builds containing runtime checks." FORCE) - set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}" CACHE STRING "Flags used by the compiler during Release with Debug Info builds." FORCE) - if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebug") - set(ENABLE_STRICT_ALIASING true CACHE BOOL "Enables strict aliasing rules for more aggressive optimizations") - if(NOT ENABLE_STRICT_ALIASING) - AddCompilerFlag(-fno-strict-aliasing) - endif(NOT ENABLE_STRICT_ALIASING) - endif() - mark_as_advanced(CMAKE_CXX_FLAGS_RELWITHDEBUG CMAKE_C_FLAGS_RELWITHDEBUG) -endmacro() - -macro(vc_add_compiler_flag VAR _flag) - AddCompilerFlag("${_flag}" CXX_FLAGS ${VAR}) -endmacro() - -macro(vc_check_assembler) - if(APPLE) - if(NOT Vc_COMPILER_IS_CLANG) - message(WARNING "Apple does not provide an assembler with AVX support. AVX will not be available. Please use Clang if you want to use AVX.") - set(Vc_DEFINITIONS "${Vc_DEFINITIONS} -DVC_NO_XGETBV") - set(Vc_AVX_INTRINSICS_BROKEN true) - set(Vc_AVX2_INTRINSICS_BROKEN true) - endif() - else(APPLE) - if(${ARGC} EQUAL 1) - set(_as "${ARGV1}") - else() - exec_program(${CMAKE_CXX_COMPILER} ARGS -print-prog-name=as OUTPUT_VARIABLE _as) - mark_as_advanced(_as) - endif() - if(NOT _as) - message(WARNING "Could not find 'as', the assembler used by GCC. Hoping everything will work out...") - else() - exec_program(${_as} ARGS --version OUTPUT_VARIABLE _as_version) - string(REGEX REPLACE "\\([^\\)]*\\)" "" _as_version "${_as_version}") - string(REGEX MATCH "[1-9]\\.[0-9]+(\\.[0-9]+)?" _as_version "${_as_version}") - if(_as_version VERSION_LESS "2.18.93") - UserWarning("Your binutils is too old (${_as_version}). Some optimizations of Vc will be disabled.") - add_definitions(-DVC_NO_XGETBV) # old assembler doesn't know the xgetbv instruction - set(Vc_AVX_INTRINSICS_BROKEN true) - set(Vc_XOP_INTRINSICS_BROKEN true) - set(Vc_FMA4_INTRINSICS_BROKEN true) - elseif(_as_version VERSION_LESS "2.21.0") - UserWarning("Your binutils is too old (${_as_version}) for XOP instructions. They will therefore not be provided in libVc.") - set(Vc_XOP_INTRINSICS_BROKEN true) - endif() - endif() - endif(APPLE) -endmacro() - -macro(vc_check_fpmath) - # if compiling for 32 bit x86 we need to use the -mfpmath=sse since the x87 is broken by design - include (CheckCXXSourceRuns) - check_cxx_source_runs("int main() { return sizeof(void*) != 8; }" Vc_VOID_PTR_IS_64BIT) - if(NOT Vc_VOID_PTR_IS_64BIT) - exec_program(${CMAKE_C_COMPILER} ARGS -dumpmachine OUTPUT_VARIABLE _gcc_machine) - if(_gcc_machine MATCHES "[x34567]86" OR _gcc_machine STREQUAL "mingw32") - vc_add_compiler_flag(Vc_DEFINITIONS "-mfpmath=sse") - endif() - endif() -endmacro() - -macro(vc_set_preferred_compiler_flags) - vc_determine_compiler() - - set(_add_warning_flags false) - set(_add_buildtype_flags false) - foreach(_arg ${ARGN}) - if(_arg STREQUAL "WARNING_FLAGS") - set(_add_warning_flags true) - elseif(_arg STREQUAL "BUILDTYPE_FLAGS") - set(_add_buildtype_flags true) - endif() - endforeach() - - set(Vc_SSE_INTRINSICS_BROKEN false) - set(Vc_AVX_INTRINSICS_BROKEN false) - set(Vc_AVX2_INTRINSICS_BROKEN false) - set(Vc_XOP_INTRINSICS_BROKEN false) - set(Vc_FMA4_INTRINSICS_BROKEN false) - - if(Vc_COMPILER_IS_OPEN64) - ################################################################################################## - # Open64 # - ################################################################################################## - if(_add_warning_flags) - AddCompilerFlag("-W") - AddCompilerFlag("-Wall") - AddCompilerFlag("-Wimplicit") - AddCompilerFlag("-Wswitch") - AddCompilerFlag("-Wformat") - AddCompilerFlag("-Wchar-subscripts") - AddCompilerFlag("-Wparentheses") - AddCompilerFlag("-Wmultichar") - AddCompilerFlag("-Wtrigraphs") - AddCompilerFlag("-Wpointer-arith") - AddCompilerFlag("-Wcast-align") - AddCompilerFlag("-Wreturn-type") - AddCompilerFlag("-Wno-unused-function") - AddCompilerFlag("-pedantic") - AddCompilerFlag("-Wno-long-long") - AddCompilerFlag("-Wshadow") - AddCompilerFlag("-Wold-style-cast") - AddCompilerFlag("-Wno-variadic-macros") - endif() - if(_add_buildtype_flags) - vc_set_gnu_buildtype_flags() - endif() - - vc_check_assembler() - - # Open64 4.5.1 still doesn't ship immintrin.h - set(Vc_AVX_INTRINSICS_BROKEN true) - set(Vc_AVX2_INTRINSICS_BROKEN true) - elseif(Vc_COMPILER_IS_GCC) - ################################################################################################## - # GCC # - ################################################################################################## - if(_add_warning_flags) - foreach(_f -W -Wall -Wswitch -Wformat -Wchar-subscripts -Wparentheses -Wmultichar -Wtrigraphs -Wpointer-arith -Wcast-align -Wreturn-type -Wno-unused-function -pedantic -Wshadow -Wundef -Wold-style-cast -Wno-variadic-macros) - AddCompilerFlag("${_f}") - endforeach() - if(Vc_GCC_VERSION VERSION_GREATER "4.5.2" AND Vc_GCC_VERSION VERSION_LESS "4.6.4") - # GCC gives bogus "array subscript is above array bounds" warnings in math.cpp - AddCompilerFlag("-Wno-array-bounds") - endif() - if(Vc_GCC_VERSION VERSION_GREATER "4.7.99") - # GCC 4.8 warns about stuff we don't care about - # Some older GCC versions have problems to note that they don't support the flag - AddCompilerFlag("-Wno-unused-local-typedefs") - endif() - endif() - if (NOT MSVC) - vc_add_compiler_flag(Vc_DEFINITIONS "-Wabi") - endif() - vc_add_compiler_flag(Vc_DEFINITIONS "-fabi-version=0") # ABI version 4 is required to make __m128 and __m256 appear as different types. 0 should give us the latest version. - - if(_add_buildtype_flags) - vc_set_gnu_buildtype_flags() - endif() - - # GCC 4.5.[01] fail at inlining some functions, creating functions with a single instructions, - # thus creating a large overhead. - if(Vc_GCC_VERSION VERSION_LESS "4.5.2" AND NOT Vc_GCC_VERSION VERSION_LESS "4.5.0") - UserWarning("GCC 4.5.0 and 4.5.1 have problems with inlining correctly. Setting early-inlining-insns=12 as workaround.") - AddCompilerFlag("--param early-inlining-insns=12") - endif() - - if(Vc_GCC_VERSION VERSION_LESS "4.1.99") - UserWarning("Your GCC is ancient and crashes on some important optimizations. The full set of SSE2 intrinsics is not supported. Vc will fall back to the scalar implementation. Use of the may_alias and always_inline attributes will be disabled. In turn all code using Vc must be compiled with -fno-strict-aliasing") - vc_add_compiler_flag(Vc_DEFINITIONS "-fno-strict-aliasing") - set(Vc_AVX_INTRINSICS_BROKEN true) - set(Vc_AVX2_INTRINSICS_BROKEN true) - set(Vc_SSE_INTRINSICS_BROKEN true) - elseif(Vc_GCC_VERSION VERSION_LESS "4.4.6") - UserWarning("Your GCC is older than 4.4.6. This is known to cause problems/bugs. Please update to the latest GCC if you can.") - set(Vc_AVX_INTRINSICS_BROKEN true) - set(Vc_AVX2_INTRINSICS_BROKEN true) - if(Vc_GCC_VERSION VERSION_LESS "4.3.0") - UserWarning("Your GCC is older than 4.3.0. It is unable to handle the full set of SSE2 intrinsics. All SSE code will be disabled. Please update to the latest GCC if you can.") - set(Vc_SSE_INTRINSICS_BROKEN true) - endif() - endif() - - if(Vc_GCC_VERSION VERSION_LESS 4.5.0) - UserWarning("GCC 4.4.x shows false positives for -Wparentheses, thus we rather disable the warning.") - string(REPLACE " -Wparentheses " " " CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") - string(REPLACE " -Wparentheses " " " CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - set(Vc_DEFINITIONS "${Vc_DEFINITIONS} -Wno-parentheses") - - UserWarning("GCC 4.4.x shows false positives for -Wstrict-aliasing, thus we rather disable the warning. Use a newer GCC for better warnings.") - AddCompilerFlag("-Wno-strict-aliasing") - - UserWarning("GCC 4.4.x shows false positives for -Wuninitialized, thus we rather disable the warning. Use a newer GCC for better warnings.") - AddCompilerFlag("-Wno-uninitialized") - elseif(Vc_GCC_VERSION VERSION_EQUAL 4.6.0) - UserWarning("GCC 4.6.0 miscompiles AVX loads/stores, leading to spurious segfaults. Disabling AVX per default.") - set(Vc_AVX_INTRINSICS_BROKEN true) - set(Vc_AVX2_INTRINSICS_BROKEN true) - elseif(Vc_GCC_VERSION VERSION_EQUAL 4.7.0) - UserWarning("GCC 4.7.0 miscompiles at -O3, adding -fno-predictive-commoning to the compiler flags as workaround") - set(Vc_DEFINITIONS "${Vc_DEFINITIONS} -fno-predictive-commoning") - endif() - - vc_check_fpmath() - vc_check_assembler() - elseif(Vc_COMPILER_IS_INTEL) - ################################################################################################## - # Intel Compiler # - ################################################################################################## - - if(_add_buildtype_flags) - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DNDEBUG -O3") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3") - set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -DNDEBUG -O3") - - set(ALIAS_FLAGS "-no-ansi-alias") - if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") - # default ICC to -no-ansi-alias because otherwise tests/utils_sse fails. So far I suspect a miscompilation... - set(ENABLE_STRICT_ALIASING true CACHE BOOL "Enables strict aliasing rules for more aggressive optimizations") - if(ENABLE_STRICT_ALIASING) - set(ALIAS_FLAGS "-ansi-alias") - endif(ENABLE_STRICT_ALIASING) - endif() - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ALIAS_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ALIAS_FLAGS}") - endif() - vc_add_compiler_flag(Vc_DEFINITIONS "-diag-disable 913") - # Disable warning #13211 "Immediate parameter to intrinsic call too large". (sse/vector.tcc rotated(int)) - vc_add_compiler_flag(Vc_DEFINITIONS "-diag-disable 13211") - - if(NOT "$ENV{DASHBOARD_TEST_FROM_CTEST}" STREQUAL "") - # disable warning #2928: the __GXX_EXPERIMENTAL_CXX0X__ macro is disabled when using GNU version 4.6 with the c++0x option - # this warning just adds noise about problems in the compiler - but I'm only interested in seeing problems in Vc - vc_add_compiler_flag(Vc_DEFINITIONS "-diag-disable 2928") - endif() - - # Intel doesn't implement the XOP or FMA4 intrinsics - set(Vc_XOP_INTRINSICS_BROKEN true) - set(Vc_FMA4_INTRINSICS_BROKEN true) - elseif(Vc_COMPILER_IS_MSVC) - ################################################################################################## - # Microsoft Visual Studio # - ################################################################################################## - - if(_add_warning_flags) - AddCompilerFlag("/wd4800") # Disable warning "forcing value to bool" - AddCompilerFlag("/wd4996") # Disable warning about strdup vs. _strdup - AddCompilerFlag("/wd4244") # Disable warning "conversion from 'unsigned int' to 'float', possible loss of data" - AddCompilerFlag("/wd4146") # Disable warning "unary minus operator applied to unsigned type, result still unsigned" - AddCompilerFlag("/wd4227") # Disable warning "anachronism used : qualifiers on reference are ignored" (this is about 'restrict' usage on references, stupid MSVC) - AddCompilerFlag("/wd4722") # Disable warning "destructor never returns, potential memory leak" (warns about ~_UnitTest_Global_Object which we don't care about) - AddCompilerFlag("/wd4748") # Disable warning "/GS can not protect parameters and local variables from local buffer overrun because optimizations are disabled in function" (I don't get it) - add_definitions(-D_CRT_SECURE_NO_WARNINGS) - endif() - - # MSVC does not support inline assembly on 64 bit! :( - # searching the help for xgetbv doesn't turn up anything. So just fall back to not supporting AVX on Windows :( - # TODO: apparently MSVC 2010 SP1 added _xgetbv - set(Vc_DEFINITIONS "${Vc_DEFINITIONS} -DVC_NO_XGETBV") - - # get rid of the min/max macros - set(Vc_DEFINITIONS "${Vc_DEFINITIONS} -DNOMINMAX") - - # MSVC doesn't implement the XOP or FMA4 intrinsics - set(Vc_XOP_INTRINSICS_BROKEN true) - set(Vc_FMA4_INTRINSICS_BROKEN true) - - if(MSVC_VERSION LESS 1700) - UserWarning("MSVC before 2012 has a broken std::vector::resize implementation. STL + Vc code will probably not compile.") - endif() - elseif(Vc_COMPILER_IS_CLANG) - ################################################################################################## - # Clang # - ################################################################################################## - - # for now I don't know of any arguments I want to pass. -march and stuff is tried by OptimizeForArchitecture... - if(Vc_CLANG_VERSION VERSION_EQUAL "3.0") - UserWarning("Clang 3.0 has serious issues to compile Vc code and will most likely crash when trying to do so.\nPlease update to a recent clang version.") - elseif(Vc_CLANG_VERSION VERSION_LESS "3.3") - # the LLVM assembler gets FMAs wrong (bug 15040) - vc_add_compiler_flag(Vc_DEFINITIONS "-no-integrated-as") - endif() - - # disable these warnings because clang shows them for function overloads that were discarded via SFINAE - vc_add_compiler_flag(Vc_DEFINITIONS "-Wno-local-type-template-args") - vc_add_compiler_flag(Vc_DEFINITIONS "-Wno-unnamed-type-template-args") - - AddCompilerFlag(-stdlib=libc++) - endif() - - if(NOT Vc_COMPILER_IS_MSVC) - vc_add_compiler_flag(Vc_DEFINITIONS "-ffp-contract=fast") - endif() - - OptimizeForArchitecture() - set(Vc_DEFINITIONS "${Vc_ARCHITECTURE_FLAGS} ${Vc_DEFINITIONS}") - - set(VC_IMPL "auto" CACHE STRING "Force the Vc implementation globally to the selected instruction set. \"auto\" lets Vc use the best available instructions.") - if(NOT VC_IMPL STREQUAL "auto") - set(Vc_DEFINITIONS "${Vc_DEFINITIONS} -DVC_IMPL=${VC_IMPL}") - if(NOT VC_IMPL STREQUAL "Scalar") - set(_use_var "USE_${VC_IMPL}") - if(VC_IMPL STREQUAL "SSE") - set(_use_var "USE_SSE2") - endif() - if(NOT ${_use_var}) - message(WARNING "The selected value for VC_IMPL (${VC_IMPL}) will not work because the relevant instructions are not enabled via compiler flags.") - endif() - endif() - endif() -endmacro() - -# helper macro for vc_compile_for_all_implementations -macro(_vc_compile_one_implementation _srcs _impl) - list(FIND _disabled_targets "${_impl}" _disabled_index) - list(FIND _only_targets "${_impl}" _only_index) - if(${_disabled_index} EQUAL -1 AND (NOT _only_targets OR ${_only_index} GREATER -1)) - set(_extra_flags) - set(_ok FALSE) - foreach(_flags ${ARGN}) - if(_flags STREQUAL "NO_FLAG") - set(_ok TRUE) - break() - endif() - string(REPLACE " " ";" _flag_list "${_flags}") - foreach(_f ${_flag_list}) - AddCompilerFlag(${_f} CXX_RESULT _ok) - if(NOT _ok) - break() - endif() - endforeach() - if(_ok) - set(_extra_flags ${_flags}) - break() - endif() - endforeach() - - if(Vc_COMPILER_IS_MSVC) - # MSVC for 64bit does not recognize /arch:SSE2 anymore. Therefore we set override _ok if _impl - # says SSE - if("${_impl}" MATCHES "SSE") - set(_ok TRUE) - endif() - endif() - - if(_ok) - get_filename_component(_out "${_vc_compile_src}" NAME_WE) - get_filename_component(_ext "${_vc_compile_src}" EXT) - set(_out "${CMAKE_CURRENT_BINARY_DIR}/${_out}_${_impl}${_ext}") - configure_file( ${_vc_compile_src} "${_out}" COPYONLY ) - set_source_files_properties( "${_out}" PROPERTIES - COMPILE_DEFINITIONS "VC_IMPL=${_impl}" - COMPILE_FLAGS "${_flags} ${_extra_flags}" - ) - list(APPEND ${_srcs} "${_out}") - endif() - endif() -endmacro() - -# Generate compile rules for the given C++ source file for all available implementations and return -# the resulting list of object files in _obj -# all remaining arguments are additional flags -# Example: -# vc_compile_for_all_implementations(_objs src/trigonometric.cpp FLAGS -DCOMPILE_BLAH EXCLUDE Scalar) -# add_executable(executable main.cpp ${_objs}) -macro(vc_compile_for_all_implementations _srcs _src) - set(_flags) - unset(_disabled_targets) - unset(_only_targets) - set(_state 0) - foreach(_arg ${ARGN}) - if(_arg STREQUAL "FLAGS") - set(_state 1) - elseif(_arg STREQUAL "EXCLUDE") - set(_state 2) - elseif(_arg STREQUAL "ONLY") - set(_state 3) - elseif(_state EQUAL 1) - set(_flags "${_flags} ${_arg}") - elseif(_state EQUAL 2) - list(APPEND _disabled_targets "${_arg}") - elseif(_state EQUAL 3) - list(APPEND _only_targets "${_arg}") - else() - message(FATAL_ERROR "incorrect argument to vc_compile_for_all_implementations") - endif() - endforeach() - - set(_vc_compile_src "${_src}") - - _vc_compile_one_implementation(${_srcs} Scalar NO_FLAG) - if(NOT Vc_SSE_INTRINSICS_BROKEN) - _vc_compile_one_implementation(${_srcs} SSE2 "-xSSE2" "-msse2" "/arch:SSE2") - _vc_compile_one_implementation(${_srcs} SSE3 "-xSSE3" "-msse3" "/arch:SSE2") - _vc_compile_one_implementation(${_srcs} SSSE3 "-xSSSE3" "-mssse3" "/arch:SSE2") - _vc_compile_one_implementation(${_srcs} SSE4_1 "-xSSE4.1" "-msse4.1" "/arch:SSE2") - _vc_compile_one_implementation(${_srcs} SSE4_2 "-xSSE4.2" "-msse4.2" "/arch:SSE2") - _vc_compile_one_implementation(${_srcs} SSE3+SSE4a "-msse4a") - endif() - if(NOT Vc_AVX_INTRINSICS_BROKEN) - _vc_compile_one_implementation(${_srcs} AVX "-xAVX" "-mavx" "/arch:AVX") - if(NOT Vc_XOP_INTRINSICS_BROKEN) - if(NOT Vc_FMA4_INTRINSICS_BROKEN) - _vc_compile_one_implementation(${_srcs} SSE+XOP+FMA4 "-mxop -mfma4" "" "") - _vc_compile_one_implementation(${_srcs} AVX+XOP+FMA4 "-mavx -mxop -mfma4" "" "") - endif() - _vc_compile_one_implementation(${_srcs} SSE+XOP+FMA "-mxop -mfma" "" "") - _vc_compile_one_implementation(${_srcs} AVX+XOP+FMA "-mavx -mxop -mfma" "" "") - endif() - _vc_compile_one_implementation(${_srcs} AVX+FMA "-mavx -mfma" "" "") - endif() - if(NOT Vc_AVX2_INTRINSICS_BROKEN) - _vc_compile_one_implementation(${_srcs} AVX2 "-xCORE-AVX2" "-mavx2" "/arch:AVX2") - endif() -endmacro() diff --git a/cmake/vc/msvc_version.c b/cmake/vc/msvc_version.c deleted file mode 100644 index 5324c7d0a9..0000000000 --- a/cmake/vc/msvc_version.c +++ /dev/null @@ -1 +0,0 @@ -MSVC _MSC_FULL_VER diff --git a/krita/data/brushes/3_texture.png b/krita/data/brushes/3_texture.png index 8a92bf39ad..61523b29d2 100755 Binary files a/krita/data/brushes/3_texture.png and b/krita/data/brushes/3_texture.png differ diff --git a/krita/data/cursors/cursors.qrc b/krita/data/cursors/cursors.qrc index fb52b2dbc9..e6630dfa3a 100644 --- a/krita/data/cursors/cursors.qrc +++ b/krita/data/cursors/cursors.qrc @@ -1,23 +1,25 @@ color-picker_image_background.xpm color-picker_image_foreground.xpm color-picker_layer_background.xpm color-picker_layer_foreground.xpm cursor-cross.xpm cursor-pixel-white.xpm cursor-pixel-black.xpm cursor-round.xpm cursor-triangle_lefthanded.xpm cursor-triangle_righthanded.xpm exposure-cursor-gesture.xpm gamma-cursor-gesture.xpm precise-pick-layer-icon.xpm rotate_discrete.xpm rotate_smooth.xpm + shear_x.xpm + shear_y.xpm zoom_discrete.xpm zoom_smooth.xpm tool_freehand_cursor.png diff --git a/krita/data/cursors/shear_x.xpm b/krita/data/cursors/shear_x.xpm new file mode 100644 index 0000000000..a4c23c98b5 --- /dev/null +++ b/krita/data/cursors/shear_x.xpm @@ -0,0 +1,37 @@ +/* XPM */ +static char * shear_x_xpm[] = { +"33 31 3 1", +" c None", +". c #FFFFFF", +"+ c #000000", +" ", +" ", +" ", +" .. ", +" .+. ", +" .++. ", +" .+++. ", +" .++++....................... ", +" .+++++++++++++++++++++++++++. ", +" .++++....................... ", +" .+++. ", +" .++. ", +" .+. ", +" .. ", +" ", +" ", +" ", +" .. ", +" .+. ", +" .++. ", +" .+++. ", +" .......................++++. ", +" .+++++++++++++++++++++++++++. ", +" .......................++++. ", +" .+++. ", +" .++. ", +" .+. ", +" .. ", +" ", +" ", +" "}; diff --git a/krita/data/cursors/shear_y.xpm b/krita/data/cursors/shear_y.xpm new file mode 100644 index 0000000000..a726e764d0 --- /dev/null +++ b/krita/data/cursors/shear_y.xpm @@ -0,0 +1,37 @@ +/* XPM */ +static char * shear_y_xpm[] = { +"33 31 3 1", +" c None", +". c #FFFFFF", +"+ c #000000", +" ", +" ... . ", +" .+. .+. ", +" .+. .+++. ", +" .+. .+++++. ", +" .+. .+++++++. ", +" .+. .+++++++++. ", +" .+. .....+..... ", +" .+. .+. ", +" .+. .+. ", +" .+. .+. ", +" .+. .+. ", +" .+. .+. ", +" .+. .+. ", +" .+. .+. ", +" .+. .+. ", +" .+. .+. ", +" .+. .+. ", +" .+. .+. ", +" .+. .+. ", +" .+. .+. ", +" .+. .+. ", +" .+. .+. ", +" .....+..... .+. ", +" .+++++++++. .+. ", +" .+++++++. .+. ", +" .+++++. .+. ", +" .+++. .+. ", +" .+. .+. ", +" . ... ", +" "}; diff --git a/krita/data/input/CMakeLists.txt b/krita/data/input/CMakeLists.txt index aa2fcda87b..eb49770abb 100644 --- a/krita/data/input/CMakeLists.txt +++ b/krita/data/input/CMakeLists.txt @@ -1,20 +1,7 @@ install(FILES kritadefault.profile painttoolsaicompatible.profile photoshopcompatible.profile DESTINATION ${DATA_INSTALL_DIR}/krita/input ) -install(FILES - kritadefault.profile - painttoolsaicompatible.profile - photoshopcompatible.profile - DESTINATION ${DATA_INSTALL_DIR}/kritagemini/input -) - -install(FILES - kritadefault.profile - painttoolsaicompatible.profile - photoshopcompatible.profile - DESTINATION ${DATA_INSTALL_DIR}/kritasketch/input -) diff --git a/krita/data/paintoppresets/pixel1.kpp b/krita/data/paintoppresets/pixel1.kpp index 767c3d4c05..2fe9628f2f 100644 Binary files a/krita/data/paintoppresets/pixel1.kpp and b/krita/data/paintoppresets/pixel1.kpp differ diff --git a/krita/data/patterns/01_canvas.png b/krita/data/patterns/01_canvas.png index dda710a785..f64751b620 100644 Binary files a/krita/data/patterns/01_canvas.png and b/krita/data/patterns/01_canvas.png differ diff --git a/krita/data/patterns/02_rough-canvas.png b/krita/data/patterns/02_rough-canvas.png index a997bcf116..02f71ee038 100644 Binary files a/krita/data/patterns/02_rough-canvas.png and b/krita/data/patterns/02_rough-canvas.png differ diff --git a/krita/data/patterns/02b_WoofTissue.png b/krita/data/patterns/02b_WoofTissue.png index 22407b7382..3be862dd49 100644 Binary files a/krita/data/patterns/02b_WoofTissue.png and b/krita/data/patterns/02b_WoofTissue.png differ diff --git a/krita/data/patterns/03_default-paper.png b/krita/data/patterns/03_default-paper.png index 396c2e624e..1a0a5f48db 100644 Binary files a/krita/data/patterns/03_default-paper.png and b/krita/data/patterns/03_default-paper.png differ diff --git a/krita/data/patterns/04_paper-C-grain.png b/krita/data/patterns/04_paper-C-grain.png index 603f33fb1d..1b35dfdd0b 100644 Binary files a/krita/data/patterns/04_paper-C-grain.png and b/krita/data/patterns/04_paper-C-grain.png differ diff --git a/krita/data/patterns/05_paper-torchon.png b/krita/data/patterns/05_paper-torchon.png index 9be53eb6ab..697d8581de 100644 Binary files a/krita/data/patterns/05_paper-torchon.png and b/krita/data/patterns/05_paper-torchon.png differ diff --git a/krita/data/patterns/06_hard-grain.png b/krita/data/patterns/06_hard-grain.png index c2eef631f2..b82746cec7 100644 Binary files a/krita/data/patterns/06_hard-grain.png and b/krita/data/patterns/06_hard-grain.png differ diff --git a/krita/data/patterns/07_big-grain.png b/krita/data/patterns/07_big-grain.png index 120b126753..4c4cb9b4e9 100644 Binary files a/krita/data/patterns/07_big-grain.png and b/krita/data/patterns/07_big-grain.png differ diff --git a/krita/data/patterns/08_bump-relief.png b/krita/data/patterns/08_bump-relief.png index cac90245bc..546d1bf141 100644 Binary files a/krita/data/patterns/08_bump-relief.png and b/krita/data/patterns/08_bump-relief.png differ diff --git a/krita/data/patterns/09_drawed_crosshatched.png b/krita/data/patterns/09_drawed_crosshatched.png index f74fe4f4e9..4ef666f624 100644 Binary files a/krita/data/patterns/09_drawed_crosshatched.png and b/krita/data/patterns/09_drawed_crosshatched.png differ diff --git a/krita/data/patterns/09b_drawed-CrossedLines.png b/krita/data/patterns/09b_drawed-CrossedLines.png index 75565ef1f7..160d5966c5 100644 Binary files a/krita/data/patterns/09b_drawed-CrossedLines.png and b/krita/data/patterns/09b_drawed-CrossedLines.png differ diff --git a/krita/data/patterns/10_drawed_dotted.png b/krita/data/patterns/10_drawed_dotted.png index 246a787b4c..2ecc98cc9d 100644 Binary files a/krita/data/patterns/10_drawed_dotted.png and b/krita/data/patterns/10_drawed_dotted.png differ diff --git a/krita/data/patterns/11_drawed_furry.png b/krita/data/patterns/11_drawed_furry.png index e611705534..e3a17bf08a 100644 Binary files a/krita/data/patterns/11_drawed_furry.png and b/krita/data/patterns/11_drawed_furry.png differ diff --git a/krita/data/patterns/12_drawed_vertical.png b/krita/data/patterns/12_drawed_vertical.png index f6643bb778..5c7952b8cb 100644 Binary files a/krita/data/patterns/12_drawed_vertical.png and b/krita/data/patterns/12_drawed_vertical.png differ diff --git a/krita/data/patterns/13_drawed_swirl.png b/krita/data/patterns/13_drawed_swirl.png index 5209840444..9ef94e6ea5 100644 Binary files a/krita/data/patterns/13_drawed_swirl.png and b/krita/data/patterns/13_drawed_swirl.png differ diff --git a/krita/data/patterns/14_texture-rock.png b/krita/data/patterns/14_texture-rock.png index fb8caf54ff..d68fb4f5fd 100644 Binary files a/krita/data/patterns/14_texture-rock.png and b/krita/data/patterns/14_texture-rock.png differ diff --git a/krita/data/patterns/15_texture-rockB.png b/krita/data/patterns/15_texture-rockB.png index 5bb4db800a..c24a5713fd 100644 Binary files a/krita/data/patterns/15_texture-rockB.png and b/krita/data/patterns/15_texture-rockB.png differ diff --git a/krita/data/patterns/16_texture-woody.png b/krita/data/patterns/16_texture-woody.png index fbab185b5a..44c4ec8f62 100644 Binary files a/krita/data/patterns/16_texture-woody.png and b/krita/data/patterns/16_texture-woody.png differ diff --git a/krita/data/patterns/17_texture-melt.png b/krita/data/patterns/17_texture-melt.png index da3f28f71a..8ceb3d4c99 100644 Binary files a/krita/data/patterns/17_texture-melt.png and b/krita/data/patterns/17_texture-melt.png differ diff --git a/krita/data/patterns/18_texture-bark.png b/krita/data/patterns/18_texture-bark.png index 485c6d12e1..1d50c5c6c5 100644 Binary files a/krita/data/patterns/18_texture-bark.png and b/krita/data/patterns/18_texture-bark.png differ diff --git a/krita/data/patterns/18b_WaveFlex.png b/krita/data/patterns/18b_WaveFlex.png index 6fa73b1653..e444937799 100644 Binary files a/krita/data/patterns/18b_WaveFlex.png and b/krita/data/patterns/18b_WaveFlex.png differ diff --git a/krita/data/patterns/19_texture-crackle.png b/krita/data/patterns/19_texture-crackle.png index 007dc1e049..1fcb42c105 100644 Binary files a/krita/data/patterns/19_texture-crackle.png and b/krita/data/patterns/19_texture-crackle.png differ diff --git a/krita/data/patterns/20_texture-vegetal.png b/krita/data/patterns/20_texture-vegetal.png index 8434be7ac4..1845341019 100644 Binary files a/krita/data/patterns/20_texture-vegetal.png and b/krita/data/patterns/20_texture-vegetal.png differ diff --git a/krita/data/patterns/21_texture-chainmail.png b/krita/data/patterns/21_texture-chainmail.png index 157af13a1f..1e0662714e 100644 Binary files a/krita/data/patterns/21_texture-chainmail.png and b/krita/data/patterns/21_texture-chainmail.png differ diff --git a/krita/data/patterns/22_texture-reptile.png b/krita/data/patterns/22_texture-reptile.png index 2ed10ca96c..b9d7971f62 100644 Binary files a/krita/data/patterns/22_texture-reptile.png and b/krita/data/patterns/22_texture-reptile.png differ diff --git a/krita/data/shaders/matrix_transform_legacy.vert b/krita/data/shaders/matrix_transform_legacy.vert new file mode 100644 index 0000000000..4119f28ff8 --- /dev/null +++ b/krita/data/shaders/matrix_transform_legacy.vert @@ -0,0 +1,16 @@ +/* + * Vertex shader for handling scaling + */ +uniform mat4 modelViewProjection; +uniform mat4 textureMatrix; + +attribute highp vec4 a_vertexPosition; +attribute mediump vec4 a_textureCoordinate; + +varying vec4 v_textureCoordinate; + +void main() +{ + gl_Position = modelViewProjection * a_vertexPosition; + v_textureCoordinate = textureMatrix * a_textureCoordinate; +} diff --git a/krita/data/shaders/shaders.qrc b/krita/data/shaders/shaders.qrc index ff74dd9ca6..0ae037efb3 100644 --- a/krita/data/shaders/shaders.qrc +++ b/krita/data/shaders/shaders.qrc @@ -1,16 +1,18 @@ bilinear_gradient.frag conical_gradient.frag conical_symetric_gradient.frag cursor.frag cursor.vert highq_downscale.frag linear_gradient.frag matrix_transform.vert radial_gradient.frag simple_texture.frag square_gradient.frag + matrix_transform_legacy.vert + simple_texture_legacy.frag diff --git a/krita/data/shaders/simple_texture_legacy.frag b/krita/data/shaders/simple_texture_legacy.frag new file mode 100644 index 0000000000..1b9a3ad472 --- /dev/null +++ b/krita/data/shaders/simple_texture_legacy.frag @@ -0,0 +1,22 @@ +/* + * shader for handling scaling + */ +uniform sampler2D texture0; + +varying mediump vec4 v_textureCoordinate; + +#ifdef USE_OCIO +uniform sampler3D texture1; +#endif /* USE_OCIO */ + + +void main() { + vec4 col = texture2D(texture0, v_textureCoordinate.st); + + +#ifdef USE_OCIO + glFragColor = OCIODisplay(col, texture1); +#else /* USE_OCIO */ + gl_FragColor = col; +#endif /* USE_OCIO */ +} diff --git a/krita/data/shortcuts/CMakeLists.txt b/krita/data/shortcuts/CMakeLists.txt index 1de5cc5927..2139b3a647 100644 --- a/krita/data/shortcuts/CMakeLists.txt +++ b/krita/data/shortcuts/CMakeLists.txt @@ -1,20 +1,6 @@ install(FILES krita_default.shortcuts paint_tool_sai_compatible.shortcuts photoshop_compatible.shortcuts DESTINATION ${DATA_INSTALL_DIR}/krita/shortcuts ) - -install(FILES - krita_default.shortcuts - paint_tool_sai_compatible.shortcuts - photoshop_compatible.shortcuts - DESTINATION ${DATA_INSTALL_DIR}/kritagemini/shortcuts -) - -install(FILES - krita_default.shortcuts - paint_tool_sai_compatible.shortcuts - photoshop_compatible.shortcuts - DESTINATION ${DATA_INSTALL_DIR}/kritasketch/shortcuts -) diff --git a/krita/data/templates/comics/BD-EuroTemplate.desktop b/krita/data/templates/comics/BD-EuroTemplate.desktop deleted file mode 100644 index 3d42a5f418..0000000000 --- a/krita/data/templates/comics/BD-EuroTemplate.desktop +++ /dev/null @@ -1,69 +0,0 @@ -[Desktop Entry] -Type=Link -URL=.source/BD-EuroTemplate.kra -Icon=template_comics_empty -Name=European BD template -Name[bs]=Evropski BD predložak -Name[ca]=Plantilla europea BD -Name[ca@valencia]=Plantilla europea BD -Name[da]=Europæisk BD-skabelon -Name[de]=Europäische „Bande Dessinée (BD)“-Vorlage -Name[el]=Ευρωπαϊκό BD πρότυπο -Name[en_GB]=European BD template -Name[es]=plantilla de cómic europeo -Name[et]=Euroopa BD mall -Name[eu]=Europako BD-txantiloia -Name[fi]=Eurooppalainen BD-pohja -Name[fr]=Modèle européen de bandes dessinées -Name[gl]=Formato europeo (2×4 viñetas) -Name[hu]=Európai BD sablon -Name[it]=Modello MD europeo -Name[kk]=Еуропалық BD үлгісі -Name[lt]=Europos DB šablonas -Name[nb]=Europeisk BD-mal -Name[nds]=Europääsch BD-Vörlaag -Name[nl]=Europees BD-sjabloon -Name[pl]=Europejski szablon BD -Name[pt]=Modelo de BD europeu -Name[pt_BR]=Modelo Europeu BD -Name[ru]=Шаблон в европейском стиле (BD) -Name[sk]=Európska BD šablóna -Name[sl]=Evropska predloga BD -Name[sv]=Europeisk BD-mall -Name[tr]=Avrupa BD Şablonu -Name[uk]=Європейський шаблон BD -Name[wa]=Modele di binde d' imådje a l' uropeyinne -Name[x-test]=xxEuropean BD templatexx -Name[zh_TW]=歐式 BD 樣本 -Comment=template for European BD-style comics -Comment[bs]=predložak za evropske BD stripove -Comment[ca]=plantilla per a còmics d'estil BD europeu -Comment[ca@valencia]=plantilla per a còmics d'estil BD europeu -Comment[da]=Skabelon til tegneserier i europæisk BD-stil -Comment[de]=Vorlage für Comics im europäischen „Bande Dessinée“-Stil -Comment[el]=πρότυπο για Ευρωπαϊκά BD-style κόμικς -Comment[en_GB]=template for European BD-style comics -Comment[es]=plantilla para cómics de estilo europeo -Comment[et]=Euroopa BD-stiilis koomiksi mall -Comment[eu]=Europako BD estiloko komikietarako txantiloia -Comment[fi]=Eurooppalaisen BD-tyylin sarjakuvan pohja -Comment[fr]=Modèle européen de bandes dessinées -Comment[gl]=Páxina de banda deseñada de formato europeo, con 2×4 viñetas regulares. -Comment[hu]=sablon az európai BD-stílusú képregényekhez -Comment[it]=modello per fumetti in stile BD europeo -Comment[kk]=Еуропалық BD-стильдегі комикс үлгісі -Comment[nb]=mal for europeiske tegneserier i BD-stil -Comment[nds]=BD-Vörlaag för europääsche Comics -Comment[nl]=sjabloon voor Europese strips in BD-stijl -Comment[pl]=szablon dla Europejskiego stylu komików BD -Comment[pt]=modelo de banda desenhada europeu -Comment[pt_BR]=Modelo de quadrinhos no estilo Europeu BD -Comment[ru]=Шаблон комиксов в европейском стиле (BD) -Comment[sk]=šablóna pre európske BD komixy -Comment[sl]=predloga za stripe v evropskem slogu BD -Comment[sv]=seriemall med europeisk BD-stil -Comment[uk]=шаблон для європейських коміксів у стилі BD -Comment[wa]=Modele po les bindes d' imådje al môde uropeyinne -Comment[x-test]=xxtemplate for European BD-style comicsxx -Comment[zh_TW]=歐式 BD-樣式漫畫的樣本 -X-Krita-Version=28 diff --git a/krita/data/templates/comics/BD-EuroTemplate.kra b/krita/data/templates/comics/BD-EuroTemplate.kra deleted file mode 100644 index b8d1afcaba..0000000000 Binary files a/krita/data/templates/comics/BD-EuroTemplate.kra and /dev/null differ diff --git a/krita/data/templates/comics/CMakeLists.txt b/krita/data/templates/comics/CMakeLists.txt index 19003d6953..7d68702a6f 100644 --- a/krita/data/templates/comics/CMakeLists.txt +++ b/krita/data/templates/comics/CMakeLists.txt @@ -1,20 +1,18 @@ install( FILES template_comics_empty.png DESTINATION ${DATA_INSTALL_DIR}/krita/pics ) install( FILES - a4_waffle_grid.kra - BD-EuroTemplate.kra + a4_waffle_grid.kra Comics-USTemplate.kra Manga-JpTemplate.kra DESTINATION ${DATA_INSTALL_DIR}/krita/templates/comics/.source) install( FILES .directory - a4_waffle_grid.desktop - BD-EuroTemplate.desktop + a4_waffle_grid.desktop Comics-USTemplate.desktop Manga-JpTemplate.desktop DESTINATION ${DATA_INSTALL_DIR}/krita/templates/comics) diff --git a/krita/krita.action b/krita/krita.action index d11322243b..c92f1b323c 100644 --- a/krita/krita.action +++ b/krita/krita.action @@ -1,2914 +1,2914 @@ General Open Resources Folder Opens a file browser at the location Krita saves resources such as brushes to. Opens a file browser at the location Krita saves resources such as brushes to. Open Resources Folder 0 0 false Cleanup removed files... Cleanup removed files Cleanup removed files 0 0 false C&ascade Cascade Cascade 10 0 false &Tile Tile Tile 10 0 false Import Resources or Bundles... Import Resources or Bundles Import Resources or Bundles 0 0 false Create Resource Bundle... Create Resource Bundle Create Resource Bundle 0 0 false Show File Toolbar Show File Toolbar Show File Toolbar false Show color selector Show color selector Show color selector Shift+I false Show MyPaint shade selector Show MyPaint shade selector Show MyPaint shade selector Shift+M false Show minimal shade selector Show minimal shade selector Show minimal shade selector Shift+N false Show color history Show color history Show color history H false Show common colors Show common colors Show common colors U false Show Tool Options Show Tool Options Show Tool Options \ false Show Brush Editor Show Brush Editor Show Brush Editor F5 false Show Brush Presets Show Brush Presets Show Brush Presets F6 false Toggle Tablet Debugger Toggle Tablet Debugger Toggle Tablet Debugger 0 0 Ctrl+Shift+T false Rename Composition... Rename Composition Rename Composition 0 0 false Painting Make brush color lighter Make brush color lighter Make brush color lighter 0 0 L false Make brush color darker Make brush color darker Make brush color darker 0 0 K false Make brush color more saturated Make brush color more saturated Make brush color more saturated false Make brush color more desaturated Make brush color more desaturated Make brush color more desaturated false Shift brush color hue clockwise Shift brush color hue clockwise Shift brush color hue clockwise false Shift brush color hue counter-clockwise Shift brush color hue counter-clockwise Shift brush color hue counter-clockwise false Make brush color more red Make brush color more red Make brush color more red false Make brush color more green Make brush color more green Make brush color more green false Make brush color more blue Make brush color more blue Make brush color more blue false Make brush color more yellow Make brush color more yellow Make brush color more yellow false Increase opacity Increase opacity Increase opacity 0 0 O false Decrease opacity Decrease opacity Decrease opacity 0 0 I false draw-eraser Set eraser mode Set eraser mode Set eraser mode 10000 0 E true view-refresh Reload Original Preset Reload Original Preset Reload Original Preset 10000 false transparency-unlocked Preserve Alpha Preserve Alpha Preserve Alpha 10000 true symmetry-horizontal Set horizontal mirror mode Set horizontal mirror mode Set horizontal mirror mode 10000 true symmetry-vertical Set vertical mirror mode Set vertical mirror mode Set vertical mirror mode 10000 true Paste at cursor Paste at cursor Paste at cursor 0 0 false &Invert Selection - foo + Invert current selection Invert Selection 10000000000 100 Ctrl+Shift+I false Fill with Foreground Color (Opacity) Fill with Foreground Color (Opacity) Fill with Foreground Color (Opacity) 10000 1 Ctrl+Shift+Backspace false Fill with Background Color (Opacity) Fill with Background Color (Opacity) Fill with Background Color (Opacity) 10000 1 Ctrl+Backspace false Fill with Pattern (Opacity) Fill with Pattern (Opacity) Fill with Pattern (Opacity) 10000 1 false &Toggle Selection Display Mode Toggle Selection Display Mode Toggle Selection Display Mode 0 0 false Next Favourite Preset Next Favourite Preset Next Favourite Preset , false Previous Favourite Preset Previous Favourite Preset Previous Favourite Preset . false Switch to Previous Preset Switch to Previous Preset Switch to Previous Preset / false Hide Brushes and Stuff Toolbar Hide Brushes and Stuff Toolbar Hide Brushes and Stuff Toolbar true zoom-in Zoom &In Zoom In Zoom In Ctrl++; Ctrl+= false zoom-out Zoom &Out Zoom Out Zoom Out Ctrl+- false Reset Foreground and Background Color Reset Foreground and Background Color Reset Foreground and Background Color D false Swap Foreground and Background Color Swap Foreground and Background Color Swap Foreground and Background Color X false Brush Smoothing: Weighted Brush Smoothing: Weighted Brush Smoothing: Weighted false Brush Smoothing: Disabled Brush Smoothing: Disabled Brush Smoothing: Disabled false Brush Smoothing: Stabilizer Brush Smoothing: Stabilizer Brush Smoothing: Stabilizer false Decrease Brush Size Decrease Brush Size Decrease Brush Size 0 0 [ false Brush Smoothing: Basic Brush Smoothing: Basic Brush Smoothing: Basic false Increase Brush Size Increase Brush Size Increase Brush Size 0 0 ] false Toggle Assistant Toggle Assistant ToggleAssistant Ctrl+Shift+L true Undo Polygon Selection Points Undo Polygon Selection Points Undo Polygon Selection Points Shift+Z false Fill with Foreground Color (Opacity) Fill with Foreground Color (Opacity) Fill with Foreground Color (Opacity) 10000 1 Ctrl+Shift+Backspace false Fill with Background Color (Opacity) Fill with Background Color (Opacity) Fill with Background Color (Opacity) 10000 1 Ctrl+Backspace false Fill with Pattern (Opacity) Fill with Pattern (Opacity) Fill with Pattern (Opacity) 10000 1 false Convert &to Shape Convert to Shape Convert to Shape 10000000000 0 false &Select Opaque Select Opaque Select Opaque 100000 100 false &Show Global Selection Mask Shows global selection as a usual selection mask in <interface>Layers</interface> docker Show Global Selection Mask 0 0 true Filters &Color to Alpha... Color to Alpha Color to Alpha 10000 0 false &Top Edge Detection Top Edge Detection Top Edge Detection 10000 0 false &Index Colors... Index Colors Index Colors 10000 0 false Emboss Horizontal &Only Emboss Horizontal Only Emboss Horizontal Only 10000 0 false D&odge Dodge Dodge 10000 0 false &Sharpen Sharpen Sharpen 10000 0 false B&urn Burn Burn 10000 0 false &Mean Removal Mean Removal Mean Removal 10000 0 false &Gaussian Blur... Gaussian Blur Gaussian Blur 10000 0 false Emboss &in All Directions Emboss in All Directions Emboss in All Directions 10000 0 false &Small Tiles... Small Tiles Small Tiles 10000 0 false &Levels... Levels Levels 10000 0 Ctrl+L false &Sobel... Sobel Sobel 10000 0 false &Wave... Wave Wave 10000 0 false &Motion Blur... Motion Blur Motion Blur 10000 0 false &Color Adjustment curves... Color Adjustment curves Color Adjustment curves 10000 0 Ctrl+M false Pi&xelize... Pixelize Pixelize 10000 0 false Emboss (&Laplacian) Emboss (Laplacian) Emboss (Laplacian) 10000 0 false &Left Edge Detection Left Edge Detection Left Edge Detection 10000 0 false &Blur... Blur Blur 10000 0 false &Raindrops... Raindrops Raindrops 10000 0 false &Bottom Edge Detection Bottom Edge Detection Bottom Edge Detection 10000 0 false &Random Noise... Random Noise Random Noise 10000 0 false &Brightness/Contrast curve... Brightness/Contrast curve Brightness/Contrast curve 10000 0 false Colo&r Balance.. Color Balance.. Color Balance.. 10000 0 Ctrl+B false &PhongBumpmap... PhongBumpmap PhongBumpmap 10000 0 false &Desaturate Desaturate Desaturate 10000 0 Ctrl+Shift+U false Color &Transfer... Color Transfer Color Transfer 10000 0 false Emboss &Vertical Only Emboss Vertical Only Emboss Vertical Only 10000 0 false &Lens Blur... Lens Blur Lens Blur 10000 0 false M&inimize Channel Minimize Channel Minimize Channel 10000 0 false M&aximize Channel Maximize Channel Maximize Channel 10000 0 false &Oilpaint... Oilpaint Oilpaint 10000 0 false &Right Edge Detection Right Edge Detection Right Edge Detection 10000 0 false &Auto Contrast Auto Contrast Auto Contrast 10000 0 false &Round Corners... Round Corners Round Corners 10000 0 false &Unsharp Mask... Unsharp Mask Unsharp Mask 10000 0 false &Emboss with Variable Depth... Emboss with Variable Depth Emboss with Variable Depth 10000 0 false Emboss &Horizontal && Vertical Emboss Horizontal & Vertical Emboss Horizontal & Vertical 10000 0 false Random &Pick... Random Pick Random Pick 10000 0 false &Gaussian Noise Reduction... Gaussian Noise Reduction Gaussian Noise Reduction 10000 0 false &Posterize... Posterize Posterize 10000 0 false &Wavelet Noise Reducer... Wavelet Noise Reducer Wavelet Noise Reducer 10000 0 false &HSV Adjustment... HSV Adjustment HSV Adjustment 10000 0 Ctrl+U false Tool Shortcuts Dynamic Brush Tool Dynamic Brush Tool Dynamic Brush Tool false Crop Tool Crop the image to an area Crop the image to an area C false Polygon Tool Polygon Tool. Shift-mouseclick ends the polygon. Polygon Tool. Shift-mouseclick ends the polygon. false References References References false Rectangle Tool Rectangle Tool Rectangle Tool false Multibrush Tool Multibrush Tool Multibrush Tool Q false Shape Manipulation Tool Shape Manipulation Tool Shape Manipulation Tool false Color Picker Select a color from the image or current layer Select a color from the image or current layer P false Text Editing Tool Text editing Text editing false Outline Selection Tool Outline Selection Tool Outline Selection Tool false Artistic Text Tool Artistic text editing Artistic text editing false Bezier Curve Selection Tool Select a Bezier Curve Selection Tool false Similar Color Selection Tool Select a Similar Color Selection Tool false Fill Tool Fill a contiguous area of color with a color, or fill a selection. Fill a contiguous area of color with a color, or fill a selection. F false Line Tool Line Tool Line Tool false Freehand Path Tool Freehand Path Tool Freehand Path Tool false Bezier Curve Tool Bezier Curve Tool. Shift-mouseclick ends the curve. Bezier Curve Tool. Shift-mouseclick ends the curve. false Ellipse Tool Ellipse Tool Ellipse Tool false Freehand Brush Tool Freehand Brush Tool Freehand Brush Tool B false Create object Create object Create object false Elliptical Selection Tool Elliptical Selection Tool Elliptical Selection Tool J false Contiguous Selection Tool Contiguous Selection Tool Contiguous Selection Tool false Pattern editing Pattern editing Pattern editing false Review Review Review false Draw a gradient. Draw a gradient. Draw a gradient. G false Polygonal Selection Tool Polygonal Selection Tool Polygonal Selection Tool false Measurement Tool Measure the distance between two points Measure the distance between two points false Rectangular Selection Tool Rectangular Selection Tool Rectangular Selection Tool Ctrl+R false Move Tool Move a layer Move a layer T false Vector Image Tool Vector Image (EMF/WMF/SVM/SVG) tool Vector Image (EMF/WMF/SVM/SVG) tool false Calligraphy Calligraphy Calligraphy false Path editing Path editing Path editing false Polyline Tool Polyline Tool. Shift-mouseclick ends the polyline. Polyline Tool. Shift-mouseclick ends the polyline. false Transform Tool Transform a layer or a selection Transform a layer or a selection Ctrl+T false Ruler assistant editor tool Ruler assistant editor tool Ruler assistant editor tool false Text tool Text tool Text tool false Gradient Editing Tool Gradient editing Gradient editing false Brush Selection Tool Brush Selection Tool Brush Selection Tool false Blending Modes Next Blending Mode Next Blending Mode Next Blending Mode Alt+Shift++ false Previous Blending Mode Previous Blending Mode Previous Blending Mode Alt+Shift+- false Select Normal Blending Mode Select Normal Blending Mode Select Normal Blending Mode 0 0 Alt+Shift+N false Select Dissolve Blending Mode Select Dissolve Blending Mode Select Dissolve Blending Mode 0 0 Alt+Shift+I false Select Behind Blending Mode Select Behind Blending Mode Select Behind Blending Mode 0 0 Alt+Shift+Q false Select Clear Blending Mode Select Clear Blending Mode Select Clear Blending Mode 0 0 Alt+Shift+R false Select Darken Blending Mode Select Darken Blending Mode Select Darken Blending Mode 0 0 Alt+Shift+K false Select Multiply Blending Mode Select Multiply Blending Mode Select Multiply Blending Mode 0 0 Alt+Shift+M false Select Color Burn Blending Mode Select Color Burn Blending Mode Select Color Burn Blending Mode 0 0 Alt+Shift+B false Select Linear Burn Blending Mode Select Linear Burn Blending Mode Select Linear Burn Blending Mode 0 0 Alt+Shift+A false Select Lighten Blending Mode Select Lighten Blending Mode Select Lighten Blending Mode 0 0 Alt+Shift+G false Select Screen Blending Mode Select Screen Blending Mode Select Screen Blending Mode 0 0 Alt+Shift+S false Select Color Dodge Blending Mode Select Color Dodge Blending Mode Select Color Dodge Blending Mode 0 0 Alt+Shift+D false Select Linear Dodge Blending Mode Select Linear Dodge Blending Mode Select Linear Dodge Blending Mode 0 0 Alt+Shift+W false Select Overlay Blending Mode Select Overlay Blending Mode Select Overlay Blending Mode 0 0 Alt+Shift+O false Select Soft Light Blending Mode Select Soft Light Blending Mode Select Soft Light Blending Mode 0 0 Alt+Shift+F false Select Hard Light Blending Mode Select Hard Light Blending Mode Select Hard Light Blending Mode 0 0 Alt+Shift+H false Select Vivid Light Blending Mode Select Vivid Light Blending Mode Select Vivid Light Blending Mode 0 0 Alt+Shift+V false Select Linear Light Blending Mode Select Linear Light Blending Mode Select Linear Light Blending Mode 0 0 Alt+Shift+J false Select Pin Light Blending Mode Select Pin Light Blending Mode Select Pin Light Blending Mode 0 0 Alt+Shift+Z false Select Hard Mix Blending Mode Select Hard Mix Blending Mode Select Hard Mix Blending Mode 0 0 Alt+Shift+L false Select Difference Blending Mode Select Difference Blending Mode Select Difference Blending Mode 0 0 Alt+Shift+E false Select Exclusion Blending Mode Select Exclusion Blending Mode Select Exclusion Blending Mode 0 0 Alt+Shift+X false Select Hue Blending Mode Select Hue Blending Mode Select Hue Blending Mode 0 0 Alt+Shift+U false Select Saturation Blending Mode Select Saturation Blending Mode Select Saturation Blending Mode 0 0 Alt+Shift+T false Select Color Blending Mode Select Color Blending Mode Select Color Blending Mode 0 0 Alt+Shift+C false Select Luminosity Blending Mode Select Luminosity Blending Mode Select Luminosity Blending Mode 0 0 Alt+Shift+Y false Animation Previous frame Move to previous frame Move to previous frame 1 0 false Next frame Move to next frame Move to next frame 1 0 false Play / pause animation Play / pause animation Play / pause animation 1 0 false Add blank frame Add blank frame Add blank frame 100000 0 false Copy Frame Add duplicate frame Add duplicate frame 100000 0 false Toggle onion skin Toggle onion skin Toggle onion skin 100000 0 false Previous Keyframe false Next Keyframe false First Frame false Last Frame false Auto Frame Mode true true Add blank frame Add blank frame Add blank frame 100000 0 false Show in Timeline true Layers Activate next layer Activate next layer Activate next layer 100000 0 PgUp false Activate previous layer Activate previous layer Activate previous layer 100000 0 PgDown false groupLayer &Group Layer Group Layer Group Layer 1000 0 false cloneLayer &Clone Layer Clone Layer Clone Layer 1000 0 false vectorLayer &Vector Layer Vector Layer Vector Layer 1000 0 false filterLayer &Filter Layer... Filter Layer Filter Layer 1000 0 false fillLayer &Fill Layer... Fill Layer Fill Layer 1000 0 false fileLayer &File Layer... File Layer File Layer 1000 0 false transparencyMask &Transparency Mask Transparency Mask Transparency Mask 100000 0 false filterMask &Filter Mask... Filter Mask Filter Mask 100000 0 false transformMask &Transform Mask... Transform Mask Transform Mask 100000 0 false selectionMask &Local Selection Local Selection Local Selection 100000 0 false view-filter &Isolate Layer Isolate Layer Isolate Layer 1000 0 true paintLayer &Paint Layer Paint Layer Paint Layer 1000 0 Insert false duplicatelayer &Duplicate Layer or Mask Duplicate Layer or Mask Duplicate Layer or Mask 1000 0 Ctrl+J false &Cut Selection to New Layer Cut Selection to New Layer Cut Selection to New Layer 100000000 1 Ctrl+Shift+J false Copy &Selection to New Layer Copy Selection to New Layer Copy Selection to New Layer 100000000 0 Ctrl+Alt+J false Copy Layer Copy layer to clipboard Copy layer to clipboard 0 0 false Cut Layer Cut layer to clipboard Cut layer to clipboard 0 0 false Paste Layer Paste layer from clipboard Paste layer from clipboard 100000000000000 0 false Quick Group Create a group layer containing selected layers Quick Group 0 0 Ctrl+G false Quick Ungroup Remove grouping of the layers or remove one layer out of the group Quick Ungroup 0 0 Ctrl+Alt+G false Quick Clipping Group Group selected layers and add a layer with clipped alpha channel Quick Clipping Group 0 0 Ctrl+Shift+G false All Layers Select all layers Select all layers 0 0 false Visible Layers Select all visible layers Select all visible layers 0 0 false Locked Layers Select all locked layers Select all locked layers 0 0 false Invisible Layers Select all invisible layers Select all invisible layers 0 0 false Unlocked Layers Select all unlocked layers Select all unlocked layers 0 0 false document-save &Save Layer/Mask... Save Layer/Mask Save Layer/Mask 1000 0 false document-save Save &Group Layers... Save Group Layers Save Group Layers 100000 0 false I&mport Layer... Import Layer Import Layer 100000 0 false paintLayer &as Paint Layer... as Paint Layer as Paint Layer 1000 0 false transparencyMask as &Transparency Mask... as Transparency Mask as Transparency Mask 1000 0 false filterMask as &Filter Mask... as Filter Mask as Filter Mask 1000 0 false selectionMask as &Selection Mask... as Selection Mask as Selection Mask 1000 0 false paintLayer to &Paint Layer to Paint Layer to Paint Layer 1000 0 false transparencyMask to &Transparency Mask to Transparency Mask to Transparency Mask 1000 0 false filterMask to &Filter Mask... to Filter Mask to Filter Mask 1000 0 false selectionMask to &Selection Mask to Selection Mask to Selection Mask 1000 0 false transparencyMask &Alpha into Mask Alpha into Mask Alpha into Mask 100000 10 false transparency-enabled &Write as Alpha Write as Alpha Write as Alpha 1000000 1 false document-save &Save Merged... Save Merged Save Merged 1000000 0 false Split Layer... Split Layer Split Layer 1000 0 false symmetry-horizontal Mirror Layer Hori&zontally Mirror Layer Horizontally Mirror Layer Horizontally 1000 1 false symmetry-vertical Mirror Layer &Vertically Mirror Layer Vertically Mirror Layer Vertically 1000 1 false &Rotate Layer... Rotate Layer Rotate Layer 100000 1 false object-rotate-right Rotate &Layer 90° to the Right Rotate Layer 90° to the Right Rotate Layer 90° to the Right 100000 1 false object-rotate-left Rotate Layer &90° to the Left Rotate Layer 90° to the Left Rotate Layer 90° to the Left 100000 1 false Rotate Layer &180° Rotate Layer 180° Rotate Layer 180° 100000 1 false Scale &Layer to new Size... Scale Layer to new Size Scale Layer to new Size 100000 1 false &Shear Layer... Shear Layer Shear Layer 100000 1 false &Offset Layer... Offset Layer Offset Layer 100000 1 false Clones &Array... Clones Array Clones Array 100000 0 false &Edit metadata... Edit metadata Edit metadata 100000 1 false &Histogram... Histogram Histogram 100000 0 false &Convert Layer Color Space... Convert Layer Color Space Convert Layer Color Space 100000 1 false &Merge with Layer Below Merge with Layer Below Merge with Layer Below 100000 0 Ctrl+E false &Flatten Layer Flatten Layer Flatten Layer 100000 0 false Ras&terize Layer Rasterize Layer Rasterize Layer 10000000 1 false Flatten ima&ge Flatten image Flatten image 100000 0 Ctrl+Shift+E false &Merge Selected Layers Merge Selected Layers Merge Selected Layers Ctrl+Alt+E false La&yer Style... Layer Style Layer Style 100000 1 false Move into previous group Move into previous group Move into previous group 0 0 false Move into next group Move into next group Move into next group 0 0 false Rename current layer Rename current layer Rename current layer 100000 0 F2 false deletelayer &Remove Layer Remove Layer Remove Layer 1000 1 Shift+Delete false arrowupblr Move Layer or Mask Up Move Layer or Mask Up Ctrl+PgUp false arrowdown Move Layer or Mask Down Move Layer or Mask Down Ctrl+PgDown false properties &Properties... Properties Properties 1000 1 F3 false diff --git a/krita/krita.appdata.xml b/krita/krita.appdata.xml index 95dc50b596..683e927dea 100644 --- a/krita/krita.appdata.xml +++ b/krita/krita.appdata.xml @@ -1,138 +1,144 @@ krita.desktop CC0-1.0 Digital Painting, Creative Freedom Pintura dixital, llibertá creativa Digitalno crtanje, kreativna sloboda Dibuix digital, Llibertat creativa Dibuix digital, Llibertat creativa Digitální malování, svoboda tvorby Digital tegning, kunstnerisk frihed Digitales Malen, kreative Freiheit Digital Painting, Creative Freedom Pintura digital, libertad creativa Digitaalne joonistamine, loominguline vabadus Digitaalimaalaus, luova vapaus Peinture numérique, liberté créatrice Debuxo dixital, liberdade creativa Pictura digital, Libertate creative Pittura digitale, libertà creativa Digital Painting, Creative Freedom Cyfrowe malowanie, Wolność Twórcza Pintura Digital, Liberdade Criativa Pintura Digital, Liberdade Criativa Digitálne maľovanie, kreatívna sloboda Digital målning, kreativ frihet Цифрове малювання, творча свобода xxDigital Painting, Creative Freedomxx 数码绘图,自由创作

Krita is the full-featured digital art studio.

Krita ye l'estudiu dixital d'arte completu.

Krita je potpuni digitalni umjetnički studio.

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

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

Krita ist ein digitales Designstudio mit umfangreichen Funktionen.

Krita is the full-featured digital art studio.

Krita es un estudio de arte digital completo

Krita on rohkete võimalustega digitaalkunstistuudio.

Krita on täyspiirteinen digitaiteen ateljee.

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

Krita é un estudio completo de arte dixital.

Krita es le studio de arte digital complete.

Krita è uno studio d'arte digitale completo.

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

Krita is de digitale kunststudio vol mogelijkheden.

Krita jest pełnowymiarowym, cyfrowym studiem artystycznym

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

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

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

Krita är den fullfjädrade digitala konststudion.

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

xxKrita is the full-featured digital art studio.xx

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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 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 pinturas mate. Krita soporta muchos espacios de colores como por ejemplo RGB y CMYK tanto en canales de enteros de 8 y 16 bits así como en canales de coma flotante de 16 y 32 bits.

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

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

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

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

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

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

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

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

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

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

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

Krita 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 — чудовий інструмент для створення концептуального живопису, коміксів, текстур для моделей та декорацій. У 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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.

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

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

https://www.krita.org/ https://krita.org/about/faq/ https://krita.org/support-us/donations/ https://docs.krita.org/Category:Tutorials - http://heap.kogmbh.net/downloads/krita_appdata_1.png + http://files.kde.org/krita/marketing/appdata/2016-05-24_screenshot_002.png - - http://heap.kogmbh.net/downloads/krita_appdata_2.png + + http://files.kde.org/krita/marketing/appdata/2016-05-24_screenshot_003.png + + + http://files.kde.org/krita/marketing/appdata/2016-05-24_screenshot_004.png + + + http://files.kde.org/krita/marketing/appdata/2016-05-24_screenshot_005.png - foundation@krita.org + foundation@krita.org KDE krita
diff --git a/krita/kritamenu.action b/krita/kritamenu.action index cbba611276..41fdecde6e 100644 --- a/krita/kritamenu.action +++ b/krita/kritamenu.action @@ -1,1674 +1,1674 @@ File document-new &New Create new document New 0 0 Ctrl+N false document-open &Open... Open an existing document Open 0 0 Ctrl+O false document-open-recent Open &Recent Open a document which was recently opened Open Recent 1 0 false document-save &Save Save Save 1 0 Ctrl+S false document-save-as Save &As... Save document under a new name Save As 1 0 Ctrl+Shift+S false Reload Reload Reload 1 0 false document-import Open ex&isting Document as Untitled Document... Open existing Document as Untitled Document Open existing Document as Untitled Document 0 0 false document-export E&xport... Export Export 1 0 false application-pdf &Export as PDF... Export as PDF Export as PDF 1 0 false Import animation frames... Import animation frames Import animation frames 1 0 false Expor&t animation... Export animation Export animation 1 0 false Save Incremental &Version Save Incremental Version Save Incremental Version 1 0 Ctrl+Alt+S false Save Incremental &Backup Save Incremental Backup Save Incremental Backup 1 0 F4 false &Create Template From Image... Create Template From Image Create Template From Image 1 0 false Create Copy &From Current Image Create Copy From Current Image Create Copy From Current Image 1 0 false document-print &Print... Print document Print 1 0 Ctrl+P false document-print-preview Print Previe&w Show a print preview of document Print Preview 1 0 false configure &Document Information Document Information Document Information 1 0 false &Close All Close All Close All 1 0 Ctrl+Shift+W false C&lose Close Close 1 0 false &Quit Quit application Quit 0 0 Ctrl+Q false Edit edit-undo Undo Undo last action Undo 1 0 Ctrl+Z false _edit-redo Redo Redo last undone action Redo 1 0 Ctrl+Shift+Z false edit-cut Cu&t Cut selection to clipboard Cut 0 0 Ctrl+X false edit-copy &Copy Copy selection to clipboard Copy 0 0 Ctrl+C false C&opy (sharp) Copy (sharp) Copy (sharp) 100000000 0 false Cut (&sharp) Cut (sharp) Cut (sharp) 100000000 0 false Copy &merged Copy merged Copy merged 100000000 0 Ctrl+Shift+C false edit-paste &Paste Paste clipboard content Paste 0 0 Ctrl+V false Paste into &New Image Paste into New Image Paste into New Image 0 0 Ctrl+Shift+N false edit-clear C&lear Clear Clear 1 0 Del false &Fill with Foreground Color Fill with Foreground Color Fill with Foreground Color 10000 1 Shift+Backspace false Fill &with Background Color Fill with Background Color Fill with Background Color 10000 1 Backspace false F&ill with Pattern Fill with Pattern Fill with Pattern 10000 1 false Stro&ke selected shapes Stroke selected shapes Stroke selected shapes 1000000000 0 false Delete keyframe Delete keyframe Delete keyframe 100000 0 false Window window-new &New Window New Window New Window 0 0 false N&ext Next Next 10 0 false Previous Previous Previous false View &Show Canvas Only Show just the canvas or the whole window Show Canvas Only 0 0 Tab true view-fullscreen F&ull Screen Mode Display the window in full screen Full Screen Mode 0 0 Ctrl+Shift+F false &Wrap Around Mode Wrap Around Mode Wrap Around Mode 1 0 W true &Instant Preview Mode Instant Preview Mode Instant Preview Mode 1 0 Shift+L true Mirror View Mirror View Mirror View M false &Reset zoom Reset zoom Reset zoom 1 0 Ctrl+0 false Zoom &In Zoom In Zoom In 0 0 Ctrl++ false Zoom &Out Zoom Out Zoom Out 0 0 Ctrl+- false Rotate &Canvas Right Rotate Canvas Right Rotate Canvas Right 1 0 Ctrl+] false Rotate Canvas &Left Rotate Canvas Left Rotate Canvas Left 1 0 Ctrl+[ false Reset Canvas Rotation Rotate Canvas Left Rotate Canvas Left 1 0 false Show &Rulers The rulers show the horizontal and vertical positions of the mouse on the image and can be used to position your mouse at the right place on the canvas. <p>Uncheck this to hide the rulers.</p> Show Rulers Show Rulers 1 0 true Rulers Track Pointer The rulers will track current mouse position and show it on screen. It can cause suptle performance slowdown Rulers Track Pointer Rulers Track Pointer 1 0 true Show Guides Show or hide guides Show Guides 1 0 true Lock Guides Lock or unlock guides Lock Guides 1 0 true Snap to Guides Snap cursor to guides position Snap to Guides 1 0 true Show Status &Bar Show or hide the status bar Show Status Bar - 1 + 0 0 true view-grid Show &Grid Show Grid Show Grid 1000 0 Ctrl+Shift+' true Snap To Grid Snap To Grid Snap To Grid 1000 Ctrl+Shift+; true Show Snap Options Popup Show Snap Options Popup Show Snap Options Popup 1000 Shift+s false Snap Orthogonal Snap Orthogonal Snap Orthogonal 1000 true Snap Node Snap Node Snap Node 1000 true Snap Extension Snap Extension Snap Extension 1000 true Snap Intersection Snap Intersection Snap Intersection 1000 true Snap Bounding Box Snap Bounding Box Snap Bounding Box 1000 true Snap Image Bounds Snap Image Bounds Snap Image Bounds 1000 true Snap Image Center Snap Image Center Snap Image Center 1000 true S&how Painting Assistants Show Painting Assistants Show Painting Assistants 1000 0 true Show &Assistant Previews Show Assistant Previews Show Assistant Previews 1000 0 true Image document-properties &Properties... Properties Properties 1000 0 false format-stroke-color &Image Background Color and Transparency... Change the background color of the image Image Background Color and Transparency 1000 0 false &Convert Image Color Space... Convert Image Color Space Convert Image Color Space 1000 0 false trim-to-image &Trim to Image Size Trim to Image Size Trim to Image Size 1 0 false Trim to Current &Layer Trim to Current Layer Trim to Current Layer 100000 0 false Trim to S&election Trim to Selection Trim to Selection 100000000 0 false &Rotate Image... Rotate Image Rotate Image 1000 0 false object-rotate-right Rotate &Image 90° to the Right Rotate Image 90° to the Right Rotate Image 90° to the Right 1000 0 false object-rotate-left - Rotate Image &90° to the Right + Rotate Image &90° to the Left - Rotate Image 90° to the Right - Rotate Image 90° to the Right + Rotate Image 90° to the Left + Rotate Image 90° to the Left 1000 0 false Rotate Image &180° Rotate Image 180° Rotate Image 180° 1000 0 false &Shear Image... Shear Image Shear Image 1000 0 false symmetry-horizontal &Mirror Image Horizontally Mirror Image Horizontally Mirror Image Horizontally 1000 0 false symmetry-vertical Mirror Image &Vertically Mirror Image Vertically Mirror Image Vertically 1000 0 false Scale Image To &New Size... Scale Image To New Size Scale Image To New Size 1000 0 Ctrl+Alt+I false &Offset Image... Offset Image Offset Image 1000 0 false R&esize Canvas... Resize Canvas Resize Canvas 1000 0 Ctrl+Alt+C false Im&age Split Image Split Image Split 1000 0 false Separate Ima&ge... Separate Image Separate Image 1000 0 false Select edit-select-all Select &All Select All Select All 0 0 Ctrl+A false edit-select-all &Deselect Deselect Deselect 1100000000 0 Ctrl+Shift+A false &Reselect Reselect Reselect 0 0 Ctrl+Shift+D false &Invert Invert Invert 10000 0 Ctrl+I false &Convert to Vector Selection Convert to Vector Selection Convert to Vector Selection 10000000000 0 false Convert Shapes to &Vector Selection Convert Shapes to Vector Selection Convert Shapes to Vector Selection 1000000000 0 false &Feather Selection... Feather Selection Feather Selection 10000000000 100 Shift+F6 false Dis&play Selection Display Selection Display Selection 1000 0 Ctrl+H true Sca&le... Scale Scale 100000000 100 false S&elect from Color Range... Select from Color Range Select from Color Range 10000 100 false Select &Opaque Select Opaque Select Opaque 10000 100 false &Grow Selection... Grow Selection Grow Selection 10000000000 100 false S&hrink Selection... Shrink Selection Shrink Selection 10000000000 100 false &Border Selection... Border Selection Border Selection 10000000000 100 false S&mooth Smooth Smooth 10000000000 100 false Filter &Apply Filter Again Apply Filter Again Apply Filter Again 0 0 Ctrl+F false Adjust Adjust Adjust false Artistic Artistic Artistic false Blur Blur Blur false Colors Colors Colors false Edge Detection Edge Detection Edge Detection false Enhance Enhance Enhance false Emboss Emboss Emboss false Map Map Map false Other Other Other false G'MIC Apply G'Mic Action Apply G'Mic Action false Tools media-record &Start recording macro Start recording macro Start recording macro 1000 0 false media-playback-stop Stop &recording actions Stop recording actions Stop recording actions 1000 0 false media-playback-start &Open and play... Open and play Open and play 0 0 false document-edit Open &and edit... Open and edit Open and edit 0 0 false Settings configure &Configure Krita... Configure Krita Configure Krita 0 0 false &Manage Resources... Manage Resources Manage Resources 0 0 false preferences-desktop-locale Switch Application &Language... Switch Application Language Switch Application Language false &Show Dockers Show Dockers Show Dockers 0 0 true Sho&w Docker Titlebars Show Docker Titlebars Show Docker Titlebars 0 0 true configure Configure Tool&bars... Configure Toolbars Configure Toolbars 0 0 false Dockers Dockers Dockers false &Themes Themes Themes false im-user Active Author Profile Active Author Profile Active Author Profile configure-shortcuts Configure S&hortcuts... Configure Shortcuts Configure Shortcuts 0 0 false &Window Window Window false Help help-contents Krita &Handbook Krita Handbook Krita Handbook F1 false tools-report-bug &Report Bug... Report Bug Report Bug false calligrakrita &About Krita About Krita About Krita false kde About &KDE About KDE About KDE false Brushes and Stuff &Gradients Gradients Gradients false &Patterns Patterns Patterns false &Color Color Color false &Painter's Tools Painter's Tools Painter's Tools false Brush composite Brush composite Brush composite false Brush option slider 1 Brush option slider 1 Brush option slider 1 false Brush option slider 2 Brush option slider 2 Brush option slider 2 false Brush option slider 3 Brush option slider 3 Brush option slider 3 false Mirror Mirror Mirror false Workspaces Workspaces Workspaces false diff --git a/krita/main.cc b/krita/main.cc index 3e0bc862e4..9b914b22e1 100644 --- a/krita/main.cc +++ b/krita/main.cc @@ -1,235 +1,234 @@ /* * 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 "data/splash/splash_screen.xpm" #include "data/splash/splash_holidays.xpm" #include "KisDocument.h" #include "kis_splash_screen.h" #include "KisPart.h" #include "KisApplicationArguments.h" #if defined Q_OS_WIN #include #include #include #elif defined HAVE_X11 #include #include #endif #if defined HAVE_KCRASH #include #elif defined USE_BREAKPAD #include "kis_crash_handler.h" #endif extern "C" int main(int argc, char **argv) { /** * Add a workaround for Qt 5.6, which implemented compression of the tablet events. * Since Qt 5.6.1 there will be this hacky environment variable option. After that, * Qt developers promised to give us better control for that. Please make sure the env * variable is set *before* the construction of QApplication! */ #if defined Q_OS_LINUX && QT_VERSION >= 0x050600 qputenv("QT_XCB_NO_EVENT_COMPRESSION", "1"); #endif bool runningInKDE = !qgetenv("KDE_FULL_SESSION").isEmpty(); /** * Disable debug output by default. (krita.input enables tablet debugging.) * Debug logs can be controlled by an environment variable QT_LOGGING_RULES. * * As an example, to get full debug output, run the following: * export QT_LOGGING_RULES="krita*=true"; krita * * See: http://doc.qt.io/qt-5/qloggingcategory.html */ QLoggingCategory::setFilterRules("krita*.debug=false\n" "krita*.warning=true\n" "krita.tabletlog=true"); // A per-user unique string, without /, because QLocalServer cannot use names with a / in it QString key = "Krita3" + QDesktopServices::storageLocation(QDesktopServices::HomeLocation).replace("/", "_"); key = key.replace(":", "_").replace("\\","_"); -#if defined HAVE_X11 - // we need to call XInitThreads() (which this does) because of gmic (and possibly others) - // do their own X11 stuff in their own threads - // this call must happen before the creation of the application (see AA_X11InitThreads docs) - QCoreApplication::setAttribute(Qt::AA_X11InitThreads, true); -#endif - QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true); QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); #if QT_VERSION >= 0x050600 if (!qgetenv("KRITA_HIDPI").isEmpty()) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); } #endif KLocalizedString::setApplicationDomain("krita"); // first create the application so we can create a pixmap KisApplication app(key, argc, argv); #ifdef Q_OS_LINUX qputenv("XDG_DATA_DIRS", QFile::encodeName(KoResourcePaths::getApplicationRoot() + "share") + ":" + qgetenv("XDG_DATA_DIRS")); #else qputenv("XDG_DATA_DIRS", QFile::encodeName(KoResourcePaths::getApplicationRoot() + "share")); #endif qDebug() << "Setting XDG_DATA_DIRS" << qgetenv("XDG_DATA_DIRS"); qDebug() << "Available translations" << KLocalizedString::availableApplicationTranslations(); qDebug() << "Available domain translations" << KLocalizedString::availableDomainTranslations("krita"); // Now that the paths are set, set the language. First check the override from the langage // selection dialog. KLocalizedString::clearLanguages(); const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings languageoverride(configPath + QStringLiteral("/klanguageoverridesrc"), QSettings::IniFormat); languageoverride.beginGroup(QStringLiteral("Language")); QString language = languageoverride.value(qAppName(), "").toString(); + + qDebug() << "Override language:" << language; + if (!language.isEmpty()) { KLocalizedString::setLanguages(language.split(":")); // And override Qt's locale, too - QLocale::setDefault(language.split(":").first()); + qputenv("LANG", language.toLatin1()); + QLocale locale(language.split(":").first()); + QLocale::setDefault(locale); + qDebug() << "Qt ui languages" << locale.uiLanguages(); } else { // And if there isn't one, check the one set by the system. // XXX: This doesn't work, for some !@#$% reason. QLocale locale = QLocale::system(); if (locale.bcp47Name() != QStringLiteral("en")) { KLocalizedString::setLanguages(QStringList() << locale.bcp47Name()); } } #ifdef Q_OS_WIN QDir appdir(KoResourcePaths::getApplicationRoot()); qputenv("PATH", QFile::encodeName(appdir.absolutePath() + "/bin" + ";" + appdir.absolutePath() + "/lib" + ";" + appdir.absolutePath() + "/lib/kde4" + ";" + appdir.absolutePath() + "/Frameworks" + ";" + appdir.absolutePath())); qDebug() << "PATH" << qgetenv("PATH"); #endif if (qApp->applicationDirPath().contains(KRITA_BUILD_DIR)) { qFatal("FATAL: You're trying to run krita from the build location. You can only run Krita from the installation location."); } #if defined HAVE_KCRASH KCrash::initialize(); #elif defined USE_BREAKPAD qputenv("KDE_DEBUG", "1"); KisCrashHandler crashHandler; Q_UNUSED(crashHandler); #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 (app.isRunning()) { // only pass arguments to main instance if they are not for batch processing // any batch processing would be done in this separate instance const bool batchRun = (args.print() || args.exportAs() || args.exportAsPdf()); if (!batchRun) { QByteArray ba = args.serialize(); if (app.sendMessage(ba)) { return 0; } } } if (!runningInKDE) { // Icons in menus are ugly and distracting app.setAttribute(Qt::AA_DontShowIconsInMenus); } #if defined HAVE_X11 app.installNativeEventFilter(KisXi2EventFilter::instance()); #endif // 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)); } else { splash = new KisSplashScreen(app.applicationVersion(), QPixmap(splash_screen_xpm)); } app.setSplashScreen(splash); #if defined Q_OS_WIN KisTabletSupportWin::init(); // app.installNativeEventFilter(new KisTabletSupportWin()); #endif if (!app.start(args)) { return 1; } // Set up remote arguments. QObject::connect(&app, SIGNAL(messageReceived(QByteArray,QObject*)), &app, SLOT(remoteArguments(QByteArray,QObject*))); QObject::connect(&app, SIGNAL(fileOpenRequest(QString)), &app, SLOT(fileOpenRequested(QString))); int state = app.exec(); return state; } diff --git a/krita/pics/Breeze-dark/breeze-dark-icons.qrc b/krita/pics/Breeze-dark/breeze-dark-icons.qrc index a89fa1ec5b..345a8a9c2d 100644 --- a/krita/pics/Breeze-dark/breeze-dark-icons.qrc +++ b/krita/pics/Breeze-dark/breeze-dark-icons.qrc @@ -1,86 +1,89 @@ dark_application-exit.svg dark_application-pdf.svg dark_applications-system.svg dark_arrow-down.svg dark_arrow-downright.svg dark_arrow-downleft.svg dark_arrow-topright.svg dark_arrow-topleft.svg dark_arrow-left.svg dark_arrow-right.svg dark_arrow-up.svg dark_bookmarks.svg dark_configure-shortcuts.svg dark_configure.svg dark_configure-toolbars.svg dark_dialog-cancel.svg dark_dialog-close.svg dark_dialog-ok.svg dark_dialog-warning.svg dark_document-edit.svg dark_document-export.svg dark_document-import.svg dark_document-new.svg dark_document-open-recent.svg dark_document-open.svg dark_document-print-preview.svg dark_document-print.svg dark_document-save-as.svg dark_document-save.svg dark_download.svg dark_drive-harddisk.svg dark_edit-clear.svg dark_edit-copy.svg dark_edit-cut.svg dark_edit-delete.svg dark_edit-paste.svg dark_edit-redo.svg dark_edit-undo.svg dark_folder-documents.svg dark_folder-pictures.svg dark_folder.svg dark_format-list-unordered.svg dark_go-home.svg + dark_go-next.svg + dark_go-previous.svg + dark_go-up.svg dark_help-contents.svg dark_im-user.svg dark_kde.svg dark_layer-visible-off.svg dark_link.svg dark_list-add.svg dark_locked.svg dark_media-playback-start.svg dark_media-playback-stop.svg dark_media-record.svg dark_object-locked.svg dark_object-rotate-left.svg dark_object-rotate-right.svg dark_object-unlocked.svg dark_preferences-desktop-color.svg dark_preferences-desktop-display.svg dark_preferences-desktop-locale.svg dark_process-stop.svg dark_select-all.svg dark_select-clear.svg dark_system-help.svg dark_tools-report-bug.svg dark_tools-wizard.svg dark_unlocked.svg dark_view-choose.svg dark_view-filter.svg dark_view-fullscreen.svg dark_view-grid.svg dark_view-list-details.svg dark_view-list-text.svg dark_view-preview.svg dark_view-refresh.svg dark_window-close.svg dark_window-new.svg dark_zoom-in.svg dark_zoom-original.svg dark_zoom-out.svg diff --git a/krita/pics/Breeze-dark/dark_go-next.svg b/krita/pics/Breeze-dark/dark_go-next.svg new file mode 120000 index 0000000000..c8686496fc --- /dev/null +++ b/krita/pics/Breeze-dark/dark_go-next.svg @@ -0,0 +1 @@ +dark_arrow-right.svg \ No newline at end of file diff --git a/krita/pics/Breeze-dark/dark_go-previous.svg b/krita/pics/Breeze-dark/dark_go-previous.svg new file mode 120000 index 0000000000..991d168307 --- /dev/null +++ b/krita/pics/Breeze-dark/dark_go-previous.svg @@ -0,0 +1 @@ +dark_arrow-left.svg \ No newline at end of file diff --git a/krita/pics/Breeze-dark/dark_go-up.svg b/krita/pics/Breeze-dark/dark_go-up.svg new file mode 120000 index 0000000000..0ce4482869 --- /dev/null +++ b/krita/pics/Breeze-dark/dark_go-up.svg @@ -0,0 +1 @@ +dark_arrow-up.svg \ No newline at end of file diff --git a/krita/pics/Breeze-light/breeze-light-icons.qrc b/krita/pics/Breeze-light/breeze-light-icons.qrc index 8a3bf04bd9..ea891c07fc 100644 --- a/krita/pics/Breeze-light/breeze-light-icons.qrc +++ b/krita/pics/Breeze-light/breeze-light-icons.qrc @@ -1,86 +1,89 @@ light_application-exit.svg light_application-pdf.svg light_applications-system.svg light_arrow-down.svg light_arrow-downright.svg light_arrow-downleft.svg light_arrow-topright.svg light_arrow-topleft.svg light_arrow-left.svg light_arrow-right.svg light_arrow-up.svg light_bookmarks.svg light_configure-shortcuts.svg light_configure.svg light_configure-toolbars.svg light_dialog-cancel.svg light_dialog-close.svg light_dialog-ok.svg light_dialog-warning.svg light_document-edit.svg light_document-export.svg light_document-import.svg light_document-new.svg light_document-open-recent.svg light_document-open.svg light_document-print-preview.svg light_document-print.svg light_document-save-as.svg light_document-save.svg light_download.svg light_drive-harddisk.svg light_edit-clear.svg light_edit-copy.svg light_edit-cut.svg light_edit-delete.svg light_edit-paste.svg light_edit-redo.svg light_edit-undo.svg light_folder-documents.svg light_folder-pictures.svg light_folder.svg light_format-list-unordered.svg light_go-home.svg + light_go-next.svg + light_go-previous.svg + light_go-up.svg light_help-contents.svg light_im-user.svg light_kde.svg light_layer-visible-off.svg light_link.svg light_list-add.svg light_locked.svg light_media-playback-start.svg light_media-playback-stop.svg light_media-record.svg light_object-locked.svg light_object-rotate-left.svg light_object-rotate-right.svg light_object-unlocked.svg light_preferences-desktop-color.svg light_preferences-desktop-display.svg light_preferences-desktop-locale.svg light_process-stop.svg light_select-all.svg light_select-clear.svg light_system-help.svg light_tools-report-bug.svg light_tools-wizard.svg light_unlocked.svg light_view-choose.svg light_view-filter.svg light_view-fullscreen.svg light_view-grid.svg light_view-list-details.svg light_view-list-text.svg light_view-preview.svg light_view-refresh.svg light_window-close.svg light_window-new.svg light_zoom-in.svg light_zoom-original.svg light_zoom-out.svg diff --git a/krita/pics/Breeze-light/light_go-next.svg b/krita/pics/Breeze-light/light_go-next.svg new file mode 120000 index 0000000000..b321e49b99 --- /dev/null +++ b/krita/pics/Breeze-light/light_go-next.svg @@ -0,0 +1 @@ +light_arrow-right.svg \ No newline at end of file diff --git a/krita/pics/Breeze-light/light_go-previous.svg b/krita/pics/Breeze-light/light_go-previous.svg new file mode 120000 index 0000000000..0411cd48dc --- /dev/null +++ b/krita/pics/Breeze-light/light_go-previous.svg @@ -0,0 +1 @@ +light_arrow-left.svg \ No newline at end of file diff --git a/krita/pics/Breeze-light/light_go-up.svg b/krita/pics/Breeze-light/light_go-up.svg new file mode 120000 index 0000000000..9e3c2c086b --- /dev/null +++ b/krita/pics/Breeze-light/light_go-up.svg @@ -0,0 +1 @@ +light_arrow-up.svg \ No newline at end of file diff --git a/libs/brush/kis_abr_brush.cpp b/libs/brush/kis_abr_brush.cpp index 4555ac39e1..fbdca10a33 100644 --- a/libs/brush/kis_abr_brush.cpp +++ b/libs/brush/kis_abr_brush.cpp @@ -1,100 +1,118 @@ /* * Copyright (c) 2010 Boudewijn Rempt * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2007 Eric Lamarque * * This program is free software; you can redistribute 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_abr_brush.h" #include "kis_abr_brush_collection.h" #include #include #include #include #include #include #include #include #include #include "kis_datamanager.h" #include "kis_paint_device.h" #include "kis_global.h" #include "kis_image.h" #define DEFAULT_SPACING 0.25 KisAbrBrush::KisAbrBrush(const QString& filename, KisAbrBrushCollection *parent) : KisBrush(filename) , m_parent(parent) { setBrushType(INVALID); setHasColor(false); setSpacing(DEFAULT_SPACING); } +KisAbrBrush::KisAbrBrush(const KisAbrBrush& rhs) + : KisBrush(rhs), + m_parent(0) +{ + // Warning! The brush became detached from the parent! +} + +KisAbrBrush::KisAbrBrush(const KisAbrBrush& rhs, KisAbrBrushCollection *parent) + : KisBrush(rhs), + m_parent(parent) +{ +} + +KisBrush* KisAbrBrush::clone() const +{ + return new KisAbrBrush(*this); +} + bool KisAbrBrush::load() { return true; } bool KisAbrBrush::loadFromDevice(QIODevice */*dev*/) { return true; } bool KisAbrBrush::save() { //Return true, otherwise the brush won't be added to the //resource server if the brush is loaded via import return true; } bool KisAbrBrush::saveToDevice(QIODevice* /*dev*/) const { return true; } void KisAbrBrush::setBrushTipImage(const QImage& image) { setValid(true); setBrushType(MASK); setHasColor(false); KisBrush::setBrushTipImage(image); } void KisAbrBrush::toXML(QDomDocument& d, QDomElement& e) const { e.setAttribute("name", name()); // legacy predefinedBrushToXML("abr_brush", e); KisBrush::toXML(d, e); } QString KisAbrBrush::defaultFileExtension() const { return QString(); } QImage KisAbrBrush::brushTipImage() const { if (KisBrush::brushTipImage().isNull() && m_parent) { m_parent->load(); } return KisBrush::brushTipImage(); } diff --git a/libs/brush/kis_abr_brush.h b/libs/brush/kis_abr_brush.h index e4d306128f..5b5af6e285 100644 --- a/libs/brush/kis_abr_brush.h +++ b/libs/brush/kis_abr_brush.h @@ -1,75 +1,78 @@ /* * Copyright (c) 2010 Boudewijn Rempt * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2007 Eric Lamarque * * This program is free software; you can redistribute 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_ABR_BRUSH_ #define KIS_ABR_BRUSH_ #include #include #include #include #include #include #include "kritabrush_export.h" class KisQImagemask; class KisAbrBrushCollection; typedef KisSharedPtr KisQImagemaskSP; class QString; class QIODevice; class BRUSH_EXPORT KisAbrBrush : public KisBrush { public: /// Construct brush to load filename later as brush KisAbrBrush(const QString& filename, KisAbrBrushCollection *parent); + KisAbrBrush(const KisAbrBrush& rhs); + KisAbrBrush(const KisAbrBrush& rhs, KisAbrBrushCollection *parent); + KisBrush* clone() const; virtual bool load(); virtual bool loadFromDevice(QIODevice *dev); virtual bool save(); virtual bool saveToDevice(QIODevice* dev) const; /** * @return default file extension for saving the brush */ virtual QString defaultFileExtension() const; virtual QImage brushTipImage() const; friend class KisAbrBrushCollection; virtual void setBrushTipImage(const QImage& image); void toXML(QDomDocument& d, QDomElement& e) const; private: KisAbrBrushCollection *m_parent; }; #endif // KIS_ABR_BRUSH_ diff --git a/libs/brush/kis_abr_brush_collection.cpp b/libs/brush/kis_abr_brush_collection.cpp index ee569d5c45..0ee1e90a91 100644 --- a/libs/brush/kis_abr_brush_collection.cpp +++ b/libs/brush/kis_abr_brush_collection.cpp @@ -1,612 +1,628 @@ /* * Copyright (c) 2010 Boudewijn Rempt * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2007 Eric Lamarque * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "kis_abr_brush_collection.h" #include "kis_abr_brush.h" #include #include #include #include #include #include #include #include #include #include #include #include struct AbrInfo { //big endian short version; short subversion; // count of the images (brushes) in the abr file short count; }; /// save the QImages as png files to directory image_tests static QImage convertToQImage(char * buffer, qint32 width, qint32 height) { // create 8-bit indexed image QImage img(width, height, QImage::Format_RGB32); int pos = 0; int value = 0; for (int y = 0; y < height; y++) { QRgb *pixel = reinterpret_cast(img.scanLine(y)); for (int x = 0; x < width; x++, pos++) { value = 255 - buffer[pos]; pixel[x] = qRgb(value, value , value); } } return img; } static qint32 rle_decode(QDataStream & abr, char *buffer, qint32 height) { qint32 n; char ptmp; char ch; int i, j, c; short *cscanline_len; char *data = buffer; // read compressed size foreach scanline cscanline_len = new short[ height ]; for (i = 0; i < height; i++) { // short abr >> cscanline_len[i]; } // unpack each scanline data for (i = 0; i < height; i++) { for (j = 0; j < cscanline_len[i];) { // char if (!abr.device()->getChar(&ptmp)) { break; } n = ptmp; j++; if (n >= 128) // force sign n -= 256; if (n < 0) { // copy the following char -n + 1 times if (n == -128) // it's a nop continue; n = -n + 1; // char if (!abr.device()->getChar(&ch)) { break; } j++; for (c = 0; c < n; c++, data++) { *data = ch; } } else { // read the following n + 1 chars (no compr) for (c = 0; c < n + 1; c++, j++, data++) { // char if (!abr.device()->getChar(data)) { break; } } } } } delete [] cscanline_len; return 0; } static QString abr_v1_brush_name(const QString filename, qint32 id) { QString result = filename; int pos = filename.lastIndexOf('.'); result.remove(pos, 4); QTextStream(&result) << "_" << id; return result; } static bool abr_supported_content(AbrInfo *abr_hdr) { switch (abr_hdr->version) { case 1: case 2: return true; break; case 6: if (abr_hdr->subversion == 1 || abr_hdr->subversion == 2) return true; break; } return false; } static bool abr_reach_8BIM_section(QDataStream & abr, const QString name) { char tag[4]; char tagname[5]; qint32 section_size = 0; int r; // find 8BIMname section while (!abr.atEnd()) { r = abr.readRawData(tag, 4); if (r != 4) { warnKrita << "Error: Cannot read 8BIM tag "; return false; } if (strncmp(tag, "8BIM", 4)) { warnKrita << "Error: Start tag not 8BIM but " << (int)tag[0] << (int)tag[1] << (int)tag[2] << (int)tag[3] << " at position " << abr.device()->pos(); return false; } r = abr.readRawData(tagname, 4); if (r != 4) { warnKrita << "Error: Cannot read 8BIM tag name"; return false; } tagname[4] = '\0'; QString s1 = QString::fromLatin1(tagname, 4); if (!s1.compare(name)) { return true; } // long abr >> section_size; abr.device()->seek(abr.device()->pos() + section_size); } return true; } static qint32 find_sample_count_v6(QDataStream & abr, AbrInfo *abr_info) { qint64 origin; qint32 sample_section_size; qint32 sample_section_end; qint32 samples = 0; qint32 data_start; qint32 brush_size; qint32 brush_end; if (!abr_supported_content(abr_info)) return 0; origin = abr.device()->pos(); if (!abr_reach_8BIM_section(abr, "samp")) { // reset to origin abr.device()->seek(origin); return 0; } // long abr >> sample_section_size; sample_section_end = sample_section_size + abr.device()->pos(); if(sample_section_end < 0 || sample_section_end > abr.device()->size()) return 0; data_start = abr.device()->pos(); while ((!abr.atEnd()) && (abr.device()->pos() < sample_section_end)) { // read long abr >> brush_size; brush_end = brush_size; // complement to 4 while (brush_end % 4 != 0) brush_end++; qint64 newPos = abr.device()->pos() + brush_end; if(newPos > 0 && newPos < abr.device()->size()) { abr.device()->seek(newPos); } else return 0; samples++; } // set stream to samples data abr.device()->seek(data_start); //dbgKrita <<"samples : "<< samples; return samples; } static bool abr_read_content(QDataStream & abr, AbrInfo *abr_hdr) { abr >> abr_hdr->version; abr_hdr->subversion = 0; abr_hdr->count = 0; switch (abr_hdr->version) { case 1: case 2: abr >> abr_hdr->count; break; case 6: abr >> abr_hdr->subversion; abr_hdr->count = find_sample_count_v6(abr, abr_hdr); break; default: // unknown versions break; } // next bytes in abr are samples data return true; } static QString abr_read_ucs2_text(QDataStream & abr) { quint32 name_size; quint32 buf_size; uint i; /* two-bytes characters encoded (UCS-2) * format: * long : size - number of characters in string * data : zero terminated UCS-2 string */ // long abr >> name_size; if (name_size == 0) { return QString(); } //buf_size = name_size * 2; buf_size = name_size; //name_ucs2 = (char*) malloc (buf_size * sizeof (char)); //name_ucs2 = new char[buf_size]; ushort * name_ucs2 = new ushort[buf_size]; for (i = 0; i < buf_size ; i++) { //* char*/ //abr >> name_ucs2[i]; // I will use ushort as that is input to fromUtf16 abr >> name_ucs2[i]; } QString name_utf8 = QString::fromUtf16(name_ucs2, buf_size); delete [] name_ucs2; return name_utf8; } quint32 KisAbrBrushCollection::abr_brush_load_v6(QDataStream & abr, AbrInfo *abr_hdr, const QString filename, qint32 image_ID, qint32 id) { Q_UNUSED(image_ID); qint32 brush_size = 0; qint32 brush_end = 0; qint32 next_brush = 0; qint32 top, left, bottom, right; top = left = bottom = right = 0; short depth; char compression; qint32 width = 0; qint32 height = 0; qint32 size = 0; qint32 layer_ID = -1; char *buffer; abr >> brush_size; brush_end = brush_size; // complement to 4 while (brush_end % 4 != 0) { brush_end++; } next_brush = abr.device()->pos() + brush_end; // discard key abr.device()->seek(abr.device()->pos() + 37); if (abr_hdr->subversion == 1) // discard short coordinates and unknown short abr.device()->seek(abr.device()->pos() + 10); else // discard unknown bytes abr.device()->seek(abr.device()->pos() + 264); // long abr >> top; abr >> left; abr >> bottom; abr >> right; // short abr >> depth; // char abr.device()->getChar(&compression); width = right - left; height = bottom - top; size = width * (depth >> 3) * height; // remove .abr and add some id, so something like test.abr -> test_12345 QString name = abr_v1_brush_name(filename, id); buffer = (char*)malloc(size); // data decoding if (!compression) { // not compressed - read raw bytes as brush data //fread (buffer, size, 1, abr); abr.readRawData(buffer, size); } else { rle_decode(abr, buffer, height); } if (width < quint16_MAX && height < quint16_MAX) { // filename - filename of the file , e.g. test.abr // name - test_number_of_the_brush, e.g test_1, test_2 KisAbrBrush* abrBrush = 0; if (m_abrBrushes.contains(name)) { abrBrush = m_abrBrushes[name]; } else { abrBrush = new KisAbrBrush(name, this); abrBrush->setMD5(md5()); } abrBrush->setBrushTipImage(convertToQImage(buffer, width, height)); // XXX: call extra setters on abrBrush for other options of ABR brushes abrBrush->setValid(true); abrBrush->setName(name); m_abrBrushes[name] = abrBrush; } free(buffer); abr.device()->seek(next_brush); layer_ID = id; return layer_ID; } qint32 KisAbrBrushCollection::abr_brush_load_v12(QDataStream & abr, AbrInfo *abr_hdr, const QString filename, qint32 image_ID, qint32 id) { Q_UNUSED(image_ID); short brush_type; qint32 brush_size; qint32 next_brush; qint32 top, left, bottom, right; qint16 depth; char compression; QString name; qint32 width, height; qint32 size; qint32 layer_ID = -1; char *buffer; // short abr >> brush_type; // long abr >> brush_size; next_brush = abr.device()->pos() + brush_size; if (brush_type == 1) { // computed brush // FIXME: support it! warnKrita << "WARNING: computed brush unsupported, skipping."; abr.device()->seek(abr.device()->pos() + next_brush); // TODO: test also this one abr.skipRawData(next_brush); } else if (brush_type == 2) { // sampled brush // discard 4 misc bytes and 2 spacing bytes abr.device()->seek(abr.device()->pos() + 6); if (abr_hdr->version == 2) name = abr_read_ucs2_text(abr); if (name.isNull()) { name = abr_v1_brush_name(filename, id); } // discard 1 byte for antialiasing and 4 x short for short bounds abr.device()->seek(abr.device()->pos() + 9); // long abr >> top; abr >> left; abr >> bottom; abr >> right; // short abr >> depth; // char abr.device()->getChar(&compression); width = right - left; height = bottom - top; size = width * (depth >> 3) * height; /* FIXME: support wide brushes */ if (height > 16384) { warnKrita << "WARNING: wide brushes not supported"; abr.device()->seek(next_brush); } else { buffer = (char*)malloc(size); if (!compression) { // not compressed - read raw bytes as brush data abr.readRawData(buffer, size); } else { rle_decode(abr, buffer, height); } KisAbrBrush* abrBrush = 0; if (m_abrBrushes.contains(name)) { abrBrush = m_abrBrushes[name]; } else { abrBrush = new KisAbrBrush(name, this); abrBrush->setMD5(md5()); } abrBrush->setBrushTipImage(convertToQImage(buffer, width, height)); // XXX: call extra setters on abrBrush for other options of ABR brushes free (buffer); abrBrush->setValid(true); abrBrush->setName(name); m_abrBrushes[name] = abrBrush; layer_ID = 1; } } else { warnKrita << "Unknown ABR brush type, skipping."; abr.device()->seek(next_brush); } return layer_ID; } qint32 KisAbrBrushCollection::abr_brush_load(QDataStream & abr, AbrInfo *abr_hdr, const QString filename, qint32 image_ID, qint32 id) { qint32 layer_ID = -1; switch (abr_hdr->version) { case 1: // fall through, version 1 and 2 are compatible case 2: layer_ID = abr_brush_load_v12(abr, abr_hdr, filename, image_ID, id); break; case 6: layer_ID = abr_brush_load_v6(abr, abr_hdr, filename, image_ID, id); break; } return layer_ID; } KisAbrBrushCollection::KisAbrBrushCollection(const QString& filename) : KisBrush(filename) { } +KisAbrBrushCollection::KisAbrBrushCollection(const KisAbrBrushCollection& rhs) + : KisBrush(rhs) +{ + for (auto it = rhs.m_abrBrushes.begin(); + it != rhs.m_abrBrushes.end(); + ++it) { + + m_abrBrushes.insert(it.key(), new KisAbrBrush(*it.value(), this)); + } +} + +KisBrush* KisAbrBrushCollection::clone() const +{ + return new KisAbrBrushCollection(*this); +} + bool KisAbrBrushCollection::load() { QFile file(filename()); // check if the file is open correctly if (!file.open(QIODevice::ReadOnly)) { warnKrita << "Can't open file " << filename(); return false; } bool res = loadFromDevice(&file); file.close(); return res; } bool KisAbrBrushCollection::loadFromDevice(QIODevice *dev) { AbrInfo abr_hdr; qint32 image_ID; int i; qint32 layer_ID; QByteArray ba = dev->readAll(); QBuffer buf(&ba); buf.open(QIODevice::ReadOnly); QDataStream abr(&buf); if (!abr_read_content(abr, &abr_hdr)) { warnKrita << "Error: cannot parse ABR file: " << filename(); return false; } if (!abr_supported_content(&abr_hdr)) { warnKrita << "ERROR: unable to decode abr format version " << abr_hdr.version << "(subver " << abr_hdr.subversion << ")"; return false; } if (abr_hdr.count == 0) { errKrita << "ERROR: no sample brush found in " << filename(); return false; } image_ID = 123456; for (i = 0; i < abr_hdr.count; i++) { layer_ID = abr_brush_load(abr, &abr_hdr, shortFilename(), image_ID, i + 1); if (layer_ID == -1) { warnKrita << "Warning: problem loading brush #" << i << " in " << filename(); } } return true; } bool KisAbrBrushCollection::save() { return false; } bool KisAbrBrushCollection::saveToDevice(QIODevice */*dev*/) const { return false; } QImage KisAbrBrushCollection::image() const { return QImage(); } void KisAbrBrushCollection::toXML(QDomDocument& d, QDomElement& e) const { Q_UNUSED(d); Q_UNUSED(e); // Do nothing... } QString KisAbrBrushCollection::defaultFileExtension() const { return QString(".abr"); } diff --git a/libs/brush/kis_abr_brush_collection.h b/libs/brush/kis_abr_brush_collection.h index 7c52a7b3b0..aca684c8a0 100644 --- a/libs/brush/kis_abr_brush_collection.h +++ b/libs/brush/kis_abr_brush_collection.h @@ -1,90 +1,93 @@ /* * Copyright (c) 2010 Boudewijn Rempt * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2007 Eric Lamarque * * This program is free software; you can redistribute 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_ABR_BRUSH_COLLECTION_H #define KIS_ABR_BRUSH_COLLECTION_H #include #include #include #include #include #include #include #include #include class QString; class QIODevice; class KisAbrBrush; struct AbrInfo; /** * load a collection of brushes from an abr file */ class BRUSH_EXPORT KisAbrBrushCollection : public KisBrush { protected: public: /// Construct brush to load filename later as brush KisAbrBrushCollection(const QString& filename); + KisBrush* clone() const; + virtual ~KisAbrBrushCollection() {} virtual bool load(); virtual bool loadFromDevice(QIODevice *dev); virtual bool save(); virtual bool saveToDevice(QIODevice* dev) const; /** * @return a preview of the brush */ virtual QImage image() const; /** * @return default file extension for saving the brush */ virtual QString defaultFileExtension() const; QList brushes() { return m_abrBrushes.values(); } protected: + KisAbrBrushCollection(const KisAbrBrushCollection& rhs); void toXML(QDomDocument& d, QDomElement& e) const; private: qint32 abr_brush_load(QDataStream & abr, AbrInfo *abr_hdr, const QString filename, qint32 image_ID, qint32 id); qint32 abr_brush_load_v12(QDataStream & abr, AbrInfo *abr_hdr, const QString filename, qint32 image_ID, qint32 id); quint32 abr_brush_load_v6(QDataStream & abr, AbrInfo *abr_hdr, const QString filename, qint32 image_ID, qint32 id); QMap m_abrBrushes; }; #endif diff --git a/libs/brush/kis_abr_translator.cpp b/libs/brush/kis_abr_translator.cpp index 6c2e55c00c..589cfecfd5 100644 --- a/libs/brush/kis_abr_translator.cpp +++ b/libs/brush/kis_abr_translator.cpp @@ -1,333 +1,333 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_abr_translator.h" #include #include #include KisAbrTranslator::KisAbrTranslator() { init(); } KisAbrTranslator::~KisAbrTranslator() { } void KisAbrTranslator::init() { m_root = m_doc.createElement("Preset"); m_root.setAttribute("paintopid", "paintbrush"); } void KisAbrTranslator::addEntry(const QString& attributeName, const QString& type, const QString& value) { // setup object type // shape dynamics is not separated in the Objc so workaround by attribute name if (type == ABR_OBJECT || attributeName == ABR_USE_TIP_DYNAMICS || attributeName == ABR_USE_SCATTER) { if (m_currentObjectName == ABR_DUAL_BRUSH && attributeName == OBJECT_NAME_BRUSH) { m_currentObjectName = ABR_DUAL_BRUSH + '_' + attributeName; } else { m_currentObjectName = attributeName; } dbgKrita << "---- Object type changed to " << m_currentObjectName; } // behaviour according object's attribute name if (m_currentObjectName == ABR_PRESET_START) { if (attributeName == ABR_PRESET_START) { clean(); } else if (attributeName == ABR_PRESET_NAME) { m_root.setAttribute("name", value); } else { //dbgKrita << "--Unknown attribute: " << attributeName; } } else if (m_currentObjectName == OBJECT_NAME_BRUSH) { m_abrBrushProperties.setupProperty(attributeName, type, value); // this is not object name but shape dynamics does not start with objc :/ // those objects has been merged with shape attributes due to serialization // e.g. minumumDiameter belongs to ABR_SZVR and is ABR_USE_TIP_DYNAMICS object } else if (m_currentObjectName == ABR_USE_TIP_DYNAMICS || m_currentObjectName == ABR_SZVR || m_currentObjectName == ABR_ANGLE_DYNAMICS || m_currentObjectName == ABR_ROUNDNESS_DYNAMICS) { m_abrTipDynamics.setupProperty(attributeName, type, value); } else if (m_currentObjectName == ABR_USE_SCATTER) { // TODO } else { dbgKrita << "Unknown attribute of " << m_currentObjectName << "| " << attributeName << type << value << " |"; } } void KisAbrTranslator::finishPreset() { m_abrBrushProperties.toXML(m_doc, m_root); m_abrTipDynamics.toXML(m_doc, m_root); m_doc.appendChild(m_root); m_abrTipDynamics.reset(); m_currentObjectName.clear(); } QString KisAbrTranslator::toString() { return m_doc.toString(); } void KisAbrTranslator::clean() { m_doc.clear(); m_root.clear(); init(); } void AbrBrushProperties::setupProperty(const QString& attributeName, const QString& type, const QString& value) { Q_UNUSED(type); double valueDbl = 0.0; QStringList list; if (attributeName == ABR_BRUSH_DIAMETER || attributeName == ABR_BRUSH_HARDNESS || attributeName == ABR_BRUSH_ANGLE || attributeName == ABR_BRUSH_ROUNDNESS || attributeName == ABR_BRUSH_SPACING || attributeName == OBJECT_NAME_BRUSH) { list = value.split(' '); //e.g. "#Pxl 10" -> ['#Pxl','10'] Q_ASSERT(list.count() == 2); valueDbl = KisDomUtils::toDouble(list.at(1)); } if (attributeName == OBJECT_NAME_BRUSH) { m_brushType = list.at(0); } else if (attributeName == ABR_BRUSH_DIAMETER) { m_diameter = valueDbl; } else if (attributeName == ABR_BRUSH_HARDNESS) { m_hardness = valueDbl; } else if (attributeName == ABR_BRUSH_ANGLE) { m_angle = valueDbl; } else if (attributeName == ABR_BRUSH_ROUNDNESS) { m_roundness = valueDbl; } else if (attributeName == ABR_BRUSH_SPACING) { m_spacing = valueDbl; } else if (attributeName == ABR_BRUSH_INTR) { m_intr = value.toInt(); } else if (attributeName == ABR_FLIP_X) { m_flipX = value.toInt(); } else if (attributeName == ABR_FLIP_Y) { m_flipY = value.toInt(); } else { dbgKrita << "Unknown attribute " << attributeName; } } // // // // ]]> // void AbrBrushProperties::toXML(QDomDocument& doc, QDomElement& root) const { if (m_brushType != BRUSH_TYPE_COMPUTED) { dbgKrita << m_brushType << "saved as computed brush..."; } QDomDocument d; QDomElement e = d.createElement("Brush"); QDomElement shapeElement = d.createElement("MaskGenerator"); - shapeElement.setAttribute("radius", (m_diameter * 0.5)); // radius == diameter / 2 - shapeElement.setAttribute("ratio", m_roundness / 100.0); // roundness in (0..100) to ratio in (0.0..1.0) - shapeElement.setAttribute("hfade", m_hardness / 100.0); // same here - shapeElement.setAttribute("vfade", m_hardness / 100.0); // and here too - shapeElement.setAttribute("spikes", 2); // just circle so far + shapeElement.setAttribute("radius", KisDomUtils::toString(m_diameter * 0.5)); // radius == diameter / 2 + shapeElement.setAttribute("ratio", KisDomUtils::toString(m_roundness / 100.0)); // roundness in (0..100) to ratio in (0.0..1.0) + shapeElement.setAttribute("hfade", KisDomUtils::toString(m_hardness / 100.0)); // same here + shapeElement.setAttribute("vfade", KisDomUtils::toString(m_hardness / 100.0)); // and here too + shapeElement.setAttribute("spikes", KisDomUtils::toString(2)); // just circle so far shapeElement.setAttribute("type", "circle"); e.appendChild(shapeElement); e.setAttribute("type", "auto_brush"); - e.setAttribute("spacing", m_spacing / 100.0); // spacing from 0..1000 to - e.setAttribute("angle", m_angle < 0 ? m_angle + 360.0 : m_angle); // angle from -180..180 to 0..360 - e.setAttribute("randomness", 0); // default here + e.setAttribute("spacing", KisDomUtils::toString(m_spacing / 100.0)); // spacing from 0..1000 to + e.setAttribute("angle", KisDomUtils::toString(m_angle < 0 ? m_angle + 360.0 : m_angle)); // angle from -180..180 to 0..360 + e.setAttribute("randomness", "0"); // default here d.appendChild(e); QDomElement elementParam = doc.createElement("param"); elementParam.setAttribute("name", "brush_definition"); QDomText text = doc.createCDATASection(d.toString()); elementParam.appendChild(text); root.appendChild(elementParam); } AbrTipDynamicsProperties::AbrTipDynamicsProperties() { m_groups[ ABR_SZVR ] = &m_sizeVarianceProperties; m_groups[ ABR_ANGLE_DYNAMICS ] = &m_angleProperties; m_groups[ ABR_ROUNDNESS_DYNAMICS ] = &m_RoundnessProperties; Q_ASSERT(m_groupType.isNull()); } void AbrTipDynamicsProperties::setupProperty(const QString& attributeName, const QString& type, const QString& value) { if (type == ABR_OBJECT) { if (!m_groups.contains(attributeName)) { dbgKrita << "Unknown " << type << " in Tip dynamics called " << attributeName << " : " << value; } else { m_groupType = attributeName; } return; } Q_UNUSED(type); double valueDbl = 0.0; QStringList list; if (attributeName == ABR_TIP_DYNAMICS_MINUMUM_DIAMETER || attributeName == ABR_TIP_DYNAMICS_MINUMUM_ROUNDNESS || attributeName == ABR_TIP_DYNAMICS_TILT_SCALE ) { list = value.split(' '); //e.g. "#Pxl 10" -> ['#Pxl','10'] Q_ASSERT(list.count() == 2); valueDbl = KisDomUtils::toDouble(list.at(1)); } if (m_groupType.isNull()) { if (attributeName == ABR_USE_TIP_DYNAMICS) { m_useTipDynamics = value.toInt(); } else if (attributeName == ABR_FLIP_X) { m_flipX = value.toInt(); } else if (attributeName == ABR_FLIP_Y) { m_flipY = value.toInt(); } else if (attributeName == ABR_TIP_DYNAMICS_MINUMUM_DIAMETER) { m_minumumDiameter = valueDbl; } else if (attributeName == ABR_TIP_DYNAMICS_MINUMUM_ROUNDNESS) { m_minumumRoundness = valueDbl; } else if (attributeName == ABR_TIP_DYNAMICS_TILT_SCALE) { m_tiltScale = valueDbl; } else { dbgKrita << "Unknown attribute for tip dynamics" << attributeName; } } else { m_groups[ m_groupType ]->setupProperty(attributeName, type, value); } } void AbrTipDynamicsProperties::toXML(QDomDocument& doc, QDomElement& root) const { QDomElement el = doc.createElement("shape_dynamics"); el.setAttribute("useTipDynamics", m_useTipDynamics); el.setAttribute("flipX", m_flipX); el.setAttribute("flipY", m_flipY); root.appendChild(el); el = doc.createElement("angleDynamics"); - el.setAttribute("angleJitter", m_angleProperties.m_sizeJitter); - el.setAttribute("angleController", m_angleProperties.m_bVTy); - el.setAttribute("angleFadeStep", m_angleProperties.m_fadeStep); + el.setAttribute("angleJitter", KisDomUtils::toString(m_angleProperties.m_sizeJitter)); + el.setAttribute("angleController", KisDomUtils::toString(m_angleProperties.m_bVTy)); + el.setAttribute("angleFadeStep", KisDomUtils::toString(m_angleProperties.m_fadeStep)); root.appendChild(el); el = doc.createElement("roundnessDynamics"); - el.setAttribute("minumumRoundness", m_minumumRoundness); - el.setAttribute("roundnessJitter", m_RoundnessProperties.m_sizeJitter); - el.setAttribute("roundnessController", m_RoundnessProperties.m_bVTy); - el.setAttribute("roundnessFadeStep", m_RoundnessProperties.m_fadeStep); + el.setAttribute("minumumRoundness", KisDomUtils::toString(m_minumumRoundness)); + el.setAttribute("roundnessJitter", KisDomUtils::toString(m_RoundnessProperties.m_sizeJitter)); + el.setAttribute("roundnessController", KisDomUtils::toString(m_RoundnessProperties.m_bVTy)); + el.setAttribute("roundnessFadeStep", KisDomUtils::toString(m_RoundnessProperties.m_fadeStep)); root.appendChild(el); el = doc.createElement("sizeDynamics"); - el.setAttribute("tiltScale", m_tiltScale); - el.setAttribute("minumumDiameter", m_minumumDiameter); - el.setAttribute("roundnessJitter", m_RoundnessProperties.m_sizeJitter); - el.setAttribute("roundnessController", m_RoundnessProperties.m_bVTy); - el.setAttribute("roundnessFadeStep", m_RoundnessProperties.m_fadeStep); + el.setAttribute("tiltScale", KisDomUtils::toString(m_tiltScale)); + el.setAttribute("minumumDiameter", KisDomUtils::toString(m_minumumDiameter)); + el.setAttribute("roundnessJitter", KisDomUtils::toString(m_RoundnessProperties.m_sizeJitter)); + el.setAttribute("roundnessController", KisDomUtils::toString(m_RoundnessProperties.m_bVTy)); + el.setAttribute("roundnessFadeStep", KisDomUtils::toString(m_RoundnessProperties.m_fadeStep)); root.appendChild(el); } // // 0,m_minimumDiameter 1,m_sizeJitter // true // // ]]> // controller void AbrGroupProperties::setupProperty(const QString& attributeName, const QString& type, const QString& value) { Q_UNUSED(type); double valueDbl = 0.0; QStringList list; if (attributeName == ABR_DYNAMICS_JITTER) { list = value.split(' '); //e.g. "#Pxl 10" -> ['#Pxl','10'] Q_ASSERT(list.count() == 2); valueDbl = KisDomUtils::toDouble(list.at(1)); } if (attributeName == ABR_DYNAMICS_FADE_STEP) { m_fadeStep = value.toInt(); } else if (attributeName == ABR_DYNAMICS_JITTER) { m_sizeJitter = valueDbl; } else if (attributeName == ABR_CONTROL) { m_bVTy = (enumAbrControllers)value.toInt(); } else { dbgKrita << "Unknown attribute for Group!" << attributeName; } } diff --git a/libs/brush/kis_auto_brush.cpp b/libs/brush/kis_auto_brush.cpp index 923a7e158f..d67d6f4373 100644 --- a/libs/brush/kis_auto_brush.cpp +++ b/libs/brush/kis_auto_brush.cpp @@ -1,358 +1,378 @@ /* * Copyright (c) 2004,2007-2009 Cyrille Berger * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2012 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include //MSVC requires that Vc come first #include "kis_auto_brush.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(_WIN32) || defined(_WIN64) #include #define srand48 srand inline double drand48() { return double(rand()) / RAND_MAX; } #endif struct KisAutoBrush::Private { - KisMaskGenerator* shape; + Private() + : randomness(0), density(1.0), idealThreadCountCached(1) {} + + Private(const Private &rhs) + : shape(rhs.shape->clone()), + randomness(rhs.randomness), + density(rhs.density), + idealThreadCountCached(rhs.idealThreadCountCached) + { + } + + QScopedPointer shape; qreal randomness; qreal density; int idealThreadCountCached; }; KisAutoBrush::KisAutoBrush(KisMaskGenerator* as, qreal angle, qreal randomness, qreal density) - : KisBrush() - , d(new Private) + : KisBrush(), + d(new Private) { - d->shape = as; + d->shape.reset(as); d->randomness = randomness; d->density = density; d->idealThreadCountCached = QThread::idealThreadCount(); setBrushType(MASK); setWidth(qMax(qreal(1.0), d->shape->width())); setHeight(qMax(qreal(1.0), d->shape->height())); QImage image = createBrushPreview(); setBrushTipImage(image); // Set angle here so brush tip image is generated unrotated setAngle(angle); image = createBrushPreview(); setImage(image); } KisAutoBrush::~KisAutoBrush() { - delete d->shape; - delete d; +} + +KisAutoBrush::KisAutoBrush(const KisAutoBrush& rhs) + : KisBrush(rhs), + d(new Private(*rhs.d)) +{ +} + +KisBrush* KisAutoBrush::clone() const +{ + return new KisAutoBrush(*this); } inline void fillPixelOptimized_4bytes(quint8 *color, quint8 *buf, int size) { /** * This version of filling uses low granularity of data transfers * (32-bit chunks) and internal processor's parallelism. It reaches * 25% better performance in KisStrokeBenchmark in comparison to * per-pixel memcpy version (tested on Sandy Bridge). */ int block1 = size / 8; int block2 = size % 8; quint32 *src = reinterpret_cast(color); quint32 *dst = reinterpret_cast(buf); // check whether all buffers are 4 bytes aligned // (uncomment if experience some problems) // Q_ASSERT(((qint64)src & 3) == 0); // Q_ASSERT(((qint64)dst & 3) == 0); for (int i = 0; i < block1; i++) { *dst = *src; *(dst + 1) = *src; *(dst + 2) = *src; *(dst + 3) = *src; *(dst + 4) = *src; *(dst + 5) = *src; *(dst + 6) = *src; *(dst + 7) = *src; dst += 8; } for (int i = 0; i < block2; i++) { *dst = *src; dst++; } } inline void fillPixelOptimized_general(quint8 *color, quint8 *buf, int size, int pixelSize) { /** * This version uses internal processor's parallelism and gives * 20% better performance in KisStrokeBenchmark in comparison to * per-pixel memcpy version (tested on Sandy Bridge (+20%) and * on Merom (+10%)). */ int block1 = size / 8; int block2 = size % 8; for (int i = 0; i < block1; i++) { quint8 *d1 = buf; quint8 *d2 = buf + pixelSize; quint8 *d3 = buf + 2 * pixelSize; quint8 *d4 = buf + 3 * pixelSize; quint8 *d5 = buf + 4 * pixelSize; quint8 *d6 = buf + 5 * pixelSize; quint8 *d7 = buf + 6 * pixelSize; quint8 *d8 = buf + 7 * pixelSize; for (int j = 0; j < pixelSize; j++) { *(d1 + j) = color[j]; *(d2 + j) = color[j]; *(d3 + j) = color[j]; *(d4 + j) = color[j]; *(d5 + j) = color[j]; *(d6 + j) = color[j]; *(d7 + j) = color[j]; *(d8 + j) = color[j]; } buf += 8 * pixelSize; } for (int i = 0; i < block2; i++) { memcpy(buf, color, pixelSize); buf += pixelSize; } } void KisAutoBrush::generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, KisBrush::ColoringInformation* coloringInformation, double scaleX, double scaleY, double angle, const KisPaintInformation& info, double subPixelX , double subPixelY, qreal softnessFactor) const { Q_UNUSED(info); // Generate the paint device from the mask const KoColorSpace* cs = dst->colorSpace(); quint32 pixelSize = cs->pixelSize(); // mask dimension methods already includes KisBrush::angle() int dstWidth = maskWidth(scaleX, angle, subPixelX, subPixelY, info); int dstHeight = maskHeight(scaleY, angle, subPixelX, subPixelY, info); QPointF hotSpot = this->hotSpot(scaleX, scaleY, angle, info); // mask size and hotSpot function take the KisBrush rotation into account angle += KisBrush::angle(); // if there's coloring information, we merely change the alpha: in that case, // the dab should be big enough! if (coloringInformation) { // old bounds QRect oldBounds = dst->bounds(); // new bounds. we don't care if there is some extra memory occcupied. dst->setRect(QRect(0, 0, dstWidth, dstHeight)); if (dstWidth * dstHeight <= oldBounds.width() * oldBounds.height()) { // just clear the data in dst, memset(dst->data(), OPACITY_TRANSPARENT_U8, dstWidth * dstHeight * dst->pixelSize()); } else { // enlarge the data dst->initialize(); } } else { if (dst->data() == 0 || dst->bounds().isEmpty()) { warnKrita << "Creating a default black dab: no coloring info and no initialized paint device to mask"; dst->clear(QRect(0, 0, dstWidth, dstHeight)); } Q_ASSERT(dst->bounds().width() >= dstWidth && dst->bounds().height() >= dstHeight); } quint8* dabPointer = dst->data(); quint8* color = 0; if (coloringInformation) { if (dynamic_cast(coloringInformation)) { color = const_cast(coloringInformation->color()); } } double centerX = hotSpot.x() - 0.5 + subPixelX; double centerY = hotSpot.y() - 0.5 + subPixelY; d->shape->setScale(scaleX, scaleY); d->shape->setSoftness(softnessFactor); if (coloringInformation) { if (color && pixelSize == 4) { fillPixelOptimized_4bytes(color, dabPointer, dstWidth * dstHeight); } else if (color) { fillPixelOptimized_general(color, dabPointer, dstWidth * dstHeight, pixelSize); } else { for (int y = 0; y < dstHeight; y++) { for (int x = 0; x < dstWidth; x++) { memcpy(dabPointer, coloringInformation->color(), pixelSize); coloringInformation->nextColumn(); dabPointer += pixelSize; } coloringInformation->nextRow(); } } } MaskProcessingData data(dst, cs, d->randomness, d->density, centerX, centerY, angle); KisBrushMaskApplicatorBase *applicator = d->shape->applicator(); applicator->initializeData(&data); int jobs = d->idealThreadCountCached; if (dstHeight > 100 && jobs >= 4) { int splitter = dstHeight / jobs; QVector rects; for (int i = 0; i < jobs - 1; i++) { rects << QRect(0, i * splitter, dstWidth, splitter); } rects << QRect(0, (jobs - 1)*splitter, dstWidth, dstHeight - (jobs - 1)*splitter); OperatorWrapper wrapper(applicator); QtConcurrent::blockingMap(rects, wrapper); } else { QRect rect(0, 0, dstWidth, dstHeight); applicator->process(rect); } } void KisAutoBrush::toXML(QDomDocument& doc, QDomElement& e) const { QDomElement shapeElt = doc.createElement("MaskGenerator"); d->shape->toXML(doc, shapeElt); e.appendChild(shapeElt); e.setAttribute("type", "auto_brush"); e.setAttribute("spacing", QString::number(spacing())); e.setAttribute("useAutoSpacing", QString::number(autoSpacingActive())); e.setAttribute("autoSpacingCoeff", QString::number(autoSpacingCoeff())); e.setAttribute("angle", QString::number(KisBrush::angle())); e.setAttribute("randomness", QString::number(d->randomness)); e.setAttribute("density", QString::number(d->density)); KisBrush::toXML(doc, e); } QImage KisAutoBrush::createBrushPreview() { srand(0); srand48(0); int width = maskWidth(1.0, 0.0, 0.0, 0.0, KisPaintInformation()); int height = maskHeight(1.0, 0.0, 0.0, 0.0, KisPaintInformation()); KisPaintInformation info(QPointF(width * 0.5, height * 0.5), 0.5, 0, 0, angle(), 0, 0, 0, 0); KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); fdev->setRect(QRect(0, 0, width, height)); fdev->initialize(); mask(fdev, KoColor(Qt::black, fdev->colorSpace()), 1.0, 1.0, 0.0, info); return fdev->convertToQImage(0); } const KisMaskGenerator* KisAutoBrush::maskGenerator() const { - return d->shape; + return d->shape.data(); } qreal KisAutoBrush::density() const { return d->density; } qreal KisAutoBrush::randomness() const { return d->randomness; } QPainterPath KisAutoBrush::outline() const { bool simpleOutline = (d->density < 1.0); if (simpleOutline) { QPainterPath path; QRectF brushBoundingbox(0, 0, width(), height()); if (maskGenerator()->type() == KisMaskGenerator::CIRCLE) { path.addEllipse(brushBoundingbox); } else { // if (maskGenerator()->type() == KisMaskGenerator::RECTANGLE) path.addRect(brushBoundingbox); } return path; } return KisBrush::boundary()->path(); } void KisAutoBrush::lodLimitations(KisPaintopLodLimitations *l) const { KisBrush::lodLimitations(l); if (!qFuzzyCompare(density(), 1.0)) { l->limitations << KoID("auto-brush-density", i18nc("PaintOp instant preview limitation", "Brush Density recommended value 100.0")); } if (!qFuzzyCompare(randomness(), 0.0)) { l->limitations << KoID("auto-brush-randomness", i18nc("PaintOp instant preview limitation", "Brush Randomness recommended value 0.0")); } } diff --git a/libs/brush/kis_auto_brush.h b/libs/brush/kis_auto_brush.h index b55d1e464f..0542d0a4ec 100644 --- a/libs/brush/kis_auto_brush.h +++ b/libs/brush/kis_auto_brush.h @@ -1,89 +1,94 @@ /* * Copyright (c) 2004 Cyrille Berger * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_AUTOBRUSH_RESOURCE_H_ #define _KIS_AUTOBRUSH_RESOURCE_H_ #include "kritabrush_export.h" #include "kis_brush.h" +#include + class KisMaskGenerator; /** * XXX: docs! */ class BRUSH_EXPORT KisAutoBrush : public KisBrush { public: KisAutoBrush(KisMaskGenerator* as, qreal angle, qreal randomness, qreal density = 1.0); + KisAutoBrush(const KisAutoBrush& rhs); + KisBrush* clone() const; virtual ~KisAutoBrush(); public: virtual KisFixedPaintDeviceSP paintDevice(const KoColorSpace*, double, double, const KisPaintInformation&, double = 0, double = 0) const { return 0; // The autobrush does NOT support images! } virtual void generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, KisBrush::ColoringInformation* src, double scaleX, double scaleY, double angle, const KisPaintInformation& info, double subPixelX = 0, double subPixelY = 0, qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const; virtual QPainterPath outline() const; public: bool load() { return false; } virtual bool loadFromDevice(QIODevice *) { return false; } bool save() { return false; } bool saveToDevice(QIODevice*) const { return false; } void toXML(QDomDocument& , QDomElement&) const; const KisMaskGenerator* maskGenerator() const; qreal randomness() const; qreal density() const; void lodLimitations(KisPaintopLodLimitations *l) const; private: QImage createBrushPreview(); +private: struct Private; - Private* const d; + const QScopedPointer d; }; #endif // _KIS_AUTOBRUSH_RESOURCE_H_ diff --git a/libs/brush/kis_auto_brush_factory.cpp b/libs/brush/kis_auto_brush_factory.cpp index 7d49787934..d597c46930 100644 --- a/libs/brush/kis_auto_brush_factory.cpp +++ b/libs/brush/kis_auto_brush_factory.cpp @@ -1,43 +1,45 @@ /* * Copyright (c) 2008 Boudewijn Rempt * Copyright (c) 2010-2011 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include //MSVC requires that Vc come first #include "kis_auto_brush_factory.h" #include #include "kis_auto_brush.h" #include "kis_mask_generator.h" #include -KisBrushSP KisAutoBrushFactory::getOrCreateBrush(const QDomElement& brushDefinition) +KisBrushSP KisAutoBrushFactory::getOrCreateBrush(const QDomElement& brushDefinition, bool forceCopy) { + Q_UNUSED(forceCopy); + KisMaskGenerator* mask = KisMaskGenerator::fromXML(brushDefinition.firstChildElement("MaskGenerator")); double angle = KisDomUtils::toDouble(brushDefinition.attribute("angle", "0.0")); double randomness = KisDomUtils::toDouble(brushDefinition.attribute("randomness", "0.0")); qreal density = KisDomUtils::toDouble(brushDefinition.attribute("density", "1.0")); double spacing = KisDomUtils::toDouble(brushDefinition.attribute("spacing", "1.0")); bool useAutoSpacing = KisDomUtils::toInt(brushDefinition.attribute("useAutoSpacing", "0")); qreal autoSpacingCoeff = KisDomUtils::toDouble(brushDefinition.attribute("autoSpacingCoeff", "1.0")); -\ + KisBrushSP brush = new KisAutoBrush(mask, angle, randomness, density); brush->setSpacing(spacing); brush->setAutoSpacing(useAutoSpacing, autoSpacingCoeff); return brush; } diff --git a/libs/brush/kis_auto_brush_factory.h b/libs/brush/kis_auto_brush_factory.h index c13ab3236b..c0524a67aa 100644 --- a/libs/brush/kis_auto_brush_factory.h +++ b/libs/brush/kis_auto_brush_factory.h @@ -1,58 +1,58 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_AUTO_BRUSH_FACTORY #define KIS_AUTO_BRUSH_FACTORY #include #include #include #include #include "kis_brush.h" #include "kis_brush_factory.h" #include "kis_fixed_paint_device.h" /** * A brush factory can create a new brush instance based * on a properties object that contains a serialized representation * of the object. */ class BRUSH_EXPORT KisAutoBrushFactory : public KisBrushFactory { public: KisAutoBrushFactory() {} virtual ~KisAutoBrushFactory() {} virtual QString id() const { return "auto_brush"; } /** * Create a a new brush from the given data or return an existing KisBrush * object. If this call leads to the creation of a resource, it should be * added to the resource provider, too. */ - KisBrushSP getOrCreateBrush(const QDomElement& brushDefinition); + KisBrushSP getOrCreateBrush(const QDomElement& brushDefinition, bool forceCopy); }; #endif diff --git a/libs/brush/kis_brush.cpp b/libs/brush/kis_brush.cpp index 5265e512ff..55c079b446 100644 --- a/libs/brush/kis_brush.cpp +++ b/libs/brush/kis_brush.cpp @@ -1,634 +1,639 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2003 Patrick Julien * Copyright (c) 2004-2008 Boudewijn Rempt * Copyright (c) 2004 Adrian Page * Copyright (c) 2005 Bart Coppens * Copyright (c) 2007 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_brush.h" #include #include #include #include #include #include #include #include #include #include #include "kis_datamanager.h" #include "kis_paint_device.h" #include "kis_global.h" #include "kis_boundary.h" #include "kis_image.h" #include "kis_iterator_ng.h" #include "kis_brush_registry.h" #include #include #include #include KisBrush::ColoringInformation::~ColoringInformation() { } KisBrush::PlainColoringInformation::PlainColoringInformation(const quint8* color) : m_color(color) { } KisBrush::PlainColoringInformation::~PlainColoringInformation() { } const quint8* KisBrush::PlainColoringInformation::color() const { return m_color; } void KisBrush::PlainColoringInformation::nextColumn() { } void KisBrush::PlainColoringInformation::nextRow() { } KisBrush::PaintDeviceColoringInformation::PaintDeviceColoringInformation(const KisPaintDeviceSP source, int width) : m_source(source) , m_iterator(m_source->createHLineConstIteratorNG(0, 0, width)) { } KisBrush::PaintDeviceColoringInformation::~PaintDeviceColoringInformation() { } const quint8* KisBrush::PaintDeviceColoringInformation::color() const { return m_iterator->oldRawData(); } void KisBrush::PaintDeviceColoringInformation::nextColumn() { m_iterator->nextPixel(); } void KisBrush::PaintDeviceColoringInformation::nextRow() { m_iterator->nextRow(); } struct KisBrush::Private { Private() : boundary(0) , angle(0) , scale(1.0) , hasColor(false) , brushType(INVALID) - , brushPyramid(0) , autoSpacingActive(false) , autoSpacingCoeff(1.0) {} ~Private() { delete boundary; } mutable KisBoundary* boundary; qreal angle; qreal scale; bool hasColor; enumBrushType brushType; qint32 width; qint32 height; double spacing; QPointF hotSpot; - mutable KisQImagePyramid *brushPyramid; + mutable QSharedPointer brushPyramid; QImage brushTipImage; bool autoSpacingActive; qreal autoSpacingCoeff; }; KisBrush::KisBrush() : KoResource("") , d(new Private) { } KisBrush::KisBrush(const QString& filename) : KoResource(filename) , d(new Private) { } KisBrush::KisBrush(const KisBrush& rhs) : KoResource("") , KisShared() , d(new Private) { setBrushTipImage(rhs.brushTipImage()); d->brushType = rhs.d->brushType; d->width = rhs.d->width; d->height = rhs.d->height; d->spacing = rhs.d->spacing; d->hotSpot = rhs.d->hotSpot; d->hasColor = rhs.d->hasColor; d->angle = rhs.d->angle; d->scale = rhs.d->scale; d->autoSpacingActive = rhs.d->autoSpacingActive; d->autoSpacingCoeff = rhs.d->autoSpacingCoeff; setFilename(rhs.filename()); - clearBrushPyramid(); + + /** + * Be careful! The pyramid is shared between two brush objects, + * therefore you cannot change it, only recreate! That i sthe + * reason why it is defined as const! + */ + d->brushPyramid = rhs.d->brushPyramid; + // don't copy the boundary, it will be regenerated -- see bug 291910 } KisBrush::~KisBrush() { clearBrushPyramid(); delete d; } QImage KisBrush::brushTipImage() const { if (d->brushTipImage.isNull()) { const_cast(this)->load(); } return d->brushTipImage; } qint32 KisBrush::width() const { return d->width; } void KisBrush::setWidth(qint32 width) { d->width = width; } qint32 KisBrush::height() const { return d->height; } void KisBrush::setHeight(qint32 height) { d->height = height; } void KisBrush::setHotSpot(QPointF pt) { double x = pt.x(); double y = pt.y(); if (x < 0) x = 0; else if (x >= width()) x = width() - 1; if (y < 0) y = 0; else if (y >= height()) y = height() - 1; d->hotSpot = QPointF(x, y); } QPointF KisBrush::hotSpot(double scaleX, double scaleY, double rotation, const KisPaintInformation& info) const { Q_UNUSED(info); QSizeF metric = characteristicSize(scaleX, scaleY, rotation); qreal w = metric.width(); qreal h = metric.height(); // The smallest brush we can produce is a single pixel. if (w < 1) { w = 1; } if (h < 1) { h = 1; } // XXX: This should take d->hotSpot into account, though it // isn't specified by gimp brushes so it would default to the center // anyway. QPointF p(w / 2, h / 2); return p; } bool KisBrush::hasColor() const { return d->hasColor; } void KisBrush::setHasColor(bool hasColor) { d->hasColor = hasColor; } bool KisBrush::isPiercedApprox() const { QImage image = brushTipImage(); qreal w = image.width(); qreal h = image.height(); qreal xPortion = qMin(0.1, 5.0 / w); qreal yPortion = qMin(0.1, 5.0 / h); int x0 = std::floor((0.5 - xPortion) * w); int x1 = std::ceil((0.5 + xPortion) * w); int y0 = std::floor((0.5 - yPortion) * h); int y1 = std::ceil((0.5 + yPortion) * h); const int maxNumSamples = (x1 - x0 + 1) * (y1 - y0 + 1); const int failedPixelsThreshold = 0.1 * maxNumSamples; const int thresholdValue = 0.95 * 255; int failedPixels = 0; for (int y = y0; y <= y1; y++) { for (int x = x0; x <= x1; x++) { QRgb pixel = image.pixel(x,y); if (qRed(pixel) > thresholdValue) { failedPixels++; } } } return failedPixels > failedPixelsThreshold; } bool KisBrush::canPaintFor(const KisPaintInformation& /*info*/) { return true; } void KisBrush::setBrushTipImage(const QImage& image) { //Q_ASSERT(!image.isNull()); d->brushTipImage = image; if (!image.isNull()) { if (image.width() > 128 || image.height() > 128) { KoResource::setImage(image.scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation)); } else { KoResource::setImage(image); } setWidth(image.width()); setHeight(image.height()); } clearBrushPyramid(); } void KisBrush::setBrushType(enumBrushType type) { d->brushType = type; } enumBrushType KisBrush::brushType() const { return d->brushType; } void KisBrush::predefinedBrushToXML(const QString &type, QDomElement& e) const { e.setAttribute("type", type); e.setAttribute("filename", shortFilename()); e.setAttribute("spacing", QString::number(spacing())); e.setAttribute("useAutoSpacing", QString::number(autoSpacingActive())); e.setAttribute("autoSpacingCoeff", QString::number(autoSpacingCoeff())); e.setAttribute("angle", QString::number(angle())); e.setAttribute("scale", QString::number(scale())); } void KisBrush::toXML(QDomDocument& /*document*/ , QDomElement& element) const { element.setAttribute("BrushVersion", "2"); } -KisBrushSP KisBrush::fromXML(const QDomElement& element) +KisBrushSP KisBrush::fromXML(const QDomElement& element, bool forceCopy) { - KisBrushSP brush = KisBrushRegistry::instance()->getOrCreateBrush(element); + KisBrushSP brush = KisBrushRegistry::instance()->getOrCreateBrush(element, forceCopy); if (brush && element.attribute("BrushVersion", "1") == "1") { brush->setScale(brush->scale() * 2.0); } return brush; } QSizeF KisBrush::characteristicSize(double scaleX, double scaleY, double rotation) const { Q_UNUSED(scaleY); qreal angle = normalizeAngle(rotation + d->angle); qreal scale = scaleX * d->scale; return KisQImagePyramid::characteristicSize(QSize(width(), height()), scale, angle); } qint32 KisBrush::maskWidth(double scale, double angle, qreal subPixelX, qreal subPixelY, const KisPaintInformation& info) const { Q_UNUSED(info); angle = normalizeAngle(angle + d->angle); scale *= d->scale; return KisQImagePyramid::imageSize(QSize(width(), height()), scale, angle, subPixelX, subPixelY).width(); } qint32 KisBrush::maskHeight(double scale, double angle, qreal subPixelX, qreal subPixelY, const KisPaintInformation& info) const { Q_UNUSED(info); angle = normalizeAngle(angle + d->angle); scale *= d->scale; return KisQImagePyramid::imageSize(QSize(width(), height()), scale, angle, subPixelX, subPixelY).height(); } double KisBrush::maskAngle(double angle) const { return normalizeAngle(angle + d->angle); } quint32 KisBrush::brushIndex(const KisPaintInformation& info) const { Q_UNUSED(info); return 0; } void KisBrush::setSpacing(double s) { if (s < 0.02) s = 0.02; d->spacing = s; } double KisBrush::spacing() const { return d->spacing; } void KisBrush::setAutoSpacing(bool active, qreal coeff) { d->autoSpacingCoeff = coeff; d->autoSpacingActive = active; } bool KisBrush::autoSpacingActive() const { return d->autoSpacingActive; } qreal KisBrush::autoSpacingCoeff() const { return d->autoSpacingCoeff; } void KisBrush::notifyStrokeStarted() { } void KisBrush::notifyCachedDabPainted(const KisPaintInformation& info) { Q_UNUSED(info); } void KisBrush::prepareBrushPyramid() const { if (!d->brushPyramid) { - d->brushPyramid = new KisQImagePyramid(brushTipImage()); + d->brushPyramid = toQShared(new KisQImagePyramid(brushTipImage())); } } void KisBrush::clearBrushPyramid() { - delete d->brushPyramid; - d->brushPyramid = 0; + d->brushPyramid.clear(); } void KisBrush::mask(KisFixedPaintDeviceSP dst, double scaleX, double scaleY, double angle, const KisPaintInformation& info , double subPixelX, double subPixelY, qreal softnessFactor) const { generateMaskAndApplyMaskOrCreateDab(dst, 0, scaleX, scaleY, angle, info, subPixelX, subPixelY, softnessFactor); } void KisBrush::mask(KisFixedPaintDeviceSP dst, const KoColor& color, double scaleX, double scaleY, double angle, const KisPaintInformation& info, double subPixelX, double subPixelY, qreal softnessFactor) const { PlainColoringInformation pci(color.data()); generateMaskAndApplyMaskOrCreateDab(dst, &pci, scaleX, scaleY, angle, info, subPixelX, subPixelY, softnessFactor); } void KisBrush::mask(KisFixedPaintDeviceSP dst, const KisPaintDeviceSP src, double scaleX, double scaleY, double angle, const KisPaintInformation& info, double subPixelX, double subPixelY, qreal softnessFactor) const { PaintDeviceColoringInformation pdci(src, maskWidth(scaleX, angle, subPixelX, subPixelY, info)); generateMaskAndApplyMaskOrCreateDab(dst, &pdci, scaleX, scaleY, angle, info, subPixelX, subPixelY, softnessFactor); } void KisBrush::generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, ColoringInformation* coloringInformation, double scaleX, double scaleY, double angle, const KisPaintInformation& info_, double subPixelX, double subPixelY, qreal softnessFactor) const { Q_ASSERT(valid()); Q_UNUSED(info_); Q_UNUSED(softnessFactor); angle = normalizeAngle(angle + d->angle); scaleX *= d->scale; scaleY *= d->scale; double scale = 0.5 * (scaleX + scaleY); prepareBrushPyramid(); QImage outputImage = d->brushPyramid->createImage(scale, -angle, subPixelX, subPixelY); qint32 maskWidth = outputImage.width(); qint32 maskHeight = outputImage.height(); dst->setRect(QRect(0, 0, maskWidth, maskHeight)); dst->initialize(); quint8* color = 0; if (coloringInformation) { if (dynamic_cast(coloringInformation)) { color = const_cast(coloringInformation->color()); } } const KoColorSpace *cs = dst->colorSpace(); qint32 pixelSize = cs->pixelSize(); quint8 *dabPointer = dst->data(); quint8 *rowPointer = dabPointer; quint8 *alphaArray = new quint8[maskWidth]; bool hasColor = this->hasColor(); for (int y = 0; y < maskHeight; y++) { const quint8* maskPointer = outputImage.constScanLine(y); if (coloringInformation) { for (int x = 0; x < maskWidth; x++) { if (color) { memcpy(dabPointer, color, pixelSize); } else { memcpy(dabPointer, coloringInformation->color(), pixelSize); coloringInformation->nextColumn(); } dabPointer += pixelSize; } } if (hasColor) { const quint8 *src = maskPointer; quint8 *dst = alphaArray; for (int x = 0; x < maskWidth; x++) { const QRgb *c = reinterpret_cast(src); *dst = KoColorSpaceMaths::multiply(255 - qGray(*c), qAlpha(*c)); src += 4; dst++; } } else { const quint8 *src = maskPointer; quint8 *dst = alphaArray; for (int x = 0; x < maskWidth; x++) { const QRgb *c = reinterpret_cast(src); *dst = KoColorSpaceMaths::multiply(255 - *src, qAlpha(*c)); src += 4; dst++; } } cs->applyAlphaU8Mask(rowPointer, alphaArray, maskWidth); rowPointer += maskWidth * pixelSize; dabPointer = rowPointer; if (!color && coloringInformation) { coloringInformation->nextRow(); } } delete[] alphaArray; } KisFixedPaintDeviceSP KisBrush::paintDevice(const KoColorSpace * colorSpace, double scale, double angle, const KisPaintInformation& info, double subPixelX, double subPixelY) const { Q_ASSERT(valid()); Q_UNUSED(info); angle = normalizeAngle(angle + d->angle); scale *= d->scale; prepareBrushPyramid(); QImage outputImage = d->brushPyramid->createImage(scale, -angle, subPixelX, subPixelY); KisFixedPaintDeviceSP dab = new KisFixedPaintDevice(colorSpace); Q_CHECK_PTR(dab); dab->convertFromQImage(outputImage, ""); return dab; } void KisBrush::resetBoundary() { delete d->boundary; d->boundary = 0; } void KisBrush::generateBoundary() const { KisFixedPaintDeviceSP dev; if (brushType() == IMAGE || brushType() == PIPE_IMAGE) { dev = paintDevice(KoColorSpaceRegistry::instance()->rgb8(), 1.0 / scale(), -angle(), KisPaintInformation()); } else { const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8(); dev = new KisFixedPaintDevice(cs); mask(dev, KoColor(Qt::black, cs) , 1.0 / scale(), 1.0 / scale(), -angle(), KisPaintInformation()); } d->boundary = new KisBoundary(dev); d->boundary->generateBoundary(); } const KisBoundary* KisBrush::boundary() const { if (!d->boundary) generateBoundary(); return d->boundary; } void KisBrush::setScale(qreal _scale) { d->scale = _scale; } qreal KisBrush::scale() const { return d->scale; } void KisBrush::setAngle(qreal _rotation) { d->angle = _rotation; } qreal KisBrush::angle() const { return d->angle; } QPainterPath KisBrush::outline() const { return boundary()->path(); } void KisBrush::lodLimitations(KisPaintopLodLimitations *l) const { if (spacing() > 0.5) { l->limitations << KoID("huge-spacing", i18nc("PaintOp instant preview limitation", "Spacing > 0.5, consider disabling Instant Preview")); } } diff --git a/libs/brush/kis_brush.h b/libs/brush/kis_brush.h index 3b00018ccd..b535a520ae 100644 --- a/libs/brush/kis_brush.h +++ b/libs/brush/kis_brush.h @@ -1,377 +1,379 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_BRUSH_ #define KIS_BRUSH_ #include #include #include #include #include class KisQImagemask; typedef KisSharedPtr KisQImagemaskSP; class QString; class KoColor; class KoColorSpace; class KisPaintInformation; class KisBoundary; class KisPaintopLodLimitations; enum enumBrushType { INVALID, MASK, IMAGE, PIPE_MASK, PIPE_IMAGE }; static const qreal DEFAULT_SOFTNESS_FACTOR = 1.0; class KisBrush; typedef KisSharedPtr KisBrushSP; /** * KisBrush is the base class for brush resources. A brush resource * defines one or more images that are used to potato-stamp along * the drawn path. The brush type defines how this brush is used -- * the important difference is between masks (which take the current * painting color) and images (which do not). It is up to the paintop * to make use of this feature. * * Brushes must be serializable to an xml representation and provide * a factory class that can recreate or retrieve the brush based on * this representation. * * XXX: This api is still a big mess -- it needs a good refactoring. * And the whole KoResource architecture is way over-designed. */ class BRUSH_EXPORT KisBrush : public KoResource, public KisShared { public: class ColoringInformation { public: virtual ~ColoringInformation(); virtual const quint8* color() const = 0; virtual void nextColumn() = 0; virtual void nextRow() = 0; }; protected: class PlainColoringInformation : public ColoringInformation { public: PlainColoringInformation(const quint8* color); virtual ~PlainColoringInformation(); virtual const quint8* color() const ; virtual void nextColumn(); virtual void nextRow(); private: const quint8* m_color; }; class PaintDeviceColoringInformation : public ColoringInformation { public: PaintDeviceColoringInformation(const KisPaintDeviceSP source, int width); virtual ~PaintDeviceColoringInformation(); virtual const quint8* color() const ; virtual void nextColumn(); virtual void nextRow(); private: const KisPaintDeviceSP m_source; KisHLineConstIteratorSP m_iterator; }; public: KisBrush(); KisBrush(const QString& filename); virtual ~KisBrush(); virtual bool load() { return false; } virtual bool loadFromDevice(QIODevice *) { return false; } virtual bool save() { return false; } virtual bool saveToDevice(QIODevice* ) const { return false; } /** * @brief brushImage the image the brush tip can paint with. Not all brush types have a single * image. * @return a valid QImage. */ virtual QImage brushTipImage() const; /** * Change the spacing of the brush. * @param spacing a spacing of 1.0 means that strokes will be separated from one time the size * of the brush. */ virtual void setSpacing(double spacing); /** * @return the spacing between two strokes for this brush */ double spacing() const; void setAutoSpacing(bool active, qreal coeff); bool autoSpacingActive() const; qreal autoSpacingCoeff() const; /** * @return the width (for scale == 1.0) */ qint32 width() const; /** * @return the height (for scale == 1.0) */ qint32 height() const; /** * @return the width of the mask for the given scale and angle */ virtual qint32 maskWidth(double scale, double angle, qreal subPixelX, qreal subPixelY, const KisPaintInformation& info) const; /** * @return the height of the mask for the given scale and angle */ virtual qint32 maskHeight(double scale, double angle, qreal subPixelX, qreal subPixelY, const KisPaintInformation& info) const; /** * @return the logical size of the brush, that is the size measured * in floating point value. * * This value should not be used for calculating future dab sizes * because it doesn't take any rounding into account. The only use * of this metric is calculation of brush-size derivatives like * hotspots and spacing. */ QSizeF characteristicSize(double scaleX, double scaleY, double rotation) const; /** * @return the angle of the mask adding the given angle */ double maskAngle(double angle = 0) const; /** * @return the index of the brush * if the brush consists of multiple images */ virtual quint32 brushIndex(const KisPaintInformation& info) const; /** * The brush type defines how the brush is used. */ virtual enumBrushType brushType() const; QPointF hotSpot(double scaleX, double scaleY, double rotation, const KisPaintInformation& info) const; /** * Returns true if this brush can return something useful for the info. This is used * by Pipe Brushes that can't paint sometimes **/ virtual bool canPaintFor(const KisPaintInformation& /*info*/); /** * Is called by the paint op when a paintop starts a stroke. The * point is that we store brushes a server while the paint ops are * are recreated all the time. Is means that upon a stroke start * the brushes may need to clear its state. */ virtual void notifyStrokeStarted(); /** * Is called by the cache, when cache hit has happened. * Having got this notification the brush can update the counters * of dabs, generate some new random values if needed. * * Currently, this is used by pipe'd brushes to implement * incremental and random parasites */ virtual void notifyCachedDabPainted(const KisPaintInformation& info); /** * Return a fixed paint device that contains a correctly scaled image dab. */ virtual KisFixedPaintDeviceSP paintDevice(const KoColorSpace * colorSpace, double scale, double angle, const KisPaintInformation& info, double subPixelX = 0, double subPixelY = 0) const; /** * Apply the brush mask to the pixels in dst. Dst should be big enough! */ void mask(KisFixedPaintDeviceSP dst, double scaleX, double scaleY, double angle, const KisPaintInformation& info, double subPixelX = 0, double subPixelY = 0, qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const; /** * clear dst fill it with a mask colored with KoColor */ void mask(KisFixedPaintDeviceSP dst, const KoColor& color, double scaleX, double scaleY, double angle, const KisPaintInformation& info, double subPixelX = 0, double subPixelY = 0, qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const; /** * clear dst and fill it with a mask colored with the corresponding colors of src */ void mask(KisFixedPaintDeviceSP dst, const KisPaintDeviceSP src, double scaleX, double scaleY, double angle, const KisPaintInformation& info, double subPixelX = 0, double subPixelY = 0, qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const; virtual bool hasColor() const; /** * Create a mask and either mask dst (that is, change all alpha values of the * existing pixels to those of the mask) or, if coloringInfo is present, clear * dst and fill dst with pixels according to coloringInfo, masked according to the * generated mask. * * @param dst the destination that will be draw on the image, and this function * will edit its alpha channel * @param coloringInfo coloring information that will be copied on the dab, it can be null * @param scale a scale applied on the alpha mask * @param angle a rotation applied on the alpha mask * @param info the painting information (this is only and should only be used by * KisImagePipeBrush and only to be backward compatible with the Gimp, * KisImagePipeBrush is ignoring scale and angle information) * @param subPixelX sub position of the brush (contained between 0.0 and 1.0) * @param subPixelY sub position of the brush (contained between 0.0 and 1.0) * * @return a mask computed from the grey-level values of the * pixels in the brush. */ virtual void generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, ColoringInformation* coloringInfo, double scaleX, double scaleY, double angle, const KisPaintInformation& info, double subPixelX = 0, double subPixelY = 0, qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const; /** * Serialize this brush to XML. */ virtual void toXML(QDomDocument& , QDomElement&) const; - static KisBrushSP fromXML(const QDomElement& element); + static KisBrushSP fromXML(const QDomElement& element, bool forceCopy = false); virtual const KisBoundary* boundary() const; virtual QPainterPath outline() const; virtual void setScale(qreal _scale); qreal scale() const; virtual void setAngle(qreal _angle); qreal angle() const; void prepareBrushPyramid() const; void clearBrushPyramid(); virtual void lodLimitations(KisPaintopLodLimitations *l) const; + virtual KisBrush* clone() const = 0; + //protected: KisBrush(const KisBrush& rhs); void setWidth(qint32 width); void setHeight(qint32 height); void setHotSpot(QPointF); /** * The image is used to represent the brush in the gui, and may also, depending on the brush type * be used to define the actual brush instance. */ virtual void setBrushTipImage(const QImage& image); /** * XXX */ virtual void setBrushType(enumBrushType type); friend class KisBrushTest; virtual void setHasColor(bool hasColor); /** * Returns true if the brush has a bunch of pixels almost * fully transparent in the very center. If the brush is pierced, * then dulling mode may not work correctly due to empty samples. * * WARNING: this method is relatively expensive since it iterates * up to 100 pixels of the brush. */ bool isPiercedApprox() const; protected: void resetBoundary(); void predefinedBrushToXML(const QString &type, QDomElement& e) const; private: friend class KisImagePipeBrushTest; // Initialize our boundary void generateBoundary() const; struct Private; Private* const d; }; #endif // KIS_BRUSH_ diff --git a/libs/brush/kis_brush_factory.h b/libs/brush/kis_brush_factory.h index 681d3c31dd..f6d694159a 100644 --- a/libs/brush/kis_brush_factory.h +++ b/libs/brush/kis_brush_factory.h @@ -1,54 +1,54 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_BRUSH_FACTORY #define KIS_BRUSH_FACTORY #include "kis_brush.h" class QDomElement; /** * A brush factory can create a new brush instance based * on a properties object that contains a serialized representation * of the object. */ class BRUSH_EXPORT KisBrushFactory { public: KisBrushFactory() {} virtual ~KisBrushFactory() {} virtual QString id() const = 0; virtual QString name() const { return QString(); } /** * Create a a new brush from the given data or return an existing KisBrush * object. If this call leads to the creation of a resource, it should be * added to the resource provider, too. */ - virtual KisBrushSP getOrCreateBrush(const QDomElement& element) = 0; + virtual KisBrushSP getOrCreateBrush(const QDomElement& element, bool forceCopy) = 0; }; #endif diff --git a/libs/brush/kis_brush_registry.cpp b/libs/brush/kis_brush_registry.cpp index 951fa6c101..ddb8ff4550 100644 --- a/libs/brush/kis_brush_registry.cpp +++ b/libs/brush/kis_brush_registry.cpp @@ -1,77 +1,77 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_brush_registry.h" #include #include #include #include #include #include "kis_brush_server.h" #include "kis_auto_brush_factory.h" #include "kis_text_brush_factory.h" #include "kis_predefined_brush_factory.h" Q_GLOBAL_STATIC(KisBrushRegistry, s_instance) KisBrushRegistry::KisBrushRegistry() { KisBrushServer::instance(); } KisBrushRegistry::~KisBrushRegistry() { Q_FOREACH (const QString & id, keys()) { delete get(id); } dbgRegistry << "deleting KisBrushRegistry"; } KisBrushRegistry* KisBrushRegistry::instance() { if (!s_instance.exists()) { s_instance->add(new KisAutoBrushFactory()); s_instance->add(new KisPredefinedBrushFactory("gbr_brush")); s_instance->add(new KisPredefinedBrushFactory("abr_brush")); s_instance->add(new KisTextBrushFactory()); s_instance->add(new KisPredefinedBrushFactory("png_brush")); s_instance->add(new KisPredefinedBrushFactory("svg_brush")); KoPluginLoader::instance()->load("Krita/Brush", "Type == 'Service' and ([X-Krita-Version] == 28)"); } return s_instance; } -KisBrushSP KisBrushRegistry::getOrCreateBrush(const QDomElement& element) +KisBrushSP KisBrushRegistry::getOrCreateBrush(const QDomElement& element, bool forceCopy) { QString brushType = element.attribute("type"); if (brushType.isEmpty()) return 0; KisBrushFactory* factory = get(brushType); if (!factory) return 0; - KisBrushSP brush = factory->getOrCreateBrush(element); + KisBrushSP brush = factory->getOrCreateBrush(element, forceCopy); return brush; } diff --git a/libs/brush/kis_brush_registry.h b/libs/brush/kis_brush_registry.h index a777769cab..429400ad03 100644 --- a/libs/brush/kis_brush_registry.h +++ b/libs/brush/kis_brush_registry.h @@ -1,52 +1,52 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_BRUSH_REGISTRY_H_ #define KIS_BRUSH_REGISTRY_H_ #include #include "kis_types.h" #include "KoGenericRegistry.h" #include #include "kis_brush.h" #include "kis_brush_factory.h" class QDomElement; class BRUSH_EXPORT KisBrushRegistry : public QObject, public KoGenericRegistry { Q_OBJECT public: KisBrushRegistry(); virtual ~KisBrushRegistry(); static KisBrushRegistry* instance(); - KisBrushSP getOrCreateBrush(const QDomElement& element); + KisBrushSP getOrCreateBrush(const QDomElement& element, bool forceCopy = false); private: KisBrushRegistry(const KisBrushRegistry&); KisBrushRegistry operator=(const KisBrushRegistry&); }; #endif // KIS_GENERATOR_REGISTRY_H_ diff --git a/libs/brush/kis_brushes_pipe.h b/libs/brush/kis_brushes_pipe.h index 97e20315cb..d56f3a20a8 100644 --- a/libs/brush/kis_brushes_pipe.h +++ b/libs/brush/kis_brushes_pipe.h @@ -1,172 +1,175 @@ /* * Copyright (c) 2012 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_BRUSHES_PIPE_H #define __KIS_BRUSHES_PIPE_H #include template class KisBrushesPipe { public: KisBrushesPipe() { } KisBrushesPipe(const KisBrushesPipe &rhs) { qDeleteAll(m_brushes); m_brushes.clear(); Q_FOREACH (BrushType * brush, rhs.m_brushes) { - m_brushes.append(brush->clone()); + BrushType *clonedBrush = dynamic_cast(brush->clone()); + KIS_ASSERT_RECOVER(clonedBrush) {continue;} + + m_brushes.append(clonedBrush); } } virtual ~KisBrushesPipe() { qDeleteAll(m_brushes); } virtual void clear() { qDeleteAll(m_brushes); m_brushes.clear(); } BrushType* firstBrush() const { return m_brushes.first(); } BrushType* lastBrush() const { return m_brushes.last(); } BrushType* currentBrush(const KisPaintInformation& info) { return !m_brushes.isEmpty() ? m_brushes.at(chooseNextBrush(info)) : 0; } int brushIndex(const KisPaintInformation& info) { return chooseNextBrush(info); } qint32 maskWidth(double scale, double angle, double subPixelX, double subPixelY, const KisPaintInformation& info) { BrushType *brush = currentBrush(info); return brush ? brush->maskWidth(scale, angle, subPixelX, subPixelY, info) : 0; } qint32 maskHeight(double scale, double angle, double subPixelX, double subPixelY, const KisPaintInformation& info) { BrushType *brush = currentBrush(info); return brush ? brush->maskHeight(scale, angle, subPixelX, subPixelY, info) : 0; } void setAngle(qreal angle) { Q_FOREACH (BrushType * brush, m_brushes) { brush->setAngle(angle); } } void setScale(qreal scale) { Q_FOREACH (BrushType * brush, m_brushes) { brush->setScale(scale); } } void setSpacing(double spacing) { Q_FOREACH (BrushType * brush, m_brushes) { brush->setSpacing(spacing); } } bool hasColor() const { Q_FOREACH (BrushType * brush, m_brushes) { if (brush->hasColor()) return true; } return false; } void notifyCachedDabPainted(const KisPaintInformation& info) { updateBrushIndexes(info); } void generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, KisBrush::ColoringInformation* coloringInformation, double scaleX, double scaleY, double angle, const KisPaintInformation& info, double subPixelX , double subPixelY, qreal softnessFactor) { BrushType *brush = currentBrush(info); if (!brush) return; brush->generateMaskAndApplyMaskOrCreateDab(dst, coloringInformation, scaleX, scaleY, angle, info, subPixelX, subPixelY, softnessFactor); updateBrushIndexes(info); } KisFixedPaintDeviceSP paintDevice(const KoColorSpace * colorSpace, double scale, double angle, const KisPaintInformation& info, double subPixelX, double subPixelY) { BrushType *brush = currentBrush(info); if (!brush) return 0; KisFixedPaintDeviceSP device = brush->paintDevice(colorSpace, scale, angle, info, subPixelX, subPixelY); updateBrushIndexes(info); return device; } QVector brushes() { return m_brushes; } void testingSelectNextBrush(const KisPaintInformation& info) { (void) chooseNextBrush(info); updateBrushIndexes(info); } /** * Is called by the paint op when a paintop starts a stroke. The * brushes are shared among different strokes, so sometimes the * brush should be reset. */ virtual void notifyStrokeStarted() = 0; protected: void addBrush(BrushType *brush) { m_brushes.append(brush); } /** * Returns the index of the brush that corresponds to the current * values of \p info. This method is called *before* the dab is * actually painted. * * The method is const, so no internal counters of the brush should * change during its execution */ virtual int chooseNextBrush(const KisPaintInformation& info) = 0; /** * Updates internal counters of the brush *after* a dab has been * painted on the canvas. Some incremental switching of the brushes * may me implemented in this method. */ virtual void updateBrushIndexes(const KisPaintInformation& info) = 0; protected: QVector m_brushes; }; #endif /* __KIS_BRUSHES_PIPE_H */ diff --git a/libs/brush/kis_gbr_brush.cpp b/libs/brush/kis_gbr_brush.cpp index 03333e0b12..6969837579 100644 --- a/libs/brush/kis_gbr_brush.cpp +++ b/libs/brush/kis_gbr_brush.cpp @@ -1,522 +1,522 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2003 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2004 Adrian Page * Copyright (c) 2005 Bart Coppens * Copyright (c) 2007 Cyrille Berger * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "kis_gbr_brush.h" #include #include #include #include #include #include #include #include #include "kis_datamanager.h" #include "kis_paint_device.h" #include "kis_global.h" #include "kis_image.h" struct GimpBrushV1Header { quint32 header_size; /* header_size = sizeof (BrushHeader) + brush name */ quint32 version; /* brush file version # */ quint32 width; /* width of brush */ quint32 height; /* height of brush */ quint32 bytes; /* depth of brush in bytes */ }; /// All fields are in MSB on disk! struct GimpBrushHeader { quint32 header_size; /* header_size = sizeof (BrushHeader) + brush name */ quint32 version; /* brush file version # */ quint32 width; /* width of brush */ quint32 height; /* height of brush */ quint32 bytes; /* depth of brush in bytes */ /* The following are only defined in version 2 */ quint32 magic_number; /* GIMP brush magic number */ quint32 spacing; /* brush spacing as % of width & height, 0 - 1000 */ }; // Needed, or the GIMP won't open it! quint32 const GimpV2BrushMagic = ('G' << 24) + ('I' << 16) + ('M' << 8) + ('P' << 0); struct KisGbrBrush::Private { QByteArray data; bool ownData; /* seems to indicate that @ref data is owned by the brush, but in Qt4.x this is already guaranteed... so in reality it seems more to indicate whether the data is loaded from file (ownData = true) or memory (ownData = false) */ bool useColorAsMask; quint32 header_size; /* header_size = sizeof (BrushHeader) + brush name */ quint32 version; /* brush file version # */ quint32 bytes; /* depth of brush in bytes */ quint32 magic_number; /* GIMP brush magic number */ }; #define DEFAULT_SPACING 0.25 KisGbrBrush::KisGbrBrush(const QString& filename) : KisBrush(filename) , d(new Private) { d->ownData = true; d->useColorAsMask = false; setHasColor(false); setSpacing(DEFAULT_SPACING); } KisGbrBrush::KisGbrBrush(const QString& filename, const QByteArray& data, qint32 & dataPos) : KisBrush(filename) , d(new Private) { d->ownData = false; d->useColorAsMask = false; setHasColor(false); setSpacing(DEFAULT_SPACING); d->data = QByteArray::fromRawData(data.data() + dataPos, data.size() - dataPos); init(); d->data.clear(); dataPos += d->header_size + (width() * height() * d->bytes); } KisGbrBrush::KisGbrBrush(KisPaintDeviceSP image, int x, int y, int w, int h) : KisBrush() , d(new Private) { d->ownData = true; d->useColorAsMask = false; setHasColor(false); setSpacing(DEFAULT_SPACING); initFromPaintDev(image, x, y, w, h); } KisGbrBrush::KisGbrBrush(const QImage& image, const QString& name) : KisBrush() , d(new Private) { d->ownData = false; d->useColorAsMask = false; setHasColor(false); setSpacing(DEFAULT_SPACING); setBrushTipImage(image); setName(name); } KisGbrBrush::KisGbrBrush(const KisGbrBrush& rhs) : KisBrush(rhs) , d(new Private(*rhs.d)) { setName(rhs.name()); d->data = QByteArray(); setValid(rhs.valid()); } KisGbrBrush::~KisGbrBrush() { delete d; } bool KisGbrBrush::load() { QFile file(filename()); if (file.size() == 0) return false; file.open(QIODevice::ReadOnly); bool res = loadFromDevice(&file); file.close(); return res; } bool KisGbrBrush::loadFromDevice(QIODevice *dev) { if (d->ownData) { d->data = dev->readAll(); } return init(); } bool KisGbrBrush::init() { GimpBrushHeader bh; if (sizeof(GimpBrushHeader) > (uint)d->data.size()) { return false; } memcpy(&bh, d->data, sizeof(GimpBrushHeader)); bh.header_size = qFromBigEndian(bh.header_size); d->header_size = bh.header_size; bh.version = qFromBigEndian(bh.version); d->version = bh.version; bh.width = qFromBigEndian(bh.width); bh.height = qFromBigEndian(bh.height); bh.bytes = qFromBigEndian(bh.bytes); d->bytes = bh.bytes; bh.magic_number = qFromBigEndian(bh.magic_number); d->magic_number = bh.magic_number; if (bh.version == 1) { // No spacing in version 1 files so use Gimp default bh.spacing = static_cast(DEFAULT_SPACING * 100); } else { bh.spacing = qFromBigEndian(bh.spacing); if (bh.spacing > 1000) { return false; } } setSpacing(bh.spacing / 100.0); if (bh.header_size > (uint)d->data.size() || bh.header_size == 0) { return false; } QString name; if (bh.version == 1) { // Version 1 has no magic number or spacing, so the name // is at a different offset. Character encoding is undefined. const char *text = d->data.constData() + sizeof(GimpBrushV1Header); name = QString::fromLatin1(text, bh.header_size - sizeof(GimpBrushV1Header) - 1); } else { // ### Version = 3->cinepaint; may be float16 data! // Version >=2: UTF-8 encoding is used name = QString::fromUtf8(d->data.constData() + sizeof(GimpBrushHeader), bh.header_size - sizeof(GimpBrushHeader) - 1); } setName(name); if (bh.width == 0 || bh.height == 0) { return false; } QImage::Format imageFormat; if (bh.bytes == 1) { imageFormat = QImage::Format_Indexed8; } else { imageFormat = QImage::Format_ARGB32; } QImage image(QImage(bh.width, bh.height, imageFormat)); if (image.isNull()) { return false; } qint32 k = bh.header_size; if (bh.bytes == 1) { QVector table; for (int i = 0; i < 256; ++i) table.append(qRgb(i, i, i)); image.setColorTable(table); // Grayscale if (static_cast(k + bh.width * bh.height) > d->data.size()) { return false; } setHasColor(false); for (quint32 y = 0; y < bh.height; y++) { uchar *pixel = reinterpret_cast(image.scanLine(y)); for (quint32 x = 0; x < bh.width; x++, k++) { qint32 val = 255 - static_cast(d->data[k]); *pixel = val; ++pixel; } } } else if (bh.bytes == 4) { // RGBA if (static_cast(k + (bh.width * bh.height * 4)) > d->data.size()) { return false; } setHasColor(true); for (quint32 y = 0; y < bh.height; y++) { QRgb *pixel = reinterpret_cast(image.scanLine(y)); for (quint32 x = 0; x < bh.width; x++, k += 4) { *pixel = qRgba(d->data[k], d->data[k + 1], d->data[k + 2], d->data[k + 3]); ++pixel; } } } else { warnKrita << "WARNING: loading of GBR brushes with" << bh.bytes << "bytes per pixel is not supported"; return false; } setWidth(image.width()); setHeight(image.height()); if (d->ownData) { d->data.resize(0); // Save some memory, we're using enough of it as it is. } setValid(image.width() != 0 && image.height() != 0); setBrushTipImage(image); return true; } bool KisGbrBrush::initFromPaintDev(KisPaintDeviceSP image, int x, int y, int w, int h) { // Forcefully convert to RGBA8 // XXX profile and exposure? setBrushTipImage(image->convertToQImage(0, x, y, w, h, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags())); setName(image->objectName()); setHasColor(true); return true; } bool KisGbrBrush::save() { QFile file(filename()); file.open(QIODevice::WriteOnly | QIODevice::Truncate); bool ok = saveToDevice(&file); file.close(); return ok; } bool KisGbrBrush::saveToDevice(QIODevice* dev) const { GimpBrushHeader bh; QByteArray utf8Name = name().toUtf8(); // Names in v2 brushes are in UTF-8 char const* name = utf8Name.data(); int nameLength = qstrlen(name); int wrote; bh.header_size = qToBigEndian((quint32)sizeof(GimpBrushHeader) + nameLength + 1); bh.version = qToBigEndian((quint32)2); // Only RGBA8 data needed atm, no cinepaint stuff bh.width = qToBigEndian((quint32)width()); bh.height = qToBigEndian((quint32)height()); // Hardcoded, 4 bytes RGBA or 1 byte GREY if (!hasColor()) { bh.bytes = qToBigEndian((quint32)1); } else { bh.bytes = qToBigEndian((quint32)4); } bh.magic_number = qToBigEndian((quint32)GimpV2BrushMagic); bh.spacing = qToBigEndian(static_cast(spacing() * 100.0)); // Write header: first bh, then the name QByteArray bytes = QByteArray::fromRawData(reinterpret_cast(&bh), sizeof(GimpBrushHeader)); wrote = dev->write(bytes); bytes.clear(); if (wrote == -1) { return false; } wrote = dev->write(name, nameLength + 1); if (wrote == -1) { return false; } int k = 0; QImage image = brushTipImage(); if (!hasColor()) { bytes.resize(width() * height()); for (qint32 y = 0; y < height(); y++) { for (qint32 x = 0; x < width(); x++) { QRgb c = image.pixel(x, y); bytes[k++] = static_cast(255 - qRed(c)); // red == blue == green } } } else { bytes.resize(width() * height() * 4); for (qint32 y = 0; y < height(); y++) { for (qint32 x = 0; x < width(); x++) { // order for gimp brushes, v2 is: RGBA QRgb pixel = image.pixel(x, y); bytes[k++] = static_cast(qRed(pixel)); bytes[k++] = static_cast(qGreen(pixel)); bytes[k++] = static_cast(qBlue(pixel)); bytes[k++] = static_cast(qAlpha(pixel)); } } } wrote = dev->write(bytes); if (wrote == -1) { return false; } KoResource::saveToDevice(dev); return true; } QImage KisGbrBrush::brushTipImage() const { QImage image = KisBrush::brushTipImage(); if (hasColor() && useColorAsMask()) { for (int y = 0; y < image.height(); y++) { QRgb *pixel = reinterpret_cast(image.scanLine(y)); for (int x = 0; x < image.width(); x++) { QRgb c = pixel[x]; int a = qGray(c); pixel[x] = qRgba(a, a, a, qAlpha(c)); } } } return image; } enumBrushType KisGbrBrush::brushType() const { return !hasColor() || useColorAsMask() ? MASK : IMAGE; } void KisGbrBrush::setBrushType(enumBrushType type) { Q_UNUSED(type); qFatal("FATAL: protected member setBrushType has no meaning for KisGbrBrush"); } void KisGbrBrush::setBrushTipImage(const QImage& image) { KisBrush::setBrushTipImage(image); setValid(true); } /*QImage KisGbrBrush::outline(double pressure) { KisLayerSP layer = image(KoColorSpaceRegistry::instance()->colorSpace("RGBA",0), KisPaintInformation(pressure)); KisBoundary bounds(layer.data()); int w = maskWidth(pressure); int h = maskHeight(pressure); bounds.generateBoundary(w, h); QPixmap pix(bounds.pixmap(w, h)); QImage result; result = pix; return result; }*/ void KisGbrBrush::makeMaskImage() { if (!hasColor()) { return; } QImage brushTip = brushTipImage(); if (brushTip.width() == width() && brushTip.height() == height()) { int imageWidth = width(); int imageHeight = height(); QImage image(imageWidth, imageHeight, QImage::Format_Indexed8); QVector table; for (int i = 0; i < 256; ++i) { table.append(qRgb(i, i, i)); } image.setColorTable(table); for (int y = 0; y < imageHeight; y++) { QRgb *pixel = reinterpret_cast(brushTip.scanLine(y)); uchar * dstPixel = image.scanLine(y); for (int x = 0; x < imageWidth; x++) { QRgb c = pixel[x]; float alpha = qAlpha(c) / 255.0f; // linear interpolation with maximum gray value which is transparent in the mask //int a = (qGray(c) * alpha) + ((1.0 - alpha) * 255); // single multiplication version int a = 255 + alpha * (qGray(c) - 255); dstPixel[x] = (uchar)a; } } setBrushTipImage(image); } setHasColor(false); setUseColorAsMask(false); resetBoundary(); clearBrushPyramid(); } -KisGbrBrush* KisGbrBrush::clone() const +KisBrush* KisGbrBrush::clone() const { return new KisGbrBrush(*this); } void KisGbrBrush::toXML(QDomDocument& d, QDomElement& e) const { predefinedBrushToXML("gbr_brush", e); e.setAttribute("ColorAsMask", QString::number((int)useColorAsMask())); KisBrush::toXML(d, e); } void KisGbrBrush::setUseColorAsMask(bool useColorAsMask) { /** * WARNING: There is a problem in the brush server, since it * returns not copies of brushes, but direct pointers to them. It * means that the brushes are shared among all the currently * present paintops, which might be a problem for e.g. Multihand * Brush Tool. * * Right now, all the instances of Multihand Brush Tool share the * same brush, so there is no problem in this sharing, unless we * reset the internal state of the brush on our way. */ if (useColorAsMask != d->useColorAsMask) { d->useColorAsMask = useColorAsMask; resetBoundary(); clearBrushPyramid(); } } bool KisGbrBrush::useColorAsMask() const { return d->useColorAsMask; } QString KisGbrBrush::defaultFileExtension() const { return QString(".gbr"); } diff --git a/libs/brush/kis_gbr_brush.h b/libs/brush/kis_gbr_brush.h index adb38bbda6..cc27d0088f 100644 --- a/libs/brush/kis_gbr_brush.h +++ b/libs/brush/kis_gbr_brush.h @@ -1,122 +1,122 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_GBR_BRUSH_ #define KIS_GBR_BRUSH_ #include #include #include "kis_brush.h" #include #include #include #include "kritabrush_export.h" class KisQImagemask; typedef KisSharedPtr KisQImagemaskSP; class QString; class QIODevice; class BRUSH_EXPORT KisGbrBrush : public KisBrush { protected: public: /// Construct brush to load filename later as brush KisGbrBrush(const QString& filename); /// Load brush from the specified data, at position dataPos, and set the filename KisGbrBrush(const QString& filename, const QByteArray & data, qint32 & dataPos); /// Load brush from the specified paint device, in the specified region KisGbrBrush(KisPaintDeviceSP image, int x, int y, int w, int h); /// Load brush as a copy from the specified QImage (handy when you need to copy a brush!) KisGbrBrush(const QImage& image, const QString& name = QString("")); virtual ~KisGbrBrush(); virtual bool load(); virtual bool loadFromDevice(QIODevice *dev); virtual bool save(); virtual bool saveToDevice(QIODevice* dev) const; /** * @return a preview of the brush */ virtual QImage brushTipImage() const; /** * If the brush image data are colorful (e.g. you created the brush from the canvas with custom brush) * and you want to paint with it as with masks, set to true. */ virtual void setUseColorAsMask(bool useColorAsMask); virtual bool useColorAsMask() const; /** * Convert the mask to inverted gray scale, so it is alpha mask. * It can be used as MASK brush type. This operates on the date of the brush, * so it destruct the original brush data */ virtual void makeMaskImage(); virtual enumBrushType brushType() const; /** * Makes a copy of this brush. */ - virtual KisGbrBrush* clone() const; + virtual KisBrush* clone() const; /** * @return default file extension for saving the brush */ virtual QString defaultFileExtension() const; protected: /** * save the content of this brush to an IO device */ friend class KisImageBrushesPipe; KisGbrBrush(const KisGbrBrush& rhs); void setBrushType(enumBrushType type); virtual void setBrushTipImage(const QImage& image); void toXML(QDomDocument& d, QDomElement& e) const; private: bool init(); bool initFromPaintDev(KisPaintDeviceSP image, int x, int y, int w, int h); struct Private; Private* const d; }; #endif // KIS_GBR_BRUSH_ diff --git a/libs/brush/kis_imagepipe_brush.cpp b/libs/brush/kis_imagepipe_brush.cpp index 4ea9ae597c..f85f312d65 100644 --- a/libs/brush/kis_imagepipe_brush.cpp +++ b/libs/brush/kis_imagepipe_brush.cpp @@ -1,498 +1,498 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2005 Bart Coppens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_imagepipe_brush.h" #include "kis_pipebrush_parasite.h" #include "kis_brushes_pipe.h" class KisImageBrushesPipe : public KisBrushesPipe { public: KisImageBrushesPipe() : m_isInitialized(false) { } /* pre and post are split because: 21:12:20 < dmitryK> boud: i guess it was somehow related to the fact that the maskWidth/maskHeight should correspond to the size of the mask returned by paintDevice() 21:13:33 < dmitryK> boud: the random stuff is called once per brush->paintDevice() call, after the device is returned to the paint op, that is "preparing the randomness for the next call" 21:14:16 < dmitryK> boud: and brushesPipe->currentBrush() always returning the same brush for any particular paintInfo. */ protected: static int selectPre(KisParasite::SelectionMode mode, int index, int rank, const KisPaintInformation& info) { qreal angle; switch (mode) { case KisParasite::Constant: case KisParasite::Incremental: case KisParasite::Random: break; case KisParasite::Pressure: index = static_cast(info.pressure() * (rank - 1) + 0.5); break; case KisParasite::Angular: // + m_d->PI_2 to be compatible with the gimp angle = info.drawingAngle() + M_PI_2; angle = normalizeAngle(angle); index = static_cast(angle / (2.0 * M_PI) * rank); break; case KisParasite::TiltX: index = qRound(info.xTilt() / 2.0 * rank) + rank / 2; break; case KisParasite::TiltY: index = qRound(info.yTilt() / 2.0 * rank) + rank / 2; break; default: warnImage << "Parasite" << mode << "is not implemented"; index = 0; } return index; } static int selectPost(KisParasite::SelectionMode mode, int index, int rank, const KisPaintInformation& info) { switch (mode) { case KisParasite::Constant: break; case KisParasite::Incremental: index = (index + 1) % rank; break; case KisParasite::Random: index = info.randomSource()->generate(0, rank); break; case KisParasite::Pressure: case KisParasite::Angular: break; case KisParasite::TiltX: case KisParasite::TiltY: break; default: warnImage << "Parasite" << mode << "is not implemented"; index = 0; } return index; } int chooseNextBrush(const KisPaintInformation& info) { quint32 brushIndex = 0; if (!m_isInitialized) { /** * Reset all the indexes to the initial values and do the * generation based on parameters. */ for (int i = 0; i < m_parasite.dim; i++) { m_parasite.index[i] = 0; } updateBrushIndexes(info); m_isInitialized = true; } for (int i = 0; i < m_parasite.dim; i++) { int index = selectPre(m_parasite.selection[i], m_parasite.index[i], m_parasite.rank[i], info); brushIndex += m_parasite.brushesCount[i] * index; } brushIndex %= m_brushes.size(); return brushIndex; } void updateBrushIndexes(const KisPaintInformation& info) { for (int i = 0; i < m_parasite.dim; i++) { m_parasite.index[i] = selectPost(m_parasite.selection[i], m_parasite.index[i], m_parasite.rank[i], info); } } public: using KisBrushesPipe::addBrush; void setParasite(const KisPipeBrushParasite& parasite) { m_parasite = parasite; } const KisPipeBrushParasite& parasite() const { return m_parasite; } void setUseColorAsMask(bool useColorAsMask) { Q_FOREACH (KisGbrBrush * brush, m_brushes) { brush->setUseColorAsMask(useColorAsMask); } } void makeMaskImage() { Q_FOREACH (KisGbrBrush * brush, m_brushes) { brush->makeMaskImage(); } } bool saveToDevice(QIODevice* dev) const { Q_FOREACH (KisGbrBrush * brush, m_brushes) { if (!brush->saveToDevice(dev)) { return false; } } return true; } void notifyStrokeStarted() { m_isInitialized = false; } private: KisPipeBrushParasite m_parasite; bool m_isInitialized; }; struct KisImagePipeBrush::Private { public: KisImageBrushesPipe brushesPipe; }; KisImagePipeBrush::KisImagePipeBrush(const QString& filename) : KisGbrBrush(filename) , m_d(new Private()) { } KisImagePipeBrush::KisImagePipeBrush(const QString& name, int w, int h, QVector< QVector > devices, QVector modes) : KisGbrBrush("") , m_d(new Private()) { Q_ASSERT(devices.count() == modes.count()); Q_ASSERT(devices.count() > 0); Q_ASSERT(devices.count() < 2); // XXX Multidimensionals not supported yet, change to MaxDim! setName(name); KisPipeBrushParasite parasite; parasite.dim = devices.count(); // XXX Change for multidim! : parasite.ncells = devices.at(0).count(); parasite.rank[0] = parasite.ncells; // ### This can masquerade some bugs, be careful here in the future parasite.selection[0] = modes.at(0); // XXX needsmovement! parasite.setBrushesCount(); setParasite(parasite); setDevices(devices, w, h); setBrushTipImage(m_d->brushesPipe.firstBrush()->brushTipImage()); } KisImagePipeBrush::KisImagePipeBrush(const KisImagePipeBrush& rhs) : KisGbrBrush(rhs), m_d(new Private(*rhs.m_d)) { } KisImagePipeBrush::~KisImagePipeBrush() { delete m_d; } bool KisImagePipeBrush::load() { QFile file(filename()); file.open(QIODevice::ReadOnly); bool res = loadFromDevice(&file); file.close(); return res; } bool KisImagePipeBrush::loadFromDevice(QIODevice *dev) { QByteArray data = dev->readAll(); return initFromData(data); } bool KisImagePipeBrush::initFromData(const QByteArray &data) { if (data.size() == 0) return false; // XXX: this doesn't correctly load the image pipe brushes yet. // XXX: This stuff is in utf-8, too. // The first line contains the name -- this means we look until we arrive at the first newline QByteArray line1; qint32 i = 0; while (data[i] != '\n' && i < data.size()) { line1.append(data[i]); i++; } setName(QString::fromUtf8(line1, line1.size())); i++; // Skip past the first newline // The second line contains the number of brushes, separated by a space from the parasite // XXX: This stuff is in utf-8, too. QByteArray line2; while (data[i] != '\n' && i < data.size()) { line2.append(data[i]); i++; } QString paramline = QString::fromUtf8(line2, line2.size()); qint32 numOfBrushes = paramline.left(paramline.indexOf(' ')).toUInt(); QString parasiteString = paramline.mid(paramline.indexOf(' ') + 1); KisPipeBrushParasite parasite = KisPipeBrushParasite(parasiteString); parasite.sanitize(); m_d->brushesPipe.setParasite(parasite); i++; // Skip past the second newline for (int brushIndex = 0; brushIndex < numOfBrushes && i < data.size(); brushIndex++) { KisGbrBrush* brush = new KisGbrBrush(name() + '_' + QString().setNum(brushIndex), data, i); m_d->brushesPipe.addBrush(brush); } if (numOfBrushes > 0) { setValid(true); setSpacing(m_d->brushesPipe.lastBrush()->spacing()); setWidth(m_d->brushesPipe.firstBrush()->width()); setHeight(m_d->brushesPipe.firstBrush()->height()); setBrushTipImage(m_d->brushesPipe.firstBrush()->brushTipImage()); } return true; } bool KisImagePipeBrush::save() { QFile file(filename()); file.open(QIODevice::WriteOnly | QIODevice::Truncate); bool ok = saveToDevice(&file); file.close(); return ok; } bool KisImagePipeBrush::saveToDevice(QIODevice* dev) const { QByteArray utf8Name = name().toUtf8(); // Names in v2 brushes are in UTF-8 char const* name = utf8Name.data(); int len = qstrlen(name); if (m_d->brushesPipe.parasite().dim != 1) { warnImage << "Save to file for pipe brushes with dim != not yet supported!"; return false; } // Save this pipe brush: first the header, and then all individual brushes consecutively // XXX: this needs some care for when we have > 1 dimension) // Gimp Pipe Brush header format: Name\n \n // The name\n if (dev->write(name, len) == -1) return false; if (!dev->putChar('\n')) return false; // Write the parasite (also writes number of brushes) if (!m_d->brushesPipe.parasite().saveToDevice(dev)) return false; if (!dev->putChar('\n')) return false; KoResource::saveToDevice(dev); // return m_d->brushesPipe.saveToDevice(dev); } void KisImagePipeBrush::notifyStrokeStarted() { m_d->brushesPipe.notifyStrokeStarted(); } void KisImagePipeBrush::notifyCachedDabPainted(const KisPaintInformation& info) { m_d->brushesPipe.notifyCachedDabPainted(info); } void KisImagePipeBrush::generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, KisBrush::ColoringInformation* coloringInformation, double scaleX, double scaleY, double angle, const KisPaintInformation& info, double subPixelX , double subPixelY, qreal softnessFactor) const { m_d->brushesPipe.generateMaskAndApplyMaskOrCreateDab(dst, coloringInformation, scaleX, scaleY, angle, info, subPixelX, subPixelY, softnessFactor); } QVector KisImagePipeBrush::brushes() const { return m_d->brushesPipe.brushes(); } KisFixedPaintDeviceSP KisImagePipeBrush::paintDevice(const KoColorSpace * colorSpace, double scale, double angle, const KisPaintInformation& info, double subPixelX, double subPixelY) const { return m_d->brushesPipe.paintDevice(colorSpace, scale, angle, info, subPixelX, subPixelY); } enumBrushType KisImagePipeBrush::brushType() const { return !hasColor() || useColorAsMask() ? PIPE_MASK : PIPE_IMAGE; } bool KisImagePipeBrush::hasColor() const { return m_d->brushesPipe.hasColor(); } void KisImagePipeBrush::makeMaskImage() { m_d->brushesPipe.makeMaskImage(); setUseColorAsMask(false); } void KisImagePipeBrush::setUseColorAsMask(bool useColorAsMask) { KisGbrBrush::setUseColorAsMask(useColorAsMask); m_d->brushesPipe.setUseColorAsMask(useColorAsMask); } const KisBoundary* KisImagePipeBrush::boundary() const { KisGbrBrush *brush = m_d->brushesPipe.firstBrush(); Q_ASSERT(brush); return brush->boundary(); } bool KisImagePipeBrush::canPaintFor(const KisPaintInformation& info) { return (!m_d->brushesPipe.parasite().needsMovement || info.drawingDistance() >= 0.5); } -KisImagePipeBrush* KisImagePipeBrush::clone() const +KisBrush* KisImagePipeBrush::clone() const { return new KisImagePipeBrush(*this); } QString KisImagePipeBrush::defaultFileExtension() const { return QString(".gih"); } quint32 KisImagePipeBrush::brushIndex(const KisPaintInformation& info) const { return m_d->brushesPipe.brushIndex(info); } qint32 KisImagePipeBrush::maskWidth(double scale, double angle, double subPixelX, double subPixelY, const KisPaintInformation& info) const { return m_d->brushesPipe.maskWidth(scale, angle, subPixelX, subPixelY, info); } qint32 KisImagePipeBrush::maskHeight(double scale, double angle, double subPixelX, double subPixelY, const KisPaintInformation& info) const { return m_d->brushesPipe.maskHeight(scale, angle, subPixelX, subPixelY, info); } void KisImagePipeBrush::setAngle(qreal _angle) { KisGbrBrush::setAngle(_angle); m_d->brushesPipe.setAngle(_angle); } void KisImagePipeBrush::setScale(qreal _scale) { KisGbrBrush::setScale(_scale); m_d->brushesPipe.setScale(_scale); } void KisImagePipeBrush::setSpacing(double _spacing) { KisGbrBrush::setSpacing(_spacing); m_d->brushesPipe.setSpacing(_spacing); } void KisImagePipeBrush::setBrushType(enumBrushType type) { Q_UNUSED(type); qFatal("FATAL: protected member setBrushType has no meaning for KisImagePipeBrush"); // brushType() is a function of hasColor() and useColorAsMask() } void KisImagePipeBrush::setHasColor(bool hasColor) { Q_UNUSED(hasColor); qFatal("FATAL: protected member setHasColor has no meaning for KisImagePipeBrush"); // hasColor() is a function of the underlying brushes } KisGbrBrush* KisImagePipeBrush::testingGetCurrentBrush(const KisPaintInformation& info) const { return m_d->brushesPipe.currentBrush(info); } void KisImagePipeBrush::testingSelectNextBrush(const KisPaintInformation& info) const { return m_d->brushesPipe.testingSelectNextBrush(info); } const KisPipeBrushParasite& KisImagePipeBrush::parasite() const { return m_d->brushesPipe.parasite(); } void KisImagePipeBrush::setParasite(const KisPipeBrushParasite ¶site) { m_d->brushesPipe.setParasite(parasite); } void KisImagePipeBrush::setDevices(QVector > devices, int w, int h) { for (int i = 0; i < devices.at(0).count(); i++) { m_d->brushesPipe.addBrush(new KisGbrBrush(devices.at(0).at(i), 0, 0, w, h)); } } diff --git a/libs/brush/kis_imagepipe_brush.h b/libs/brush/kis_imagepipe_brush.h index a0c56ac8b9..8b9f5d607d 100644 --- a/libs/brush/kis_imagepipe_brush.h +++ b/libs/brush/kis_imagepipe_brush.h @@ -1,142 +1,142 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2005 Bart Coppens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_IMAGEPIPE_BRUSH_ #define KIS_IMAGEPIPE_BRUSH_ #include #include #include #include #include "kis_gbr_brush.h" #include "kis_global.h" class KisPipeBrushParasite; /** * Velocity won't be supported, atm Tilt isn't either, * but have chances of implementation */ namespace KisParasite { enum SelectionMode { Constant, Incremental, Angular, Velocity, Random, Pressure, TiltX, TiltY }; } class BRUSH_EXPORT KisImagePipeBrush : public KisGbrBrush { public: KisImagePipeBrush(const QString& filename); /** * Specialized constructor that makes a new pipe brush from a sequence of samesize * devices. The fact that it's a vector of a vector, is to support multidimensional * brushes (not yet supported!) */ KisImagePipeBrush(const QString& name, int w, int h, QVector< QVector > devices, QVector modes); virtual ~KisImagePipeBrush(); virtual bool load(); virtual bool loadFromDevice(QIODevice *dev); virtual bool save(); virtual bool saveToDevice(QIODevice* dev) const; /** * @return the next image in the pipe. */ virtual KisFixedPaintDeviceSP paintDevice(const KoColorSpace * colorSpace, double scale, double angle, const KisPaintInformation& info, double subPixelX = 0, double subPixelY = 0) const; virtual void setUseColorAsMask(bool useColorAsMask); virtual bool hasColor() const; virtual enumBrushType brushType() const; virtual const KisBoundary* boundary() const; virtual bool canPaintFor(const KisPaintInformation& info); virtual void makeMaskImage(); - virtual KisImagePipeBrush* clone() const; + virtual KisBrush* clone() const; virtual QString defaultFileExtension() const; void setAngle(qreal _angle); void setScale(qreal _scale); void setSpacing(double _spacing); quint32 brushIndex(const KisPaintInformation& info) const; qint32 maskWidth(double scale, double angle, double subPixelX, double subPixelY, const KisPaintInformation& info) const; qint32 maskHeight(double scale, double angle, double subPixelX, double subPixelY, const KisPaintInformation& info) const; void notifyStrokeStarted(); void notifyCachedDabPainted(const KisPaintInformation& info); void generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, KisBrush::ColoringInformation* coloringInformation, double scaleX, double scaleY, double angle, const KisPaintInformation& info, double subPixelX = 0, double subPixelY = 0, qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const; QVector brushes() const; const KisPipeBrushParasite ¶site() const; void setParasite(const KisPipeBrushParasite& parasite); void setDevices(QVector< QVector > devices, int w, int h); protected: void setBrushType(enumBrushType type); void setHasColor(bool hasColor); /// Will call KisBrush's saveToDevice as well KisImagePipeBrush(const KisImagePipeBrush& rhs); private: friend class KisImagePipeBrushTest; KisGbrBrush* testingGetCurrentBrush(const KisPaintInformation& info) const; void testingSelectNextBrush(const KisPaintInformation& info) const; bool initFromData(const QByteArray &data); private: struct Private; Private * const m_d; }; #endif // KIS_IMAGEPIPE_BRUSH_ diff --git a/libs/brush/kis_png_brush.cpp b/libs/brush/kis_png_brush.cpp index c54568fcb6..12f9390433 100644 --- a/libs/brush/kis_png_brush.cpp +++ b/libs/brush/kis_png_brush.cpp @@ -1,130 +1,135 @@ /* * Copyright (c) 2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_png_brush.h" #include #include #include #include #include #include KisPngBrush::KisPngBrush(const QString& filename) : KisBrush(filename) { setBrushType(INVALID); setSpacing(0.25); setHasColor(false); } +KisBrush* KisPngBrush::clone() const +{ + return new KisPngBrush(*this); +} + bool KisPngBrush::load() { QFile f(filename()); if (f.size() == 0) return false; if (!f.exists()) return false; if (!f.open(QIODevice::ReadOnly)) { warnKrita << "Can't open file " << filename(); return false; } bool res = loadFromDevice(&f); f.close(); return res; } bool KisPngBrush::loadFromDevice(QIODevice *dev) { // Workaround for some OS (Debian, Ubuntu), where loading directly from the QIODevice // fails with "libpng error: IDAT: CRC error" QByteArray data = dev->readAll(); QBuffer buf(&data); buf.open(QIODevice::ReadOnly); QImageReader reader(&buf, "PNG"); if (!reader.canRead()) { setValid(false); return false; } if (reader.textKeys().contains("brush_spacing")) { setSpacing(KisDomUtils::toDouble(reader.text("brush_spacing"))); } if (reader.textKeys().contains("brush_name")) { setName(reader.text("brush_name")); } else { QFileInfo info(filename()); setName(info.baseName()); } QImage image = reader.read(); if (image.isNull()) { dbgKrita << "Could not read brush" << filename() << ". Error:" << reader.errorString(); setValid(false); return false; } setBrushTipImage(image); setValid(!brushTipImage().isNull()); if (brushTipImage().isGrayscale()) { setBrushType(MASK); setHasColor(false); } else { setBrushType(IMAGE); setHasColor(true); } setWidth(brushTipImage().width()); setHeight(brushTipImage().height()); return !brushTipImage().isNull(); } bool KisPngBrush::save() { QFile f(filename()); if (!f.open(QFile::WriteOnly)) return false; bool res = saveToDevice(&f); f.close(); return res; } bool KisPngBrush::saveToDevice(QIODevice *dev) const { if(brushTipImage().save(dev, "PNG")) { KoResource::saveToDevice(dev); return true; } return false; } QString KisPngBrush::defaultFileExtension() const { return QString(".png"); } void KisPngBrush::toXML(QDomDocument& d, QDomElement& e) const { predefinedBrushToXML("png_brush", e); KisBrush::toXML(d, e); } diff --git a/libs/brush/kis_png_brush.h b/libs/brush/kis_png_brush.h index 63ff3bd1ae..c993847946 100644 --- a/libs/brush/kis_png_brush.h +++ b/libs/brush/kis_png_brush.h @@ -1,37 +1,40 @@ /* * Copyright (c) 2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_PNG_BRUSH_ #define KIS_PNG_BRUSH_ #include "kis_brush.h" class BRUSH_EXPORT KisPngBrush : public KisBrush { public: /// Construct brush to load filename later as brush KisPngBrush(const QString& filename); + + KisBrush* clone() const; + virtual bool load(); virtual bool loadFromDevice(QIODevice *dev); virtual bool save(); virtual bool saveToDevice(QIODevice *dev) const; virtual QString defaultFileExtension() const; void toXML(QDomDocument& d, QDomElement& e) const; }; #endif diff --git a/libs/brush/kis_predefined_brush_factory.cpp b/libs/brush/kis_predefined_brush_factory.cpp index 45a0530227..4a2d32aa0a 100644 --- a/libs/brush/kis_predefined_brush_factory.cpp +++ b/libs/brush/kis_predefined_brush_factory.cpp @@ -1,78 +1,82 @@ /* * 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_predefined_brush_factory.h" #include #include "kis_brush_server.h" #include "kis_gbr_brush.h" #include KisPredefinedBrushFactory::KisPredefinedBrushFactory(const QString &brushType) : m_id(brushType) { } QString KisPredefinedBrushFactory::id() const { return m_id; } -KisBrushSP KisPredefinedBrushFactory::getOrCreateBrush(const QDomElement& brushDefinition) +KisBrushSP KisPredefinedBrushFactory::getOrCreateBrush(const QDomElement& brushDefinition, bool forceCopy) { KisBrushResourceServer *rServer = KisBrushServer::instance()->brushServer(); QString brushFileName = brushDefinition.attribute("filename", ""); KisBrushSP brush = rServer->resourceByFilename(brushFileName); //Fallback for files that still use the old format if (!brush) { QFileInfo info(brushFileName); brush = rServer->resourceByFilename(info.fileName()); } if (!brush) { brush = rServer->resources().first(); } Q_ASSERT(brush); + if (forceCopy) { + brush = brush->clone(); + } + double spacing = KisDomUtils::toDouble(brushDefinition.attribute("spacing", "0.25")); brush->setSpacing(spacing); bool useAutoSpacing = KisDomUtils::toInt(brushDefinition.attribute("useAutoSpacing", "0")); qreal autoSpacingCoeff = KisDomUtils::toDouble(brushDefinition.attribute("autoSpacingCoeff", "1.0")); brush->setAutoSpacing(useAutoSpacing, autoSpacingCoeff); double angle = KisDomUtils::toDouble(brushDefinition.attribute("angle", "0.0")); brush->setAngle(angle); double scale = KisDomUtils::toDouble(brushDefinition.attribute("scale", "1.0")); brush->setScale(scale); if (m_id == "gbr_brush") { KisGbrBrush *gbrbrush = dynamic_cast(brush.data()); if (gbrbrush) { /** * WARNING: see comment in KisGbrBrush::setUseColorAsMask() */ gbrbrush->setUseColorAsMask((bool)brushDefinition.attribute("ColorAsMask").toInt()); } } return brush; } diff --git a/libs/brush/kis_predefined_brush_factory.h b/libs/brush/kis_predefined_brush_factory.h index c65f889f8d..46bd7c32cf 100644 --- a/libs/brush/kis_predefined_brush_factory.h +++ b/libs/brush/kis_predefined_brush_factory.h @@ -1,41 +1,41 @@ /* * 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_PREDEFINED_BRUSH_FACTORY_H #define __KIS_PREDEFINED_BRUSH_FACTORY_H #include #include #include "kis_brush_factory.h" #include "kis_brush.h" class KisPredefinedBrushFactory : public KisBrushFactory { public: KisPredefinedBrushFactory(const QString &brushType); QString id() const; - KisBrushSP getOrCreateBrush(const QDomElement& brushDefinition); + KisBrushSP getOrCreateBrush(const QDomElement& brushDefinition, bool forceCopy); private: const QString m_id; }; #endif /* __KIS_PREDEFINED_BRUSH_FACTORY_H */ diff --git a/libs/brush/kis_qimage_pyramid.cpp b/libs/brush/kis_qimage_pyramid.cpp index 52fba9da70..997cfa2d2a 100644 --- a/libs/brush/kis_qimage_pyramid.cpp +++ b/libs/brush/kis_qimage_pyramid.cpp @@ -1,311 +1,311 @@ /* * 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_qimage_pyramid.h" #include #include #include #define MIPMAP_SIZE_THRESHOLD 512 #define MAX_MIPMAP_SCALE 8.0 #define QPAINTER_WORKAROUND_BORDER 1 KisQImagePyramid::KisQImagePyramid(const QImage &baseImage) { Q_ASSERT(!baseImage.isNull()); m_originalSize = baseImage.size(); qreal scale = MAX_MIPMAP_SCALE; while (scale > 1.0) { QSize scaledSize = m_originalSize * scale; if (scaledSize.width() <= MIPMAP_SIZE_THRESHOLD || scaledSize.height() <= MIPMAP_SIZE_THRESHOLD) { if (m_levels.isEmpty()) { m_baseScale = scale; } appendPyramidLevel(baseImage.scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); } scale *= 0.5; } if (m_levels.isEmpty()) { m_baseScale = 1.0; } appendPyramidLevel(baseImage); scale = 0.5; while (true) { QSize scaledSize = m_originalSize * scale; if (scaledSize.width() == 0 || scaledSize.height() == 0) break; appendPyramidLevel(baseImage.scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); scale *= 0.5; } } KisQImagePyramid::~KisQImagePyramid() { } -int KisQImagePyramid::findNearestLevel(qreal scale, qreal *baseScale) +int KisQImagePyramid::findNearestLevel(qreal scale, qreal *baseScale) const { const qreal scale_epsilon = 1e-6; qreal levelScale = m_baseScale; int level = 0; int lastLevel = m_levels.size() - 1; while ((0.5 * levelScale > scale || qAbs(0.5 * levelScale - scale) < scale_epsilon) && level < lastLevel) { levelScale *= 0.5; level++; } *baseScale = levelScale; return level; } inline QRect roundRect(const QRectF &rc) { /** * This is an analog of toAlignedRect() with the only difference * that it ensures the rect position will never be below zero. * * Warning: be *very* careful with using bottom()/right() values * of a pure QRect (we don't use it here for the dangers * it can lead to). */ QRectF rect(rc); KIS_ASSERT_RECOVER_NOOP(rect.x() > -1e-6); KIS_ASSERT_RECOVER_NOOP(rect.y() > -1e-6); if (rect.x() < 0.0) { rect.setLeft(0.0); } if (rect.y() < 0.0) { rect.setTop(0.0); } return rect.toAlignedRect(); } QTransform baseBrushTransform(qreal scaleX, qreal scaleY, qreal rotation, qreal subPixelX, qreal subPixelY, const QRectF &baseBounds) { QTransform transform; if (!qFuzzyCompare(rotation, 0)) { QTransform rotationTransform; rotationTransform.rotateRadians(rotation); QRectF rotatedBounds = rotationTransform.mapRect(baseBounds); transform = rotationTransform * QTransform::fromTranslate(-rotatedBounds.x(), -rotatedBounds.y()); } return transform * QTransform::fromScale(scaleX, scaleY) * QTransform::fromTranslate(subPixelX, subPixelY); } void KisQImagePyramid::calculateParams(qreal scale, qreal rotation, qreal subPixelX, qreal subPixelY, const QSize &originalSize, QTransform *outputTransform, QSize *outputSize) { calculateParams(scale, rotation, subPixelX, subPixelY, originalSize, 1.0, originalSize, outputTransform, outputSize); } void KisQImagePyramid::calculateParams(qreal scale, qreal rotation, qreal subPixelX, qreal subPixelY, const QSize &originalSize, qreal baseScale, const QSize &baseSize, QTransform *outputTransform, QSize *outputSize) { Q_UNUSED(baseScale); QRectF originalBounds = QRectF(QPointF(), originalSize); QTransform originalTransform = baseBrushTransform(scale, scale, rotation, subPixelX, subPixelY, originalBounds); qreal realBaseScaleX = qreal(baseSize.width()) / originalSize.width(); qreal realBaseScaleY = qreal(baseSize.height()) / originalSize.height(); qreal scaleX = scale / realBaseScaleX; qreal scaleY = scale / realBaseScaleY; QRectF baseBounds = QRectF(QPointF(), baseSize); QTransform transform = baseBrushTransform(scaleX, scaleY, rotation, subPixelX, subPixelY, baseBounds); QRect expectedDstRect = roundRect(originalTransform.mapRect(originalBounds)); #if 0 // Only enable when debugging; users shouldn't see this warning { QRect testingRect = roundRect(transform.mapRect(baseBounds)); if (testingRect != expectedDstRect) { warnKrita << "WARNING: expected and real dab rects do not coincide!"; warnKrita << " expected rect:" << expectedDstRect; warnKrita << " real rect: " << testingRect; } } #endif KIS_ASSERT_RECOVER_NOOP(expectedDstRect.x() >= 0); KIS_ASSERT_RECOVER_NOOP(expectedDstRect.y() >= 0); int width = expectedDstRect.x() + expectedDstRect.width(); int height = expectedDstRect.y() + expectedDstRect.height(); // we should not return invalid image, so adjust the image to be // at least 1 px in size. width = qMax(1, width); height = qMax(1, height); *outputTransform = transform; *outputSize = QSize(width, height); } QSize KisQImagePyramid::imageSize(const QSize &originalSize, qreal scale, qreal rotation, qreal subPixelX, qreal subPixelY) { QTransform transform; QSize dstSize; calculateParams(scale, rotation, subPixelX, subPixelY, originalSize, &transform, &dstSize); return dstSize; } QSizeF KisQImagePyramid::characteristicSize(const QSize &originalSize, qreal scale, qreal rotation) { QRectF originalRect(QPointF(), originalSize); QTransform transform = baseBrushTransform(scale, scale, rotation, 0.0, 0.0, originalRect); return transform.mapRect(originalRect).size(); } void KisQImagePyramid::appendPyramidLevel(const QImage &image) { /** * QPainter has a bug: when doing a transformation it decides that * all the pixels outside of the image (source rect) are equal to * the border pixels (CLAMP in terms of openGL). This means that * there will be no smooth scaling on the border of the image when * it is rotated. To workaround this bug we need to add one pixel * wide border to the image, so that it transforms smoothly. * * See a unittest in: KisBrushTest::testQPainterTransformationBorder */ QSize levelSize = image.size(); QImage tmp = image.convertToFormat(QImage::Format_ARGB32); tmp = tmp.copy(-QPAINTER_WORKAROUND_BORDER, -QPAINTER_WORKAROUND_BORDER, image.width() + 2 * QPAINTER_WORKAROUND_BORDER, image.height() + 2 * QPAINTER_WORKAROUND_BORDER); m_levels.append(PyramidLevel(tmp, levelSize)); } QImage KisQImagePyramid::createImage(qreal scale, qreal rotation, - qreal subPixelX, qreal subPixelY) + qreal subPixelX, qreal subPixelY) const { qreal baseScale = -1.0; int level = findNearestLevel(scale, &baseScale); const QImage &srcImage = m_levels[level].image; QTransform transform; QSize dstSize; calculateParams(scale, rotation, subPixelX, subPixelY, m_originalSize, baseScale, m_levels[level].size, &transform, &dstSize); if (transform.isIdentity() && srcImage.format() == QImage::Format_ARGB32) { return srcImage.copy(QPAINTER_WORKAROUND_BORDER, QPAINTER_WORKAROUND_BORDER, srcImage.width() - 2 * QPAINTER_WORKAROUND_BORDER, srcImage.height() - 2 * QPAINTER_WORKAROUND_BORDER); } QImage dstImage(dstSize, QImage::Format_ARGB32); dstImage.fill(0); /** * QPainter has one more bug: when a QTransform is TxTranslate, it * does wrong sampling (probably, Nearest Neighbour) even though * we tell it directly that we need SmoothPixmapTransform. * * So here is a workaround: we set a negligible scale to convince * Qt we use a non-only-translating transform. */ while (transform.type() == QTransform::TxTranslate) { const qreal scale = transform.m11(); const qreal fakeScale = scale - 10 * std::numeric_limits::epsilon(); transform *= QTransform::fromScale(fakeScale, fakeScale); } QPainter gc(&dstImage); gc.setTransform( QTransform::fromTranslate(-QPAINTER_WORKAROUND_BORDER, -QPAINTER_WORKAROUND_BORDER) * transform); gc.setRenderHints(QPainter::SmoothPixmapTransform); gc.drawImage(QPointF(), srcImage); gc.end(); return dstImage; } diff --git a/libs/brush/kis_qimage_pyramid.h b/libs/brush/kis_qimage_pyramid.h index cfb777cbaf..6f3394dfb7 100644 --- a/libs/brush/kis_qimage_pyramid.h +++ b/libs/brush/kis_qimage_pyramid.h @@ -1,74 +1,74 @@ /* * 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_QIMAGE_PYRAMID_H #define __KIS_QIMAGE_PYRAMID_H #include #include #include class BRUSH_EXPORT KisQImagePyramid { public: KisQImagePyramid(const QImage &baseImage); ~KisQImagePyramid(); static QSize imageSize(const QSize &originalSize, qreal scale, qreal rotation, qreal subPixelX, qreal subPixelY); static QSizeF characteristicSize(const QSize &originalSize, qreal scale, qreal rotation); QImage createImage(qreal scale, qreal rotation, - qreal subPixelX, qreal subPixelY); + qreal subPixelX, qreal subPixelY) const; private: friend class KisBrushTest; - int findNearestLevel(qreal scale, qreal *baseScale); + int findNearestLevel(qreal scale, qreal *baseScale) const; void appendPyramidLevel(const QImage &image); static void calculateParams(qreal scale, qreal rotation, qreal subPixelX, qreal subPixelY, const QSize &originalSize, QTransform *outputTransform, QSize *outputSize); static void calculateParams(qreal scale, qreal rotation, qreal subPixelX, qreal subPixelY, const QSize &originalSize, qreal baseScale, const QSize &baseSize, QTransform *outputTransform, QSize *outputSize); private: QSize m_originalSize; qreal m_baseScale; struct PyramidLevel { PyramidLevel() {} PyramidLevel(QImage _image, QSize _size) : image(_image), size(_size) {} QImage image; QSize size; }; QVector m_levels; }; #endif /* __KIS_QIMAGE_PYRAMID_H */ diff --git a/libs/brush/kis_svg_brush.cpp b/libs/brush/kis_svg_brush.cpp index ddaee5773a..efc58938e7 100644 --- a/libs/brush/kis_svg_brush.cpp +++ b/libs/brush/kis_svg_brush.cpp @@ -1,117 +1,128 @@ /* * Copyright (c) 2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_svg_brush.h" #include #include #include #include #include KisSvgBrush::KisSvgBrush(const QString& filename) : KisBrush(filename) { setBrushType(INVALID); setSpacing(0.25); setHasColor(false); } +KisSvgBrush::KisSvgBrush(const KisSvgBrush& rhs) + : KisBrush(rhs), + m_svg(rhs.m_svg) +{ +} + +KisBrush* KisSvgBrush::clone() const +{ + return new KisSvgBrush(*this); +} + bool KisSvgBrush::load() { QFile f(filename()); if (f.size() == 0) return false; if (!f.exists()) return false; if (!f.open(QIODevice::ReadOnly)) { warnKrita << "Can't open file " << filename(); return false; } bool res = loadFromDevice(&f); f.close(); return res; } bool KisSvgBrush::loadFromDevice(QIODevice *dev) { m_svg = dev->readAll(); QSvgRenderer renderer(m_svg); QRect box = renderer.viewBox(); if (box.isEmpty()) return false; QImage image_(1000, (1000 * box.height()) / box.width(), QImage::Format_ARGB32); { QPainter p(&image_); p.fillRect(0, 0, image_.width(), image_.height(), Qt::white); renderer.render(&p); } QVector table; for (int i = 0; i < 256; ++i) table.push_back(qRgb(i, i, i)); image_ = image_.convertToFormat(QImage::Format_Indexed8, table); setBrushTipImage(image_); setValid(true); // Well for now, always true if (brushTipImage().isGrayscale()) { setBrushType(MASK); setHasColor(false); } else { setBrushType(IMAGE); setHasColor(true); } setWidth(brushTipImage().width()); setHeight(brushTipImage().height()); return !brushTipImage().isNull(); } bool KisSvgBrush::save() { QFile f(filename()); if (!f.open(QFile::WriteOnly)) return false; bool res = saveToDevice(&f); f.close(); return res; } bool KisSvgBrush::saveToDevice(QIODevice *dev) const { if((dev->write(m_svg.constData(), m_svg.size()) == m_svg.size())) { KoResource::saveToDevice(dev); return true; } return false; } QString KisSvgBrush::defaultFileExtension() const { return QString(".svg"); } void KisSvgBrush::toXML(QDomDocument& d, QDomElement& e) const { predefinedBrushToXML("svg_brush", e); KisBrush::toXML(d, e); } diff --git a/libs/brush/kis_svg_brush.h b/libs/brush/kis_svg_brush.h index 52f976386a..3e6b038805 100644 --- a/libs/brush/kis_svg_brush.h +++ b/libs/brush/kis_svg_brush.h @@ -1,40 +1,43 @@ /* * Copyright (c) 2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_SVG_BRUSH_ #define KIS_SVG_BRUSH_ #include "kis_brush.h" class BRUSH_EXPORT KisSvgBrush : public KisBrush { public: /// Construct brush to load filename later as brush KisSvgBrush(const QString& filename); + KisSvgBrush(const KisSvgBrush& rhs); + KisBrush* clone() const; + virtual bool load(); virtual bool loadFromDevice(QIODevice *dev); virtual bool save(); virtual bool saveToDevice(QIODevice *dev) const; virtual QString defaultFileExtension() const; void toXML(QDomDocument& d, QDomElement& e) const; private: QByteArray m_svg; }; #endif diff --git a/libs/brush/kis_text_brush.cpp b/libs/brush/kis_text_brush.cpp index 40b1f98441..d28acfd8b7 100644 --- a/libs/brush/kis_text_brush.cpp +++ b/libs/brush/kis_text_brush.cpp @@ -1,305 +1,305 @@ /* * Copyright (c) 2004 Cyrille Berger * Copyright (c) 2011 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_text_brush.h" #include #include #include #include #include "kis_gbr_brush.h" #include "kis_brushes_pipe.h" - +#include #include #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND #include #include #include #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ class KisTextBrushesPipe : public KisBrushesPipe { public: KisTextBrushesPipe() { m_charIndex = 0; m_currentBrushIndex = 0; } KisTextBrushesPipe(const KisTextBrushesPipe &rhs) : KisBrushesPipe(rhs) { m_brushesMap.clear(); QMapIterator iter(rhs.m_brushesMap); while (iter.hasNext()) { iter.next(); m_brushesMap.insert(iter.key(), iter.value()); } } void setText(const QString &text, const QFont &font) { m_text = text; m_charIndex = 0; clear(); for (int i = 0; i < m_text.length(); i++) { QChar letter = m_text.at(i); QImage image = renderChar(letter, font); KisGbrBrush *brush = new KisGbrBrush(image, letter); brush->setSpacing(0.1); // support for letter spacing? brush->makeMaskImage(); m_brushesMap.insert(letter, brush); KisBrushesPipe::addBrush(brush); } } static QImage renderChar(const QString& text, const QFont &font) { #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND QWidget *focusWidget = qApp->focusWidget(); if (focusWidget) { QThread *guiThread = focusWidget->thread(); if (guiThread != QThread::currentThread()) { warnKrita << "WARNING: Rendering text in non-GUI thread!" << "That may lead to hangups and crashes on some" << "versions of X11/Qt!"; } } #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ QFontMetrics metric(font); QRect rect = metric.boundingRect(text); if (rect.isEmpty()) { rect = QRect(0, 0, 1, 1); // paint at least something } QRect paintingRect = rect.translated(-rect.x(), -rect.y()); QImage renderedChar(paintingRect.size(), QImage::Format_ARGB32); QPainter p; p.begin(&renderedChar); p.setFont(font); p.fillRect(paintingRect, Qt::white); p.setPen(Qt::black); p.drawText(-rect.x(), -rect.y(), text); p.end(); return renderedChar; } void clear() { m_brushesMap.clear(); KisBrushesPipe::clear(); } KisGbrBrush* firstBrush() const { Q_ASSERT(m_text.size() > 0); Q_ASSERT(m_brushesMap.size() > 0); return m_brushesMap.value(m_text.at(0)); } void notifyStrokeStarted() { m_charIndex = 0; updateBrushIndexesImpl(); } protected: int chooseNextBrush(const KisPaintInformation& info) { Q_UNUSED(info); return m_currentBrushIndex; } void updateBrushIndexes(const KisPaintInformation& info) { Q_UNUSED(info); m_charIndex++; updateBrushIndexesImpl(); } private: void updateBrushIndexesImpl() { if (m_text.isEmpty()) return; if (m_charIndex >= m_text.size()) { m_charIndex = 0; } QChar letter = m_text.at(m_charIndex); Q_ASSERT(m_brushesMap.contains(letter)); m_currentBrushIndex = m_brushes.indexOf(m_brushesMap.value(letter)); } private: QMap m_brushesMap; QString m_text; int m_charIndex; int m_currentBrushIndex; }; KisTextBrush::KisTextBrush() : m_brushesPipe(new KisTextBrushesPipe()) { setPipeMode(false); } KisTextBrush::KisTextBrush(const KisTextBrush &rhs) : KisBrush(rhs), m_brushesPipe(new KisTextBrushesPipe(*rhs.m_brushesPipe)) { } KisTextBrush::~KisTextBrush() { delete m_brushesPipe; } void KisTextBrush::setPipeMode(bool pipe) { setBrushType(pipe ? PIPE_MASK : MASK); } bool KisTextBrush::pipeMode() const { return brushType() == PIPE_MASK; } void KisTextBrush::setText(const QString& txt) { m_text = txt; } QString KisTextBrush::text(void) const { return m_text; } void KisTextBrush::setFont(const QFont& font) { m_font = font; } QFont KisTextBrush::font() { return m_font; } void KisTextBrush::notifyStrokeStarted() { m_brushesPipe->notifyStrokeStarted(); } void KisTextBrush::notifyCachedDabPainted(const KisPaintInformation& info) { m_brushesPipe->notifyCachedDabPainted(info); } void KisTextBrush::generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, KisBrush::ColoringInformation* coloringInformation, double scaleX, double scaleY, double angle, const KisPaintInformation& info, double subPixelX, double subPixelY, qreal softnessFactor) const { if (brushType() == MASK) { KisBrush::generateMaskAndApplyMaskOrCreateDab(dst, coloringInformation, scaleX, scaleY, angle, info, subPixelX, subPixelY, softnessFactor); } else { /* if (brushType() == PIPE_MASK)*/ m_brushesPipe->generateMaskAndApplyMaskOrCreateDab(dst, coloringInformation, scaleX, scaleY, angle, info, subPixelX, subPixelY, softnessFactor); } } KisFixedPaintDeviceSP KisTextBrush::paintDevice(const KoColorSpace * colorSpace, double scale, double angle, const KisPaintInformation& info, double subPixelX, double subPixelY) const { if (brushType() == MASK) { return KisBrush::paintDevice(colorSpace, scale, angle, info, subPixelX, subPixelY); } else { /* if (brushType() == PIPE_MASK)*/ return m_brushesPipe->paintDevice(colorSpace, scale, angle, info, subPixelX, subPixelY); } } void KisTextBrush::toXML(QDomDocument& doc, QDomElement& e) const { Q_UNUSED(doc); e.setAttribute("type", "kis_text_brush"); - e.setAttribute("spacing", spacing()); + e.setAttribute("spacing", KisDomUtils::toString(spacing())); e.setAttribute("text", m_text); e.setAttribute("font", m_font.toString()); e.setAttribute("pipe", (brushType() == PIPE_MASK) ? "true" : "false"); KisBrush::toXML(doc, e); } void KisTextBrush::updateBrush() { Q_ASSERT((brushType() == PIPE_MASK) || (brushType() == MASK)); - + if (brushType() == PIPE_MASK) { m_brushesPipe->setText(m_text, m_font); setBrushTipImage(m_brushesPipe->firstBrush()->brushTipImage()); } else { /* if (brushType() == MASK)*/ setBrushTipImage(KisTextBrushesPipe::renderChar(m_text, m_font)); } resetBoundary(); setValid(true); } quint32 KisTextBrush::brushIndex(const KisPaintInformation& info) const { return brushType() == MASK ? 0 : 1 + m_brushesPipe->brushIndex(info); } qint32 KisTextBrush::maskWidth(double scale, double angle, double subPixelX, double subPixelY, const KisPaintInformation& info) const { return brushType() == MASK ? KisBrush::maskWidth(scale, angle, subPixelX, subPixelY, info) : m_brushesPipe->maskWidth(scale, angle, subPixelX, subPixelY, info); } qint32 KisTextBrush::maskHeight(double scale, double angle, double subPixelX, double subPixelY, const KisPaintInformation& info) const { return brushType() == MASK ? KisBrush::maskHeight(scale, angle, subPixelX, subPixelY, info) : m_brushesPipe->maskHeight(scale, angle, subPixelX, subPixelY, info); } void KisTextBrush::setAngle(qreal _angle) { KisBrush::setAngle(_angle); m_brushesPipe->setAngle(_angle); } void KisTextBrush::setScale(qreal _scale) { KisBrush::setScale(_scale); m_brushesPipe->setScale(_scale); } void KisTextBrush::setSpacing(double _spacing) { KisBrush::setSpacing(_spacing); m_brushesPipe->setSpacing(_spacing); } KisBrush* KisTextBrush::clone() const { return new KisTextBrush(*this); } diff --git a/libs/brush/kis_text_brush_factory.cpp b/libs/brush/kis_text_brush_factory.cpp index 0fc1b248d5..51b4a8b79d 100644 --- a/libs/brush/kis_text_brush_factory.cpp +++ b/libs/brush/kis_text_brush_factory.cpp @@ -1,43 +1,45 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_text_brush_factory.h" #include #include #include #include "kis_text_brush.h" -KisBrushSP KisTextBrushFactory::getOrCreateBrush(const QDomElement& brushDefinition) +KisBrushSP KisTextBrushFactory::getOrCreateBrush(const QDomElement& brushDefinition, bool forceCopy) { + Q_UNUSED(forceCopy); + QString text = brushDefinition.attribute("text", "The quick brown fox ate your text"); QFont font; font.fromString(brushDefinition.attribute("font")); double spacing = KisDomUtils::toDouble(brushDefinition.attribute("spacing", "1.0")); QString pipeMode = brushDefinition.attribute("pipe", "false"); bool pipe = (pipeMode == "true") ? true : false; KisTextBrush *brush = new KisTextBrush(); brush->setText(text); brush->setFont(font); brush->setPipeMode(pipe); brush->setSpacing(spacing); brush->updateBrush(); return brush; } diff --git a/libs/brush/kis_text_brush_factory.h b/libs/brush/kis_text_brush_factory.h index 8fc7712611..6e70c8a50b 100644 --- a/libs/brush/kis_text_brush_factory.h +++ b/libs/brush/kis_text_brush_factory.h @@ -1,54 +1,54 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_TEXT_BRUSH_FACTORY #define KIS_TEXT_BRUSH_FACTORY #include #include #include "kis_brush_factory.h" #include "kis_brush.h" /** * A brush factory can create a new brush instance based * on a properties object that contains a serialized representation * of the object. */ class BRUSH_EXPORT KisTextBrushFactory : public KisBrushFactory { public: KisTextBrushFactory() {} virtual ~KisTextBrushFactory() {} virtual QString id() const { return "kis_text_brush"; } /** * Create a a new brush from the given data or return an existing KisBrush * object. If this call leads to the creation of a resource, it should be * added to the resource provider, too. */ - KisBrushSP getOrCreateBrush(const QDomElement& brushDefinition); + KisBrushSP getOrCreateBrush(const QDomElement& brushDefinition, bool forceCopy); }; #endif diff --git a/libs/brush/tests/kis_auto_brush_test.cpp b/libs/brush/tests/kis_auto_brush_test.cpp index 189b56b2a7..77ba8de5f5 100644 --- a/libs/brush/tests/kis_auto_brush_test.cpp +++ b/libs/brush/tests/kis_auto_brush_test.cpp @@ -1,176 +1,196 @@ /* * Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org * Copyright (c) 2009 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include //MSVC requires that Vc come first #include "kis_auto_brush_test.h" #include #include #include "../kis_auto_brush.h" #include "kis_mask_generator.h" #include "kis_paint_device.h" #include "kis_fill_painter.h" #include #include #include #include #include #include void KisAutoBrushTest::testCreation() { KisCircleMaskGenerator circle(10, 1.0, 1.0, 1.0, 2, true); KisRectangleMaskGenerator rect(10, 1.0, 1.0, 1.0, 2, true); } void KisAutoBrushTest::testMaskGeneration() { KisCircleMaskGenerator* circle = new KisCircleMaskGenerator(10, 1.0, 1.0, 1.0, 2, false); KisBrushSP a = new KisAutoBrush(circle, 0.0, 0.0); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintInformation info(QPointF(100.0, 100.0), 0.5); // check masking an existing paint device KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs); fdev->setRect(QRect(0, 0, 100, 100)); fdev->initialize(); cs->setOpacity(fdev->data(), OPACITY_OPAQUE_U8, 100 * 100); QPoint errpoint; QImage result(QString(FILES_DATA_DIR) + QDir::separator() + "result_autobrush_1.png"); QImage image = fdev->convertToQImage(0); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_autobrush_test_1.png"); QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } // Check creating a mask dab with a single color fdev = new KisFixedPaintDevice(cs); a->mask(fdev, KoColor(Qt::black, cs), 1.0, 1.0, 0.0, info); result = QImage(QString(FILES_DATA_DIR) + QDir::separator() + "result_autobrush_3.png"); image = fdev->convertToQImage(0); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_autobrush_test_3.png"); QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } // Check creating a mask dab with a color taken from a paint device KoColor red(Qt::red, cs); cs->setOpacity(red.data(), quint8(128), 1); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->fill(0, 0, 100, 100, red.data()); fdev = new KisFixedPaintDevice(cs); a->mask(fdev, dev, 1.0, 1.0, 0.0, info); result = QImage(QString(FILES_DATA_DIR) + QDir::separator() + "result_autobrush_4.png"); image = fdev->convertToQImage(0); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_autobrush_test_4.png"); QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisAutoBrushTest::testSizeRotation() { { KisCircleMaskGenerator* circle = new KisCircleMaskGenerator(10, 0.5, 1.0, 1.0, 2, false); KisBrushSP a = new KisAutoBrush(circle, 0.0, 0.0); QCOMPARE(a->width(), 10); QCOMPARE(a->height(), 5); QCOMPARE(a->maskWidth(1.0, 0.0, 0.0, 0.0, KisPaintInformation()), 10); QCOMPARE(a->maskHeight(1.0, 0.0, 0.0, 0.0, KisPaintInformation()), 5); QCOMPARE(a->maskWidth(2.0, 0.0, 0.0, 0.0, KisPaintInformation()), 20); QCOMPARE(a->maskHeight(2.0, 0.0, 0.0, 0.0, KisPaintInformation()), 10); QCOMPARE(a->maskWidth(0.5, 0.0, 0.0, 0.0, KisPaintInformation()), 5); QCOMPARE(a->maskHeight(0.5, 0.0, 0.0, 0.0, KisPaintInformation()), 3); QCOMPARE(a->maskWidth(1.0, M_PI, 0.0, 0.0, KisPaintInformation()), 10); QCOMPARE(a->maskHeight(1.0, M_PI, 0.0, 0.0, KisPaintInformation()), 5); QCOMPARE(a->maskWidth(1.0, M_PI_2, 0.0, 0.0, KisPaintInformation()), 6); // ceil-rule QCOMPARE(a->maskHeight(1.0, M_PI_2, 0.0, 0.0, KisPaintInformation()), 10); QCOMPARE(a->maskWidth(1.0, -M_PI_2, 0.0, 0.0, KisPaintInformation()), 6); // ceil rule QCOMPARE(a->maskHeight(1.0, -M_PI_2, 0.0, 0.0, KisPaintInformation()), 11); // ceil rule QCOMPARE(a->maskWidth(1.0, 0.25 * M_PI, 0.0, 0.0, KisPaintInformation()), 11); QCOMPARE(a->maskHeight(1.0, 0.25 * M_PI, 0.0, 0.0, KisPaintInformation()), 11); QCOMPARE(a->maskWidth(2.0, 0.25 * M_PI, 0.0, 0.0, KisPaintInformation()), 22); // ceil rule QCOMPARE(a->maskHeight(2.0, 0.25 * M_PI, 0.0, 0.0, KisPaintInformation()), 22); // ceil rule QCOMPARE(a->maskWidth(0.5, 0.25 * M_PI, 0.0, 0.0, KisPaintInformation()), 6); // ceil rule QCOMPARE(a->maskHeight(0.5, 0.25 * M_PI, 0.0, 0.0, KisPaintInformation()), 6); // ceil rule } } //#define SAVE_OUTPUT_IMAGES void KisAutoBrushTest::testCopyMasking() { int w = 64; int h = 64; int x = 0; int y = 0; QRect rc(x, y, w, h); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KoColor black(Qt::black, cs); KoColor red(Qt::red, cs); KisPaintDeviceSP tempDev = new KisPaintDevice(cs); tempDev->fill(0, 0, w, h, red.data()); #ifdef SAVE_OUTPUT_IMAGES tempDev->convertToQImage(0).save("tempDev.png"); #endif KisCircleMaskGenerator * mask = new KisCircleMaskGenerator(w, 1.0, 0.5, 0.5, 2, true); KisAutoBrush brush(mask, 0, 0); KisFixedPaintDeviceSP maskDab = new KisFixedPaintDevice(cs); brush.mask(maskDab, black, 1, 1, 0, KisPaintInformation()); maskDab->convertTo(KoColorSpaceRegistry::instance()->alpha8()); #ifdef SAVE_OUTPUT_IMAGES maskDab->convertToQImage(0, 0, 0, 64, 64).save("maskDab.png"); #endif QCOMPARE(tempDev->exactBounds(), rc); QCOMPARE(maskDab->bounds(), rc); KisFixedPaintDeviceSP dev2fixed = new KisFixedPaintDevice(cs); dev2fixed->setRect(rc); dev2fixed->initialize(); tempDev->readBytes(dev2fixed->data(), rc); dev2fixed->convertToQImage(0).save("converted-tempDev-to-fixed.png"); KisPaintDeviceSP dev = new KisPaintDevice(cs); KisPainter painter(dev); painter.setCompositeOp(COMPOSITE_COPY); painter.bltFixedWithFixedSelection(x, y, dev2fixed, maskDab, 0, 0, 0, 0, rc.width(), rc.height()); //painter.bitBltWithFixedSelection(x, y, tempDev, maskDab, 0, 0, 0, 0, rc.width(), rc.height()); #ifdef SAVE_OUTPUT_IMAGES dev->convertToQImage(0).save("final.png"); #endif } +void KisAutoBrushTest::testClone() +{ + const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); + + KisCircleMaskGenerator* circle = new KisCircleMaskGenerator(10, 0.7, 0.85, 0.5, 2, true); + KisBrushSP brush = new KisAutoBrush(circle, 0.5, 0.0); + + KisPaintInformation info(QPointF(100.0, 100.0), 0.5); + KisFixedPaintDeviceSP fdev1 = new KisFixedPaintDevice(cs); + brush->mask(fdev1, KoColor(Qt::black, cs), 0.8, 0.8, 8.0, info); + QImage res1 = fdev1->convertToQImage(0); + + KisBrushSP clone = brush->clone(); + + KisFixedPaintDeviceSP fdev2 = new KisFixedPaintDevice(cs); + clone->mask(fdev2, KoColor(Qt::black, cs), 0.8, 0.8, 8.0, info); + QImage res2 = fdev2->convertToQImage(0); + + QCOMPARE(res1, res2); +} QTEST_MAIN(KisAutoBrushTest) diff --git a/libs/brush/tests/kis_auto_brush_test.h b/libs/brush/tests/kis_auto_brush_test.h index 1c173bd716..73e30ae7d3 100644 --- a/libs/brush/tests/kis_auto_brush_test.h +++ b/libs/brush/tests/kis_auto_brush_test.h @@ -1,36 +1,38 @@ /* * Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org * * This program is free software; you can redistribute 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_AUTOBRUSH_RESOURCE_TEST_H #define KIS_AUTOBRUSH_RESOURCE_TEST_H #include class KisAutoBrushTest : public QObject { Q_OBJECT private Q_SLOTS: void testCreation(); void testMaskGeneration(); void testSizeRotation(); void testCopyMasking(); + void testClone(); + }; #endif diff --git a/libs/color/CMakeLists.txt b/libs/color/CMakeLists.txt index 851d53018b..818b2190de 100644 --- a/libs/color/CMakeLists.txt +++ b/libs/color/CMakeLists.txt @@ -1,23 +1,25 @@ if (UNIX AND NOT APPLE AND HAVE_DBUS) add_subdirectory(colord) set(kritacolor_LIB_SRCS kis_color_manager.h linux/kis_color_manager.cpp) set(kritacolor_EXTRA_LIBRARIES kritacolord) + set_target_properties(kritacolord PROPERTIES + VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) elseif (MSVC) set(kritacolor_LIB_SRCS kis_color_manager.h dummy/kis_color_manager.cpp ${CMAKE_CURRENT_BINARY_DIR}/kritacolor_export.h) else () set(kritacolor_LIB_SRCS kis_color_manager.h dummy/kis_color_manager.cpp) endif () add_library(kritacolor SHARED ${kritacolor_LIB_SRCS} ) generate_export_header(kritacolor BASE_NAME kritacolor) target_link_libraries(kritacolor kritaglobal ${QT_QTCORE_LIBRARY} KF5::I18n ${kritacolor_EXTRA_LIBRARIES}) target_link_libraries(kritacolor LINK_INTERFACE_LIBRARIES kritaglobal ${QT_QTCORE_LIBRARY}) set_target_properties(kritacolor PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritacolor ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/libs/flake/KoPatternBackground.cpp b/libs/flake/KoPatternBackground.cpp index 4f788a4b62..a2289b7a31 100644 --- a/libs/flake/KoPatternBackground.cpp +++ b/libs/flake/KoPatternBackground.cpp @@ -1,487 +1,486 @@ /* This file is part of the KDE project * Copyright (C) 2008 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 "KoPatternBackground.h" #include "KoShapeBackground_p.h" #include "KoShapeSavingContext.h" #include "KoImageData.h" #include "KoImageCollection.h" #include #include #include #include #include #include #include #include #include #include #include #include #include class KoPatternBackgroundPrivate : public KoShapeBackgroundPrivate { public: KoPatternBackgroundPrivate() : repeat(KoPatternBackground::Tiled) , refPoint(KoPatternBackground::TopLeft) , imageCollection(0) , imageData(0) { } ~KoPatternBackgroundPrivate() { delete imageData; } QSizeF targetSize() const { QSizeF size = imageData->imageSize(); if (targetImageSizePercent.width() > 0.0) size.setWidth(0.01 * targetImageSizePercent.width() * size.width()); else if (targetImageSize.width() > 0.0) size.setWidth(targetImageSize.width()); if (targetImageSizePercent.height() > 0.0) size.setHeight(0.01 * targetImageSizePercent.height() * size.height()); else if (targetImageSize.height() > 0.0) size.setHeight(targetImageSize.height()); return size; } QPointF offsetFromRect(const QRectF &fillRect, const QSizeF &imageSize) const { QPointF offset; switch (refPoint) { case KoPatternBackground::TopLeft: offset = fillRect.topLeft(); break; case KoPatternBackground::Top: offset.setX(fillRect.center().x() - 0.5 * imageSize.width()); offset.setY(fillRect.top()); break; case KoPatternBackground::TopRight: offset.setX(fillRect.right() - imageSize.width()); offset.setY(fillRect.top()); break; case KoPatternBackground::Left: offset.setX(fillRect.left()); offset.setY(fillRect.center().y() - 0.5 * imageSize.height()); break; case KoPatternBackground::Center: offset.setX(fillRect.center().x() - 0.5 * imageSize.width()); offset.setY(fillRect.center().y() - 0.5 * imageSize.height()); break; case KoPatternBackground::Right: offset.setX(fillRect.right() - imageSize.width()); offset.setY(fillRect.center().y() - 0.5 * imageSize.height()); break; case KoPatternBackground::BottomLeft: offset.setX(fillRect.left()); offset.setY(fillRect.bottom() - imageSize.height()); break; case KoPatternBackground::Bottom: offset.setX(fillRect.center().x() - 0.5 * imageSize.width()); offset.setY(fillRect.bottom() - imageSize.height()); break; case KoPatternBackground::BottomRight: offset.setX(fillRect.right() - imageSize.width()); offset.setY(fillRect.bottom() - imageSize.height()); break; default: break; } if (refPointOffsetPercent.x() > 0.0) offset += QPointF(0.01 * refPointOffsetPercent.x() * imageSize.width(), 0); if (refPointOffsetPercent.y() > 0.0) offset += QPointF(0, 0.01 * refPointOffsetPercent.y() * imageSize.height()); return offset; } QTransform matrix; KoPatternBackground::PatternRepeat repeat; KoPatternBackground::ReferencePoint refPoint; QSizeF targetImageSize; QSizeF targetImageSizePercent; QPointF refPointOffsetPercent; QPointF tileRepeatOffsetPercent; KoImageCollection * imageCollection; KoImageData * imageData; }; // ---------------------------------------------------------------- KoPatternBackground::KoPatternBackground(KoImageCollection * imageCollection) : KoShapeBackground(*(new KoPatternBackgroundPrivate())) { Q_D(KoPatternBackground); d->imageCollection = imageCollection; Q_ASSERT(d->imageCollection); } KoPatternBackground::~KoPatternBackground() { Q_D(KoPatternBackground); - delete d->imageCollection; } void KoPatternBackground::setTransform(const QTransform &matrix) { Q_D(KoPatternBackground); d->matrix = matrix; } QTransform KoPatternBackground::transform() const { Q_D(const KoPatternBackground); return d->matrix; } void KoPatternBackground::setPattern(const QImage &pattern) { Q_D(KoPatternBackground); delete d->imageData; d->imageData = d->imageCollection->createImageData(pattern); } void KoPatternBackground::setPattern(KoImageData *imageData) { Q_D(KoPatternBackground); delete d->imageData; d->imageData = imageData; } QImage KoPatternBackground::pattern() const { Q_D(const KoPatternBackground); if (d->imageData) return d->imageData->image(); return QImage(); } void KoPatternBackground::setRepeat(PatternRepeat repeat) { Q_D(KoPatternBackground); d->repeat = repeat; } KoPatternBackground::PatternRepeat KoPatternBackground::repeat() const { Q_D(const KoPatternBackground); return d->repeat; } KoPatternBackground::ReferencePoint KoPatternBackground::referencePoint() const { Q_D(const KoPatternBackground); return d->refPoint; } void KoPatternBackground::setReferencePoint(ReferencePoint referencePoint) { Q_D(KoPatternBackground); d->refPoint = referencePoint; } QPointF KoPatternBackground::referencePointOffset() const { Q_D(const KoPatternBackground); return d->refPointOffsetPercent; } void KoPatternBackground::setReferencePointOffset(const QPointF &offset) { Q_D(KoPatternBackground); qreal ox = qMax(qreal(0.0), qMin(qreal(100.0), offset.x())); qreal oy = qMax(qreal(0.0), qMin(qreal(100.0), offset.y())); d->refPointOffsetPercent = QPointF(ox, oy); } QPointF KoPatternBackground::tileRepeatOffset() const { Q_D(const KoPatternBackground); return d->tileRepeatOffsetPercent; } void KoPatternBackground::setTileRepeatOffset(const QPointF &offset) { Q_D(KoPatternBackground); d->tileRepeatOffsetPercent = offset; } QSizeF KoPatternBackground::patternDisplaySize() const { Q_D(const KoPatternBackground); return d->targetSize(); } void KoPatternBackground::setPatternDisplaySize(const QSizeF &size) { Q_D(KoPatternBackground); d->targetImageSizePercent = QSizeF(); d->targetImageSize = size; } QSizeF KoPatternBackground::patternOriginalSize() const { Q_D(const KoPatternBackground); return d->imageData->imageSize(); } void KoPatternBackground::paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &/*context*/, const QPainterPath &fillPath) const { Q_D(const KoPatternBackground); if (! d->imageData) return; painter.save(); if (d->repeat == Tiled) { // calculate scaling of pixmap QSizeF targetSize = d->targetSize(); QSizeF imageSize = d->imageData->imageSize(); qreal scaleX = targetSize.width() / imageSize.width(); qreal scaleY = targetSize.height() / imageSize.height(); QRectF targetRect = fillPath.boundingRect(); // undo scaling on target rectangle targetRect.setWidth(targetRect.width() / scaleX); targetRect.setHeight(targetRect.height() / scaleY); // determine pattern offset QPointF offset = d->offsetFromRect(targetRect, imageSize); // create matrix for pixmap scaling QTransform matrix; matrix.scale(scaleX, scaleY); painter.setClipPath(fillPath); painter.setWorldTransform(matrix, true); painter.drawTiledPixmap(targetRect, d->imageData->pixmap(imageSize.toSize()), -offset); } else if (d->repeat == Original) { QRectF sourceRect(QPointF(0, 0), d->imageData->imageSize()); QRectF targetRect(QPoint(0, 0), d->targetSize()); targetRect.moveCenter(fillPath.boundingRect().center()); painter.setClipPath(fillPath); painter.drawPixmap(targetRect, d->imageData->pixmap(sourceRect.size().toSize()), sourceRect); } else if (d->repeat == Stretched) { painter.setClipPath(fillPath); // undo conversion of the scaling so that we can use a nicely scaled image of the correct size qreal zoomX, zoomY; converter.zoom(&zoomX, &zoomY); zoomX = zoomX ? 1 / zoomX : zoomX; zoomY = zoomY ? 1 / zoomY : zoomY; painter.scale(zoomX, zoomY); QRectF targetRect = converter.documentToView(fillPath.boundingRect()); painter.drawPixmap(targetRect.topLeft(), d->imageData->pixmap(targetRect.size().toSize())); } painter.restore(); } void KoPatternBackground::fillStyle(KoGenStyle &style, KoShapeSavingContext &context) { Q_D(KoPatternBackground); if (! d->imageData) return; switch (d->repeat) { case Original: style.addProperty("style:repeat", "no-repeat"); break; case Tiled: style.addProperty("style:repeat", "repeat"); break; case Stretched: style.addProperty("style:repeat", "stretch"); break; } if (d->repeat == Tiled) { QString refPointId = "top-left"; switch (d->refPoint) { case TopLeft: refPointId = "top-left"; break; case Top: refPointId = "top"; break; case TopRight: refPointId = "top-right"; break; case Left: refPointId = "left"; break; case Center: refPointId = "center"; break; case Right: refPointId = "right"; break; case BottomLeft: refPointId = "bottom-left"; break; case Bottom: refPointId = "bottom"; break; case BottomRight: refPointId = "bottom-right"; break; } style.addProperty("draw:fill-image-ref-point", refPointId); if (d->refPointOffsetPercent.x() > 0.0) style.addProperty("draw:fill-image-ref-point-x", QString("%1%").arg(d->refPointOffsetPercent.x())); if (d->refPointOffsetPercent.y() > 0.0) style.addProperty("draw:fill-image-ref-point-y", QString("%1%").arg(d->refPointOffsetPercent.y())); } if (d->repeat != Stretched) { QSizeF targetSize = d->targetSize(); QSizeF imageSize = d->imageData->imageSize(); if (targetSize.height() != imageSize.height()) style.addPropertyPt("draw:fill-image-height", targetSize.height()); if (targetSize.width() != imageSize.width()) style.addPropertyPt("draw:fill-image-width", targetSize.width()); } KoGenStyle patternStyle(KoGenStyle::FillImageStyle /*no family name*/); patternStyle.addAttribute("xlink:show", "embed"); patternStyle.addAttribute("xlink:actuate", "onLoad"); patternStyle.addAttribute("xlink:type", "simple"); patternStyle.addAttribute("xlink:href", context.imageHref(d->imageData)); QString patternStyleName = context.mainStyles().insert(patternStyle, "picture"); style.addProperty("draw:fill", "bitmap"); style.addProperty("draw:fill-image-name", patternStyleName); context.addDataCenter(d->imageCollection); } bool KoPatternBackground::loadStyle(KoOdfLoadingContext &context, const QSizeF &) { Q_D(KoPatternBackground); KoStyleStack &styleStack = context.styleStack(); if (! styleStack.hasProperty(KoXmlNS::draw, "fill")) return false; QString fillStyle = styleStack.property(KoXmlNS::draw, "fill"); if (fillStyle != "bitmap") return false; QString styleName = styleStack.property(KoXmlNS::draw, "fill-image-name"); KoXmlElement* e = context.stylesReader().drawStyles("fill-image")[styleName]; if (! e) return false; const QString href = e->attributeNS(KoXmlNS::xlink, "href", QString()); if (href.isEmpty()) return false; delete d->imageData; d->imageData = d->imageCollection->createImageData(href, context.store()); if (! d->imageData) return false; // read the pattern repeat style QString style = styleStack.property(KoXmlNS::style, "repeat"); if (style == "stretch") d->repeat = Stretched; else if (style == "no-repeat") d->repeat = Original; else d->repeat = Tiled; if (style != "stretch") { // optional attributes which can override original image size if (styleStack.hasProperty(KoXmlNS::draw, "fill-image-height")) { QString height = styleStack.property(KoXmlNS::draw, "fill-image-height"); if (height.endsWith('%')) d->targetImageSizePercent.setHeight(height.remove('%').toDouble()); else d->targetImageSize.setHeight(KoUnit::parseValue(height)); } if (styleStack.hasProperty(KoXmlNS::draw, "fill-image-width")) { QString width = styleStack.property(KoXmlNS::draw, "fill-image-width"); if (width.endsWith('%')) d->targetImageSizePercent.setWidth(width.remove('%').toDouble()); else d->targetImageSize.setWidth(KoUnit::parseValue(width)); } } if (style == "repeat") { if (styleStack.hasProperty(KoXmlNS::draw, "fill-image-ref-point")) { // align pattern to the given size QString align = styleStack.property(KoXmlNS::draw, "fill-image-ref-point"); if (align == "top-left") d->refPoint = TopLeft; else if (align == "top") d->refPoint = Top; else if (align == "top-right") d->refPoint = TopRight; else if (align == "left") d->refPoint = Left; else if (align == "center") d->refPoint = Center; else if (align == "right") d->refPoint = Right; else if (align == "bottom-left") d->refPoint = BottomLeft; else if (align == "bottom") d->refPoint = Bottom; else if (align == "bottom-right") d->refPoint = BottomRight; } if (styleStack.hasProperty(KoXmlNS::draw, "fill-image-ref-point-x")) { QString pointX = styleStack.property(KoXmlNS::draw, "fill-image-ref-point-x"); d->refPointOffsetPercent.setX(pointX.remove('%').toDouble()); } if (styleStack.hasProperty(KoXmlNS::draw, "fill-image-ref-point-y")) { QString pointY = styleStack.property(KoXmlNS::draw, "fill-image-ref-point-y"); d->refPointOffsetPercent.setY(pointY.remove('%').toDouble()); } if (styleStack.hasProperty(KoXmlNS::draw, "tile-repeat-offset")) { QString repeatOffset = styleStack.property(KoXmlNS::draw, "tile-repeat-offset"); QStringList tokens = repeatOffset.split('%'); if (tokens.count() == 2) { QString direction = tokens[1].simplified(); if (direction == "horizontal") d->tileRepeatOffsetPercent.setX(tokens[0].toDouble()); else if (direction == "vertical") d->tileRepeatOffsetPercent.setY(tokens[0].toDouble()); } } } return true; } QRectF KoPatternBackground::patternRectFromFillSize(const QSizeF &size) { Q_D(KoPatternBackground); QRectF rect; switch (d->repeat) { case Tiled: rect.setTopLeft(d->offsetFromRect(QRectF(QPointF(), size), d->targetSize())); rect.setSize(d->targetSize()); break; case Original: rect.setLeft(0.5 * (size.width() - d->targetSize().width())); rect.setTop(0.5 * (size.height() - d->targetSize().height())); rect.setSize(d->targetSize()); break; case Stretched: rect.setTopLeft(QPointF(0.0, 0.0)); rect.setSize(size); break; } return rect; } diff --git a/libs/image/3rdparty/einspline/bspline_create.cpp b/libs/image/3rdparty/einspline/bspline_create.cpp index 833d14f806..9112b545c7 100644 --- a/libs/image/3rdparty/einspline/bspline_create.cpp +++ b/libs/image/3rdparty/einspline/bspline_create.cpp @@ -1,1873 +1,1873 @@ ///////////////////////////////////////////////////////////////////////////// // einspline: a library for creating and evaluating B-splines // // Copyright (C) 2007 Kenneth P. Esler, Jr. // // // // This program is free software; you can redistribute 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 "bspline_create.h" #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 600 #endif #ifndef __USE_XOPEN2K #define __USE_XOPEN2K #endif #include #include #include int posix_memalign(void **memptr, size_t alignment, size_t size); //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// //// Helper functions for spline creation //// //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// void init_sse_data(); void find_coefs_1d_d (Ugrid grid, BCtype_d bc, double *data, intptr_t dstride, double *coefs, intptr_t cstride); void solve_deriv_interp_1d_s (float bands[], float coefs[], int M, int cstride) { // Solve interpolating equations // First and last rows are different bands[4*(0)+1] /= bands[4*(0)+0]; bands[4*(0)+2] /= bands[4*(0)+0]; bands[4*(0)+3] /= bands[4*(0)+0]; bands[4*(0)+0] = 1.0; bands[4*(1)+1] -= bands[4*(1)+0]*bands[4*(0)+1]; bands[4*(1)+2] -= bands[4*(1)+0]*bands[4*(0)+2]; bands[4*(1)+3] -= bands[4*(1)+0]*bands[4*(0)+3]; bands[4*(0)+0] = 0.0; bands[4*(1)+2] /= bands[4*(1)+1]; bands[4*(1)+3] /= bands[4*(1)+1]; bands[4*(1)+1] = 1.0; // Now do rows 2 through M+1 for (int row=2; row < (M+1); row++) { bands[4*(row)+1] -= bands[4*(row)+0]*bands[4*(row-1)+2]; bands[4*(row)+3] -= bands[4*(row)+0]*bands[4*(row-1)+3]; bands[4*(row)+2] /= bands[4*(row)+1]; bands[4*(row)+3] /= bands[4*(row)+1]; bands[4*(row)+0] = 0.0; bands[4*(row)+1] = 1.0; } // Do last row bands[4*(M+1)+1] -= bands[4*(M+1)+0]*bands[4*(M-1)+2]; bands[4*(M+1)+3] -= bands[4*(M+1)+0]*bands[4*(M-1)+3]; bands[4*(M+1)+2] -= bands[4*(M+1)+1]*bands[4*(M)+2]; bands[4*(M+1)+3] -= bands[4*(M+1)+1]*bands[4*(M)+3]; bands[4*(M+1)+3] /= bands[4*(M+1)+2]; bands[4*(M+1)+2] = 1.0; coefs[(M+1)*cstride] = bands[4*(M+1)+3]; // Now back substitute up for (int row=M; row>0; row--) coefs[row*cstride] = bands[4*(row)+3] - bands[4*(row)+2]*coefs[cstride*(row+1)]; // Finish with first row coefs[0] = bands[4*(0)+3] - bands[4*(0)+1]*coefs[1*cstride] - bands[4*(0)+2]*coefs[2*cstride]; } // On input, bands should be filled with: // row 0 : abcdInitial from boundary conditions // rows 1:M: basis functions in first 3 cols, data in last // row M+1 : abcdFinal from boundary conditions // cstride gives the stride between values in coefs. // On exit, coefs with contain interpolating B-spline coefs void solve_periodic_interp_1d_s (float bands[], float coefs[], int M, int cstride) { std::vector lastCol(M); // Now solve: // First and last rows are different bands[4*(0)+2] /= bands[4*(0)+1]; bands[4*(0)+0] /= bands[4*(0)+1]; bands[4*(0)+3] /= bands[4*(0)+1]; bands[4*(0)+1] = 1.0; bands[4*(M-1)+1] -= bands[4*(M-1)+2]*bands[4*(0)+0]; bands[4*(M-1)+3] -= bands[4*(M-1)+2]*bands[4*(0)+3]; bands[4*(M-1)+2] = -bands[4*(M-1)+2]*bands[4*(0)+2]; lastCol[0] = bands[4*(0)+0]; for (int row=1; row < (M-1); row++) { bands[4*(row)+1] -= bands[4*(row)+0] * bands[4*(row-1)+2]; bands[4*(row)+3] -= bands[4*(row)+0] * bands[4*(row-1)+3]; lastCol[row] = -bands[4*(row)+0] * lastCol[row-1]; bands[4*(row)+0] = 0.0; bands[4*(row)+2] /= bands[4*(row)+1]; bands[4*(row)+3] /= bands[4*(row)+1]; lastCol[row] /= bands[4*(row)+1]; bands[4*(row)+1] = 1.0; if (row < (M-2)) { bands[4*(M-1)+3] -= bands[4*(M-1)+2]*bands[4*(row)+3]; bands[4*(M-1)+1] -= bands[4*(M-1)+2]*lastCol[row]; bands[4*(M-1)+2] = -bands[4*(M-1)+2]*bands[4*(row)+2]; } } // Now do last row // The [2] element and [0] element are now on top of each other bands[4*(M-1)+0] += bands[4*(M-1)+2]; bands[4*(M-1)+1] -= bands[4*(M-1)+0] * (bands[4*(M-2)+2]+lastCol[M-2]); bands[4*(M-1)+3] -= bands[4*(M-1)+0] * bands[4*(M-2)+3]; bands[4*(M-1)+3] /= bands[4*(M-1)+1]; coefs[M*cstride] = bands[4*(M-1)+3]; for (int row=M-2; row>=0; row--) coefs[(row+1)*cstride] = bands[4*(row)+3] - bands[4*(row)+2]*coefs[(row+2)*cstride] - lastCol[row]*coefs[M*cstride]; coefs[0*cstride] = coefs[M*cstride]; coefs[(M+1)*cstride] = coefs[1*cstride]; coefs[(M+2)*cstride] = coefs[2*cstride]; } // On input, bands should be filled with: // row 0 : abcdInitial from boundary conditions // rows 1:M: basis functions in first 3 cols, data in last // row M+1 : abcdFinal from boundary conditions // cstride gives the stride between values in coefs. // On exit, coefs with contain interpolating B-spline coefs void solve_antiperiodic_interp_1d_s (float bands[], float coefs[], int M, int cstride) { bands[4*0+0] *= -1.0; bands[4*(M-1)+2] *= -1.0; std::vector lastCol(M); // Now solve: // First and last rows are different bands[4*(0)+2] /= bands[4*(0)+1]; bands[4*(0)+0] /= bands[4*(0)+1]; bands[4*(0)+3] /= bands[4*(0)+1]; bands[4*(0)+1] = 1.0; bands[4*(M-1)+1] -= bands[4*(M-1)+2]*bands[4*(0)+0]; bands[4*(M-1)+3] -= bands[4*(M-1)+2]*bands[4*(0)+3]; bands[4*(M-1)+2] = -bands[4*(M-1)+2]*bands[4*(0)+2]; lastCol[0] = bands[4*(0)+0]; for (int row=1; row < (M-1); row++) { bands[4*(row)+1] -= bands[4*(row)+0] * bands[4*(row-1)+2]; bands[4*(row)+3] -= bands[4*(row)+0] * bands[4*(row-1)+3]; lastCol[row] = -bands[4*(row)+0] * lastCol[row-1]; bands[4*(row)+0] = 0.0; bands[4*(row)+2] /= bands[4*(row)+1]; bands[4*(row)+3] /= bands[4*(row)+1]; lastCol[row] /= bands[4*(row)+1]; bands[4*(row)+1] = 1.0; if (row < (M-2)) { bands[4*(M-1)+3] -= bands[4*(M-1)+2]*bands[4*(row)+3]; bands[4*(M-1)+1] -= bands[4*(M-1)+2]*lastCol[row]; bands[4*(M-1)+2] = -bands[4*(M-1)+2]*bands[4*(row)+2]; } } // Now do last row // The [2] element and [0] element are now on top of each other bands[4*(M-1)+0] += bands[4*(M-1)+2]; bands[4*(M-1)+1] -= bands[4*(M-1)+0] * (bands[4*(M-2)+2]+lastCol[M-2]); bands[4*(M-1)+3] -= bands[4*(M-1)+0] * bands[4*(M-2)+3]; bands[4*(M-1)+3] /= bands[4*(M-1)+1]; coefs[M*cstride] = bands[4*(M-1)+3]; for (int row=M-2; row>=0; row--) coefs[(row+1)*cstride] = bands[4*(row)+3] - bands[4*(row)+2]*coefs[(row+2)*cstride] - lastCol[row]*coefs[M*cstride]; coefs[0*cstride] = -coefs[M*cstride]; coefs[(M+1)*cstride] = -coefs[1*cstride]; coefs[(M+2)*cstride] = -coefs[2*cstride]; } #ifdef HIGH_PRECISION void find_coefs_1d_s (Ugrid grid, BCtype_s bc, float *data, intptr_t dstride, float *coefs, intptr_t cstride) { BCtype_d d_bc; double *d_data, *d_coefs; d_bc.lCode = bc.lCode; d_bc.rCode = bc.rCode; d_bc.lVal = bc.lVal; d_bc.rVal = bc.rVal; int M = grid.num, N; if (bc.lCode == PERIODIC || bc.lCode == ANTIPERIODIC) N = M+3; else N = M+2; d_data = new double[N]; d_coefs = new double[N]; for (int i=0; ispcode = U1D; spline->tcode = SINGLE_REAL; spline->xBC = xBC; spline->x_grid = x_grid; // Setup internal variables int M = x_grid.num; int N; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) { x_grid.delta = (x_grid.end-x_grid.start)/(double)(x_grid.num); N = M+3; } else { x_grid.delta = (x_grid.end-x_grid.start)/(double)(x_grid.num-1); N = M+2; } x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; #ifndef HAVE_SSE2 - spline->coefs = new float[N]; + spline->coefs = (float*)malloc (sizeof(float)*N); #else posix_memalign ((void**)&spline->coefs, 16, (sizeof(float)*N)); #endif find_coefs_1d_s (spline->x_grid, xBC, data, 1, spline->coefs, 1); init_sse_data(); return spline; } void recompute_UBspline_1d_s (UBspline_1d_s* spline, float *data) { find_coefs_1d_s (spline->x_grid, spline->xBC, data, 1, spline->coefs, 1); } UBspline_2d_s* create_UBspline_2d_s (Ugrid x_grid, Ugrid y_grid, BCtype_s xBC, BCtype_s yBC, float *data) { // Create new spline UBspline_2d_s* restrict spline = new UBspline_2d_s; spline->spcode = U2D; spline->tcode = SINGLE_REAL; spline->xBC = xBC; spline->yBC = yBC; // Setup internal variables int Mx = x_grid.num; int My = y_grid.num; int Nx, Ny; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; x_grid.delta = (x_grid.end - x_grid.start)/(double)(Nx-3); x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; if (yBC.lCode == PERIODIC || yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; y_grid.delta = (y_grid.end - y_grid.start)/(double)(Ny-3); y_grid.delta_inv = 1.0/y_grid.delta; spline->y_grid = y_grid; spline->x_stride = Ny; #ifndef HAVE_SSE2 - spline->coefs = new float[Nx*Ny]; + spline->coefs = (float*)malloc (sizeof(float)*Nx*Ny); #else posix_memalign ((void**)&spline->coefs, 16, sizeof(float)*Nx*Ny); #endif // First, solve in the X-direction for (int iy=0; iyx_grid, spline->xBC, data+doffset, My, spline->coefs+coffset, Ny); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, spline->yBC, spline->coefs+doffset, 1, spline->coefs+coffset, 1); } init_sse_data(); return spline; } void recompute_UBspline_2d_s (UBspline_2d_s* spline, float *data) { int Mx = spline->x_grid.num; int My = spline->y_grid.num; int Nx, Ny; if (spline->xBC.lCode == PERIODIC || spline->xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; if (spline->yBC.lCode == PERIODIC || spline->yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; // First, solve in the X-direction for (int iy=0; iyx_grid, spline->xBC, data+doffset, My, spline->coefs+coffset, Ny); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, spline->yBC, spline->coefs+doffset, 1, spline->coefs+coffset, 1); } } UBspline_3d_s* create_UBspline_3d_s (Ugrid x_grid, Ugrid y_grid, Ugrid z_grid, BCtype_s xBC, BCtype_s yBC, BCtype_s zBC, float *data) { // Create new spline UBspline_3d_s* restrict spline = new UBspline_3d_s; spline->spcode = U3D; spline->tcode = SINGLE_REAL; spline->xBC = xBC; spline->yBC = yBC; spline->zBC = zBC; // Setup internal variables int Mx = x_grid.num; int My = y_grid.num; int Mz = z_grid.num; int Nx, Ny, Nz; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; x_grid.delta = (x_grid.end - x_grid.start)/(double)(Nx-3); x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; if (yBC.lCode == PERIODIC || yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; y_grid.delta = (y_grid.end - y_grid.start)/(double)(Ny-3); y_grid.delta_inv = 1.0/y_grid.delta; spline->y_grid = y_grid; if (zBC.lCode == PERIODIC || zBC.lCode == ANTIPERIODIC) Nz = Mz+3; else Nz = Mz+2; z_grid.delta = (z_grid.end - z_grid.start)/(double)(Nz-3); z_grid.delta_inv = 1.0/z_grid.delta; spline->z_grid = z_grid; spline->x_stride = Ny*Nz; spline->y_stride = Nz; #ifndef HAVE_SSE2 spline->coefs = new float[Nx*Ny*Nz]; #else posix_memalign ((void**)&spline->coefs, 16, (sizeof(float)*Nx*Ny*Nz)); #endif // First, solve in the X-direction for (int iy=0; iyx_grid, xBC, data+doffset, My*Mz, spline->coefs+coffset, Ny*Nz); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, yBC, spline->coefs+doffset, Nz, spline->coefs+coffset, Nz); } // Now, solve in the Z-direction for (int ix=0; ixz_grid, zBC, spline->coefs+doffset, 1, spline->coefs+coffset, 1); } init_sse_data(); return spline; } void recompute_UBspline_3d_s (UBspline_3d_s* spline, float *data) { int Mx = spline->x_grid.num; int My = spline->y_grid.num; int Mz = spline->z_grid.num; int Nx, Ny, Nz; if (spline->xBC.lCode == PERIODIC || spline->xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; if (spline->yBC.lCode == PERIODIC || spline->yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; if (spline->zBC.lCode == PERIODIC || spline->zBC.lCode == ANTIPERIODIC) Nz = Mz+3; else Nz = Mz+2; // First, solve in the X-direction for (int iy=0; iyx_grid, spline->xBC, data+doffset, My*Mz, spline->coefs+coffset, Ny*Nz); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, spline->yBC, spline->coefs+doffset, Nz, spline->coefs+coffset, Nz); } // Now, solve in the Z-direction for (int ix=0; ixz_grid, spline->zBC, spline->coefs+doffset, 1, spline->coefs+coffset, 1); } } //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// //// Single-Precision, Complex Creation Routines //// //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// // On input, bands should be filled with: // row 0 : abcdInitial from boundary conditions // rows 1:M: basis functions in first 3 cols, data in last // row M+1 : abcdFinal from boundary conditions // cstride gives the stride between values in coefs. // On exit, coefs with contain interpolating B-spline coefs UBspline_1d_c* create_UBspline_1d_c (Ugrid x_grid, BCtype_c xBC, complex_float *data) { // Create new spline UBspline_1d_c* restrict spline = new UBspline_1d_c; spline->spcode = U1D; spline->tcode = SINGLE_COMPLEX; spline->xBC = xBC; // Setup internal variables int M = x_grid.num; int N; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) { x_grid.delta = (x_grid.end-x_grid.start)/(double)(x_grid.num); N = M+3; } else { x_grid.delta = (x_grid.end-x_grid.start)/(double)(x_grid.num-1); N = M+2; } x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; #ifndef HAVE_SSE2 - spline->coefs = new complex_float[N]; + spline->coefs = (complex_float*)malloc (2*sizeof(float)*N); #else posix_memalign ((void**)&spline->coefs, 16, 2*sizeof(float)*N); #endif BCtype_s xBC_r, xBC_i; xBC_r.lCode = xBC.lCode; xBC_r.rCode = xBC.rCode; xBC_r.lVal = xBC.lVal_r; xBC_r.rVal = xBC.rVal_r; xBC_i.lCode = xBC.lCode; xBC_i.rCode = xBC.rCode; xBC_i.lVal = xBC.lVal_i; xBC_i.rVal = xBC.rVal_i; // Real part find_coefs_1d_s (spline->x_grid, xBC_r, (float*)data, 2, (float*)spline->coefs, 2); // Imaginarty part find_coefs_1d_s (spline->x_grid, xBC_i, ((float*)data)+1, 2, ((float*)spline->coefs+1), 2); init_sse_data(); return spline; } void recompute_UBspline_1d_c (UBspline_1d_c* spline, complex_float *data) { BCtype_s xBC_r, xBC_i; xBC_r.lCode = spline->xBC.lCode; xBC_r.rCode = spline->xBC.rCode; xBC_r.lVal = spline->xBC.lVal_r; xBC_r.rVal = spline->xBC.rVal_r; xBC_i.lCode = spline->xBC.lCode; xBC_i.rCode = spline->xBC.rCode; xBC_i.lVal = spline->xBC.lVal_i; xBC_i.rVal = spline->xBC.rVal_i; // Real part find_coefs_1d_s (spline->x_grid, xBC_r, (float*)data, 2, (float*)spline->coefs, 2); // Imaginarty part find_coefs_1d_s (spline->x_grid, xBC_i, ((float*)data)+1, 2, ((float*)spline->coefs+1), 2); } UBspline_2d_c* create_UBspline_2d_c (Ugrid x_grid, Ugrid y_grid, BCtype_c xBC, BCtype_c yBC, complex_float *data) { // Create new spline UBspline_2d_c* restrict spline = new UBspline_2d_c; spline->spcode = U2D; spline->tcode = SINGLE_COMPLEX; spline->xBC = xBC; spline->yBC = yBC; // Setup internal variables int Mx = x_grid.num; int My = y_grid.num; int Nx, Ny; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; x_grid.delta = (x_grid.end - x_grid.start)/(double)(Nx-3); x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; if (yBC.lCode == PERIODIC || yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; y_grid.delta = (y_grid.end - y_grid.start)/(double)(Ny-3); y_grid.delta_inv = 1.0/y_grid.delta; spline->y_grid = y_grid; spline->x_stride = Ny; #ifndef HAVE_SSE2 - spline->coefs = new complex_float[Nx*Ny]; + spline->coefs = (complex_float*)malloc (2*sizeof(float)*Nx*Ny); #else posix_memalign ((void**)&spline->coefs, 16, 2*sizeof(float)*Nx*Ny); #endif BCtype_s xBC_r, xBC_i, yBC_r, yBC_i; xBC_r.lCode = xBC.lCode; xBC_r.rCode = xBC.rCode; xBC_r.lVal = xBC.lVal_r; xBC_r.rVal = xBC.rVal_r; xBC_i.lCode = xBC.lCode; xBC_i.rCode = xBC.rCode; xBC_i.lVal = xBC.lVal_i; xBC_i.rVal = xBC.rVal_i; yBC_r.lCode = yBC.lCode; yBC_r.rCode = yBC.rCode; yBC_r.lVal = yBC.lVal_r; yBC_r.rVal = yBC.rVal_r; yBC_i.lCode = yBC.lCode; yBC_i.rCode = yBC.rCode; yBC_i.lVal = yBC.lVal_i; yBC_i.rVal = yBC.rVal_i; // First, solve in the X-direction for (int iy=0; iyx_grid, xBC_r, ((float*)data)+doffset, 2*My, (float*)spline->coefs+coffset, 2*Ny); // Imag part find_coefs_1d_s (spline->x_grid, xBC_i, ((float*)data)+doffset+1, 2*My, ((float*)spline->coefs)+coffset+1, 2*Ny); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, yBC_r, ((float*)spline->coefs)+doffset, 2, ((float*)spline->coefs)+coffset, 2); // Imag part find_coefs_1d_s (spline->y_grid, yBC_i, ((float*)spline->coefs)+doffset+1, 2, ((float*)spline->coefs)+coffset+1, 2); } init_sse_data(); return spline; } void recompute_UBspline_2d_c (UBspline_2d_c* spline, complex_float *data) { // Setup internal variables int Mx = spline->x_grid.num; int My = spline->y_grid.num; int Nx, Ny; if (spline->xBC.lCode == PERIODIC || spline->xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; if (spline->yBC.lCode == PERIODIC || spline->yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; BCtype_s xBC_r, xBC_i, yBC_r, yBC_i; xBC_r.lCode = spline->xBC.lCode; xBC_r.rCode = spline->xBC.rCode; xBC_r.lVal = spline->xBC.lVal_r; xBC_r.rVal = spline->xBC.rVal_r; xBC_i.lCode = spline->xBC.lCode; xBC_i.rCode = spline->xBC.rCode; xBC_i.lVal = spline->xBC.lVal_i; xBC_i.rVal = spline->xBC.rVal_i; yBC_r.lCode = spline->yBC.lCode; yBC_r.rCode = spline->yBC.rCode; yBC_r.lVal = spline->yBC.lVal_r; yBC_r.rVal = spline->yBC.rVal_r; yBC_i.lCode = spline->yBC.lCode; yBC_i.rCode = spline->yBC.rCode; yBC_i.lVal = spline->yBC.lVal_i; yBC_i.rVal = spline->yBC.rVal_i; // First, solve in the X-direction for (int iy=0; iyx_grid, xBC_r, ((float*)data)+doffset, 2*My, (float*)spline->coefs+coffset, 2*Ny); // Imag part find_coefs_1d_s (spline->x_grid, xBC_i, ((float*)data)+doffset+1, 2*My, ((float*)spline->coefs)+coffset+1, 2*Ny); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, yBC_r, ((float*)spline->coefs)+doffset, 2, ((float*)spline->coefs)+coffset, 2); // Imag part find_coefs_1d_s (spline->y_grid, yBC_i, ((float*)spline->coefs)+doffset+1, 2, ((float*)spline->coefs)+coffset+1, 2); } } UBspline_3d_c* create_UBspline_3d_c (Ugrid x_grid, Ugrid y_grid, Ugrid z_grid, BCtype_c xBC, BCtype_c yBC, BCtype_c zBC, complex_float *data) { // Create new spline UBspline_3d_c* restrict spline = new UBspline_3d_c; spline->spcode = U3D; spline->tcode = SINGLE_COMPLEX; spline->xBC = xBC; spline->yBC = yBC; spline->zBC = zBC; // Setup internal variables int Mx = x_grid.num; int My = y_grid.num; int Mz = z_grid.num; int Nx, Ny, Nz; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; x_grid.delta = (x_grid.end - x_grid.start)/(double)(Nx-3); x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; if (yBC.lCode == PERIODIC || yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; y_grid.delta = (y_grid.end - y_grid.start)/(double)(Ny-3); y_grid.delta_inv = 1.0/y_grid.delta; spline->y_grid = y_grid; if (zBC.lCode == PERIODIC || zBC.lCode == ANTIPERIODIC) Nz = Mz+3; else Nz = Mz+2; z_grid.delta = (z_grid.end - z_grid.start)/(double)(Nz-3); z_grid.delta_inv = 1.0/z_grid.delta; spline->z_grid = z_grid; spline->x_stride = Ny*Nz; spline->y_stride = Nz; #ifndef HAVE_SSE2 spline->coefs = new complex_float[Nx*Ny*Nz]; #else posix_memalign ((void**)&spline->coefs, 16, 2*sizeof(float)*Nx*Ny*Nz); #endif BCtype_s xBC_r, xBC_i, yBC_r, yBC_i, zBC_r, zBC_i; xBC_r.lCode = xBC.lCode; xBC_r.rCode = xBC.rCode; xBC_r.lVal = xBC.lVal_r; xBC_r.rVal = xBC.rVal_r; xBC_i.lCode = xBC.lCode; xBC_i.rCode = xBC.rCode; xBC_i.lVal = xBC.lVal_i; xBC_i.rVal = xBC.rVal_i; yBC_r.lCode = yBC.lCode; yBC_r.rCode = yBC.rCode; yBC_r.lVal = yBC.lVal_r; yBC_r.rVal = yBC.rVal_r; yBC_i.lCode = yBC.lCode; yBC_i.rCode = yBC.rCode; yBC_i.lVal = yBC.lVal_i; yBC_i.rVal = yBC.rVal_i; zBC_r.lCode = zBC.lCode; zBC_r.rCode = zBC.rCode; zBC_r.lVal = zBC.lVal_r; zBC_r.rVal = zBC.rVal_r; zBC_i.lCode = zBC.lCode; zBC_i.rCode = zBC.rCode; zBC_i.lVal = zBC.lVal_i; zBC_i.rVal = zBC.rVal_i; // First, solve in the X-direction for (int iy=0; iyx_grid, xBC_r, ((float*)data)+doffset, 2*My*Mz, ((float*)spline->coefs)+coffset, 2*Ny*Nz); // Imag part find_coefs_1d_s (spline->x_grid, xBC_i, ((float*)data)+doffset+1, 2*My*Mz, ((float*)spline->coefs)+coffset+1, 2*Ny*Nz); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, yBC_r, ((float*)spline->coefs)+doffset, 2*Nz, ((float*)spline->coefs)+coffset, 2*Nz); // Imag part find_coefs_1d_s (spline->y_grid, yBC_i, ((float*)spline->coefs)+doffset+1, 2*Nz, ((float*)spline->coefs)+coffset+1, 2*Nz); } // Now, solve in the Z-direction for (int ix=0; ixz_grid, zBC_r, ((float*)spline->coefs)+doffset, 2, ((float*)spline->coefs)+coffset, 2); // Imag part find_coefs_1d_s (spline->z_grid, zBC_i, ((float*)spline->coefs)+doffset+1, 2, ((float*)spline->coefs)+coffset+1, 2); } init_sse_data(); return spline; } void recompute_UBspline_3d_c (UBspline_3d_c* spline, complex_float *data) { // Setup internal variables int Mx = spline->x_grid.num; int My = spline->y_grid.num; int Mz = spline->z_grid.num; int Nx, Ny, Nz; if (spline->xBC.lCode == PERIODIC || spline->xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; if (spline->yBC.lCode == PERIODIC || spline->yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; if (spline->zBC.lCode == PERIODIC || spline->zBC.lCode == ANTIPERIODIC) Nz = Mz+3; else Nz = Mz+2; BCtype_s xBC_r, xBC_i, yBC_r, yBC_i, zBC_r, zBC_i; xBC_r.lCode = spline->xBC.lCode; xBC_r.rCode = spline->xBC.rCode; xBC_r.lVal = spline->xBC.lVal_r; xBC_r.rVal = spline->xBC.rVal_r; xBC_i.lCode = spline->xBC.lCode; xBC_i.rCode = spline->xBC.rCode; xBC_i.lVal = spline->xBC.lVal_i; xBC_i.rVal = spline->xBC.rVal_i; yBC_r.lCode = spline->yBC.lCode; yBC_r.rCode = spline->yBC.rCode; yBC_r.lVal = spline->yBC.lVal_r; yBC_r.rVal = spline->yBC.rVal_r; yBC_i.lCode = spline->yBC.lCode; yBC_i.rCode = spline->yBC.rCode; yBC_i.lVal = spline->yBC.lVal_i; yBC_i.rVal = spline->yBC.rVal_i; zBC_r.lCode = spline->zBC.lCode; zBC_r.rCode = spline->zBC.rCode; zBC_r.lVal = spline->zBC.lVal_r; zBC_r.rVal = spline->zBC.rVal_r; zBC_i.lCode = spline->zBC.lCode; zBC_i.rCode = spline->zBC.rCode; zBC_i.lVal = spline->zBC.lVal_i; zBC_i.rVal = spline->zBC.rVal_i; // First, solve in the X-direction for (int iy=0; iyx_grid, xBC_r, ((float*)data)+doffset, 2*My*Mz, ((float*)spline->coefs)+coffset, 2*Ny*Nz); // Imag part find_coefs_1d_s (spline->x_grid, xBC_i, ((float*)data)+doffset+1, 2*My*Mz, ((float*)spline->coefs)+coffset+1, 2*Ny*Nz); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, yBC_r, ((float*)spline->coefs)+doffset, 2*Nz, ((float*)spline->coefs)+coffset, 2*Nz); // Imag part find_coefs_1d_s (spline->y_grid, yBC_i, ((float*)spline->coefs)+doffset+1, 2*Nz, ((float*)spline->coefs)+coffset+1, 2*Nz); } // Now, solve in the Z-direction for (int ix=0; ixz_grid, zBC_r, ((float*)spline->coefs)+doffset, 2, ((float*)spline->coefs)+coffset, 2); // Imag part find_coefs_1d_s (spline->z_grid, zBC_i, ((float*)spline->coefs)+doffset+1, 2, ((float*)spline->coefs)+coffset+1, 2); } } //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// //// Double-Precision, Real Creation Routines //// //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// // On input, bands should be filled with: // row 0 : abcdInitial from boundary conditions // rows 1:M: basis functions in first 3 cols, data in last // row M+1 : abcdFinal from boundary conditions // cstride gives the stride between values in coefs. // On exit, coefs with contain interpolating B-spline coefs void solve_deriv_interp_1d_d (double bands[], double coefs[], int M, int cstride) { // Solve interpolating equations // First and last rows are different bands[4*(0)+1] /= bands[4*(0)+0]; bands[4*(0)+2] /= bands[4*(0)+0]; bands[4*(0)+3] /= bands[4*(0)+0]; bands[4*(0)+0] = 1.0; bands[4*(1)+1] -= bands[4*(1)+0]*bands[4*(0)+1]; bands[4*(1)+2] -= bands[4*(1)+0]*bands[4*(0)+2]; bands[4*(1)+3] -= bands[4*(1)+0]*bands[4*(0)+3]; bands[4*(0)+0] = 0.0; bands[4*(1)+2] /= bands[4*(1)+1]; bands[4*(1)+3] /= bands[4*(1)+1]; bands[4*(1)+1] = 1.0; // Now do rows 2 through M+1 for (int row=2; row < (M+1); row++) { bands[4*(row)+1] -= bands[4*(row)+0]*bands[4*(row-1)+2]; bands[4*(row)+3] -= bands[4*(row)+0]*bands[4*(row-1)+3]; bands[4*(row)+2] /= bands[4*(row)+1]; bands[4*(row)+3] /= bands[4*(row)+1]; bands[4*(row)+0] = 0.0; bands[4*(row)+1] = 1.0; } // Do last row bands[4*(M+1)+1] -= bands[4*(M+1)+0]*bands[4*(M-1)+2]; bands[4*(M+1)+3] -= bands[4*(M+1)+0]*bands[4*(M-1)+3]; bands[4*(M+1)+2] -= bands[4*(M+1)+1]*bands[4*(M)+2]; bands[4*(M+1)+3] -= bands[4*(M+1)+1]*bands[4*(M)+3]; bands[4*(M+1)+3] /= bands[4*(M+1)+2]; bands[4*(M+1)+2] = 1.0; coefs[(M+1)*cstride] = bands[4*(M+1)+3]; // Now back substitute up for (int row=M; row>0; row--) coefs[row*cstride] = bands[4*(row)+3] - bands[4*(row)+2]*coefs[cstride*(row+1)]; // Finish with first row coefs[0] = bands[4*(0)+3] - bands[4*(0)+1]*coefs[1*cstride] - bands[4*(0)+2]*coefs[2*cstride]; } // On input, bands should be filled with: // row 0 : abcdInitial from boundary conditions // rows 1:M: basis functions in first 3 cols, data in last // row M+1 : abcdFinal from boundary conditions // cstride gives the stride between values in coefs. // On exit, coefs with contain interpolating B-spline coefs void solve_periodic_interp_1d_d (double bands[], double coefs[], int M, int cstride) { std::vector lastCol(M); // Now solve: // First and last rows are different bands[4*(0)+2] /= bands[4*(0)+1]; bands[4*(0)+0] /= bands[4*(0)+1]; bands[4*(0)+3] /= bands[4*(0)+1]; bands[4*(0)+1] = 1.0; bands[4*(M-1)+1] -= bands[4*(M-1)+2]*bands[4*(0)+0]; bands[4*(M-1)+3] -= bands[4*(M-1)+2]*bands[4*(0)+3]; bands[4*(M-1)+2] = -bands[4*(M-1)+2]*bands[4*(0)+2]; lastCol[0] = bands[4*(0)+0]; for (int row=1; row < (M-1); row++) { bands[4*(row)+1] -= bands[4*(row)+0] * bands[4*(row-1)+2]; bands[4*(row)+3] -= bands[4*(row)+0] * bands[4*(row-1)+3]; lastCol[row] = -bands[4*(row)+0] * lastCol[row-1]; bands[4*(row)+0] = 0.0; bands[4*(row)+2] /= bands[4*(row)+1]; bands[4*(row)+3] /= bands[4*(row)+1]; lastCol[row] /= bands[4*(row)+1]; bands[4*(row)+1] = 1.0; if (row < (M-2)) { bands[4*(M-1)+3] -= bands[4*(M-1)+2]*bands[4*(row)+3]; bands[4*(M-1)+1] -= bands[4*(M-1)+2]*lastCol[row]; bands[4*(M-1)+2] = -bands[4*(M-1)+2]*bands[4*(row)+2]; } } // Now do last row // The [2] element and [0] element are now on top of each other bands[4*(M-1)+0] += bands[4*(M-1)+2]; bands[4*(M-1)+1] -= bands[4*(M-1)+0] * (bands[4*(M-2)+2]+lastCol[M-2]); bands[4*(M-1)+3] -= bands[4*(M-1)+0] * bands[4*(M-2)+3]; bands[4*(M-1)+3] /= bands[4*(M-1)+1]; coefs[M*cstride] = bands[4*(M-1)+3]; for (int row=M-2; row>=0; row--) coefs[(row+1)*cstride] = bands[4*(row)+3] - bands[4*(row)+2]*coefs[(row+2)*cstride] - lastCol[row]*coefs[M*cstride]; coefs[0*cstride] = coefs[M*cstride]; coefs[(M+1)*cstride] = coefs[1*cstride]; coefs[(M+2)*cstride] = coefs[2*cstride]; } // On input, bands should be filled with: // row 0 : abcdInitial from boundary conditions // rows 1:M: basis functions in first 3 cols, data in last // row M+1 : abcdFinal from boundary conditions // cstride gives the stride between values in coefs. // On exit, coefs with contain interpolating B-spline coefs void solve_antiperiodic_interp_1d_d (double bands[], double coefs[], int M, int cstride) { std::vector lastCol(M); bands[4*0+0] *= -1.0; bands[4*(M-1)+2] *= -1.0; // Now solve: // First and last rows are different bands[4*(0)+2] /= bands[4*(0)+1]; bands[4*(0)+0] /= bands[4*(0)+1]; bands[4*(0)+3] /= bands[4*(0)+1]; bands[4*(0)+1] = 1.0; bands[4*(M-1)+1] -= bands[4*(M-1)+2]*bands[4*(0)+0]; bands[4*(M-1)+3] -= bands[4*(M-1)+2]*bands[4*(0)+3]; bands[4*(M-1)+2] = -bands[4*(M-1)+2]*bands[4*(0)+2]; lastCol[0] = bands[4*(0)+0]; for (int row=1; row < (M-1); row++) { bands[4*(row)+1] -= bands[4*(row)+0] * bands[4*(row-1)+2]; bands[4*(row)+3] -= bands[4*(row)+0] * bands[4*(row-1)+3]; lastCol[row] = -bands[4*(row)+0] * lastCol[row-1]; bands[4*(row)+0] = 0.0; bands[4*(row)+2] /= bands[4*(row)+1]; bands[4*(row)+3] /= bands[4*(row)+1]; lastCol[row] /= bands[4*(row)+1]; bands[4*(row)+1] = 1.0; if (row < (M-2)) { bands[4*(M-1)+3] -= bands[4*(M-1)+2]*bands[4*(row)+3]; bands[4*(M-1)+1] -= bands[4*(M-1)+2]*lastCol[row]; bands[4*(M-1)+2] = -bands[4*(M-1)+2]*bands[4*(row)+2]; } } // Now do last row // The [2] element and [0] element are now on top of each other bands[4*(M-1)+0] += bands[4*(M-1)+2]; bands[4*(M-1)+1] -= bands[4*(M-1)+0] * (bands[4*(M-2)+2]+lastCol[M-2]); bands[4*(M-1)+3] -= bands[4*(M-1)+0] * bands[4*(M-2)+3]; bands[4*(M-1)+3] /= bands[4*(M-1)+1]; coefs[M*cstride] = bands[4*(M-1)+3]; for (int row=M-2; row>=0; row--) coefs[(row+1)*cstride] = bands[4*(row)+3] - bands[4*(row)+2]*coefs[(row+2)*cstride] - lastCol[row]*coefs[M*cstride]; coefs[0*cstride] = -coefs[M*cstride]; coefs[(M+1)*cstride] = -coefs[1*cstride]; coefs[(M+2)*cstride] = -coefs[2*cstride]; } void find_coefs_1d_d (Ugrid grid, BCtype_d bc, double *data, intptr_t dstride, double *coefs, intptr_t cstride) { int M = grid.num; double basis[4] = {1.0/6.0, 2.0/3.0, 1.0/6.0, 0.0}; if (bc.lCode == PERIODIC || bc.lCode == ANTIPERIODIC) { #ifdef HAVE_C_VARARRAYS double bands[M*4]; #else double *bands = new double[4*M]; #endif for (int i=0; ispcode = U1D; spline->tcode = DOUBLE_REAL; spline->xBC = xBC; // Setup internal variables int M = x_grid.num; int N; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) { x_grid.delta = (x_grid.end-x_grid.start)/(double)(x_grid.num); N = M+3; } else { x_grid.delta = (x_grid.end-x_grid.start)/(double)(x_grid.num-1); N = M+2; } x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; #ifndef HAVE_SSE2 - spline->coefs = new double[N]; + spline->coefs = (double*)malloc (sizeof(double)*N); #else posix_memalign ((void**)&spline->coefs, 16, sizeof(double)*N); #endif find_coefs_1d_d (spline->x_grid, xBC, data, 1, spline->coefs, 1); init_sse_data(); return spline; } void recompute_UBspline_1d_d (UBspline_1d_d* spline, double *data) { find_coefs_1d_d (spline->x_grid, spline->xBC, data, 1, spline->coefs, 1); } UBspline_2d_d* create_UBspline_2d_d (Ugrid x_grid, Ugrid y_grid, BCtype_d xBC, BCtype_d yBC, double *data) { // Create new spline UBspline_2d_d* restrict spline = new UBspline_2d_d; spline->spcode = U2D; spline->tcode = DOUBLE_REAL; spline->xBC = xBC; spline->yBC = yBC; // Setup internal variables int Mx = x_grid.num; int My = y_grid.num; int Nx, Ny; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; x_grid.delta = (x_grid.end - x_grid.start)/(double)(Nx-3); x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; if (yBC.lCode == PERIODIC || yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; y_grid.delta = (y_grid.end - y_grid.start)/(double)(Ny-3); y_grid.delta_inv = 1.0/y_grid.delta; spline->y_grid = y_grid; spline->x_stride = Ny; #ifndef HAVE_SSE2 - spline->coefs = new double[Nx*Ny]; + spline->coefs = (double*)malloc (sizeof(double)*Nx*Ny); #else posix_memalign ((void**)&spline->coefs, 16, (sizeof(double)*Nx*Ny)); #endif // First, solve in the X-direction for (int iy=0; iyx_grid, xBC, data+doffset, My, spline->coefs+coffset, Ny); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, yBC, spline->coefs+doffset, 1, spline->coefs+coffset, 1); } init_sse_data(); return spline; } void recompute_UBspline_2d_d (UBspline_2d_d* spline, double *data) { int Mx = spline->x_grid.num; int My = spline->y_grid.num; int Nx, Ny; if (spline->xBC.lCode == PERIODIC || spline->xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; if (spline->yBC.lCode == PERIODIC || spline->yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; // First, solve in the X-direction for (int iy=0; iyx_grid, spline->xBC, data+doffset, My, spline->coefs+coffset, Ny); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, spline->yBC, spline->coefs+doffset, 1, spline->coefs+coffset, 1); } } UBspline_3d_d* create_UBspline_3d_d (Ugrid x_grid, Ugrid y_grid, Ugrid z_grid, BCtype_d xBC, BCtype_d yBC, BCtype_d zBC, double *data) { // Create new spline UBspline_3d_d* restrict spline = new UBspline_3d_d; spline->spcode = U3D; spline->tcode = DOUBLE_REAL; spline->xBC = xBC; spline->yBC = yBC; spline->zBC = zBC; // Setup internal variables int Mx = x_grid.num; int My = y_grid.num; int Mz = z_grid.num; int Nx, Ny, Nz; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; x_grid.delta = (x_grid.end - x_grid.start)/(double)(Nx-3); x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; if (yBC.lCode == PERIODIC || yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; y_grid.delta = (y_grid.end - y_grid.start)/(double)(Ny-3); y_grid.delta_inv = 1.0/y_grid.delta; spline->y_grid = y_grid; if (zBC.lCode == PERIODIC || zBC.lCode == ANTIPERIODIC) Nz = Mz+3; else Nz = Mz+2; z_grid.delta = (z_grid.end - z_grid.start)/(double)(Nz-3); z_grid.delta_inv = 1.0/z_grid.delta; spline->z_grid = z_grid; spline->x_stride = Ny*Nz; spline->y_stride = Nz; #ifndef HAVE_SSE2 spline->coefs = new double[Nx*Ny*Nz]; #else posix_memalign ((void**)&spline->coefs, 16, (sizeof(double)*Nx*Ny*Nz)); #endif // First, solve in the X-direction for (int iy=0; iyx_grid, xBC, data+doffset, My*Mz, spline->coefs+coffset, Ny*Nz); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, yBC, spline->coefs+doffset, Nz, spline->coefs+coffset, Nz); } // Now, solve in the Z-direction for (int ix=0; ixz_grid, zBC, spline->coefs+doffset, 1, spline->coefs+coffset, 1); } init_sse_data(); return spline; } void recompute_UBspline_3d_d (UBspline_3d_d* spline, double *data) { int Mx = spline->x_grid.num; int My = spline->y_grid.num; int Mz = spline->z_grid.num; int Nx, Ny, Nz; if (spline->xBC.lCode == PERIODIC || spline->xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; if (spline->yBC.lCode == PERIODIC || spline->yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; if (spline->zBC.lCode == PERIODIC || spline->zBC.lCode == ANTIPERIODIC) Nz = Mz+3; else Nz = Mz+2; // First, solve in the X-direction for (int iy=0; iyx_grid, spline->xBC, data+doffset, My*Mz, spline->coefs+coffset, Ny*Nz); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, spline->yBC, spline->coefs+doffset, Nz, spline->coefs+coffset, Nz); } // Now, solve in the Z-direction for (int ix=0; ixz_grid, spline->zBC, spline->coefs+doffset, 1, spline->coefs+coffset, 1); } } //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// //// Double-Precision, Complex Creation Routines //// //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// // On input, bands should be filled with: // row 0 : abcdInitial from boundary conditions // rows 1:M: basis functions in first 3 cols, data in last // row M+1 : abcdFinal from boundary conditions // cstride gives the stride between values in coefs. // On exit, coefs with contain interpolating B-spline coefs UBspline_1d_z* create_UBspline_1d_z (Ugrid x_grid, BCtype_z xBC, complex_double *data) { // Create new spline UBspline_1d_z* restrict spline = new UBspline_1d_z; spline->spcode = U1D; spline->tcode = DOUBLE_COMPLEX; spline->xBC = xBC; // Setup internal variables int M = x_grid.num; int N; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) { x_grid.delta = (x_grid.end-x_grid.start)/(double)(x_grid.num); N = M+3; } else { x_grid.delta = (x_grid.end-x_grid.start)/(double)(x_grid.num-1); N = M+2; } x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; #ifndef HAVE_SSE2 - spline->coefs = new complex_double[N]; + spline->coefs = (complex_double*)malloc (2*sizeof(double)*N); #else posix_memalign ((void**)&spline->coefs, 16, 2*sizeof(double)*N); #endif BCtype_d xBC_r, xBC_i; xBC_r.lCode = xBC.lCode; xBC_r.rCode = xBC.rCode; xBC_r.lVal = xBC.lVal_r; xBC_r.rVal = xBC.rVal_r; xBC_i.lCode = xBC.lCode; xBC_i.rCode = xBC.rCode; xBC_i.lVal = xBC.lVal_i; xBC_i.rVal = xBC.rVal_i; // Real part find_coefs_1d_d (spline->x_grid, xBC_r, (double*)data, 2, (double*)spline->coefs, 2); // Imaginarty part find_coefs_1d_d (spline->x_grid, xBC_i, ((double*)data)+1, 2, ((double*)spline->coefs)+1, 2); init_sse_data(); return spline; } void recompute_UBspline_1d_z (UBspline_1d_z* spline, complex_double *data) { // int M = spline->x_grid.num; // int N; // if (spline->xBC.lCode == PERIODIC || spline->xBC.lCode == ANTIPERIODIC) // N = M+3; // else // N = M+2; BCtype_d xBC_r, xBC_i; xBC_r.lCode = spline->xBC.lCode; xBC_r.rCode = spline->xBC.rCode; xBC_r.lVal = spline->xBC.lVal_r; xBC_r.rVal = spline->xBC.rVal_r; xBC_i.lCode = spline->xBC.lCode; xBC_i.rCode = spline->xBC.rCode; xBC_i.lVal = spline->xBC.lVal_i; xBC_i.rVal = spline->xBC.rVal_i; // Real part find_coefs_1d_d (spline->x_grid, xBC_r, (double*)data, 2, (double*)spline->coefs, 2); // Imaginarty part find_coefs_1d_d (spline->x_grid, xBC_i, ((double*)data)+1, 2, ((double*)spline->coefs)+1, 2); } UBspline_2d_z* create_UBspline_2d_z (Ugrid x_grid, Ugrid y_grid, BCtype_z xBC, BCtype_z yBC, complex_double *data) { // Create new spline UBspline_2d_z* restrict spline = new UBspline_2d_z; spline->spcode = U2D; spline->tcode = DOUBLE_COMPLEX; spline->xBC = xBC; spline->yBC = yBC; // Setup internal variables int Mx = x_grid.num; int My = y_grid.num; int Nx, Ny; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; x_grid.delta = (x_grid.end - x_grid.start)/(double)(Nx-3); x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; if (yBC.lCode == PERIODIC || yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; y_grid.delta = (y_grid.end - y_grid.start)/(double)(Ny-3); y_grid.delta_inv = 1.0/y_grid.delta; spline->y_grid = y_grid; spline->x_stride = Ny; #ifndef HAVE_SSE2 - spline->coefs = new complex_double[Nx*Ny]; + spline->coefs = (complex_double*)malloc (2*sizeof(double)*Nx*Ny); #else posix_memalign ((void**)&spline->coefs, 16, 2*sizeof(double)*Nx*Ny); #endif BCtype_d xBC_r, xBC_i, yBC_r, yBC_i; xBC_r.lCode = xBC.lCode; xBC_r.rCode = xBC.rCode; xBC_r.lVal = xBC.lVal_r; xBC_r.rVal = xBC.rVal_r; xBC_i.lCode = xBC.lCode; xBC_i.rCode = xBC.rCode; xBC_i.lVal = xBC.lVal_i; xBC_i.rVal = xBC.rVal_i; yBC_r.lCode = yBC.lCode; yBC_r.rCode = yBC.rCode; yBC_r.lVal = yBC.lVal_r; yBC_r.rVal = yBC.rVal_r; yBC_i.lCode = yBC.lCode; yBC_i.rCode = yBC.rCode; yBC_i.lVal = yBC.lVal_i; yBC_i.rVal = yBC.rVal_i; // First, solve in the X-direction for (int iy=0; iyx_grid, xBC_r, ((double*)data+doffset), 2*My, (double*)spline->coefs+coffset, 2*Ny); // Imag part find_coefs_1d_d (spline->x_grid, xBC_i, ((double*)data)+doffset+1, 2*My, ((double*)spline->coefs)+coffset+1, 2*Ny); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, yBC_r, ((double*)spline->coefs)+doffset, 2, (double*)spline->coefs+coffset, 2); // Imag part find_coefs_1d_d (spline->y_grid, yBC_i, (double*)spline->coefs+doffset+1, 2, ((double*)spline->coefs)+coffset+1, 2); } init_sse_data(); return spline; } void recompute_UBspline_2d_z (UBspline_2d_z* spline, complex_double *data) { int Mx = spline->x_grid.num; int My = spline->y_grid.num; int Nx, Ny; if (spline->xBC.lCode == PERIODIC || spline->xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; if (spline->yBC.lCode == PERIODIC || spline->yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; BCtype_d xBC_r, xBC_i, yBC_r, yBC_i; xBC_r.lCode = spline->xBC.lCode; xBC_r.rCode = spline->xBC.rCode; xBC_r.lVal = spline->xBC.lVal_r; xBC_r.rVal = spline->xBC.rVal_r; xBC_i.lCode = spline->xBC.lCode; xBC_i.rCode = spline->xBC.rCode; xBC_i.lVal = spline->xBC.lVal_i; xBC_i.rVal = spline->xBC.rVal_i; yBC_r.lCode = spline->yBC.lCode; yBC_r.rCode = spline->yBC.rCode; yBC_r.lVal = spline->yBC.lVal_r; yBC_r.rVal = spline->yBC.rVal_r; yBC_i.lCode = spline->yBC.lCode; yBC_i.rCode = spline->yBC.rCode; yBC_i.lVal = spline->yBC.lVal_i; yBC_i.rVal = spline->yBC.rVal_i; // First, solve in the X-direction for (int iy=0; iyx_grid, xBC_r, ((double*)data+doffset), 2*My, (double*)spline->coefs+coffset, 2*Ny); // Imag part find_coefs_1d_d (spline->x_grid, xBC_i, ((double*)data)+doffset+1, 2*My, ((double*)spline->coefs)+coffset+1, 2*Ny); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, yBC_r, ((double*)spline->coefs)+doffset, 2, (double*)spline->coefs+coffset, 2); // Imag part find_coefs_1d_d (spline->y_grid, yBC_i, (double*)spline->coefs+doffset+1, 2, ((double*)spline->coefs)+coffset+1, 2); } } UBspline_3d_z* create_UBspline_3d_z (Ugrid x_grid, Ugrid y_grid, Ugrid z_grid, BCtype_z xBC, BCtype_z yBC, BCtype_z zBC, complex_double *data) { // Create new spline UBspline_3d_z* restrict spline = new UBspline_3d_z; spline->spcode = U3D; spline->tcode = DOUBLE_COMPLEX; spline->xBC = xBC; spline->yBC = yBC; spline->zBC = zBC; // Setup internal variables int Mx = x_grid.num; int My = y_grid.num; int Mz = z_grid.num; int Nx, Ny, Nz; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; x_grid.delta = (x_grid.end - x_grid.start)/(double)(Nx-3); x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; if (yBC.lCode == PERIODIC || yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; y_grid.delta = (y_grid.end - y_grid.start)/(double)(Ny-3); y_grid.delta_inv = 1.0/y_grid.delta; spline->y_grid = y_grid; if (zBC.lCode == PERIODIC || zBC.lCode == ANTIPERIODIC) Nz = Mz+3; else Nz = Mz+2; z_grid.delta = (z_grid.end - z_grid.start)/(double)(Nz-3); z_grid.delta_inv = 1.0/z_grid.delta; spline->z_grid = z_grid; spline->x_stride = Ny*Nz; spline->y_stride = Nz; #ifndef HAVE_SSE2 spline->coefs = new complex_double[Nx*Ny*Nz]; #else posix_memalign ((void**)&spline->coefs, 16, 2*sizeof(double)*Nx*Ny*Nz); #endif BCtype_d xBC_r, xBC_i, yBC_r, yBC_i, zBC_r, zBC_i; xBC_r.lCode = xBC.lCode; xBC_r.rCode = xBC.rCode; xBC_r.lVal = xBC.lVal_r; xBC_r.rVal = xBC.rVal_r; xBC_i.lCode = xBC.lCode; xBC_i.rCode = xBC.rCode; xBC_i.lVal = xBC.lVal_i; xBC_i.rVal = xBC.rVal_i; yBC_r.lCode = yBC.lCode; yBC_r.rCode = yBC.rCode; yBC_r.lVal = yBC.lVal_r; yBC_r.rVal = yBC.rVal_r; yBC_i.lCode = yBC.lCode; yBC_i.rCode = yBC.rCode; yBC_i.lVal = yBC.lVal_i; yBC_i.rVal = yBC.rVal_i; zBC_r.lCode = zBC.lCode; zBC_r.rCode = zBC.rCode; zBC_r.lVal = zBC.lVal_r; zBC_r.rVal = zBC.rVal_r; zBC_i.lCode = zBC.lCode; zBC_i.rCode = zBC.rCode; zBC_i.lVal = zBC.lVal_i; zBC_i.rVal = zBC.rVal_i; // First, solve in the X-direction for (int iy=0; iyx_grid, xBC_r, ((double*)data)+doffset, 2*My*Mz, ((double*)spline->coefs)+coffset, 2*Ny*Nz); // Imag part find_coefs_1d_d (spline->x_grid, xBC_i, ((double*)data)+doffset+1, 2*My*Mz, ((double*)spline->coefs)+coffset+1, 2*Ny*Nz); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, yBC_r, ((double*)spline->coefs)+doffset, 2*Nz, ((double*)spline->coefs)+coffset, 2*Nz); // Imag part find_coefs_1d_d (spline->y_grid, yBC_i, ((double*)spline->coefs)+doffset+1, 2*Nz, ((double*)spline->coefs)+coffset+1, 2*Nz); } // Now, solve in the Z-direction for (int ix=0; ixz_grid, zBC_r, ((double*)spline->coefs)+doffset, 2, ((double*)spline->coefs)+coffset, 2); // Imag part find_coefs_1d_d (spline->z_grid, zBC_i, ((double*)spline->coefs)+doffset+1, 2, ((double*)spline->coefs)+coffset+1, 2); } init_sse_data(); return spline; } void recompute_UBspline_3d_z (UBspline_3d_z* spline, complex_double *data) { // Setup internal variables int Mx = spline->x_grid.num; int My = spline->y_grid.num; int Mz = spline->z_grid.num; int Nx, Ny, Nz; if (spline->xBC.lCode == PERIODIC || spline->xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; if (spline->yBC.lCode == PERIODIC || spline->yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; if (spline->zBC.lCode == PERIODIC || spline->zBC.lCode == ANTIPERIODIC) Nz = Mz+3; else Nz = Mz+2; BCtype_d xBC_r, xBC_i, yBC_r, yBC_i, zBC_r, zBC_i; xBC_r.lCode = spline->xBC.lCode; xBC_r.rCode = spline->xBC.rCode; xBC_r.lVal = spline->xBC.lVal_r; xBC_r.rVal = spline->xBC.rVal_r; xBC_i.lCode = spline->xBC.lCode; xBC_i.rCode = spline->xBC.rCode; xBC_i.lVal = spline->xBC.lVal_i; xBC_i.rVal = spline->xBC.rVal_i; yBC_r.lCode = spline->yBC.lCode; yBC_r.rCode = spline->yBC.rCode; yBC_r.lVal = spline->yBC.lVal_r; yBC_r.rVal = spline->yBC.rVal_r; yBC_i.lCode = spline->yBC.lCode; yBC_i.rCode = spline->yBC.rCode; yBC_i.lVal = spline->yBC.lVal_i; yBC_i.rVal = spline->yBC.rVal_i; zBC_r.lCode = spline->zBC.lCode; zBC_r.rCode = spline->zBC.rCode; zBC_r.lVal = spline->zBC.lVal_r; zBC_r.rVal = spline->zBC.rVal_r; zBC_i.lCode = spline->zBC.lCode; zBC_i.rCode = spline->zBC.rCode; zBC_i.lVal = spline->zBC.lVal_i; zBC_i.rVal = spline->zBC.rVal_i; // First, solve in the X-direction for (int iy=0; iyx_grid, xBC_r, ((double*)data)+doffset, 2*My*Mz, ((double*)spline->coefs)+coffset, 2*Ny*Nz); // Imag part find_coefs_1d_d (spline->x_grid, xBC_i, ((double*)data)+doffset+1, 2*My*Mz, ((double*)spline->coefs)+coffset+1, 2*Ny*Nz); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, yBC_r, ((double*)spline->coefs)+doffset, 2*Nz, ((double*)spline->coefs)+coffset, 2*Nz); // Imag part find_coefs_1d_d (spline->y_grid, yBC_i, ((double*)spline->coefs)+doffset+1, 2*Nz, ((double*)spline->coefs)+coffset+1, 2*Nz); } // Now, solve in the Z-direction for (int ix=0; ixz_grid, zBC_r, ((double*)spline->coefs)+doffset, 2, ((double*)spline->coefs)+coffset, 2); // Imag part find_coefs_1d_d (spline->z_grid, zBC_i, ((double*)spline->coefs)+doffset+1, 2, ((double*)spline->coefs)+coffset+1, 2); } } void destroy_UBspline (Bspline *spline) { free(spline->coefs); delete spline; } void destroy_NUBspline (Bspline *spline); void destroy_multi_UBspline (Bspline *spline); void destroy_Bspline (void *spline) { Bspline *sp = (Bspline *)spline; if (sp->sp_code <= U3D) destroy_UBspline (sp); else if (sp->sp_code <= NU3D) destroy_NUBspline (sp); else if (sp->sp_code <= MULTI_U3D) destroy_multi_UBspline (sp); else fprintf (stderr, "Error in destroy_Bspline: invalide spline code %d.\n", sp->sp_code); } diff --git a/libs/image/3rdparty/einspline/multi_bspline_create.cpp b/libs/image/3rdparty/einspline/multi_bspline_create.cpp index 421a35f995..79616aa0d2 100644 --- a/libs/image/3rdparty/einspline/multi_bspline_create.cpp +++ b/libs/image/3rdparty/einspline/multi_bspline_create.cpp @@ -1,1485 +1,1485 @@ ///////////////////////////////////////////////////////////////////////////// // einspline: a library for creating and evaluating B-splines // // Copyright (C) 2007 Kenneth P. Esler, Jr. // // // // This program is free software; you can redistribute 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 "multi_bspline_create.h" #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 600 #endif #ifndef __USE_XOPEN2K #define __USE_XOPEN2K #endif #include #include int posix_memalign(void **memptr, size_t alignment, size_t size); //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// //// Helper functions for spline creation //// //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// void init_sse_data(); void find_coefs_1d_d (Ugrid grid, BCtype_d bc, double *data, intptr_t dstride, double *coefs, intptr_t cstride); void solve_deriv_interp_1d_s (float bands[], float coefs[], int M, int cstride); // On input, bands should be filled with: // row 0 : abcdInitial from boundary conditions // rows 1:M: basis functions in first 3 cols, data in last // row M+1 : abcdFinal from boundary conditions // cstride gives the stride between values in coefs. // On exit, coefs with contain interpolating B-spline coefs void solve_periodic_interp_1d_s (float bands[], float coefs[], int M, int cstride); // On input, bands should be filled with: // row 0 : abcdInitial from boundary conditions // rows 1:M: basis functions in first 3 cols, data in last // row M+1 : abcdFinal from boundary conditions // cstride gives the stride between values in coefs. // On exit, coefs with contain interpolating B-spline coefs void solve_antiperiodic_interp_1d_s (float bands[], float coefs[], int M, int cstride); void find_coefs_1d_s (Ugrid grid, BCtype_s bc, float *data, intptr_t dstride, float *coefs, intptr_t cstride); //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// //// Single-Precision, Real Creation Routines //// //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// // On input, bands should be filled with: // row 0 : abcdInitial from boundary conditions // rows 1:M: basis functions in first 3 cols, data in last // row M+1 : abcdFinal from boundary conditions // cstride gives the stride between values in coefs. // On exit, coefs with contain interpolating B-spline coefs multi_UBspline_1d_s* create_multi_UBspline_1d_s (Ugrid x_grid, BCtype_s xBC, int num_splines) { // Create new spline multi_UBspline_1d_s* restrict spline = new multi_UBspline_1d_s; if (!spline) { fprintf (stderr, "Out of memory allocating spline in create_multi_UBspline_1d_s.\n"); abort(); } spline->spcode = MULTI_U1D; spline->tcode = SINGLE_REAL; spline->xBC = xBC; spline->x_grid = x_grid; spline->num_splines = num_splines; // Setup internal variables int Mx = x_grid.num; int Nx; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) { x_grid.delta = (x_grid.end-x_grid.start)/(double)(x_grid.num); Nx = Mx+3; } else { x_grid.delta = (x_grid.end-x_grid.start)/(double)(x_grid.num-1); Nx = Mx+2; } int N = num_splines; #ifdef HAVE_SSE if (N % 4) N += 4 - (N % 4); #endif spline->x_stride = N; x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; #ifndef HAVE_POSIX_MEMALIGN - spline->coefs = new float[Nx*N]; + spline->coefs = (float*)malloc (sizeof(float)*Nx*N); #else posix_memalign ((void**)&spline->coefs, 64, (sizeof(float)*Nx*N)); #endif #ifdef HAVE_SSE init_sse_data(); #endif if (!spline->coefs) { fprintf (stderr, "Out of memory allocating spline coefficient in create_multi_UBspline_1d_s.\n"); abort(); } return spline; } void set_multi_UBspline_1d_s (multi_UBspline_1d_s *spline, int num, float *data) { float *coefs = spline->coefs + num; int xs = spline->x_stride; find_coefs_1d_s (spline->x_grid, spline->xBC, data, 1, coefs, xs); } multi_UBspline_2d_s* create_multi_UBspline_2d_s (Ugrid x_grid, Ugrid y_grid, BCtype_s xBC, BCtype_s yBC, int num_splines) { // Create new spline multi_UBspline_2d_s* restrict spline = new multi_UBspline_2d_s; if (!spline) { fprintf (stderr, "Out of memory allocating spline in create_multi_UBspline_2d_s.\n"); abort(); } spline->spcode = MULTI_U2D; spline->tcode = SINGLE_REAL; spline->xBC = xBC; spline->yBC = yBC; spline->num_splines = num_splines; // Setup internal variables int Mx = x_grid.num; int My = y_grid.num; int Nx, Ny; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; x_grid.delta = (x_grid.end - x_grid.start)/(double)(Nx-3); x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; if (yBC.lCode == PERIODIC || yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; y_grid.delta = (y_grid.end - y_grid.start)/(double)(Ny-3); y_grid.delta_inv = 1.0/y_grid.delta; spline->y_grid = y_grid; int N = num_splines; #ifdef HAVE_SSE if (N % 4) N += 4 - (N % 4); #endif spline->x_stride = Ny*N; spline->y_stride = N; #ifndef HAVE_POSIX_MEMALIGN - spline->coefs = new float[Nx*Ny*N]; + spline->coefs = (float*)malloc (sizeof(float)*Nx*Ny*N); #else posix_memalign ((void**)&spline->coefs, 64, sizeof(float)*Nx*Ny*N); #endif #ifdef HAVE_SSE init_sse_data(); #endif if (!spline->coefs) { fprintf (stderr, "Out of memory allocating spline coefficients in create_multi_UBspline_2d_s.\n"); abort(); } return spline; } void set_multi_UBspline_2d_s (multi_UBspline_2d_s* spline, int num, float *data) { int Mx = spline->x_grid.num; int My = spline->y_grid.num; int Nx, Ny; if (spline->xBC.lCode == PERIODIC || spline->xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; if (spline->yBC.lCode == PERIODIC || spline->yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; float *coefs = spline->coefs + num; int ys = spline->y_stride; // First, solve in the X-direction for (int iy=0; iyx_grid, spline->xBC, data+doffset, (intptr_t)My, coefs+coffset, (intptr_t)Ny*ys); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, spline->yBC, coefs+doffset, (intptr_t)ys, coefs+coffset, (intptr_t)ys); } } multi_UBspline_3d_s* create_multi_UBspline_3d_s (Ugrid x_grid, Ugrid y_grid, Ugrid z_grid, BCtype_s xBC, BCtype_s yBC, BCtype_s zBC, int num_splines) { // Create new spline multi_UBspline_3d_s* restrict spline = new multi_UBspline_3d_s; if (!spline) { fprintf (stderr, "Out of memory allocating spline in create_multi_UBspline_3d_s.\n"); abort(); } spline->spcode = MULTI_U3D; spline->tcode = SINGLE_REAL; spline->xBC = xBC; spline->yBC = yBC; spline->zBC = zBC; spline->num_splines = num_splines; // Setup internal variables int Mx = x_grid.num; int My = y_grid.num; int Mz = z_grid.num; int Nx, Ny, Nz; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; x_grid.delta = (x_grid.end - x_grid.start)/(double)(Nx-3); x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; if (yBC.lCode == PERIODIC || yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; y_grid.delta = (y_grid.end - y_grid.start)/(double)(Ny-3); y_grid.delta_inv = 1.0/y_grid.delta; spline->y_grid = y_grid; if (zBC.lCode == PERIODIC || zBC.lCode == ANTIPERIODIC) Nz = Mz+3; else Nz = Mz+2; z_grid.delta = (z_grid.end - z_grid.start)/(double)(Nz-3); z_grid.delta_inv = 1.0/z_grid.delta; spline->z_grid = z_grid; int N = num_splines; #ifdef HAVE_SSE if (N % 4) N += 4 - (N % 4); #endif spline->x_stride = Ny*Nz*N; spline->y_stride = Nz*N; spline->z_stride = N; #ifndef HAVE_POSIX_MEMALIGN spline->coefs = new float[Nx*Ny*Nz*N]; #else posix_memalign ((void**)&spline->coefs, 64, (sizeof(float)*Nx*Ny*Nz*N)); #endif #ifdef HAVE_SSE init_sse_data(); #endif if (!spline->coefs) { fprintf (stderr, "Out of memory allocating spline coefficients in create_multi_UBspline_3d_s.\n"); abort(); } return spline; } void set_multi_UBspline_3d_s (multi_UBspline_3d_s* spline, int num, float *data) { int Mx = spline->x_grid.num; int My = spline->y_grid.num; int Mz = spline->z_grid.num; int Nx, Ny, Nz; if (spline->xBC.lCode == PERIODIC || spline->xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; if (spline->yBC.lCode == PERIODIC || spline->yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; if (spline->zBC.lCode == PERIODIC || spline->zBC.lCode == ANTIPERIODIC) Nz = Mz+3; else Nz = Mz+2; float *coefs = spline->coefs + num; int zs = spline->z_stride; // First, solve in the X-direction for (int iy=0; iyx_grid, spline->xBC, data+doffset, (intptr_t)My*Mz, coefs+coffset, (intptr_t)Ny*Nz*zs); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, spline->yBC, coefs+doffset, (intptr_t)Nz*zs, coefs+coffset, (intptr_t)Nz*zs); } // Now, solve in the Z-direction for (int ix=0; ixz_grid, spline->zBC, coefs+doffset, (intptr_t)zs, coefs+coffset, (intptr_t)zs); } } //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// //// Single-Precision, Complex Creation Routines //// //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// // On input, bands should be filled with: // row 0 : abcdInitial from boundary conditions // rows 1:M: basis functions in first 3 cols, data in last // row M+1 : abcdFinal from boundary conditions // cstride gives the stride between values in coefs. // On exit, coefs with contain interpolating B-spline coefs multi_UBspline_1d_c* create_multi_UBspline_1d_c (Ugrid x_grid, BCtype_c xBC, int num_splines) { // Create new spline multi_UBspline_1d_c* restrict spline = new multi_UBspline_1d_c; if (!spline) { fprintf (stderr, "Out of memory allocating spline in create_multi_UBspline_1d_c.\n"); abort(); } spline->spcode = MULTI_U1D; spline->tcode = SINGLE_COMPLEX; spline->xBC = xBC; spline->num_splines = num_splines; // Setup internal variables int M = x_grid.num; int N; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) { x_grid.delta = (x_grid.end-x_grid.start)/(double)(x_grid.num); N = M+3; } else { x_grid.delta = (x_grid.end-x_grid.start)/(double)(x_grid.num-1); N = M+2; } x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; spline->x_stride = num_splines; #ifndef HAVE_POSIX_MEMALIGN - spline->coefs = new complex_float[N*num_splines]; + spline->coefs = (complex_float*)malloc (2*sizeof(float)*N*num_splines); #else posix_memalign ((void**)&spline->coefs, 64, 2*sizeof(float)*N*num_splines); #endif #ifdef HAVE_SSE init_sse_data(); #endif if (!spline->coefs) { fprintf (stderr, "Out of memory allocating spline coefficients in create_multi_UBspline_1d_c.\n"); abort(); } return spline; } void set_multi_UBspline_1d_c (multi_UBspline_1d_c* spline, int num, complex_float *data) { complex_float *coefs = spline->coefs + num; BCtype_s xBC_r, xBC_i; xBC_r.lCode = spline->xBC.lCode; xBC_r.rCode = spline->xBC.rCode; xBC_r.lVal = spline->xBC.lVal_r; xBC_r.rVal = spline->xBC.rVal_r; xBC_i.lCode = spline->xBC.lCode; xBC_i.rCode = spline->xBC.rCode; xBC_i.lVal = spline->xBC.lVal_i; xBC_i.rVal = spline->xBC.rVal_i; int xs = spline->x_stride; // Real part find_coefs_1d_s (spline->x_grid, xBC_r, (float*)data, (intptr_t)2, (float*)coefs, (intptr_t)2*xs); // Imaginarty part find_coefs_1d_s (spline->x_grid, xBC_i, ((float*)data)+1, (intptr_t)2, ((float*)coefs+1), (intptr_t)2*xs); } multi_UBspline_2d_c* create_multi_UBspline_2d_c (Ugrid x_grid, Ugrid y_grid, BCtype_c xBC, BCtype_c yBC, int num_splines) { // Create new spline multi_UBspline_2d_c* restrict spline = new multi_UBspline_2d_c; if (!spline) { fprintf (stderr, "Out of memory allocating spline in create_multi_UBspline_2d_c.\n"); abort(); } spline->spcode = MULTI_U2D; spline->tcode = SINGLE_COMPLEX; spline->xBC = xBC; spline->yBC = yBC; spline->num_splines = num_splines; // Setup internal variables int Mx = x_grid.num; int My = y_grid.num; int Nx, Ny; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; x_grid.delta = (x_grid.end - x_grid.start)/(double)(Nx-3); x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; if (yBC.lCode == PERIODIC || yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; y_grid.delta = (y_grid.end - y_grid.start)/(double)(Ny-3); y_grid.delta_inv = 1.0/y_grid.delta; spline->y_grid = y_grid; int N = num_splines; #ifdef HAVE_SSE if (N % 2) N++; #endif spline->x_stride = Ny*N; spline->y_stride = N; #ifndef HAVE_POSIX_MEMALIGN - spline->coefs = new complex_float[Nx*Ny*N]; - spline->lapl2 = new complex_float[2*N]; + spline->coefs = (complex_float*)malloc (2*sizeof(float)*Nx*Ny*N); + spline->lapl2 = (complex_float*)malloc (4*sizeof(float)*N); #else posix_memalign ((void**)&spline->coefs, 64, 2*sizeof(float)*Nx*Ny*N); posix_memalign ((void**)&spline->lapl2, 64, 4*sizeof(float)*N); #endif #ifdef HAVE_SSE init_sse_data(); #endif if (!spline->coefs || !spline->lapl2) { fprintf (stderr, "Out of memory allocating spline coefficients in create_multi_UBspline_2d_c.\n"); abort(); } return spline; } void set_multi_UBspline_2d_c (multi_UBspline_2d_c* spline, int num, complex_float *data) { // Setup internal variables int Mx = spline->x_grid.num; int My = spline->y_grid.num; int Nx, Ny; complex_float* coefs = spline->coefs + num; if (spline->xBC.lCode == PERIODIC || spline->xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; if (spline->yBC.lCode == PERIODIC || spline->yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; BCtype_s xBC_r, xBC_i, yBC_r, yBC_i; xBC_r.lCode = spline->xBC.lCode; xBC_r.rCode = spline->xBC.rCode; xBC_r.lVal = spline->xBC.lVal_r; xBC_r.rVal = spline->xBC.rVal_r; xBC_i.lCode = spline->xBC.lCode; xBC_i.rCode = spline->xBC.rCode; xBC_i.lVal = spline->xBC.lVal_i; xBC_i.rVal = spline->xBC.rVal_i; yBC_r.lCode = spline->yBC.lCode; yBC_r.rCode = spline->yBC.rCode; yBC_r.lVal = spline->yBC.lVal_r; yBC_r.rVal = spline->yBC.rVal_r; yBC_i.lCode = spline->yBC.lCode; yBC_i.rCode = spline->yBC.rCode; yBC_i.lVal = spline->yBC.lVal_i; yBC_i.rVal = spline->yBC.rVal_i; int ys = spline->y_stride; // First, solve in the X-direction for (int iy=0; iyx_grid, xBC_r, ((float*)data)+doffset, (intptr_t)2*My, (float*)coefs+coffset, (intptr_t)2*Ny*ys); // Imag part find_coefs_1d_s (spline->x_grid, xBC_i, ((float*)data)+doffset+1, (intptr_t)2*My, ((float*)coefs)+coffset+1, (intptr_t)2*Ny*ys); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, yBC_r, ((float*)coefs)+doffset, (intptr_t)2*ys, ((float*)coefs)+coffset, (intptr_t)2*ys); // Imag part find_coefs_1d_s (spline->y_grid, yBC_i, ((float*)coefs)+doffset+1, (intptr_t)2*ys, ((float*)coefs)+coffset+1, (intptr_t)2*ys); } } multi_UBspline_3d_c* create_multi_UBspline_3d_c (Ugrid x_grid, Ugrid y_grid, Ugrid z_grid, BCtype_c xBC, BCtype_c yBC, BCtype_c zBC, int num_splines) { // Create new spline multi_UBspline_3d_c* restrict spline = new multi_UBspline_3d_c; if (!spline) { fprintf (stderr, "Out of memory allocating spline in create_multi_UBspline_3d_c.\n"); abort(); } spline->spcode = MULTI_U3D; spline->tcode = SINGLE_COMPLEX; spline->xBC = xBC; spline->yBC = yBC; spline->zBC = zBC; spline->num_splines = num_splines; // Setup internal variables int Mx = x_grid.num; int My = y_grid.num; int Mz = z_grid.num; int Nx, Ny, Nz; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; x_grid.delta = (x_grid.end - x_grid.start)/(double)(Nx-3); x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; if (yBC.lCode == PERIODIC || yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; y_grid.delta = (y_grid.end - y_grid.start)/(double)(Ny-3); y_grid.delta_inv = 1.0/y_grid.delta; spline->y_grid = y_grid; if (zBC.lCode == PERIODIC || zBC.lCode == ANTIPERIODIC) Nz = Mz+3; else Nz = Mz+2; z_grid.delta = (z_grid.end - z_grid.start)/(double)(Nz-3); z_grid.delta_inv = 1.0/z_grid.delta; spline->z_grid = z_grid; int N = spline->num_splines; #ifdef HAVE_SSE if (N % 2) N++; #endif spline->x_stride = Ny*Nz*N; spline->y_stride = Nz*N; spline->z_stride = N; #ifndef HAVE_POSIX_MEMALIGN - spline->coefs = new complex_float[Nx*Ny*Nz*N]; - spline->lapl3 = new complex_float[3*N]; + spline->coefs = (complex_float*)malloc (2*sizeof(float)*Nx*Ny*Nz*N); + spline->lapl3 = (complex_float*)malloc (6*sizeof(float)*N); #else posix_memalign ((void**)&spline->coefs, 64, 2*sizeof(float)*Nx*Ny*Nz*N); posix_memalign ((void**)&spline->lapl3, 64, 6*sizeof(float)*N); #endif #ifdef HAVE_SSE init_sse_data(); #endif if (!spline->coefs || !spline->lapl3) { fprintf (stderr, "Out of memory allocating spline coefficients in create_multi_UBspline_3d_c.\n"); abort(); } return spline; } void set_multi_UBspline_3d_c (multi_UBspline_3d_c* spline, int num, complex_float *data) { // Setup internal variables int Mx = spline->x_grid.num; int My = spline->y_grid.num; int Mz = spline->z_grid.num; int Nx, Ny, Nz; if (spline->xBC.lCode == PERIODIC || spline->xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; if (spline->yBC.lCode == PERIODIC || spline->yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; if (spline->zBC.lCode == PERIODIC || spline->zBC.lCode == ANTIPERIODIC) Nz = Mz+3; else Nz = Mz+2; BCtype_s xBC_r, xBC_i, yBC_r, yBC_i, zBC_r, zBC_i; xBC_r.lCode = spline->xBC.lCode; xBC_r.rCode = spline->xBC.rCode; xBC_r.lVal = spline->xBC.lVal_r; xBC_r.rVal = spline->xBC.rVal_r; xBC_i.lCode = spline->xBC.lCode; xBC_i.rCode = spline->xBC.rCode; xBC_i.lVal = spline->xBC.lVal_i; xBC_i.rVal = spline->xBC.rVal_i; yBC_r.lCode = spline->yBC.lCode; yBC_r.rCode = spline->yBC.rCode; yBC_r.lVal = spline->yBC.lVal_r; yBC_r.rVal = spline->yBC.rVal_r; yBC_i.lCode = spline->yBC.lCode; yBC_i.rCode = spline->yBC.rCode; yBC_i.lVal = spline->yBC.lVal_i; yBC_i.rVal = spline->yBC.rVal_i; zBC_r.lCode = spline->zBC.lCode; zBC_r.rCode = spline->zBC.rCode; zBC_r.lVal = spline->zBC.lVal_r; zBC_r.rVal = spline->zBC.rVal_r; zBC_i.lCode = spline->zBC.lCode; zBC_i.rCode = spline->zBC.rCode; zBC_i.lVal = spline->zBC.lVal_i; zBC_i.rVal = spline->zBC.rVal_i; complex_float *coefs = spline->coefs + num; int zs = spline->z_stride; // First, solve in the X-direction for (int iy=0; iyx_grid, xBC_r, ((float*)data)+doffset, (intptr_t)2*My*Mz, ((float*)coefs)+coffset, (intptr_t)2*Ny*Nz*zs); // Imag part find_coefs_1d_s (spline->x_grid, xBC_i, ((float*)data)+doffset+1, (intptr_t)2*My*Mz, ((float*)coefs)+coffset+1, (intptr_t)2*Ny*Nz*zs); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, yBC_r, ((float*)coefs)+doffset, (intptr_t)2*Nz*zs, ((float*)coefs)+coffset, (intptr_t)2*Nz*zs); // Imag part find_coefs_1d_s (spline->y_grid, yBC_i, ((float*)coefs)+doffset+1, (intptr_t)2*Nz*zs, ((float*)coefs)+coffset+1, (intptr_t)2*Nz*zs); } // Now, solve in the Z-direction for (int ix=0; ixz_grid, zBC_r, ((float*)coefs)+doffset, (intptr_t)2*zs, ((float*)coefs)+coffset, (intptr_t)2*zs); // Imag part find_coefs_1d_s (spline->z_grid, zBC_i, ((float*)coefs)+doffset+1, (intptr_t)2*zs, ((float*)coefs)+coffset+1, (intptr_t)2*zs); } } //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// //// Double-Precision, Real Creation Routines //// //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// // On input, bands should be filled with: // row 0 : abcdInitial from boundary conditions // rows 1:M: basis functions in first 3 cols, data in last // row M+1 : abcdFinal from boundary conditions // cstride gives the stride between values in coefs. // On exit, coefs with contain interpolating B-spline coefs void solve_deriv_interp_1d_d (double bands[], double coefs[], int M, int cstride); // On input, bands should be filled with: // row 0 : abcdInitial from boundary conditions // rows 1:M: basis functions in first 3 cols, data in last // row M+1 : abcdFinal from boundary conditions // cstride gives the stride between values in coefs. // On exit, coefs with contain interpolating B-spline coefs void solve_periodic_interp_1d_d (double bands[], double coefs[], int M, int cstride); void find_coefs_1d_d (Ugrid grid, BCtype_d bc, double *data, intptr_t dstride, double *coefs, intptr_t cstride); multi_UBspline_1d_d* create_multi_UBspline_1d_d (Ugrid x_grid, BCtype_d xBC, int num_splines) { // Create new spline multi_UBspline_1d_d* restrict spline = new multi_UBspline_1d_d; if (!spline) { fprintf (stderr, "Out of memory allocating spline in create_multi_UBspline_1d_d.\n"); abort(); } spline->spcode = MULTI_U1D; spline->tcode = DOUBLE_REAL; spline->xBC = xBC; spline->num_splines = num_splines; // Setup internal variables int Mx = x_grid.num; int Nx; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) { x_grid.delta = (x_grid.end-x_grid.start)/(double)(x_grid.num); Nx = Mx+3; } else { x_grid.delta = (x_grid.end-x_grid.start)/(double)(x_grid.num-1); Nx = Mx+2; } x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; int N = num_splines; #ifdef HAVE_SSE2 // We must pad to keep data aligned for SSE operations if (N & 1) N++; #endif spline->x_stride = N; #ifndef HAVE_POSIX_MEMALIGN - spline->coefs = new double[Nx*N]; + spline->coefs = (double*)malloc (sizeof(double)*Nx*N); #else posix_memalign ((void**)&spline->coefs, 64, sizeof(double)*Nx*N); #endif #ifdef HAVE_SSE2 init_sse_data(); #endif if (!spline->coefs) { fprintf (stderr, "Out of memory allocating spline coefficients in create_multi_UBspline_1d_d.\n"); abort(); } return spline; } void set_multi_UBspline_1d_d (multi_UBspline_1d_d* spline, int num, double *data) { double *coefs = spline->coefs + num; int xs = spline->x_stride; find_coefs_1d_d (spline->x_grid, spline->xBC, data, 1, coefs, xs); } void set_multi_UBspline_1d_d_BC (multi_UBspline_1d_d* spline, int num, double *data, BCtype_d xBC) { double *coefs = spline->coefs + num; int xs = spline->x_stride; find_coefs_1d_d (spline->x_grid, xBC, data, 1, coefs, xs); } multi_UBspline_2d_d* create_multi_UBspline_2d_d (Ugrid x_grid, Ugrid y_grid, BCtype_d xBC, BCtype_d yBC, int num_splines) { // Create new spline multi_UBspline_2d_d* restrict spline = new multi_UBspline_2d_d; if (!spline) { fprintf (stderr, "Out of memory allocating spline in create_multi_UBspline_2d_d.\n"); abort(); } spline->spcode = MULTI_U2D; spline->tcode = DOUBLE_REAL; spline->xBC = xBC; spline->yBC = yBC; spline->num_splines = num_splines; // Setup internal variables int Mx = x_grid.num; int My = y_grid.num; int Nx, Ny; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; x_grid.delta = (x_grid.end - x_grid.start)/(double)(Nx-3); x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; if (yBC.lCode == PERIODIC || yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; y_grid.delta = (y_grid.end - y_grid.start)/(double)(Ny-3); y_grid.delta_inv = 1.0/y_grid.delta; spline->y_grid = y_grid; int N = num_splines; #ifdef HAVE_SSE2 // We must pad to keep data align for SSE operations if (num_splines & 1) N++; #endif spline->x_stride = Ny*N; spline->y_stride = N; #ifndef HAVE_POSIX_MEMALIGN - spline->coefs = new double[Nx*Ny*N]; + spline->coefs = (double*)malloc (sizeof(double)*Nx*Ny*N); #else posix_memalign ((void**)&spline->coefs, 64, (sizeof(double)*Nx*Ny*N)); #endif #ifdef HAVE_SSE2 init_sse_data(); #endif if (!spline->coefs) { fprintf (stderr, "Out of memory allocating spline coefficients in create_multi_UBspline_2d_d.\n"); abort(); } return spline; } void set_multi_UBspline_2d_d (multi_UBspline_2d_d* spline, int num, double *data) { int Mx = spline->x_grid.num; int My = spline->y_grid.num; int Nx, Ny; double *coefs = spline->coefs + num; if (spline->xBC.lCode == PERIODIC || spline->xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; if (spline->yBC.lCode == PERIODIC || spline->yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; int ys = spline->y_stride; // First, solve in the X-direction for (int iy=0; iyx_grid, spline->xBC, data+doffset, (intptr_t)My, coefs+coffset, (intptr_t)Ny*ys); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, spline->yBC, coefs+doffset, (intptr_t)ys, coefs+coffset, (intptr_t)ys); } } multi_UBspline_3d_d* create_multi_UBspline_3d_d (Ugrid x_grid, Ugrid y_grid, Ugrid z_grid, BCtype_d xBC, BCtype_d yBC, BCtype_d zBC, int num_splines) { // Create new spline multi_UBspline_3d_d* restrict spline; #ifdef HAVE_POSIX_MEMALIGN posix_memalign ((void**)&spline, 64, (size_t)sizeof(multi_UBspline_3d_d)); #else spline = new multi_UBspline_3d_d; #endif if (!spline) { fprintf (stderr, "Out of memory allocating spline in create_multi_UBspline_3d_d.\n"); abort(); } spline->spcode = MULTI_U3D; spline->tcode = DOUBLE_REAL; spline->xBC = xBC; spline->yBC = yBC; spline->zBC = zBC; spline->num_splines = num_splines; // Setup internal variables int Mx = x_grid.num; int My = y_grid.num; int Mz = z_grid.num; int Nx, Ny, Nz; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; x_grid.delta = (x_grid.end - x_grid.start)/(double)(Nx-3); x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; if (yBC.lCode == PERIODIC || yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; y_grid.delta = (y_grid.end - y_grid.start)/(double)(Ny-3); y_grid.delta_inv = 1.0/y_grid.delta; spline->y_grid = y_grid; if (zBC.lCode == PERIODIC || zBC.lCode == ANTIPERIODIC) Nz = Mz+3; else Nz = Mz+2; z_grid.delta = (z_grid.end - z_grid.start)/(double)(Nz-3); z_grid.delta_inv = 1.0/z_grid.delta; spline->z_grid = z_grid; int N = num_splines; #if defined HAVE_SSE2 || defined HAVE_VSX // We must pad to keep data align for SSE operations if (N & 1) N++; #endif spline->x_stride = Ny*Nz*N; spline->y_stride = Nz*N; spline->z_stride = N; #ifdef HAVE_POSIX_MEMALIGN posix_memalign ((void**)&spline->coefs, 64, ((size_t)sizeof(double)*Nx*Ny*Nz*N)); #else spline->coefs = new double[Nx*Ny*Nz*N]; #endif #ifdef HAVE_SSE2 init_sse_data(); #endif if (!spline->coefs) { fprintf (stderr, "Out of memory allocating spline coefficients in create_multi_UBspline_3d_d.\n"); abort(); } return spline; } void set_multi_UBspline_3d_d (multi_UBspline_3d_d* spline, int num, double *data) { int Mx = spline->x_grid.num; int My = spline->y_grid.num; int Mz = spline->z_grid.num; int Nx, Ny, Nz; if (spline->xBC.lCode == PERIODIC || spline->xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; if (spline->yBC.lCode == PERIODIC || spline->yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; if (spline->zBC.lCode == PERIODIC || spline->zBC.lCode == ANTIPERIODIC) Nz = Mz+3; else Nz = Mz+2; double *coefs = spline->coefs + num; intptr_t zs = spline->z_stride; // First, solve in the X-direction for (int iy=0; iyx_grid, spline->xBC, data+doffset, (intptr_t)My*Mz, coefs+coffset, (intptr_t)Ny*Nz*zs); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, spline->yBC, coefs+doffset, (intptr_t)Nz*zs, coefs+coffset, (intptr_t)Nz*zs); } // Now, solve in the Z-direction for (int ix=0; ixz_grid, spline->zBC, coefs+doffset, (intptr_t)zs, coefs+coffset, (intptr_t)zs); } } //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// //// Double-Precision, Complex Creation Routines //// //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// // On input, bands should be filled with: // row 0 : abcdInitial from boundary conditions // rows 1:M: basis functions in first 3 cols, data in last // row M+1 : abcdFinal from boundary conditions // cstride gives the stride between values in coefs. // On exit, coefs with contain interpolating B-spline coefs multi_UBspline_1d_z* create_multi_UBspline_1d_z (Ugrid x_grid, BCtype_z xBC, int num_splines) { // Create new spline multi_UBspline_1d_z* restrict spline = new multi_UBspline_1d_z; if (!spline) { fprintf (stderr, "Out of memory allocating spline in create_multi_UBspline_1d_z.\n"); abort(); } spline->spcode = MULTI_U1D; spline->tcode = DOUBLE_COMPLEX; spline->xBC = xBC; spline->num_splines = num_splines; // Setup internal variables int Mx = x_grid.num; int Nx; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) { x_grid.delta = (x_grid.end-x_grid.start)/(double)(x_grid.num); Nx = Mx+3; } else { x_grid.delta = (x_grid.end-x_grid.start)/(double)(x_grid.num-1); Nx = Mx+2; } x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; spline->x_stride = num_splines; #ifndef HAVE_POSIX_MEMALIGN - spline->coefs = new complex_double[Nx*num_splines]; + spline->coefs = (complex_double*)malloc (2*sizeof(double)*Nx*num_splines); #else posix_memalign ((void**)&spline->coefs, 64, 2*sizeof(double)*Nx*num_splines); #endif #ifdef HAVE_SSE2 init_sse_data(); #endif if (!spline->coefs) { fprintf (stderr, "Out of memory allocating spline coefficients in create_multi_UBspline_1d_z.\n"); abort(); } return spline; } void set_multi_UBspline_1d_z (multi_UBspline_1d_z* spline, int num, complex_double *data) { // int Mx = spline->x_grid.num; // Set but not used // int Nx; complex_double *coefs = spline->coefs + num; // if (spline->xBC.lCode == PERIODIC || spline->xBC.lCode == ANTIPERIODIC) // Nx = Mx+3; // else // Nx = Mx+2; BCtype_d xBC_r, xBC_i; xBC_r.lCode = spline->xBC.lCode; xBC_r.rCode = spline->xBC.rCode; xBC_r.lVal = spline->xBC.lVal_r; xBC_r.rVal = spline->xBC.rVal_r; xBC_i.lCode = spline->xBC.lCode; xBC_i.rCode = spline->xBC.rCode; xBC_i.lVal = spline->xBC.lVal_i; xBC_i.rVal = spline->xBC.rVal_i; int xs = spline->x_stride; // Real part find_coefs_1d_d (spline->x_grid, xBC_r, (double*)data, (intptr_t)2, ((double*)coefs), (intptr_t)2*xs); // Imaginary part find_coefs_1d_d (spline->x_grid, xBC_i, ((double*)data)+1, (intptr_t)2, ((double*)coefs)+1, (intptr_t)2*xs); } void set_multi_UBspline_1d_z_BC (multi_UBspline_1d_z *spline, int num, complex_double *data, BCtype_z xBC) { // int Mx = spline->x_grid.num; // int Nx; complex_double *coefs = spline->coefs + num; // if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) // Nx = Mx+3; // else // Nx = Mx+2; BCtype_d xBC_r, xBC_i; xBC_r.lCode = xBC.lCode; xBC_r.rCode = xBC.rCode; xBC_r.lVal = xBC.lVal_r; xBC_r.rVal = xBC.rVal_r; xBC_i.lCode = xBC.lCode; xBC_i.rCode = xBC.rCode; xBC_i.lVal = xBC.lVal_i; xBC_i.rVal = xBC.rVal_i; int xs = spline->x_stride; // Real part find_coefs_1d_d (spline->x_grid, xBC_r, (double*)data, (intptr_t)2, ((double*)coefs), (intptr_t)2*xs); // Imaginary part find_coefs_1d_d (spline->x_grid, xBC_i, ((double*)data)+1, (intptr_t)2, ((double*)coefs)+1, (intptr_t)2*xs); } multi_UBspline_2d_z* create_multi_UBspline_2d_z (Ugrid x_grid, Ugrid y_grid, BCtype_z xBC, BCtype_z yBC, int num_splines) { // Create new spline multi_UBspline_2d_z* restrict spline = new multi_UBspline_2d_z; if (!spline) { fprintf (stderr, "Out of memory allocating spline in create_multi_UBspline_2d_z.\n"); abort(); } spline->spcode = MULTI_U2D; spline->tcode = DOUBLE_COMPLEX; spline->xBC = xBC; spline->yBC = yBC; spline->num_splines = num_splines; // Setup internal variables int Mx = x_grid.num; int My = y_grid.num; int Nx, Ny; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; x_grid.delta = (x_grid.end - x_grid.start)/(double)(Nx-3); x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; if (yBC.lCode == PERIODIC || yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; y_grid.delta = (y_grid.end - y_grid.start)/(double)(Ny-3); y_grid.delta_inv = 1.0/y_grid.delta; spline->y_grid = y_grid; spline->x_stride = Ny*num_splines; spline->y_stride = num_splines; #ifndef HAVE_POSIX_MEMALIGN - spline->coefs = new complex_double[Nx*Ny*num_splines]; - spline->lapl2 = new complex_double[2*num_splines]; + spline->coefs = (complex_double*)malloc (2*sizeof(double)*Nx*Ny*num_splines); + spline->lapl2 = (complex_double*)malloc (4*sizeof(double)*num_splines); #else posix_memalign ((void**)&spline->coefs, 64, 2*sizeof(double)*Nx*Ny*num_splines); posix_memalign ((void**)&spline->lapl2, 64, 4*sizeof(double)*num_splines); #endif #ifdef HAVE_SSE2 init_sse_data(); #endif if (!spline->coefs || !spline->lapl2) { fprintf (stderr, "Out of memory allocating spline coefficients in create_multi_UBspline_2d_z.\n"); abort(); } return spline; } void set_multi_UBspline_2d_z (multi_UBspline_2d_z* spline, int num, complex_double *data) { int Mx = spline->x_grid.num; int My = spline->y_grid.num; int Nx, Ny; if (spline->xBC.lCode == PERIODIC || spline->xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; if (spline->yBC.lCode == PERIODIC || spline->yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; BCtype_d xBC_r, xBC_i, yBC_r, yBC_i; xBC_r.lCode = spline->xBC.lCode; xBC_r.rCode = spline->xBC.rCode; xBC_r.lVal = spline->xBC.lVal_r; xBC_r.rVal = spline->xBC.rVal_r; xBC_i.lCode = spline->xBC.lCode; xBC_i.rCode = spline->xBC.rCode; xBC_i.lVal = spline->xBC.lVal_i; xBC_i.rVal = spline->xBC.rVal_i; yBC_r.lCode = spline->yBC.lCode; yBC_r.rCode = spline->yBC.rCode; yBC_r.lVal = spline->yBC.lVal_r; yBC_r.rVal = spline->yBC.rVal_r; yBC_i.lCode = spline->yBC.lCode; yBC_i.rCode = spline->yBC.rCode; yBC_i.lVal = spline->yBC.lVal_i; yBC_i.rVal = spline->yBC.rVal_i; complex_double *coefs = spline->coefs + num; int ys = spline->y_stride; // First, solve in the X-direction for (int iy=0; iyx_grid, xBC_r, ((double*)data+doffset), (intptr_t)2*My, (double*)coefs+coffset, (intptr_t)2*Ny*ys); // Imag part find_coefs_1d_d (spline->x_grid, xBC_i, ((double*)data)+doffset+1, (intptr_t)2*My, ((double*)coefs)+coffset+1, (intptr_t)2*Ny*ys); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, yBC_r, ((double*)coefs)+doffset, (intptr_t)2*ys, (double*)coefs+coffset, (intptr_t)2*ys); // Imag part find_coefs_1d_d (spline->y_grid, yBC_i, (double*)coefs+doffset+1, (intptr_t)2*ys, ((double*)coefs)+coffset+1, (intptr_t)2*ys); } } multi_UBspline_3d_z* create_multi_UBspline_3d_z (Ugrid x_grid, Ugrid y_grid, Ugrid z_grid, BCtype_z xBC, BCtype_z yBC, BCtype_z zBC, int num_splines) { // Create new spline multi_UBspline_3d_z* restrict spline = new multi_UBspline_3d_z; if (!spline) { fprintf (stderr, "Out of memory allocating spline in create_multi_UBspline_3d_z.\n"); abort(); } spline->spcode = MULTI_U3D; spline->tcode = DOUBLE_COMPLEX; spline->xBC = xBC; spline->yBC = yBC; spline->zBC = zBC; spline->num_splines = num_splines; // Setup internal variables int Mx = x_grid.num; int My = y_grid.num; int Mz = z_grid.num; int Nx, Ny, Nz; if (xBC.lCode == PERIODIC || xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; x_grid.delta = (x_grid.end - x_grid.start)/(double)(Nx-3); x_grid.delta_inv = 1.0/x_grid.delta; spline->x_grid = x_grid; if (yBC.lCode == PERIODIC || yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; y_grid.delta = (y_grid.end - y_grid.start)/(double)(Ny-3); y_grid.delta_inv = 1.0/y_grid.delta; spline->y_grid = y_grid; if (zBC.lCode == PERIODIC || zBC.lCode == ANTIPERIODIC) Nz = Mz+3; else Nz = Mz+2; z_grid.delta = (z_grid.end - z_grid.start)/(double)(Nz-3); z_grid.delta_inv = 1.0/z_grid.delta; spline->z_grid = z_grid; int N = num_splines; #ifdef HAVE_SSE2 if (N & 3) N += 4-(N & 3); #endif spline->x_stride = (intptr_t)Ny*(intptr_t)Nz*N; spline->y_stride = Nz*N; spline->z_stride = N; #ifndef HAVE_POSIX_MEMALIGN spline->coefs = new complex_double[Nx*Ny*Nz*N]; spline->lapl3 = new complex_double[3*N]; #else posix_memalign ((void**)&spline->coefs, 64, (size_t)2*sizeof(double)*Nx*Ny*Nz*N); posix_memalign ((void**)&spline->lapl3, 64, 6*sizeof(double)*N); #endif #ifdef HAVE_SSE2 init_sse_data(); #endif if (!spline->coefs || !spline->lapl3) { fprintf (stderr, "Out of memory allocating spline coefficients in create_multi_UBspline_3d_z.\n"); abort(); } return spline; } void set_multi_UBspline_3d_z (multi_UBspline_3d_z* spline, int num, complex_double *data) { // Setup internal variables int Mx = spline->x_grid.num; int My = spline->y_grid.num; int Mz = spline->z_grid.num; int Nx, Ny, Nz; if (spline->xBC.lCode == PERIODIC || spline->xBC.lCode == ANTIPERIODIC) Nx = Mx+3; else Nx = Mx+2; if (spline->yBC.lCode == PERIODIC || spline->yBC.lCode == ANTIPERIODIC) Ny = My+3; else Ny = My+2; if (spline->zBC.lCode == PERIODIC || spline->zBC.lCode == ANTIPERIODIC) Nz = Mz+3; else Nz = Mz+2; BCtype_d xBC_r, xBC_i, yBC_r, yBC_i, zBC_r, zBC_i; xBC_r.lCode = spline->xBC.lCode; xBC_r.rCode = spline->xBC.rCode; xBC_r.lVal = spline->xBC.lVal_r; xBC_r.rVal = spline->xBC.rVal_r; xBC_i.lCode = spline->xBC.lCode; xBC_i.rCode = spline->xBC.rCode; xBC_i.lVal = spline->xBC.lVal_i; xBC_i.rVal = spline->xBC.rVal_i; yBC_r.lCode = spline->yBC.lCode; yBC_r.rCode = spline->yBC.rCode; yBC_r.lVal = spline->yBC.lVal_r; yBC_r.rVal = spline->yBC.rVal_r; yBC_i.lCode = spline->yBC.lCode; yBC_i.rCode = spline->yBC.rCode; yBC_i.lVal = spline->yBC.lVal_i; yBC_i.rVal = spline->yBC.rVal_i; zBC_r.lCode = spline->zBC.lCode; zBC_r.rCode = spline->zBC.rCode; zBC_r.lVal = spline->zBC.lVal_r; zBC_r.rVal = spline->zBC.rVal_r; zBC_i.lCode = spline->zBC.lCode; zBC_i.rCode = spline->zBC.rCode; zBC_i.lVal = spline->zBC.lVal_i; zBC_i.rVal = spline->zBC.rVal_i; complex_double *coefs = spline->coefs + num; // unused variable // int N = spline->num_splines; int zs = spline->z_stride; // First, solve in the X-direction for (int iy=0; iyx_grid, xBC_r, ((double*)data)+doffset, (intptr_t)2*My*Mz, ((double*)coefs)+coffset, (intptr_t)2*Ny*Nz*zs); // Imag part find_coefs_1d_d (spline->x_grid, xBC_i, ((double*)data)+doffset+1, (intptr_t)2*My*Mz, ((double*)coefs)+coffset+1, (intptr_t)2*Ny*Nz*zs); } // Now, solve in the Y-direction for (int ix=0; ixy_grid, yBC_r, ((double*)coefs)+doffset, (intptr_t)2*Nz*zs, ((double*)coefs)+coffset, (intptr_t)2*Nz*zs); // Imag part find_coefs_1d_d (spline->y_grid, yBC_i, ((double*)coefs)+doffset+1, (intptr_t)2*Nz*zs, ((double*)coefs)+coffset+1, (intptr_t)2*Nz*zs); } // Now, solve in the Z-direction for (int ix=0; ixz_grid, zBC_r, ((double*)coefs)+doffset, (intptr_t)2*zs, ((double*)coefs)+coffset, (intptr_t)2*zs); // Imag part find_coefs_1d_d (spline->z_grid, zBC_i, ((double*)coefs)+doffset+1, (intptr_t)2*zs, ((double*)coefs)+coffset+1, (intptr_t)2*zs); } } void destroy_multi_UBspline (Bspline *spline) { free(spline->coefs); delete spline; } diff --git a/libs/image/3rdparty/einspline/nubspline_create.cpp b/libs/image/3rdparty/einspline/nubspline_create.cpp index 440fb8bd1e..4a2ee65429 100644 --- a/libs/image/3rdparty/einspline/nubspline_create.cpp +++ b/libs/image/3rdparty/einspline/nubspline_create.cpp @@ -1,1110 +1,1110 @@ ///////////////////////////////////////////////////////////////////////////// // einspline: a library for creating and evaluating B-splines // // Copyright (C) 2007 Kenneth P. Esler, Jr. // // // // This program is free software; you can redistribute 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 "nubspline_create.h" #include #include #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 600 #endif #ifndef __USE_XOPEN2K #define __USE_XOPEN2K #endif #include #include //////////////////////////////////////////////////////// // Notes on conventions: // // Below, M (and Mx, My, Mz) represent the number of // // data points to be interpolated. With derivative // // boundary conditions, it is equal to the number of // // grid points. With periodic boundary conditions, // // it is one less than the number of grid points. // // N (and Nx, Ny, Nz) is the number of B-spline // // coefficients, which is #(grid points)+2 for all // // boundary conditions. // //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// //// Single-precision real creation routines //// //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// void solve_NUB_deriv_interp_1d_s (NUBasis* restrict basis, float* restrict data, int datastride, float* restrict p, int pstride, float abcdInitial[4], float abcdFinal[4]) { int M = basis->grid->num_points; int N = M+2; // Banded matrix storage. The first three elements in the // tinyvector store the tridiagonal coefficients. The last element // stores the RHS data. #ifdef HAVE_C_VARARRAYS float bands[4*N]; #else float *bands = new float[4*N]; #endif // Fill up bands for (int i=0; i<4; i++) { bands[i] = abcdInitial[i]; bands[4*(N-1)+i] = abcdFinal[i]; } for (int i=0; i0; row--) p[pstride*(row)] = bands[4*(row)+3] - bands[4*(row)+2]*p[pstride*(row+1)]; // Finish with first row p[0] = bands[4*(0)+3] - bands[4*(0)+1]*p[pstride*1] - bands[4*(0)+2]*p[pstride*2]; #ifndef HAVE_C_VARARRAYS delete[] bands; #endif } // The number of elements in data should be one less than the number // of grid points void solve_NUB_periodic_interp_1d_s (NUBasis* restrict basis, float* restrict data, int datastride, float* restrict p, int pstride) { int M = basis->grid->num_points-1; // Banded matrix storage. The first three elements in each row // store the tridiagonal coefficients. The last element // stores the RHS data. #ifdef HAVE_C_VARARRAYS float bands[4*M], lastCol[M]; #else float *bands = new float[4*M]; float *lastCol = new float[ M]; #endif // Fill up bands for (int i=0; i=0; row--) p[pstride*(row+1)] = bands[4*(row)+3] - bands[4*(row)+2]*p[pstride*(row+2)] - lastCol[row]*p[pstride*M]; p[pstride* 0 ] = p[pstride*M]; p[pstride*(M+1)] = p[pstride*1]; p[pstride*(M+2)] = p[pstride*2]; #ifndef HAVE_C_VARARRAYS delete[] bands; delete[] lastCol; #endif } void find_NUBcoefs_1d_s (NUBasis* restrict basis, BCtype_s bc, float *data, int dstride, float *coefs, int cstride) { if (bc.lCode == PERIODIC) solve_NUB_periodic_interp_1d_s (basis, data, dstride, coefs, cstride); else { int M = basis->grid->num_points; // Setup boundary conditions float bfuncs[4], dbfuncs[4], abcd_left[4], abcd_right[4]; // Left boundary if (bc.lCode == FLAT || bc.lCode == NATURAL) bc.lVal = 0.0; if (bc.lCode == FLAT || bc.lCode == DERIV1) { get_NUBasis_dfuncs_si (basis, 0, bfuncs, abcd_left); abcd_left[3] = bc.lVal; } if (bc.lCode == NATURAL || bc.lCode == DERIV2) { get_NUBasis_d2funcs_si (basis, 0, bfuncs, dbfuncs, abcd_left); abcd_left[3] = bc.lVal; } // Right boundary if (bc.rCode == FLAT || bc.rCode == NATURAL) bc.rVal = 0.0; if (bc.rCode == FLAT || bc.rCode == DERIV1) { get_NUBasis_dfuncs_si (basis, M-1, bfuncs, abcd_right); abcd_right[3] = bc.rVal; } if (bc.rCode == NATURAL || bc.rCode == DERIV2) { get_NUBasis_d2funcs_si (basis, M-1, bfuncs, dbfuncs, abcd_right); abcd_right[3] = bc.rVal; } // Now, solve for coefficients solve_NUB_deriv_interp_1d_s (basis, data, dstride, coefs, cstride, abcd_left, abcd_right); } } NUBspline_1d_s * create_NUBspline_1d_s (NUgrid* x_grid, BCtype_s xBC, float *data) { // First, create the spline structure NUBspline_1d_s* spline = new NUBspline_1d_s; if (spline == NULL) return spline; spline->sp_code = NU1D; spline->t_code = SINGLE_REAL; // Next, create the basis spline->x_basis = create_NUBasis (x_grid, xBC.lCode==PERIODIC); // M is the number of data points (but is unused) // int M; // if (xBC.lCode == PERIODIC) M = x_grid->num_points - 1; // else M = x_grid->num_points; int N = x_grid->num_points + 2; // Allocate coefficients and solve - spline->coefs = new float[N]; + spline->coefs = (float*)malloc (sizeof(float)*N); find_NUBcoefs_1d_s (spline->x_basis, xBC, data, 1, spline->coefs, 1); return spline; } NUBspline_2d_s * create_NUBspline_2d_s (NUgrid* x_grid, NUgrid* y_grid, BCtype_s xBC, BCtype_s yBC, float *data) { // First, create the spline structure NUBspline_2d_s* spline = new NUBspline_2d_s; if (spline == NULL) return spline; spline->sp_code = NU2D; spline->t_code = SINGLE_REAL; // Next, create the bases spline->x_basis = create_NUBasis (x_grid, xBC.lCode==PERIODIC); spline->y_basis = create_NUBasis (y_grid, yBC.lCode==PERIODIC); // set but unused //int Mx, int My, Nx, Ny; // if (xBC.lCode == PERIODIC) Mx = x_grid->num_points - 1; // else Mx = x_grid->num_points; if (yBC.lCode == PERIODIC) My = y_grid->num_points - 1; else My = y_grid->num_points; Nx = x_grid->num_points + 2; Ny = y_grid->num_points + 2; spline->x_stride = Ny; #ifndef HAVE_SSE2 - spline->coefs = new float[Nx*Ny]; + spline->coefs = (float*)malloc (sizeof(float)*Nx*Ny); #else posix_memalign ((void**)&spline->coefs, 16, sizeof(float)*Nx*Ny); #endif // First, solve in the X-direction for (int iy=0; iyx_basis, xBC, data+doffset, My, spline->coefs+coffset, Ny); } // Now, solve in the Y-direction for (int ix=0; ixy_basis, yBC, spline->coefs+doffset, 1, spline->coefs+coffset, 1); } return spline; } NUBspline_3d_s * create_NUBspline_3d_s (NUgrid* x_grid, NUgrid* y_grid, NUgrid* z_grid, BCtype_s xBC, BCtype_s yBC, BCtype_s zBC, float *data) { // First, create the spline structure NUBspline_3d_s* spline = new NUBspline_3d_s; if (spline == NULL) return spline; spline->sp_code = NU3D; spline->t_code = SINGLE_REAL; // Next, create the bases spline->x_basis = create_NUBasis (x_grid, xBC.lCode==PERIODIC); spline->y_basis = create_NUBasis (y_grid, yBC.lCode==PERIODIC); spline->z_basis = create_NUBasis (z_grid, zBC.lCode==PERIODIC); // set but unused // int Mx, int My, Mz, Nx, Ny, Nz; // if (xBC.lCode == PERIODIC) Mx = x_grid->num_points - 1; // else Mx = x_grid->num_points; if (yBC.lCode == PERIODIC) My = y_grid->num_points - 1; else My = y_grid->num_points; if (zBC.lCode == PERIODIC) Mz = z_grid->num_points - 1; else Mz = z_grid->num_points; Nx = x_grid->num_points + 2; Ny = y_grid->num_points + 2; Nz = z_grid->num_points + 2; // Allocate coefficients and solve spline->x_stride = Ny*Nz; spline->y_stride = Nz; #ifndef HAVE_SSE2 - spline->coefs = new float[Nx*Ny*Nz]; + spline->coefs = (float*)malloc (sizeof(float)*Nx*Ny*Nz); #else posix_memalign ((void**)&spline->coefs, 16, sizeof(float)*Nx*Ny*Nz); #endif // First, solve in the X-direction for (int iy=0; iyx_basis, xBC, data+doffset, My*Mz, spline->coefs+coffset, Ny*Nz); } // Now, solve in the Y-direction for (int ix=0; ixy_basis, yBC, spline->coefs+doffset, Nz, spline->coefs+coffset, Nz); } // Now, solve in the Z-direction for (int ix=0; ixz_basis, zBC, spline->coefs+doffset, 1, spline->coefs+coffset, 1); } return spline; } //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// //// Double-precision real creation routines //// //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// void solve_NUB_deriv_interp_1d_d (NUBasis* restrict basis, double* restrict data, int datastride, double* restrict p, int pstride, double abcdInitial[4], double abcdFinal[4]) { int M = basis->grid->num_points; int N = M+2; // Banded matrix storage. The first three elements in the // tinyvector store the tridiagonal coefficients. The last element // stores the RHS data. #ifdef HAVE_C_VARARRAYS double bands[4*N]; #else double *bands = new double[4*N]; #endif // Fill up bands for (int i=0; i<4; i++) { bands[i] = abcdInitial[i]; bands[4*(N-1)+i] = abcdFinal[i]; } for (int i=0; i0; row--) p[pstride*(row)] = bands[4*(row)+3] - bands[4*(row)+2]*p[pstride*(row+1)]; // Finish with first row p[0] = bands[4*(0)+3] - bands[4*(0)+1]*p[pstride*1] - bands[4*(0)+2]*p[pstride*2]; #ifndef HAVE_C_VARARRAYS delete[] bands; #endif } void solve_NUB_periodic_interp_1d_d (NUBasis* restrict basis, double* restrict data, int datastride, double* restrict p, int pstride) { int M = basis->grid->num_points-1; // Banded matrix storage. The first three elements in the // tinyvector store the tridiagonal coefficients. The last element // stores the RHS data. #ifdef HAVE_C_VARARRAYS double bands[4*M], lastCol[M]; #else double *bands = new double[4*M]; double *lastCol = new double[ M]; #endif // Fill up bands for (int i=0; i=0; row--) p[pstride*(row+1)] = bands[4*(row)+3] - bands[4*(row)+2]*p[pstride*(row+2)] - lastCol[row]*p[pstride*M]; p[pstride* 0 ] = p[pstride*M]; p[pstride*(M+1)] = p[pstride*1]; p[pstride*(M+2)] = p[pstride*2]; #ifndef HAVE_C_VARARRAYS delete[] bands; delete[] lastCol; #endif } void find_NUBcoefs_1d_d (NUBasis* restrict basis, BCtype_d bc, double *data, int dstride, double *coefs, int cstride) { if (bc.lCode == PERIODIC) solve_NUB_periodic_interp_1d_d (basis, data, dstride, coefs, cstride); else { int M = basis->grid->num_points; // Setup boundary conditions double bfuncs[4], dbfuncs[4], abcd_left[4], abcd_right[4]; // Left boundary if (bc.lCode == FLAT || bc.lCode == NATURAL) bc.lVal = 0.0; if (bc.lCode == FLAT || bc.lCode == DERIV1) { get_NUBasis_dfuncs_di (basis, 0, bfuncs, abcd_left); abcd_left[3] = bc.lVal; } if (bc.lCode == NATURAL || bc.lCode == DERIV2) { get_NUBasis_d2funcs_di (basis, 0, bfuncs, dbfuncs, abcd_left); abcd_left[3] = bc.lVal; } // Right boundary if (bc.rCode == FLAT || bc.rCode == NATURAL) bc.rVal = 0.0; if (bc.rCode == FLAT || bc.rCode == DERIV1) { get_NUBasis_dfuncs_di (basis, M-1, bfuncs, abcd_right); abcd_right[3] = bc.rVal; } if (bc.rCode == NATURAL || bc.rCode == DERIV2) { get_NUBasis_d2funcs_di (basis, M-1, bfuncs, dbfuncs, abcd_right); abcd_right[3] = bc.rVal; } // Now, solve for coefficients solve_NUB_deriv_interp_1d_d (basis, data, dstride, coefs, cstride, abcd_left, abcd_right); } } NUBspline_1d_d * create_NUBspline_1d_d (NUgrid* x_grid, BCtype_d xBC, double *data) { // First, create the spline structure NUBspline_1d_d* spline = new NUBspline_1d_d; if (spline == NULL) return spline; spline->sp_code = NU1D; spline->t_code = DOUBLE_REAL; // Next, create the basis spline->x_basis = create_NUBasis (x_grid, xBC.lCode==PERIODIC); // M is the number of data points (set but unused) // int M; // if (xBC.lCode == PERIODIC) M = x_grid->num_points - 1; // else M = x_grid->num_points; int N = x_grid->num_points + 2; // Allocate coefficients and solve - spline->coefs = new double[N]; + spline->coefs = (double*)malloc (sizeof(double)*N); find_NUBcoefs_1d_d (spline->x_basis, xBC, data, 1, spline->coefs, 1); return spline; } NUBspline_2d_d * create_NUBspline_2d_d (NUgrid* x_grid, NUgrid* y_grid, BCtype_d xBC, BCtype_d yBC, double *data) { // First, create the spline structure NUBspline_2d_d* spline = new NUBspline_2d_d; if (spline == NULL) return spline; spline->sp_code = NU2D; spline->t_code = DOUBLE_REAL; // Next, create the bases spline->x_basis = create_NUBasis (x_grid, xBC.lCode==PERIODIC); spline->y_basis = create_NUBasis (y_grid, yBC.lCode==PERIODIC); // int Mx, (set but unused) int My, Nx, Ny; // if (xBC.lCode == PERIODIC) Mx = x_grid->num_points - 1; // else Mx = x_grid->num_points; if (yBC.lCode == PERIODIC) My = y_grid->num_points - 1; else My = y_grid->num_points; Nx = x_grid->num_points + 2; Ny = y_grid->num_points + 2; spline->x_stride = Ny; #ifndef HAVE_SSE2 - spline->coefs = new double[Nx*Ny]; + spline->coefs = (double*)malloc (sizeof(double)*Nx*Ny); #else posix_memalign ((void**)&spline->coefs, 16, sizeof(double)*Nx*Ny); #endif // First, solve in the X-direction for (int iy=0; iyx_basis, xBC, data+doffset, My, spline->coefs+coffset, Ny); } // Now, solve in the Y-direction for (int ix=0; ixy_basis, yBC, spline->coefs+doffset, 1, spline->coefs+coffset, 1); } return spline; } NUBspline_3d_d * create_NUBspline_3d_d (NUgrid* x_grid, NUgrid* y_grid, NUgrid* z_grid, BCtype_d xBC, BCtype_d yBC, BCtype_d zBC, double *data) { // First, create the spline structure NUBspline_3d_d* spline = new NUBspline_3d_d; if (spline == NULL) return spline; spline->sp_code = NU3D; spline->t_code = DOUBLE_REAL; // Next, create the bases spline->x_basis = create_NUBasis (x_grid, xBC.lCode==PERIODIC); spline->y_basis = create_NUBasis (y_grid, yBC.lCode==PERIODIC); spline->z_basis = create_NUBasis (z_grid, zBC.lCode==PERIODIC); // set but unused //int Mx, int My, Mz, Nx, Ny, Nz; // if (xBC.lCode == PERIODIC) Mx = x_grid->num_points - 1; // else Mx = x_grid->num_points; if (yBC.lCode == PERIODIC) My = y_grid->num_points - 1; else My = y_grid->num_points; if (zBC.lCode == PERIODIC) Mz = z_grid->num_points - 1; else Mz = z_grid->num_points; Nx = x_grid->num_points + 2; Ny = y_grid->num_points + 2; Nz = z_grid->num_points + 2; spline->x_stride = Ny*Nz; spline->y_stride = Nz; #ifndef HAVE_SSE2 - spline->coefs = new double[Nx*Ny*Nz]; + spline->coefs = (double*)malloc (sizeof(double)*Nx*Ny*Nz); #else posix_memalign ((void**)&spline->coefs, 16, sizeof(double)*Nx*Ny*Nz); #endif // First, solve in the X-direction for (int iy=0; iyx_basis, xBC, data+doffset, My*Mz, spline->coefs+coffset, Ny*Nz); } // Now, solve in the Y-direction for (int ix=0; ixy_basis, yBC, spline->coefs+doffset, Nz, spline->coefs+coffset, Nz); } // Now, solve in the Z-direction for (int ix=0; ixz_basis, zBC, spline->coefs+doffset, 1, spline->coefs+coffset, 1); } return spline; } //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// //// Single-precision complex creation routines //// //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// void find_NUBcoefs_1d_c (NUBasis* restrict basis, BCtype_c bc, complex_float *data, int dstride, complex_float *coefs, int cstride) { BCtype_s bc_r, bc_i; bc_r.lCode = bc.lCode; bc_i.lCode = bc.lCode; bc_r.rCode = bc.rCode; bc_i.rCode = bc.rCode; bc_r.lVal = bc.lVal_r; bc_r.rVal = bc.rVal_r; bc_i.lVal = bc.lVal_i; bc_i.rVal = bc.rVal_i; float *data_r = ((float*)data ); float *data_i = ((float*)data )+1; float *coefs_r = ((float*)coefs); float *coefs_i = ((float*)coefs)+1; find_NUBcoefs_1d_s (basis, bc_r, data_r, 2*dstride, coefs_r, 2*cstride); find_NUBcoefs_1d_s (basis, bc_i, data_i, 2*dstride, coefs_i, 2*cstride); } NUBspline_1d_c * create_NUBspline_1d_c (NUgrid* x_grid, BCtype_c xBC, complex_float *data) { // First, create the spline structure NUBspline_1d_c* spline = new NUBspline_1d_c; if (spline == NULL) return spline; spline->sp_code = NU1D; spline->t_code = SINGLE_COMPLEX; // Next, create the basis spline->x_basis = create_NUBasis (x_grid, xBC.lCode==PERIODIC); // M is the number of data points // int M; // if (xBC.lCode == PERIODIC) M = x_grid->num_points - 1; // else M = x_grid->num_points; int N = x_grid->num_points + 2; // Allocate coefficients and solve - spline->coefs = new complex_float[N]; + spline->coefs = (complex_float*)malloc (sizeof(complex_float)*N); find_NUBcoefs_1d_c (spline->x_basis, xBC, data, 1, spline->coefs, 1); return spline; } NUBspline_2d_c * create_NUBspline_2d_c (NUgrid* x_grid, NUgrid* y_grid, BCtype_c xBC, BCtype_c yBC, complex_float *data) { // First, create the spline structure NUBspline_2d_c* spline = new NUBspline_2d_c; if (spline == NULL) return spline; spline->sp_code = NU2D; spline->t_code = SINGLE_COMPLEX; // Next, create the bases spline->x_basis = create_NUBasis (x_grid, xBC.lCode==PERIODIC); spline->y_basis = create_NUBasis (y_grid, yBC.lCode==PERIODIC); // int Mx, int My, Nx, Ny; // if (xBC.lCode == PERIODIC) Mx = x_grid->num_points - 1; // else Mx = x_grid->num_points; if (yBC.lCode == PERIODIC) My = y_grid->num_points - 1; else My = y_grid->num_points; Nx = x_grid->num_points + 2; Ny = y_grid->num_points + 2; spline->x_stride = Ny; #ifndef HAVE_SSE2 - spline->coefs = new complex_float[Nx*Ny]; + spline->coefs = (complex_float*)malloc (sizeof(complex_float)*Nx*Ny); #else posix_memalign ((void**)&spline->coefs, 16, sizeof(complex_float)*Nx*Ny); #endif // First, solve in the X-direction for (int iy=0; iyx_basis, xBC, data+doffset, My, spline->coefs+coffset, Ny); } // Now, solve in the Y-direction for (int ix=0; ixy_basis, yBC, spline->coefs+doffset, 1, spline->coefs+coffset, 1); } return spline; } NUBspline_3d_c * create_NUBspline_3d_c (NUgrid* x_grid, NUgrid* y_grid, NUgrid* z_grid, BCtype_c xBC, BCtype_c yBC, BCtype_c zBC, complex_float *data) { // First, create the spline structure NUBspline_3d_c* spline = new NUBspline_3d_c; if (spline == NULL) return spline; spline->sp_code = NU3D; spline->t_code = SINGLE_COMPLEX; // Next, create the bases spline->x_basis = create_NUBasis (x_grid, xBC.lCode==PERIODIC); spline->y_basis = create_NUBasis (y_grid, yBC.lCode==PERIODIC); spline->z_basis = create_NUBasis (z_grid, zBC.lCode==PERIODIC); // int Mx, int My, Mz, Nx, Ny, Nz; // if (xBC.lCode == PERIODIC) Mx = x_grid->num_points - 1; // else Mx = x_grid->num_points; if (yBC.lCode == PERIODIC) My = y_grid->num_points - 1; else My = y_grid->num_points; if (zBC.lCode == PERIODIC) Mz = z_grid->num_points - 1; else Mz = z_grid->num_points; Nx = x_grid->num_points + 2; Ny = y_grid->num_points + 2; Nz = z_grid->num_points + 2; // Allocate coefficients and solve spline->x_stride = Ny*Nz; spline->y_stride = Nz; #ifndef HAVE_SSE2 - spline->coefs = new complex_float[Nx*Ny*Nz]; + spline->coefs = (complex_float*)malloc (sizeof(complex_float)*Nx*Ny*Nz); #else posix_memalign ((void**)&spline->coefs, 16, sizeof(complex_float)*Nx*Ny*Nz); #endif // First, solve in the X-direction for (int iy=0; iyx_basis, xBC, data+doffset, My*Mz, spline->coefs+coffset, Ny*Nz); } // Now, solve in the Y-direction for (int ix=0; ixy_basis, yBC, spline->coefs+doffset, Nz, spline->coefs+coffset, Nz); } // Now, solve in the Z-direction for (int ix=0; ixz_basis, zBC, spline->coefs+doffset, 1, spline->coefs+coffset, 1); } return spline; } //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// //// Double-precision complex creation routines //// //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// void find_NUBcoefs_1d_z (NUBasis* restrict basis, BCtype_z bc, complex_double *data, int dstride, complex_double *coefs, int cstride) { BCtype_d bc_r, bc_i; bc_r.lCode = bc.lCode; bc_i.lCode = bc.lCode; bc_r.rCode = bc.rCode; bc_i.rCode = bc.rCode; bc_r.lVal = bc.lVal_r; bc_r.rVal = bc.rVal_r; bc_i.lVal = bc.lVal_i; bc_i.rVal = bc.rVal_i; double *data_r = ((double*)data ); double *data_i = ((double*)data )+1; double *coefs_r = ((double*)coefs); double *coefs_i = ((double*)coefs)+1; find_NUBcoefs_1d_d (basis, bc_r, data_r, 2*dstride, coefs_r, 2*cstride); find_NUBcoefs_1d_d (basis, bc_i, data_i, 2*dstride, coefs_i, 2*cstride); } NUBspline_1d_z * create_NUBspline_1d_z (NUgrid* x_grid, BCtype_z xBC, complex_double *data) { // First, create the spline structure NUBspline_1d_z* spline = new NUBspline_1d_z; if (spline == NULL) return spline; spline->sp_code = NU1D; spline->t_code = DOUBLE_COMPLEX; // Next, create the basis spline->x_basis = create_NUBasis (x_grid, xBC.lCode==PERIODIC); // M is the number of data points // int M; // if (xBC.lCode == PERIODIC) M = x_grid->num_points - 1; // else M = x_grid->num_points; int N = x_grid->num_points + 2; // Allocate coefficients and solve - spline->coefs = new complex_double[N]; + spline->coefs = (complex_double*)malloc (sizeof(complex_double)*N); find_NUBcoefs_1d_z (spline->x_basis, xBC, data, 1, spline->coefs, 1); return spline; } NUBspline_2d_z * create_NUBspline_2d_z (NUgrid* x_grid, NUgrid* y_grid, BCtype_z xBC, BCtype_z yBC, complex_double *data) { // First, create the spline structure NUBspline_2d_z* spline = new NUBspline_2d_z; if (spline == NULL) return spline; spline->sp_code = NU2D; spline->t_code = DOUBLE_COMPLEX; // Next, create the bases spline->x_basis = create_NUBasis (x_grid, xBC.lCode==PERIODIC); spline->y_basis = create_NUBasis (y_grid, yBC.lCode==PERIODIC); // int Mx, int My, Nx, Ny; // if (xBC.lCode == PERIODIC) Mx = x_grid->num_points - 1; // else Mx = x_grid->num_points; if (yBC.lCode == PERIODIC) My = y_grid->num_points - 1; else My = y_grid->num_points; Nx = x_grid->num_points + 2; Ny = y_grid->num_points + 2; spline->x_stride = Ny; #ifndef HAVE_SSE2 - spline->coefs = new complex_double[Nx*Ny]; + spline->coefs = (complex_double*)malloc (sizeof(complex_double)*Nx*Ny); #else posix_memalign ((void**)&spline->coefs, 16, sizeof(complex_double)*Nx*Ny); #endif // First, solve in the X-direction for (int iy=0; iyx_basis, xBC, data+doffset, My, spline->coefs+coffset, Ny); } // Now, solve in the Y-direction for (int ix=0; ixy_basis, yBC, spline->coefs+doffset, 1, spline->coefs+coffset, 1); } return spline; } NUBspline_3d_z * create_NUBspline_3d_z (NUgrid* x_grid, NUgrid* y_grid, NUgrid* z_grid, BCtype_z xBC, BCtype_z yBC, BCtype_z zBC, complex_double *data) { // First, create the spline structure NUBspline_3d_z* spline = new NUBspline_3d_z; if (spline == NULL) return spline; spline->sp_code = NU3D; spline->t_code = DOUBLE_COMPLEX; spline->x_grid = x_grid; spline->y_grid = y_grid; spline->z_grid = z_grid; // Next, create the bases spline->x_basis = create_NUBasis (x_grid, xBC.lCode==PERIODIC); spline->y_basis = create_NUBasis (y_grid, yBC.lCode==PERIODIC); spline->z_basis = create_NUBasis (z_grid, zBC.lCode==PERIODIC); // int Mx, int My, Mz, Nx, Ny, Nz; // if (xBC.lCode == PERIODIC) Mx = x_grid->num_points - 1; // else Mx = x_grid->num_points; if (yBC.lCode == PERIODIC) My = y_grid->num_points - 1; else My = y_grid->num_points; if (zBC.lCode == PERIODIC) Mz = z_grid->num_points - 1; else Mz = z_grid->num_points; Nx = x_grid->num_points + 2; Ny = y_grid->num_points + 2; Nz = z_grid->num_points + 2; // Allocate coefficients and solve spline->x_stride = Ny*Nz; spline->y_stride = Nz; #ifndef HAVE_SSE2 - spline->coefs = new complex_double[Nx*Ny*Nz]; + spline->coefs = (complex_double*)malloc (sizeof(complex_double)*Nx*Ny*Nz); #else posix_memalign ((void**)&spline->coefs, 16, sizeof(complex_double)*Nx*Ny*Nz); #endif // First, solve in the X-direction for (int iy=0; iyx_basis, xBC, data+doffset, My*Mz, spline->coefs+coffset, Ny*Nz); /* for (int ix=0; ixcoefs[coffset+ix*spline->x_stride]; if (isnan(creal(z))) fprintf (stderr, "NAN encountered in create_NUBspline_3d_z at real part of (%d,%d,%d)\n", ix,iy,iz); if (isnan(cimag(z))) fprintf (stderr, "NAN encountered in create_NUBspline_3d_z at imag part of (%d,%d,%d)\n", ix,iy,iz); } */ } // Now, solve in the Y-direction for (int ix=0; ixy_basis, yBC, spline->coefs+doffset, Nz, spline->coefs+coffset, Nz); } // Now, solve in the Z-direction for (int ix=0; ixz_basis, zBC, spline->coefs+doffset, 1, spline->coefs+coffset, 1); } return spline; } void destroy_NUBspline(Bspline *spline) { free(spline->coefs); switch (spline->sp_code) { case NU1D: destroy_NUBasis (((NUBspline_1d*)spline)->x_basis); break; case NU2D: destroy_NUBasis (((NUBspline_2d*)spline)->x_basis); destroy_NUBasis (((NUBspline_2d*)spline)->y_basis); break; case NU3D: destroy_NUBasis (((NUBspline_3d*)spline)->x_basis); destroy_NUBasis (((NUBspline_3d*)spline)->y_basis); destroy_NUBasis (((NUBspline_3d*)spline)->z_basis); break; case U1D: case U2D: case MULTI_U1D: case MULTI_U2D: case MULTI_U3D: case MULTI_NU1D: case MULTI_NU2D: case MULTI_NU3D: default: ; } delete spline; } diff --git a/libs/image/CMakeLists.txt b/libs/image/CMakeLists.txt index b314643398..8368696bc8 100644 --- a/libs/image/CMakeLists.txt +++ b/libs/image/CMakeLists.txt @@ -1,377 +1,379 @@ add_subdirectory( tests ) add_subdirectory( tiles3 ) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/metadata ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty ${CMAKE_CURRENT_SOURCE_DIR}/brushengine ${CMAKE_CURRENT_SOURCE_DIR}/commands ${CMAKE_CURRENT_SOURCE_DIR}/commands_new ${CMAKE_CURRENT_SOURCE_DIR}/filter ${CMAKE_CURRENT_SOURCE_DIR}/floodfill ${CMAKE_CURRENT_SOURCE_DIR}/generator ${CMAKE_CURRENT_SOURCE_DIR}/layerstyles ${CMAKE_CURRENT_SOURCE_DIR}/processing ${CMAKE_CURRENT_SOURCE_DIR}/recorder ${CMAKE_SOURCE_DIR}/sdk/tests ) include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR} ${Boost_INCLUDE_DIRS} ) if(FFTW3_FOUND) include_directories(SYSTEM ${FFTW3_INCLUDE_DIR}) endif() if(HAVE_VC) include_directories(SYSTEM ${Vc_INCLUDE_DIR} ${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS}) ko_compile_for_all_implementations(__per_arch_circle_mask_generator_objs kis_brush_mask_applicator_factories.cpp) else() set(__per_arch_circle_mask_generator_objs kis_brush_mask_applicator_factories.cpp) endif() set(kritaimage_LIB_SRCS tiles3/kis_tile.cc tiles3/kis_tile_data.cc tiles3/kis_tile_data_store.cc tiles3/kis_tile_data_pooler.cc tiles3/kis_tiled_data_manager.cc tiles3/kis_memento_manager.cc tiles3/kis_hline_iterator.cpp tiles3/kis_vline_iterator.cpp tiles3/kis_random_accessor.cc tiles3/swap/kis_abstract_compression.cpp tiles3/swap/kis_lzf_compression.cpp tiles3/swap/kis_abstract_tile_compressor.cpp tiles3/swap/kis_legacy_tile_compressor.cpp tiles3/swap/kis_tile_compressor_2.cpp tiles3/swap/kis_chunk_allocator.cpp tiles3/swap/kis_memory_window.cpp tiles3/swap/kis_swapped_data_store.cpp tiles3/swap/kis_tile_data_swapper.cpp kis_distance_information.cpp kis_painter.cc kis_progress_updater.cpp brushengine/kis_paint_information.cc brushengine/kis_random_source.cpp brushengine/kis_stroke_random_source.cpp brushengine/kis_paintop.cc brushengine/kis_paintop_factory.cpp brushengine/kis_paintop_preset.cpp brushengine/kis_paintop_registry.cc brushengine/kis_paintop_settings.cpp brushengine/kis_locked_properties.cc brushengine/kis_locked_properties_proxy.cpp brushengine/kis_locked_properties_server.cpp brushengine/kis_paintop_config_widget.cpp commands/kis_deselect_global_selection_command.cpp commands/kis_image_change_layers_command.cpp commands/kis_image_command.cpp commands/kis_image_set_projection_color_space_command.cpp commands/kis_image_layer_add_command.cpp commands/kis_image_layer_move_command.cpp commands/kis_image_layer_remove_command.cpp commands/kis_image_layer_remove_command_impl.cpp commands/kis_image_lock_command.cpp commands/kis_layer_command.cpp commands/kis_node_command.cpp commands/kis_node_compositeop_command.cpp commands/kis_node_opacity_command.cpp commands/kis_node_property_list_command.cpp commands/kis_reselect_global_selection_command.cpp commands/kis_set_global_selection_command.cpp commands_new/kis_saved_commands.cpp commands_new/kis_processing_command.cpp commands_new/kis_image_resize_command.cpp commands_new/kis_image_set_resolution_command.cpp commands_new/kis_node_move_command2.cpp commands_new/kis_set_layer_style_command.cpp commands_new/kis_selection_move_command2.cpp commands_new/kis_update_command.cpp commands_new/kis_switch_current_time_command.cpp commands_new/kis_change_projection_color_command.cpp commands_new/kis_activate_selection_mask_command.cpp processing/kis_do_nothing_processing_visitor.cpp processing/kis_simple_processing_visitor.cpp processing/kis_crop_processing_visitor.cpp processing/kis_crop_selections_processing_visitor.cpp processing/kis_transform_processing_visitor.cpp processing/kis_mirror_processing_visitor.cpp filter/kis_filter.cc filter/kis_filter_configuration.cc filter/kis_color_transformation_configuration.cc filter/kis_filter_registry.cc filter/kis_color_transformation_filter.cc generator/kis_generator.cpp generator/kis_generator_layer.cpp generator/kis_generator_registry.cpp floodfill/kis_fill_interval_map.cpp floodfill/kis_scanline_fill.cpp kis_adjustment_layer.cc kis_selection_based_layer.cpp kis_node_filter_interface.cpp kis_base_accessor.cpp kis_base_node.cpp kis_base_processor.cpp kis_bookmarked_configuration_manager.cc kis_clone_info.cpp kis_clone_layer.cpp kis_colorspace_convert_visitor.cpp kis_config_widget.cpp kis_convolution_kernel.cc kis_convolution_painter.cc kis_gaussian_kernel.cpp kis_cubic_curve.cpp kis_default_bounds.cpp kis_default_bounds_base.cpp kis_effect_mask.cc kis_fast_math.cpp kis_fill_painter.cc kis_filter_mask.cpp kis_filter_strategy.cc kis_transform_mask.cpp kis_transform_mask_params_interface.cpp kis_recalculate_transform_mask_job.cpp kis_recalculate_generator_layer_job.cpp kis_transform_mask_params_factory_registry.cpp kis_safe_transform.cpp kis_gradient_painter.cc kis_gradient_shape_strategy.cpp kis_cached_gradient_shape_strategy.cpp kis_polygonal_gradient_shape_strategy.cpp kis_iterator_ng.cpp kis_async_merger.cpp kis_merge_walker.cc kis_updater_context.cpp kis_update_job_item.cpp kis_stroke_strategy_undo_command_based.cpp kis_simple_stroke_strategy.cpp kis_stroke_job_strategy.cpp kis_stroke_strategy.cpp kis_stroke.cpp kis_strokes_queue.cpp kis_simple_update_queue.cpp kis_update_scheduler.cpp kis_queues_progress_updater.cpp kis_composite_progress_proxy.cpp kis_sync_lod_cache_stroke_strategy.cpp kis_lod_capable_layer_offset.cpp kis_update_time_monitor.cpp kis_group_layer.cc kis_count_visitor.cpp kis_histogram.cc kis_image_interfaces.cpp kis_image_animation_interface.cpp kis_time_range.cpp kis_node_graph_listener.cpp kis_image.cc kis_image_signal_router.cpp kis_image_config.cpp kis_projection_updates_filter.cpp kis_suspend_projection_updates_stroke_strategy.cpp kis_regenerate_frame_stroke_strategy.cpp kis_crop_saved_extra_data.cpp kis_signal_compressor.cpp kis_signal_compressor_with_param.cpp kis_thread_safe_signal_compressor.cpp kis_acyclic_signal_connector.cpp kis_timed_signal_threshold.cpp kis_layer.cc kis_indirect_painting_support.cpp kis_abstract_projection_plane.cpp kis_layer_projection_plane.cpp kis_command_utils.cpp kis_layer_utils.cpp kis_mask_projection_plane.cpp kis_projection_leaf.cpp kis_mask.cc kis_base_mask_generator.cpp kis_rect_mask_generator.cpp kis_circle_mask_generator.cpp kis_gauss_circle_mask_generator.cpp kis_gauss_rect_mask_generator.cpp ${__per_arch_circle_mask_generator_objs} kis_curve_circle_mask_generator.cpp kis_curve_rect_mask_generator.cpp kis_math_toolbox.cpp kis_memory_statistics_server.cpp kis_name_server.cpp kis_node.cpp kis_node_facade.cpp kis_node_progress_proxy.cpp kis_busy_progress_indicator.cpp kis_node_visitor.cpp kis_paint_device.cc kis_paint_device_debug_utils.cpp kis_fixed_paint_device.cpp kis_paint_layer.cc kis_perspective_math.cpp kis_pixel_selection.cpp kis_processing_information.cpp kis_properties_configuration.cc kis_random_accessor_ng.cpp kis_random_generator.cc kis_random_sub_accessor.cpp kis_wrapped_random_accessor.cpp kis_selection.cc kis_selection_mask.cpp kis_update_outline_job.cpp kis_update_selection_job.cpp kis_serializable_configuration.cc kis_transaction_data.cpp kis_transform_worker.cc kis_perspectivetransform_worker.cpp bsplines/kis_bspline_1d.cpp bsplines/kis_bspline_2d.cpp bsplines/kis_nu_bspline_2d.cpp kis_warptransform_worker.cc kis_cage_transform_worker.cpp kis_liquify_transform_worker.cpp kis_green_coordinates_math.cpp kis_algebra_2d.cpp kis_transparency_mask.cc kis_undo_store.cpp kis_undo_stores.cpp kis_undo_adapter.cpp kis_surrogate_undo_adapter.cpp kis_legacy_undo_adapter.cpp kis_post_execution_undo_adapter.cpp kis_processing_visitor.cpp kis_processing_applicator.cpp krita_utils.cpp kis_outline_generator.cpp kis_layer_composition.cpp kis_selection_filters.cpp metadata/kis_meta_data_entry.cc metadata/kis_meta_data_filter.cc metadata/kis_meta_data_filter_p.cc metadata/kis_meta_data_filter_registry.cc metadata/kis_meta_data_filter_registry_model.cc metadata/kis_meta_data_io_backend.cc metadata/kis_meta_data_merge_strategy.cc metadata/kis_meta_data_merge_strategy_p.cc metadata/kis_meta_data_merge_strategy_registry.cc metadata/kis_meta_data_parser.cc metadata/kis_meta_data_schema.cc metadata/kis_meta_data_schema_registry.cc metadata/kis_meta_data_store.cc metadata/kis_meta_data_type_info.cc metadata/kis_meta_data_validator.cc metadata/kis_meta_data_value.cc recorder/kis_action_recorder.cc recorder/kis_macro.cc recorder/kis_macro_player.cc recorder/kis_node_query_path.cc recorder/kis_play_info.cc recorder/kis_recorded_action.cc recorder/kis_recorded_action_factory_registry.cc recorder/kis_recorded_action_load_context.cpp recorder/kis_recorded_action_save_context.cpp recorder/kis_recorded_filter_action.cpp recorder/kis_recorded_fill_paint_action.cpp recorder/kis_recorded_node_action.cc recorder/kis_recorded_paint_action.cpp recorder/kis_recorded_path_paint_action.cpp recorder/kis_recorded_shape_paint_action.cpp kis_keyframe.cpp kis_keyframe_channel.cpp kis_scalar_keyframe_channel.cpp kis_raster_keyframe_channel.cpp kis_onion_skin_compositor.cpp kis_onion_skin_cache.cpp kis_idle_watcher.cpp kis_psd_layer_style.cpp kis_layer_properties_icons.cpp layerstyles/kis_multiple_projection.cpp layerstyles/kis_layer_style_filter.cpp layerstyles/kis_layer_style_filter_environment.cpp layerstyles/kis_layer_style_filter_projection_plane.cpp layerstyles/kis_layer_style_projection_plane.cpp layerstyles/kis_ls_drop_shadow_filter.cpp layerstyles/kis_ls_satin_filter.cpp layerstyles/kis_ls_stroke_filter.cpp layerstyles/kis_ls_bevel_emboss_filter.cpp layerstyles/kis_ls_overlay_filter.cpp layerstyles/kis_ls_utils.cpp layerstyles/gimp_bump_map.cpp ) set(einspline_SRCS 3rdparty/einspline/bspline_create.cpp 3rdparty/einspline/bspline_data.cpp 3rdparty/einspline/multi_bspline_create.cpp 3rdparty/einspline/nubasis.cpp 3rdparty/einspline/nubspline_create.cpp 3rdparty/einspline/nugrid.cpp ) add_library(kritaimage SHARED ${kritaimage_LIB_SRCS} ${einspline_SRCS}) generate_export_header(kritaimage BASE_NAME kritaimage) target_link_libraries(kritaimage PUBLIC kritaversion kritawidgets kritaglobal kritapsd kritaodf kritapigment kritaundo2 kritawidgetutils Qt5::Concurrent ) target_link_libraries(kritaimage PUBLIC ${Boost_SYSTEM_LIBRARY}) if(OPENEXR_FOUND) target_link_libraries(kritaimage PUBLIC ${OPENEXR_LIBRARIES}) endif() if(FFTW3_FOUND) target_link_libraries(kritaimage PRIVATE ${FFTW3_LIBRARIES}) endif() if(HAVE_VC) target_link_libraries(kritaimage PUBLIC ${Vc_LIBRARIES}) + if (NOT PACKAGERS_BUILD) + set_property(TARGET kritaimage APPEND PROPERTY COMPILE_OPTIONS "${Vc_ARCHITECTURE_FLAGS}") + endif() endif() if (NOT GSL_FOUND) message (WARNING "KRITA WARNING! No GNU Scientific Library was found! Krita's Shaped Gradients might be non-normalized! Please install GSL library.") else () target_link_libraries(kritaimage PRIVATE ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES}) endif () target_include_directories(kritaimage PUBLIC $ $ $ $ $ - $ $ $ ) set_target_properties(kritaimage PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritaimage ${INSTALL_TARGETS_DEFAULT_ARGS}) ########### install schemas ############# install( FILES metadata/schemas/dc.schema metadata/schemas/exif.schema metadata/schemas/tiff.schema metadata/schemas/mkn.schema metadata/schemas/xmp.schema metadata/schemas/xmpmm.schema metadata/schemas/xmprights.schema DESTINATION ${DATA_INSTALL_DIR}/krita/metadata/schemas) diff --git a/libs/image/brushengine/kis_paint_information.cc b/libs/image/brushengine/kis_paint_information.cc index 1f8050835f..6e6eb8e711 100644 --- a/libs/image/brushengine/kis_paint_information.cc +++ b/libs/image/brushengine/kis_paint_information.cc @@ -1,570 +1,570 @@ /* * Copyright (c) 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 #include #include #include "kis_paintop.h" #include "kis_algebra_2d.h" #include "kis_lod_transform.h" #include struct KisPaintInformation::Private { EIGEN_MAKE_ALIGNED_OPERATOR_NEW Private(const QPointF & pos_, qreal pressure_, qreal xTilt_, qreal yTilt_, qreal rotation_, qreal tangentialPressure_, qreal perspective_, qreal time_, qreal speed_, bool isHoveringMode_) : pos(pos_), pressure(pressure_), xTilt(xTilt_), yTilt(yTilt_), rotation(rotation_), tangentialPressure(tangentialPressure_), perspective(perspective_), time(time_), speed(speed_), isHoveringMode(isHoveringMode_), randomSource(0), currentDistanceInfo(0), levelOfDetail(0) { } ~Private() { KIS_ASSERT_RECOVER_NOOP(!currentDistanceInfo); } Private(const Private &rhs) { copy(rhs); } Private& operator=(const Private &rhs) { copy(rhs); return *this; } void copy(const Private &rhs) { pos = rhs.pos; pressure = rhs.pressure; xTilt = rhs.xTilt; yTilt = rhs.yTilt; rotation = rhs.rotation; tangentialPressure = rhs.tangentialPressure; perspective = rhs.perspective; time = rhs.time; speed = rhs.speed; isHoveringMode = rhs.isHoveringMode; randomSource = rhs.randomSource; currentDistanceInfo = rhs.currentDistanceInfo; canvasRotation = rhs.canvasRotation; canvasMirroredH = rhs.canvasMirroredH; if (rhs.drawingAngleOverride) { drawingAngleOverride.reset(new qreal(*rhs.drawingAngleOverride)); } levelOfDetail = rhs.levelOfDetail; } QPointF pos; qreal pressure; qreal xTilt; qreal yTilt; qreal rotation; qreal tangentialPressure; qreal perspective; qreal time; qreal speed; bool isHoveringMode; KisRandomSourceSP randomSource; int canvasRotation; bool canvasMirroredH; QScopedPointer drawingAngleOverride; KisDistanceInformation *currentDistanceInfo; int levelOfDetail; void registerDistanceInfo(KisDistanceInformation *di) { currentDistanceInfo = di; } void unregisterDistanceInfo() { currentDistanceInfo = 0; } }; KisPaintInformation::DistanceInformationRegistrar:: DistanceInformationRegistrar(KisPaintInformation *_p, KisDistanceInformation *distanceInfo) : p(_p) { p->d->registerDistanceInfo(distanceInfo); } KisPaintInformation::DistanceInformationRegistrar:: ~DistanceInformationRegistrar() { p->d->unregisterDistanceInfo(); } KisPaintInformation::KisPaintInformation(const QPointF & pos, qreal pressure, qreal xTilt, qreal yTilt, qreal rotation, qreal tangentialPressure, qreal perspective, qreal time, qreal speed) : d(new Private(pos, pressure, xTilt, yTilt, rotation, tangentialPressure, perspective, time, speed, false)) { } KisPaintInformation::KisPaintInformation(const QPointF & pos, qreal pressure, qreal xTilt, qreal yTilt, qreal rotation) : d(new Private(pos, pressure, xTilt, yTilt, rotation, 0.0, 1.0, 0.0, 0.0, false)) { } KisPaintInformation::KisPaintInformation(const QPointF &pos, qreal pressure) : d(new Private(pos, pressure, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, false)) { } KisPaintInformation::KisPaintInformation(const KisPaintInformation& rhs) : d(new Private(*rhs.d)) { } void KisPaintInformation::operator=(const KisPaintInformation & rhs) { *d = *rhs.d; } KisPaintInformation::~KisPaintInformation() { delete d; } bool KisPaintInformation::isHoveringMode() const { return d->isHoveringMode; } KisPaintInformation KisPaintInformation::createHoveringModeInfo(const QPointF &pos, qreal pressure, qreal xTilt, qreal yTilt, qreal rotation, qreal tangentialPressure, qreal perspective, qreal speed, int canvasrotation, bool canvasMirroredH) { KisPaintInformation info(pos, pressure, xTilt, yTilt, rotation, tangentialPressure, perspective, 0, speed); info.d->isHoveringMode = true; info.d->canvasRotation = canvasrotation; info.d->canvasMirroredH = canvasMirroredH; return info; } int KisPaintInformation::canvasRotation() const { return d->canvasRotation; } void KisPaintInformation::setCanvasRotation(int rotation) { if (rotation < 0) { d->canvasRotation= 360- abs(rotation % 360); } else { d->canvasRotation= rotation % 360; } } bool KisPaintInformation::canvasMirroredH() const { return d->canvasMirroredH; } void KisPaintInformation::setCanvasHorizontalMirrorState(bool mir) { d->canvasMirroredH = mir; } void KisPaintInformation::toXML(QDomDocument&, QDomElement& e) const { // hovering mode infos are not supposed to be saved KIS_ASSERT_RECOVER_NOOP(!d->isHoveringMode); e.setAttribute("pointX", QString::number(pos().x(), 'g', 15)); e.setAttribute("pointY", QString::number(pos().y(), 'g', 15)); e.setAttribute("pressure", QString::number(pressure(), 'g', 15)); e.setAttribute("xTilt", QString::number(xTilt(), 'g', 15)); e.setAttribute("yTilt", QString::number(yTilt(), 'g', 15)); e.setAttribute("rotation", QString::number(rotation(), 'g', 15)); e.setAttribute("tangentialPressure", QString::number(tangentialPressure(), 'g', 15)); e.setAttribute("perspective", QString::number(perspective(), 'g', 15)); - e.setAttribute("time", d->time); - e.setAttribute("speed", d->speed); + e.setAttribute("time", QString::number(d->time, 'g', 15)); + e.setAttribute("speed", QString::number(d->speed, 'g', 15)); } KisPaintInformation KisPaintInformation::fromXML(const QDomElement& e) { qreal pointX = qreal(KisDomUtils::toDouble(e.attribute("pointX", "0.0"))); qreal pointY = qreal(KisDomUtils::toDouble(e.attribute("pointY", "0.0"))); qreal pressure = qreal(KisDomUtils::toDouble(e.attribute("pressure", "0.0"))); qreal rotation = qreal(KisDomUtils::toDouble(e.attribute("rotation", "0.0"))); qreal tangentialPressure = qreal(KisDomUtils::toDouble(e.attribute("tangentialPressure", "0.0"))); qreal perspective = qreal(KisDomUtils::toDouble(e.attribute("perspective", "0.0"))); qreal xTilt = qreal(KisDomUtils::toDouble(e.attribute("xTilt", "0.0"))); qreal yTilt = qreal(KisDomUtils::toDouble(e.attribute("yTilt", "0.0"))); qreal time = KisDomUtils::toDouble(e.attribute("time", "0")); qreal speed = KisDomUtils::toDouble(e.attribute("speed", "0")); return KisPaintInformation(QPointF(pointX, pointY), pressure, xTilt, yTilt, rotation, tangentialPressure, perspective, time, speed); } const QPointF& KisPaintInformation::pos() const { return d->pos; } void KisPaintInformation::setPos(const QPointF& p) { d->pos = p; } qreal KisPaintInformation::pressure() const { return d->pressure; } void KisPaintInformation::setPressure(qreal p) { d->pressure = p; } qreal KisPaintInformation::xTilt() const { return d->xTilt; } qreal KisPaintInformation::yTilt() const { return d->yTilt; } void KisPaintInformation::overrideDrawingAngle(qreal angle) { d->drawingAngleOverride.reset(new qreal(angle)); } qreal KisPaintInformation::drawingAngleSafe(const KisDistanceInformation &distance) const { if (d->drawingAngleOverride) return *d->drawingAngleOverride; QVector2D diff(pos() - distance.lastPosition()); return atan2(diff.y(), diff.x()); } KisPaintInformation::DistanceInformationRegistrar KisPaintInformation::registerDistanceInformation(KisDistanceInformation *distance) { return DistanceInformationRegistrar(this, distance); } qreal KisPaintInformation::drawingAngle() const { if (d->drawingAngleOverride) return *d->drawingAngleOverride; if (!d->currentDistanceInfo || !d->currentDistanceInfo->hasLastDabInformation()) { warnKrita << "KisPaintInformation::drawingAngle()" << "Cannot access Distance Info last dab data"; return 0.0; } if (d->currentDistanceInfo->hasLockedDrawingAngle()) { return d->currentDistanceInfo->lockedDrawingAngle(); } QVector2D diff(pos() - d->currentDistanceInfo->lastPosition()); return atan2(diff.y(), diff.x()); } void KisPaintInformation::lockCurrentDrawingAngle(qreal alpha_unused) const { Q_UNUSED(alpha_unused); if (!d->currentDistanceInfo) { warnKrita << "KisPaintInformation::lockCurrentDrawingAngle()" << "Cannot access Distance Info last dab data"; return; } const QVector2D diff(pos() - d->currentDistanceInfo->lastPosition()); const qreal angle = atan2(diff.y(), diff.x()); qreal newAngle = angle; if (d->currentDistanceInfo->hasLockedDrawingAngle()) { const qreal stabilizingCoeff = 20.0; const qreal dist = stabilizingCoeff * d->currentDistanceInfo->currentSpacing().scalarApprox(); const qreal alpha = qMax(0.0, dist - d->currentDistanceInfo->scalarDistanceApprox()) / dist; const qreal oldAngle = d->currentDistanceInfo->lockedDrawingAngle(); if (shortestAngularDistance(oldAngle, newAngle) < M_PI / 6) { newAngle = (1.0 - alpha) * oldAngle + alpha * newAngle; } else { newAngle = oldAngle; } } d->currentDistanceInfo->setLockedDrawingAngle(newAngle); } QPointF KisPaintInformation::drawingDirectionVector() const { if (d->drawingAngleOverride) { qreal angle = *d->drawingAngleOverride; return QPointF(cos(angle), sin(angle)); } if (!d->currentDistanceInfo || !d->currentDistanceInfo->hasLastDabInformation()) { warnKrita << "KisPaintInformation::drawingDirectionVector()" << "Cannot access Distance Info last dab data"; return QPointF(1.0, 0.0); } QPointF diff(pos() - d->currentDistanceInfo->lastPosition()); return KisAlgebra2D::normalize(diff); } qreal KisPaintInformation::drawingDistance() const { if (!d->currentDistanceInfo || !d->currentDistanceInfo->hasLastDabInformation()) { warnKrita << "KisPaintInformation::drawingDistance()" << "Cannot access Distance Info last dab data"; return 1.0; } QVector2D diff(pos() - d->currentDistanceInfo->lastPosition()); qreal length = diff.length(); if (d->levelOfDetail) { length *= KisLodTransform::lodToInvScale(d->levelOfDetail); } return length; } qreal KisPaintInformation::drawingSpeed() const { return d->speed; } qreal KisPaintInformation::rotation() const { return d->rotation; } qreal KisPaintInformation::tangentialPressure() const { return d->tangentialPressure; } qreal KisPaintInformation::perspective() const { return d->perspective; } qreal KisPaintInformation::currentTime() const { return d->time; } KisRandomSourceSP KisPaintInformation::randomSource() const { if (!d->randomSource) { d->randomSource = new KisRandomSource(); } return d->randomSource; } void KisPaintInformation::setRandomSource(KisRandomSourceSP value) { d->randomSource = value; } void KisPaintInformation::setLevelOfDetail(int levelOfDetail) const { d->levelOfDetail = levelOfDetail; } QDebug operator<<(QDebug dbg, const KisPaintInformation &info) { #ifdef NDEBUG Q_UNUSED(info); #else dbg.nospace() << "Position: " << info.pos(); dbg.nospace() << ", Pressure: " << info.pressure(); dbg.nospace() << ", X Tilt: " << info.xTilt(); dbg.nospace() << ", Y Tilt: " << info.yTilt(); dbg.nospace() << ", Rotation: " << info.rotation(); dbg.nospace() << ", Tangential Pressure: " << info.tangentialPressure(); dbg.nospace() << ", Perspective: " << info.perspective(); dbg.nospace() << ", Drawing Angle: " << info.drawingAngle(); dbg.nospace() << ", Drawing Speed: " << info.drawingSpeed(); dbg.nospace() << ", Drawing Distance: " << info.drawingDistance(); dbg.nospace() << ", Time: " << info.currentTime(); #endif return dbg.space(); } KisPaintInformation KisPaintInformation::mixOnlyPosition(qreal t, const KisPaintInformation& mixedPi, const KisPaintInformation& basePi) { QPointF pt = (1 - t) * mixedPi.pos() + t * basePi.pos(); KisPaintInformation result(pt, basePi.pressure(), basePi.xTilt(), basePi.yTilt(), basePi.rotation(), basePi.tangentialPressure(), basePi.perspective(), basePi.currentTime(), basePi.drawingSpeed()); result.setRandomSource(basePi.randomSource()); return result; } KisPaintInformation KisPaintInformation::mix(qreal t, const KisPaintInformation& pi1, const KisPaintInformation& pi2) { QPointF pt = (1 - t) * pi1.pos() + t * pi2.pos(); return mix(pt, t, pi1, pi2); } KisPaintInformation KisPaintInformation::mix(const QPointF& p, qreal t, const KisPaintInformation& pi1, const KisPaintInformation& pi2) { qreal pressure = (1 - t) * pi1.pressure() + t * pi2.pressure(); qreal xTilt = (1 - t) * pi1.xTilt() + t * pi2.xTilt(); qreal yTilt = (1 - t) * pi1.yTilt() + t * pi2.yTilt(); qreal rotation = pi1.rotation(); if (pi1.rotation() != pi2.rotation()) { qreal a1 = kisDegreesToRadians(pi1.rotation()); qreal a2 = kisDegreesToRadians(pi2.rotation()); qreal distance = shortestAngularDistance(a2, a1); rotation = kisRadiansToDegrees(incrementInDirection(a1, t * distance, a2)); } qreal tangentialPressure = (1 - t) * pi1.tangentialPressure() + t * pi2.tangentialPressure(); qreal perspective = (1 - t) * pi1.perspective() + t * pi2.perspective(); qreal time = (1 - t) * pi1.currentTime() + t * pi2.currentTime(); qreal speed = (1 - t) * pi1.drawingSpeed() + t * pi2.drawingSpeed(); KisPaintInformation result(p, pressure, xTilt, yTilt, rotation, tangentialPressure, perspective, time, speed); KIS_ASSERT_RECOVER_NOOP(pi1.isHoveringMode() == pi2.isHoveringMode()); result.d->isHoveringMode = pi1.isHoveringMode(); result.d->levelOfDetail = pi1.d->levelOfDetail; result.d->randomSource = pi1.d->randomSource; result.d->canvasRotation = pi2.canvasRotation(); result.d->canvasMirroredH = pi2.canvasMirroredH(); return result; } qreal KisPaintInformation::tiltDirection(const KisPaintInformation& info, bool normalize) { qreal xTilt = info.xTilt(); qreal yTilt = info.yTilt(); // radians -PI, PI qreal tiltDirection = atan2(-xTilt, yTilt); // if normalize is true map to 0.0..1.0 return normalize ? (tiltDirection / (2 * M_PI) + 0.5) : tiltDirection; } qreal KisPaintInformation::tiltElevation(const KisPaintInformation& info, qreal maxTiltX, qreal maxTiltY, bool normalize) { qreal xTilt = qBound(qreal(-1.0), info.xTilt() / maxTiltX , qreal(1.0)); qreal yTilt = qBound(qreal(-1.0), info.yTilt() / maxTiltY , qreal(1.0)); qreal e; if (fabs(xTilt) > fabs(yTilt)) { e = sqrt(qreal(1.0) + yTilt * yTilt); } else { e = sqrt(qreal(1.0) + xTilt * xTilt); } qreal cosAlpha = sqrt(xTilt * xTilt + yTilt * yTilt) / e; qreal tiltElevation = acos(cosAlpha); // in radians in [0, 0.5 * PI] // mapping to 0.0..1.0 if normalize is true return normalize ? (tiltElevation / (M_PI * qreal(0.5))) : tiltElevation; } 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 6cc160513e..9e0d433eba 100644 --- a/libs/image/commands_new/kis_change_projection_color_command.cpp +++ b/libs/image/commands_new/kis_change_projection_color_command.cpp @@ -1,68 +1,70 @@ /* * 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/ 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() { m_image->setDefaultProjectionColor(m_newColor); + m_image->animationInterface()->setDefaultProjectionColor(m_newColor); } void KisChangeProjectionColorCommand::undo() { m_image->setDefaultProjectionColor(m_oldColor); + m_image->animationInterface()->setDefaultProjectionColor(m_oldColor); } diff --git a/libs/image/generator/kis_generator_layer.cpp b/libs/image/generator/kis_generator_layer.cpp index 840c321684..24132118f4 100644 --- a/libs/image/generator/kis_generator_layer.cpp +++ b/libs/image/generator/kis_generator_layer.cpp @@ -1,166 +1,166 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_generator_layer.h" #include #include "kis_debug.h" #include #include #include "kis_selection.h" #include "filter/kis_filter_configuration.h" #include "kis_processing_information.h" #include "generator/kis_generator_registry.h" #include "generator/kis_generator.h" #include "kis_node_visitor.h" #include "kis_processing_visitor.h" -#include "kis_signal_compressor.h" +#include "kis_thread_safe_signal_compressor.h" #include "kis_recalculate_generator_layer_job.h" #define UPDATE_DELAY 100 /*ms */ struct Q_DECL_HIDDEN KisGeneratorLayer::Private { Private() : updateSignalCompressor(UPDATE_DELAY, KisSignalCompressor::FIRST_INACTIVE) { } - KisSignalCompressor updateSignalCompressor; + KisThreadSafeSignalCompressor updateSignalCompressor; }; KisGeneratorLayer::KisGeneratorLayer(KisImageWSP image, const QString &name, KisFilterConfiguration *kfc, KisSelectionSP selection) : KisSelectionBasedLayer(image, name, selection, kfc, true), m_d(new Private) { connect(&m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDelayedStaticUpdate())); update(); } KisGeneratorLayer::KisGeneratorLayer(const KisGeneratorLayer& rhs) : KisSelectionBasedLayer(rhs), m_d(new Private) { connect(&m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDelayedStaticUpdate())); } KisGeneratorLayer::~KisGeneratorLayer() { } void KisGeneratorLayer::setFilter(KisFilterConfiguration *filterConfig) { KisSelectionBasedLayer::setFilter(filterConfig); update(); } void KisGeneratorLayer::slotDelayedStaticUpdate() { /** * The mask might have been deleted from the layers stack in the * meanwhile. Just ignore the updates in the case. */ KisLayerSP parentLayer = dynamic_cast(parent().data()); if (!parentLayer) return; KisImageSP image = parentLayer->image(); if (image) { image->addSpontaneousJob(new KisRecalculateGeneratorLayerJob(this)); } } void KisGeneratorLayer::update() { KisSafeFilterConfigurationSP filterConfig = filter(); if (!filterConfig) { warnImage << "BUG: No Filter configuration in KisGeneratorLayer"; return; } KisGeneratorSP f = KisGeneratorRegistry::instance()->value(filterConfig->name()); if (!f) return; QRect processRect = exactBounds(); resetCache(f->colorSpace()); KisPaintDeviceSP originalDevice = original(); KisProcessingInformation dstCfg(originalDevice, processRect.topLeft(), 0); f->generate(dstCfg, processRect.size(), filterConfig.data()); // hack alert! // this avoids cyclic loop with KisRecalculateGeneratorLayerJob::run() KisSelectionBasedLayer::setDirty(extent()); } bool KisGeneratorLayer::accept(KisNodeVisitor & v) { return v.visit(this); } void KisGeneratorLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) { return visitor.visit(this, undoAdapter); } QIcon KisGeneratorLayer::icon() const { return KisIconUtils::loadIcon("fillLayer"); } KisBaseNode::PropertyList KisGeneratorLayer::sectionModelProperties() const { KisSafeFilterConfigurationSP filterConfig = filter(); KisBaseNode::PropertyList l = KisLayer::sectionModelProperties(); l << KisBaseNode::Property(KoID("generator", i18n("Generator")), KisGeneratorRegistry::instance()->value(filterConfig->name())->name()); return l; } void KisGeneratorLayer::setX(qint32 x) { KisSelectionBasedLayer::setX(x); m_d->updateSignalCompressor.start(); } void KisGeneratorLayer::setY(qint32 y) { KisSelectionBasedLayer::setY(y); m_d->updateSignalCompressor.start(); } void KisGeneratorLayer::setDirty(const QRect & rect) { KisSelectionBasedLayer::setDirty(rect); m_d->updateSignalCompressor.start(); } diff --git a/libs/image/kis_antialiasing_fade_maker.h b/libs/image/kis_antialiasing_fade_maker.h index 4d57aec058..cc9b05d412 100644 --- a/libs/image/kis_antialiasing_fade_maker.h +++ b/libs/image/kis_antialiasing_fade_maker.h @@ -1,170 +1,192 @@ /* * Copyright (c) 2014 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_ANTIALIASING_FADE_MAKER_H #define __KIS_ANTIALIASING_FADE_MAKER_H #include "kis_global.h" template class KisAntialiasingFadeMaker1D { public: KisAntialiasingFadeMaker1D(const BaseFade &baseFade, bool enableAntialiasing) : m_radius(0.0), m_fadeStartValue(0), m_antialiasingFadeStart(0), m_antialiasingFadeCoeff(0), m_enableAntialiasing(enableAntialiasing), m_baseFade(baseFade) { } + KisAntialiasingFadeMaker1D(const KisAntialiasingFadeMaker1D &rhs, const BaseFade &baseFade) + : m_radius(rhs.m_radius), + m_fadeStartValue(rhs.m_fadeStartValue), + m_antialiasingFadeStart(rhs.m_antialiasingFadeStart), + m_antialiasingFadeCoeff(rhs.m_antialiasingFadeCoeff), + m_enableAntialiasing(rhs.m_enableAntialiasing), + m_baseFade(baseFade) + { + } + void setSquareNormCoeffs(qreal xcoeff, qreal ycoeff) { m_radius = 1.0; qreal xf = qMax(0.0, ((1.0 / xcoeff) - 1.0) * xcoeff); qreal yf = qMax(0.0, ((1.0 / ycoeff) - 1.0) * ycoeff); m_antialiasingFadeStart = pow2(0.5 * (xf + yf)); m_fadeStartValue = m_baseFade.value(m_antialiasingFadeStart); m_antialiasingFadeCoeff = qMax(0.0, 255.0 - m_fadeStartValue) / (m_radius - m_antialiasingFadeStart); } void setRadius(qreal radius) { m_radius = radius; m_antialiasingFadeStart = qMax(0.0, m_radius - 1.0); m_fadeStartValue = m_baseFade.value(m_antialiasingFadeStart); m_antialiasingFadeCoeff = qMax(0.0, 255.0 - m_fadeStartValue) / (m_radius - m_antialiasingFadeStart); } inline bool needFade(qreal dist, quint8 *value) { if (dist > m_radius) { *value = 255; return true; } if (!m_enableAntialiasing) { return false; } if (dist > m_antialiasingFadeStart) { *value = m_fadeStartValue + (dist - m_antialiasingFadeStart) * m_antialiasingFadeCoeff; return true; } return false; } private: qreal m_radius; quint8 m_fadeStartValue; qreal m_antialiasingFadeStart; qreal m_antialiasingFadeCoeff; bool m_enableAntialiasing; const BaseFade &m_baseFade; }; template class KisAntialiasingFadeMaker2D { public: KisAntialiasingFadeMaker2D(const BaseFade &baseFade, bool enableAntialiasing) : m_xLimit(0), m_yLimit(0), m_xFadeLimitStart(0), m_yFadeLimitStart(0), m_xFadeCoeff(0), m_yFadeCoeff(0), m_enableAntialiasing(enableAntialiasing), m_baseFade(baseFade) { } + KisAntialiasingFadeMaker2D(const KisAntialiasingFadeMaker2D &rhs, const BaseFade &baseFade) + : m_xLimit(rhs.m_xLimit), + m_yLimit(rhs.m_yLimit), + m_xFadeLimitStart(rhs.m_xFadeLimitStart), + m_yFadeLimitStart(rhs.m_yFadeLimitStart), + m_xFadeCoeff(rhs.m_xFadeCoeff), + m_yFadeCoeff(rhs.m_yFadeCoeff), + m_enableAntialiasing(rhs.m_enableAntialiasing), + m_baseFade(baseFade) + { + } + void setLimits(qreal halfWidth, qreal halfHeight) { m_xLimit = halfWidth; m_yLimit = halfHeight; m_xFadeLimitStart = m_xLimit - 1.0; m_yFadeLimitStart = m_yLimit - 1.0; m_xFadeCoeff = 1.0 / (m_xLimit - m_xFadeLimitStart); m_yFadeCoeff = 1.0 / (m_yLimit - m_yFadeLimitStart); } inline bool needFade(qreal x, qreal y, quint8 *value) { x = qAbs(x); y = qAbs(y); if (x > m_xLimit) { *value = 255; return true; } if (y > m_yLimit) { *value = 255; return true; } if (!m_enableAntialiasing) { return false; } if (x > m_xFadeLimitStart) { quint8 baseValue = m_baseFade.value(x, y); *value = baseValue + (255.0 - baseValue) * (x - m_xFadeLimitStart) * m_xFadeCoeff; if (y > m_yFadeLimitStart && *value < 255) { *value += (255.0 - *value) * (y - m_yFadeLimitStart) * m_yFadeCoeff; } return true; } if (y > m_yFadeLimitStart) { quint8 baseValue = m_baseFade.value(x, y); *value = baseValue + (255.0 - baseValue) * (y - m_yFadeLimitStart) * m_yFadeCoeff; if (x > m_xFadeLimitStart && *value < 255) { *value += (255.0 - *value) * (x - m_xFadeLimitStart) * m_xFadeCoeff; } return true; } return false; } private: qreal m_xLimit; qreal m_yLimit; qreal m_xFadeLimitStart; qreal m_yFadeLimitStart; qreal m_xFadeCoeff; qreal m_yFadeCoeff; bool m_enableAntialiasing; const BaseFade &m_baseFade; }; #endif /* __KIS_ANTIALIASING_FADE_MAKER_H */ diff --git a/libs/image/kis_async_merger.cpp b/libs/image/kis_async_merger.cpp index d1cf2499de..ed443bce83 100644 --- a/libs/image/kis_async_merger.cpp +++ b/libs/image/kis_async_merger.cpp @@ -1,362 +1,362 @@ /* 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(QRect updateRect, KisPaintDeviceSP projection, QRect cropRect) + KisUpdateOriginalVisitor(const QRect &updateRect, KisPaintDeviceSP projection, const QRect &cropRect) : m_updateRect(updateRect), m_cropRect(cropRect), m_projection(projection) { } ~KisUpdateOriginalVisitor() { } public: using KisNodeVisitor::visit; bool visit(KisAdjustmentLayer* layer) { 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; } KisPaintDeviceSP originalDevice = layer->original(); originalDevice->clear(m_updateRect); const QRect applyRect = m_updateRect & m_projection->extent(); // If the intersection of the updaterect and the projection extent is // null, we are finish here. if(applyRect.isNull()) return true; KisSafeFilterConfigurationSP 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*) { return true; } bool visit(KisGeneratorLayer*) { return true; } bool visit(KisPaintLayer*) { return true; } bool visit(KisGroupLayer*) { return true; } bool visit(KisCloneLayer *layer) { 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; } Q_FOREACH (const QRect &rect, prepareRegion.rects()) { walker.collectRects(srcLayer, rect); merger.startMerge(walker, false); } return true; } bool visit(KisNode*) { return true; } bool visit(KisFilterMask*) { return true; } bool visit(KisTransformMask*) { return true; } bool visit(KisTransparencyMask*) { return true; } bool visit(KisSelectionMask*) { 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; if(currentLeaf->isRoot()) continue; // All the masks should be filtered by the walkers Q_ASSERT(currentLeaf->isLayer()); QRect applyRect = item.m_applyRect; 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); currentLeaf->accept(originalVisitor); if (currentLeaf->visible()) { 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()) { currentLeaf->accept(originalVisitor); if (currentLeaf->visible()) { 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->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 << " There must be an inconsistency in the walkers happened!"; warnImage << " Please report a bug describing how you got this message."; // reset projection to avoid artefacts 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, QRect rect) { +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_async_merger.h b/libs/image/kis_async_merger.h index 57ec23880e..e9f52fdf08 100644 --- a/libs/image/kis_async_merger.h +++ b/libs/image/kis_async_merger.h @@ -1,69 +1,69 @@ /* 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. */ #ifndef __KIS_ASYNC_MERGER_H #define __KIS_ASYNC_MERGER_H #include "kritaimage_export.h" #include "kis_types.h" class QRect; class KisBaseRectsWalker; class KRITAIMAGE_EXPORT KisAsyncMerger { public: void startMerge(KisBaseRectsWalker &walker, bool notifyClones = true); private: inline void resetProjection(); inline void setupProjection(KisProjectionLeafSP currentLeaf, const QRect& rect, bool useTempProjection); - inline void writeProjection(KisProjectionLeafSP topmostLeaf, bool useTempProjection, QRect rect); + inline void writeProjection(KisProjectionLeafSP topmostLeaf, bool useTempProjection, const QRect &rect); inline bool compositeWithProjection(KisProjectionLeafSP leaf, const QRect &rect); inline void doNotifyClones(KisBaseRectsWalker &walker); private: /** * The place where intermediate results of layer's merge * are going. It may be equal to m_finalProjection. In * this case the projection will be written directly to * the original of the parent layer */ KisPaintDeviceSP m_currentProjection; /** * The final destination of every projection of all * the layers. It is equal to the original of a parental * node. * NOTE: This pointer is cached here to avoid * race conditions */ KisPaintDeviceSP m_finalProjection; /** * Creation of the paint device is quite expensive, so we'll just * save the pointer to our temporary device here and will get it when * needed. This variable must not be used anywhere out of * setupProjection() */ KisPaintDeviceSP m_cachedPaintDevice; }; #endif /* __KIS_ASYNC_MERGER_H */ diff --git a/libs/image/kis_base_mask_generator.cpp b/libs/image/kis_base_mask_generator.cpp index 7b29da4ea7..13821749eb 100644 --- a/libs/image/kis_base_mask_generator.cpp +++ b/libs/image/kis_base_mask_generator.cpp @@ -1,292 +1,332 @@ /* * Copyright (c) 2004,2007-2009 Cyrille Berger * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2011 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 //vc.h must come first #include "kis_brush_mask_applicator_factories.h" #include "kis_mask_generator.h" #include "kis_brush_mask_applicator_base.h" #include #include "kis_fast_math.h" #include #include "kis_circle_mask_generator.h" #include "kis_rect_mask_generator.h" #include "kis_gauss_circle_mask_generator.h" #include "kis_gauss_rect_mask_generator.h" #include "kis_cubic_curve.h" #include "kis_curve_circle_mask_generator.h" #include "kis_curve_rect_mask_generator.h" #include struct KisMaskGenerator::Private { + Private() + : diameter(1.0), + ratio(1.0), + softness(1.0), + fh(1.0), + fv(1.0), + cs(0.0), + ss(0.0), + cachedSpikesAngle(0.0), + spikes(2), + empty(true), + antialiasEdges(false), + type(CIRCLE), + scaleX(1.0), + scaleY(1.0) + { + } + + Private(const Private &rhs) + : diameter(rhs.diameter), + ratio(rhs.ratio), + softness(rhs.softness), + fh(rhs.fh), + fv(rhs.fv), + cs(rhs.cs), + ss(rhs.ss), + cachedSpikesAngle(rhs.cachedSpikesAngle), + spikes(rhs.spikes), + empty(rhs.empty), + antialiasEdges(rhs.antialiasEdges), + type(rhs.type), + curveString(rhs.curveString), + scaleX(rhs.scaleX), + scaleY(rhs.scaleY) + { + } + qreal diameter, ratio; qreal softness; qreal fh, fv; qreal cs, ss; qreal cachedSpikesAngle; int spikes; bool empty; bool antialiasEdges; Type type; QString curveString; qreal scaleX; qreal scaleY; - KisBrushMaskApplicatorBase *defaultMaskProcessor; + QScopedPointer defaultMaskProcessor; }; KisMaskGenerator::KisMaskGenerator(qreal diameter, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges, Type type, const KoID& id) : d(new Private), m_id(id) { d->diameter = diameter; d->ratio = ratio; d->fh = 0.5 * fh; d->fv = 0.5 * fv; d->softness = 1.0; // by default don't change fade/softness/hardness d->spikes = spikes; d->cachedSpikesAngle = M_PI / d->spikes; d->type = type; - d->defaultMaskProcessor = 0; d->antialiasEdges = antialiasEdges; d->scaleX = 1.0; d->scaleY = 1.0; init(); } KisMaskGenerator::~KisMaskGenerator() { - delete d->defaultMaskProcessor; - delete d; +} + +KisMaskGenerator::KisMaskGenerator(const KisMaskGenerator &rhs) + : d(new Private(*rhs.d)), + m_id(rhs.m_id) +{ } void KisMaskGenerator::init() { d->cs = cos(- 2 * M_PI / d->spikes); d->ss = sin(- 2 * M_PI / d->spikes); d->empty = (d->ratio == 0.0 || d->diameter == 0.0); } bool KisMaskGenerator::shouldSupersample() const { return false; } bool KisMaskGenerator::shouldVectorize() const { return false; } bool KisMaskGenerator::isEmpty() const { return d->empty; } KisBrushMaskApplicatorBase* KisMaskGenerator::applicator() { if (!d->defaultMaskProcessor) { - d->defaultMaskProcessor = - createOptimizedClass >(this); + d->defaultMaskProcessor.reset( + createOptimizedClass >(this)); } - return d->defaultMaskProcessor; + return d->defaultMaskProcessor.data(); } void KisMaskGenerator::toXML(QDomDocument& doc, QDomElement& e) const { Q_UNUSED(doc); //e.setAttribute("radius", d->radius); e.setAttribute("diameter", QString::number(d->diameter)); e.setAttribute("ratio", QString::number(d->ratio)); e.setAttribute("hfade", QString::number(horizontalFade())); e.setAttribute("vfade", QString::number(verticalFade())); e.setAttribute("spikes", d->spikes); e.setAttribute("type", d->type == CIRCLE ? "circle" : "rect"); e.setAttribute("antialiasEdges", d->antialiasEdges); e.setAttribute("id", id()); } KisMaskGenerator* KisMaskGenerator::fromXML(const QDomElement& elt) { double diameter = 1.0; // backward compatibility -- it was mistakenly named radius for 2.2 if (elt.hasAttribute("radius")){ diameter = KisDomUtils::toDouble(elt.attribute("radius", "1.0")); } else /*if (elt.hasAttribute("diameter"))*/{ diameter = KisDomUtils::toDouble(elt.attribute("diameter", "1.0")); } double ratio = KisDomUtils::toDouble(elt.attribute("ratio", "1.0")); double hfade = KisDomUtils::toDouble(elt.attribute("hfade", "0.0")); double vfade = KisDomUtils::toDouble(elt.attribute("vfade", "0.0")); int spikes = elt.attribute("spikes", "2").toInt(); QString typeShape = elt.attribute("type", "circle"); QString id = elt.attribute("id", DefaultId.id()); bool antialiasEdges = elt.attribute("antialiasEdges", "0").toInt(); if (id == DefaultId.id()) { if (typeShape == "circle") { return new KisCircleMaskGenerator(diameter, ratio, hfade, vfade, spikes, antialiasEdges); } else { return new KisRectangleMaskGenerator(diameter, ratio, hfade, vfade, spikes, antialiasEdges); } } if (id == SoftId.id()) { KisCubicCurve curve; curve.fromString(elt.attribute("softness_curve","0,0;1,1")); if (typeShape == "circle") { return new KisCurveCircleMaskGenerator(diameter, ratio, hfade, vfade, spikes, curve, antialiasEdges); } else { return new KisCurveRectangleMaskGenerator(diameter, ratio, hfade, vfade, spikes, curve, antialiasEdges); } } if (id == GaussId.id()) { if (typeShape == "circle") { return new KisGaussCircleMaskGenerator(diameter, ratio, hfade, vfade, spikes, antialiasEdges); } else { return new KisGaussRectangleMaskGenerator(diameter, ratio, hfade, vfade, spikes, antialiasEdges); } } // if unknown return new KisCircleMaskGenerator(diameter, ratio, hfade, vfade, spikes, true); } qreal KisMaskGenerator::width() const { return d->diameter; } qreal KisMaskGenerator::height() const { if (d->spikes == 2) { return d->diameter * d->ratio; } return d->diameter; } qreal KisMaskGenerator::effectiveSrcWidth() const { return d->diameter * d->scaleX; } qreal KisMaskGenerator::effectiveSrcHeight() const { /** * This height is related to the source of the brush mask, so we * don't take spikes into account, they will be generated from * this data. */ return d->diameter * d->ratio * d->scaleX; } qreal KisMaskGenerator::diameter() const { return d->diameter; } qreal KisMaskGenerator::ratio() const { return d->ratio; } qreal KisMaskGenerator::softness() const { return d->softness; } void KisMaskGenerator::setSoftness(qreal softness) { d->softness = softness; } qreal KisMaskGenerator::horizontalFade() const { return 2.0 * d->fh; // 'cause in init we divide it again } qreal KisMaskGenerator::verticalFade() const { return 2.0 * d->fv; // 'cause in init we divide it again } int KisMaskGenerator::spikes() const { return d->spikes; } KisMaskGenerator::Type KisMaskGenerator::type() const { return d->type; } QList< KoID > KisMaskGenerator::maskGeneratorIds() { QList ids; ids << DefaultId << SoftId << GaussId; return ids; } QString KisMaskGenerator::curveString() const { return d->curveString; } void KisMaskGenerator::setCurveString(const QString& curveString) { d->curveString = curveString; } bool KisMaskGenerator::antialiasEdges() const { return d->antialiasEdges; } void KisMaskGenerator::setScale(qreal scaleX, qreal scaleY) { d->scaleX = scaleX; d->scaleY = scaleY; } void KisMaskGenerator::fixRotation(qreal &xr, qreal &yr) const { if (d->spikes > 2) { double angle = (KisFastMath::atan2(yr, xr)); while (angle > d->cachedSpikesAngle){ double sx = xr; double sy = yr; xr = d->cs * sx - d->ss * sy; yr = d->ss * sx + d->cs * sy; angle -= 2 * d->cachedSpikesAngle; } } } diff --git a/libs/image/kis_base_mask_generator.h b/libs/image/kis_base_mask_generator.h index a21a60c035..ef7cb34b91 100644 --- a/libs/image/kis_base_mask_generator.h +++ b/libs/image/kis_base_mask_generator.h @@ -1,123 +1,128 @@ /* * Copyright (c) 2008-2009 Cyrille Berger * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_MASK_GENERATOR_H_ #define _KIS_MASK_GENERATOR_H_ +#include + #include //MSVC requires that Vc come first #include #include #include "kritaimage_export.h" class QDomElement; class QDomDocument; class KisBrushMaskApplicatorBase; const KoID DefaultId("default", ki18n("Default")); ///< generate Krita default mask generator const KoID SoftId("soft", ki18n("Soft")); ///< generate brush mask from former softbrush paintop, where softness is based on curve const KoID GaussId("gauss", ki18n("Gaussian")); ///< generate brush mask with a Gaussian-blurred edge static const int OVERSAMPLING = 4; /** * This is the base class for mask shapes * You should subclass it if you want to create a new * shape. */ class KRITAIMAGE_EXPORT KisMaskGenerator { public: enum Type { CIRCLE, RECTANGLE }; public: /** * This function creates an auto brush shape with the following value : * @param w width * @param h height * @param fh horizontal fade (fh \< w / 2 ) * @param fv vertical fade (fv \< h / 2 ) */ KisMaskGenerator(qreal radius, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges, Type type, const KoID& id = DefaultId); + KisMaskGenerator(const KisMaskGenerator &rhs); virtual ~KisMaskGenerator(); + virtual KisMaskGenerator* clone() const = 0; + private: void init(); public: /** * @return the alpha value at the position (x,y) */ virtual quint8 valueAt(qreal x, qreal y) const = 0; virtual bool shouldSupersample() const; virtual bool shouldVectorize() const; virtual KisBrushMaskApplicatorBase* applicator(); virtual void toXML(QDomDocument& , QDomElement&) const; /** * Unserialise a \ref KisMaskGenerator */ static KisMaskGenerator* fromXML(const QDomElement&); qreal width() const; qreal height() const; qreal diameter() const; qreal ratio() const; qreal horizontalFade() const; qreal verticalFade() const; int spikes() const; Type type() const; bool isEmpty() const; void fixRotation(qreal &xr, qreal &yr) const; inline QString id() const { return m_id.id(); } inline QString name() const { return m_id.name(); } static QList maskGeneratorIds(); qreal softness() const; virtual void setSoftness(qreal softness); QString curveString() const; void setCurveString(const QString& curveString); bool antialiasEdges() const; virtual void setScale(qreal scaleX, qreal scaleY); protected: qreal effectiveSrcWidth() const; qreal effectiveSrcHeight() const; private: struct Private; - Private* d; + const QScopedPointer d; const KoID& m_id; }; #endif diff --git a/libs/image/kis_brush_mask_applicator_factories.cpp b/libs/image/kis_brush_mask_applicator_factories.cpp index f29784001b..2c54a61ef8 100644 --- a/libs/image/kis_brush_mask_applicator_factories.cpp +++ b/libs/image/kis_brush_mask_applicator_factories.cpp @@ -1,119 +1,137 @@ /* * Copyright (c) 2012 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_brush_mask_applicator_factories.h" #include "kis_circle_mask_generator.h" #include "kis_circle_mask_generator_p.h" #include "kis_brush_mask_applicators.h" #include "kis_brush_mask_applicator_base.h" #define a(_s) #_s #define b(_s) a(_s) template<> template<> MaskApplicatorFactory::ReturnType -MaskApplicatorFactory::create(ParamType maskGenerator) +MaskApplicatorFactory::create(ParamType maskGenerator) { - return new KisBrushMaskScalarApplicator(maskGenerator); + return new KisBrushMaskScalarApplicator(maskGenerator); } template<> template<> MaskApplicatorFactory::ReturnType -MaskApplicatorFactory::create(ParamType maskGenerator) +MaskApplicatorFactory::create(ParamType maskGenerator) { - return new KisBrushMaskVectorApplicator(maskGenerator); + return new KisBrushMaskVectorApplicator(maskGenerator); } #if defined HAVE_VC struct KisCircleMaskGenerator::FastRowProcessor { FastRowProcessor(KisCircleMaskGenerator *maskGenerator) - : d(maskGenerator->d) {} + : d(maskGenerator->d.data()) {} template void process(float* buffer, int width, float y, float cosa, float sina, float centerX, float centerY); KisCircleMaskGenerator::Private *d; }; template<> void KisCircleMaskGenerator:: -FastRowProcessor::process(float* buffer, int width, float y, float cosa, float sina, +FastRowProcessor::process(float* buffer, int width, float y, float cosa, float sina, float centerX, float centerY) { const bool useSmoothing = d->copyOfAntialiasEdges; + const bool noFading = d->noFading; float y_ = y - centerY; float sinay_ = sina * y_; float cosay_ = cosa * y_; float* bufferPointer = buffer; - Vc::float_v currentIndices(Vc::int_v::IndexesFromZero()); + Vc::float_v currentIndices = Vc::float_v::IndexesFromZero(); - Vc::float_v increment((float)Vc::float_v::Size); + Vc::float_v increment((float)Vc::float_v::size()); Vc::float_v vCenterX(centerX); Vc::float_v vCosa(cosa); Vc::float_v vSina(sina); Vc::float_v vCosaY_(cosay_); Vc::float_v vSinaY_(sinay_); Vc::float_v vXCoeff(d->xcoef); Vc::float_v vYCoeff(d->ycoef); Vc::float_v vTransformedFadeX(d->transformedFadeX); Vc::float_v vTransformedFadeY(d->transformedFadeY); - Vc::float_v vOne(1.0f); + Vc::float_v vOne(Vc::One); - for (int i=0; i < width; i+= Vc::float_v::Size){ + for (int i=0; i < width; i+= Vc::float_v::size()){ Vc::float_v x_ = currentIndices - vCenterX; Vc::float_v xr = x_ * vCosa - vSinaY_; Vc::float_v yr = x_ * vSina + vCosaY_; Vc::float_v n = pow2(xr * vXCoeff) + pow2(yr * vYCoeff); + Vc::float_m outsideMask = n > vOne; - if (useSmoothing) { - xr = Vc::abs(xr) + vOne; - yr = Vc::abs(yr) + vOne; - } + if (!outsideMask.isFull()) { + + if (noFading) { + Vc::float_v vFade(Vc::Zero); + vFade(outsideMask) = vOne; + vFade.store(bufferPointer, Vc::Aligned); + } else { + if (useSmoothing) { + xr = Vc::abs(xr) + vOne; + yr = Vc::abs(yr) + vOne; + } + + Vc::float_v vNormFade = pow2(xr * vTransformedFadeX) + pow2(yr * vTransformedFadeY); - Vc::float_v vNormFade = pow2(xr * vTransformedFadeX) + pow2(yr * vTransformedFadeY); + //255 * n * (normeFade - 1) / (normeFade - n) + Vc::float_v vFade = n * (vNormFade - vOne) / (vNormFade - n); - //255 * n * (normeFade - 1) / (normeFade - n) - Vc::float_v vFade = n * (vNormFade - vOne) / (vNormFade - n); - // Mask out the inner circe of the mask - Vc::float_m mask = vNormFade < vOne; - vFade.setZero(mask); - vFade = Vc::min(vFade, vOne); + // Mask in the inner circe of the mask + Vc::float_m mask = vNormFade < vOne; + vFade.setZero(mask); + + // Mask out the outer circe of the mask + vFade(outsideMask) = vOne; + + vFade.store(bufferPointer, Vc::Aligned); + } + } else { + // Mask out everything outside the circle + vOne.store(bufferPointer, Vc::Aligned); + } - vFade.store(bufferPointer); currentIndices = currentIndices + increment; - bufferPointer += Vc::float_v::Size; + bufferPointer += Vc::float_v::size(); } } #endif /* defined HAVE_VC */ diff --git a/libs/image/kis_brush_mask_applicators.h b/libs/image/kis_brush_mask_applicators.h index ef6c5492e2..55a0c76b69 100644 --- a/libs/image/kis_brush_mask_applicators.h +++ b/libs/image/kis_brush_mask_applicators.h @@ -1,214 +1,214 @@ /* * Copyright (c) 2012 Sven Langkamp * Copyright (c) 2012 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_BRUSH_MASK_APPLICATORS_H #define __KIS_BRUSH_MASK_APPLICATORS_H #include "kis_brush_mask_applicator_base.h" #include "kis_global.h" // 3x3 supersampling #define SUPERSAMPLING 3 #if defined(_WIN32) || defined(_WIN64) #include #define srand48 srand inline double drand48() { return double(rand()) / RAND_MAX; } #endif #include template struct KisBrushMaskScalarApplicator : public KisBrushMaskApplicatorBase { KisBrushMaskScalarApplicator(MaskGenerator *maskGenerator) : m_maskGenerator(maskGenerator) { } void process(const QRect &rect) { processScalar(rect); } protected: void processScalar(const QRect &rect); protected: MaskGenerator *m_maskGenerator; }; #if defined HAVE_VC template struct KisBrushMaskVectorApplicator : public KisBrushMaskScalarApplicator { KisBrushMaskVectorApplicator(MaskGenerator *maskGenerator) : KisBrushMaskScalarApplicator(maskGenerator) { } void process(const QRect &rect) { startProcessing(rect, TypeHelper()); } protected: void processVector(const QRect &rect); private: template struct TypeHelper {}; private: template inline void startProcessing(const QRect &rect, TypeHelper) { KisBrushMaskScalarApplicator::processScalar(rect); } template inline void startProcessing(const QRect &rect, TypeHelper) { MaskGenerator *m_maskGenerator = KisBrushMaskScalarApplicator::m_maskGenerator; if (m_maskGenerator->shouldVectorize()) { processVector(rect); } else { KisBrushMaskScalarApplicator::processScalar(rect); } } }; template void KisBrushMaskVectorApplicator::processVector(const QRect &rect) { const MaskProcessingData *m_d = KisBrushMaskApplicatorBase::m_d; MaskGenerator *m_maskGenerator = KisBrushMaskScalarApplicator::m_maskGenerator; qreal random = 1.0; quint8* dabPointer = m_d->device->data() + rect.y() * rect.width() * m_d->pixelSize; quint8 alphaValue = OPACITY_TRANSPARENT_U8; // this offset is needed when brush size is smaller then fixed device size int offset = (m_d->device->bounds().width() - rect.width()) * m_d->pixelSize; int width = rect.width(); // We need to calculate with a multiple of the width of the simd register int alignOffset = 0; - if (width % Vc::float_v::Size != 0) { - alignOffset = Vc::float_v::Size - (width % Vc::float_v::Size); + if (width % Vc::float_v::size() != 0) { + alignOffset = Vc::float_v::size() - (width % Vc::float_v::size()); } int simdWidth = width + alignOffset; float *buffer = Vc::malloc(simdWidth); typename MaskGenerator::FastRowProcessor processor(m_maskGenerator); for (int y = rect.y(); y < rect.y() + rect.height(); y++) { processor.template process<_impl>(buffer, simdWidth, y, m_d->cosa, m_d->sina, m_d->centerX, m_d->centerY); if (m_d->randomness != 0.0 || m_d->density != 1.0) { for (int x = 0; x < width; x++) { if (m_d->randomness!= 0.0){ random = (1.0 - m_d->randomness) + m_d->randomness * float(rand()) / RAND_MAX; } alphaValue = quint8( (OPACITY_OPAQUE_U8 - buffer[x]*255) * random); // avoid computation of random numbers if density is full if (m_d->density != 1.0){ // compute density only for visible pixels of the mask if (alphaValue != OPACITY_TRANSPARENT_U8){ if ( !(m_d->density >= drand48()) ){ alphaValue = OPACITY_TRANSPARENT_U8; } } } m_d->colorSpace->applyAlphaU8Mask(dabPointer, &alphaValue, 1); dabPointer += m_d->pixelSize; } } else { m_d->colorSpace->applyInverseNormedFloatMask(dabPointer, buffer, width); dabPointer += width * m_d->pixelSize; }//endfor x dabPointer += offset; }//endfor y Vc::free(buffer); } #endif /* defined HAVE_VC */ template void KisBrushMaskScalarApplicator::processScalar(const QRect &rect) { const MaskProcessingData *m_d = KisBrushMaskApplicatorBase::m_d; MaskGenerator *m_maskGenerator = KisBrushMaskScalarApplicator::m_maskGenerator; std::random_device rand_dev; std::default_random_engine rand_engine{rand_dev()}; std::uniform_real_distribution<> rand_distr(0.0f, 1.0f); qreal random = 1.0; quint8* dabPointer = m_d->device->data() + rect.y() * rect.width() * m_d->pixelSize; quint8 alphaValue = OPACITY_TRANSPARENT_U8; // this offset is needed when brush size is smaller then fixed device size int offset = (m_d->device->bounds().width() - rect.width()) * m_d->pixelSize; int supersample = (m_maskGenerator->shouldSupersample() ? SUPERSAMPLING : 1); double invss = 1.0 / supersample; int samplearea = pow2(supersample); for (int y = rect.y(); y < rect.y() + rect.height(); y++) { for (int x = rect.x(); x < rect.x() + rect.width(); x++) { int value = 0; for (int sy = 0; sy < supersample; sy++) { for (int sx = 0; sx < supersample; sx++) { double x_ = x + sx * invss - m_d->centerX; double y_ = y + sy * invss - m_d->centerY; double maskX = m_d->cosa * x_ - m_d->sina * y_; double maskY = m_d->sina * x_ + m_d->cosa * y_; value += m_maskGenerator->valueAt(maskX, maskY); } } if (supersample != 1) value /= samplearea; if (m_d->randomness!= 0.0){ random = (1.0 - m_d->randomness) + m_d->randomness * rand_distr(rand_engine); } alphaValue = quint8( (OPACITY_OPAQUE_U8 - value) * random); // avoid computation of random numbers if density is full if (m_d->density != 1.0){ // compute density only for visible pixels of the mask if (alphaValue != OPACITY_TRANSPARENT_U8){ if ( !(m_d->density >= rand_distr(rand_engine)) ){ alphaValue = OPACITY_TRANSPARENT_U8; } } } m_d->colorSpace->applyAlphaU8Mask(dabPointer, &alphaValue, 1); dabPointer += m_d->pixelSize; }//endfor x dabPointer += offset; }//endfor y } #endif /* __KIS_BRUSH_MASK_APPLICATORS_H */ diff --git a/libs/image/kis_busy_progress_indicator.cpp b/libs/image/kis_busy_progress_indicator.cpp index 5104254882..e8ea2955ec 100644 --- a/libs/image/kis_busy_progress_indicator.cpp +++ b/libs/image/kis_busy_progress_indicator.cpp @@ -1,107 +1,113 @@ /* * 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_busy_progress_indicator.h" #include #include #include "KoProgressProxy.h" struct KisBusyProgressIndicator::Private { Private(KisBusyProgressIndicator *_q) : timer(new QTimer(_q)), numEmptyTicks(0), isStarted(false) {} QTimer *timer; // owned by QObject hierarchy int numEmptyTicks; QAtomicInt numUpdates; QAtomicInt timerStarted; KoProgressProxy *progressProxy; bool isStarted; - void startProgressReport() { + void startProgressReport() + { + if (!progressProxy) { + return; + } isStarted = true; progressProxy->setRange(0, 0); } - void stopProgressReport() { - if (!isStarted) return; - + void stopProgressReport() + { + if (!isStarted || !progressProxy) { + return; + } progressProxy->setRange(0, 100); progressProxy->setValue(100); isStarted = false; } }; KisBusyProgressIndicator::KisBusyProgressIndicator(KoProgressProxy *progressProxy) : m_d(new Private(this)) { connect(m_d->timer, SIGNAL(timeout()), SLOT(timerFinished())); connect(this, SIGNAL(sigStartTimer()), SLOT(slotStartTimer())); m_d->timer->setInterval(200); m_d->progressProxy = progressProxy; } KisBusyProgressIndicator::~KisBusyProgressIndicator() { m_d->stopProgressReport(); } -void KisBusyProgressIndicator::endUpdatesBeforeDestroying() +void KisBusyProgressIndicator::prepareDestroying() { - m_d->stopProgressReport(); + m_d->progressProxy = 0; } void KisBusyProgressIndicator::timerFinished() { int value = m_d->numUpdates.fetchAndStoreOrdered(0); if (!value) { m_d->numEmptyTicks++; if (m_d->numEmptyTicks > 2) { m_d->timerStarted = 0; m_d->timer->stop(); m_d->stopProgressReport(); } } else { m_d->numEmptyTicks = 0; } } void KisBusyProgressIndicator::update() { m_d->numUpdates.ref(); if (!m_d->timerStarted) { emit sigStartTimer(); } } void KisBusyProgressIndicator::slotStartTimer() { m_d->timerStarted.ref(); m_d->timer->start(); m_d->startProgressReport(); } diff --git a/libs/image/kis_busy_progress_indicator.h b/libs/image/kis_busy_progress_indicator.h index 6c648b6983..8358d37db8 100644 --- a/libs/image/kis_busy_progress_indicator.h +++ b/libs/image/kis_busy_progress_indicator.h @@ -1,52 +1,63 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_BUSY_PROGRESS_INDICATOR_H #define __KIS_BUSY_PROGRESS_INDICATOR_H #include #include class KoProgressProxy; - class KisBusyProgressIndicator : public QObject { Q_OBJECT public: - KisBusyProgressIndicator(KoProgressProxy *progressProxy); + explicit KisBusyProgressIndicator(KoProgressProxy *progressProxy); ~KisBusyProgressIndicator(); - void endUpdatesBeforeDestroying(); +public: + /** + * To be called when progressProxy is and will be no longer available + * and this object is going to be deleted as well. + */ + void prepareDestroying(); public Q_SLOTS: + /** + * Trigger update of progress state. + */ void update(); private Q_SLOTS: + /** + * Call only via emitting sigStartTimer, to ensure it is called in + * the context of the QObject's thread. + */ void slotStartTimer(); void timerFinished(); Q_SIGNALS: void sigStartTimer(); private: struct Private; const QScopedPointer m_d; }; #endif /* __KIS_BUSY_PROGRESS_INDICATOR_H */ diff --git a/libs/image/kis_cage_transform_worker.cpp b/libs/image/kis_cage_transform_worker.cpp index a15cd67ac5..d52068ee58 100644 --- a/libs/image/kis_cage_transform_worker.cpp +++ b/libs/image/kis_cage_transform_worker.cpp @@ -1,447 +1,447 @@ /* * 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_cage_transform_worker.h" #include "kis_grid_interpolation_tools.h" #include "kis_green_coordinates_math.h" #include #include "KoColor.h" #include "kis_selection.h" #include "kis_painter.h" #include "kis_image.h" #include "krita_utils.h" #include struct Q_DECL_HIDDEN KisCageTransformWorker::Private { Private(KisPaintDeviceSP _dev, const QVector &_origCage, KoUpdater *_progress, int _pixelPrecision) : dev(_dev), origCage(_origCage), progress(_progress), pixelPrecision(_pixelPrecision) { } KisPaintDeviceSP dev; QImage srcImage; QPointF srcImageOffset; QVector origCage; QVector transfCage; KoUpdater *progress; int pixelPrecision; QVector allToValidPointsMap; QVector validPoints; /** * Contains all points fo the grid including non-defined * points (the ones which are placed outside the cage). */ QVector allSrcPoints; KisGreenCoordinatesMath cage; QSize gridSize; bool isGridEmpty() const { return allSrcPoints.isEmpty(); } QVector calculateTransformedPoints(); inline QVector calculateMappedIndexes(int col, int row, int *numExistingPoints); int tryGetValidIndex(const QPoint &cellPt); struct MapIndexesOp; }; KisCageTransformWorker::KisCageTransformWorker(KisPaintDeviceSP dev, const QVector &origCage, KoUpdater *progress, int pixelPrecision) : m_d(new Private(dev, origCage, progress, pixelPrecision)) { } KisCageTransformWorker::KisCageTransformWorker(const QImage &srcImage, const QPointF &srcImageOffset, const QVector &origCage, KoUpdater *progress, int pixelPrecision) : m_d(new Private(0, origCage, progress, pixelPrecision)) { m_d->srcImage = srcImage; m_d->srcImageOffset = srcImageOffset; } KisCageTransformWorker::~KisCageTransformWorker() { } void KisCageTransformWorker::setTransformedCage(const QVector &transformedCage) { m_d->transfCage = transformedCage; } struct PointsFetcherOp { PointsFetcherOp(const QPolygonF &cagePolygon) : m_cagePolygon(cagePolygon), m_numValidPoints(0) { m_polygonDirection = KisAlgebra2D::polygonDirection(cagePolygon); } inline void processPoint(int col, int row, int prevCol, int prevRow, int colIndex, int rowIndex) { Q_UNUSED(prevCol); Q_UNUSED(prevRow); Q_UNUSED(colIndex); Q_UNUSED(rowIndex); QPointF pt(col, row); if (m_cagePolygon.containsPoint(pt, Qt::OddEvenFill)) { KisAlgebra2D::adjustIfOnPolygonBoundary(m_cagePolygon, m_polygonDirection, &pt); m_points << pt; m_pointValid << true; m_numValidPoints++; } else { m_points << pt; m_pointValid << false; } } inline void nextLine() { } QVector m_pointValid; QVector m_points; QPolygonF m_cagePolygon; int m_polygonDirection; int m_numValidPoints; }; void KisCageTransformWorker::prepareTransform() { if (m_d->origCage.size() < 3) return; const QPolygonF srcPolygon(m_d->origCage); QRect srcBounds = m_d->dev ? m_d->dev->region().boundingRect() : QRectF(m_d->srcImageOffset, m_d->srcImage.size()).toAlignedRect(); srcBounds &= srcPolygon.boundingRect().toAlignedRect(); // no need to process empty devices if (srcBounds.isEmpty()) return; m_d->gridSize = GridIterationTools::calcGridSize(srcBounds, m_d->pixelPrecision); PointsFetcherOp pointsOp(srcPolygon); GridIterationTools::processGrid(pointsOp, srcBounds, m_d->pixelPrecision); const int numPoints = pointsOp.m_points.size(); KIS_ASSERT_RECOVER_RETURN(numPoints == m_d->gridSize.width() * m_d->gridSize.height()); m_d->allSrcPoints = pointsOp.m_points; m_d->allToValidPointsMap.resize(pointsOp.m_points.size()); m_d->validPoints.resize(pointsOp.m_numValidPoints); { int validIdx = 0; for (int i = 0; i < numPoints; i++) { const QPointF &pt = pointsOp.m_points[i]; const bool pointValid = pointsOp.m_pointValid[i]; if (pointValid) { m_d->validPoints[validIdx] = pt; m_d->allToValidPointsMap[i] = validIdx; validIdx++; } else { m_d->allToValidPointsMap[i] = -1; } } KIS_ASSERT_RECOVER_NOOP(validIdx == m_d->validPoints.size()); } m_d->cage.precalculateGreenCoordinates(m_d->origCage, m_d->validPoints); } QVector KisCageTransformWorker::Private::calculateTransformedPoints() { cage.generateTransformedCageNormals(transfCage); const int numValidPoints = validPoints.size(); QVector transformedPoints(numValidPoints); for (int i = 0; i < numValidPoints; i++) { transformedPoints[i] = cage.transformedPoint(i, transfCage); if (qIsNaN(transformedPoints[i].x()) || qIsNaN(transformedPoints[i].y())) { warnKrita << "WARNING: One grid point has been removed from consideration" << validPoints[i]; transformedPoints[i] = validPoints[i]; } } return transformedPoints; } inline QVector KisCageTransformWorker::Private:: calculateMappedIndexes(int col, int row, int *numExistingPoints) { *numExistingPoints = 0; QVector cellIndexes = GridIterationTools::calculateCellIndexes(col, row, gridSize); for (int i = 0; i < 4; i++) { cellIndexes[i] = allToValidPointsMap[cellIndexes[i]]; *numExistingPoints += cellIndexes[i] >= 0; } return cellIndexes; } int KisCageTransformWorker::Private:: tryGetValidIndex(const QPoint &cellPt) { int index = -1; return cellPt.x() >= 0 && cellPt.y() >= 0 && cellPt.x() < gridSize.width() - 1 && cellPt.y() < gridSize.height() - 1 && (index = allToValidPointsMap[GridIterationTools::pointToIndex(cellPt, gridSize)]) >= 0, index; } struct KisCageTransformWorker::Private::MapIndexesOp { MapIndexesOp(KisCageTransformWorker::Private *d) : m_d(d), m_srcCagePolygon(QPolygonF(m_d->origCage)) { } inline QVector calculateMappedIndexes(int col, int row, int *numExistingPoints) const { return m_d->calculateMappedIndexes(col, row, numExistingPoints); } inline int tryGetValidIndex(const QPoint &cellPt) const { return m_d->tryGetValidIndex(cellPt); } inline QPointF getSrcPointForce(const QPoint &cellPt) const { return m_d->allSrcPoints[GridIterationTools::pointToIndex(cellPt, m_d->gridSize)]; } inline const QPolygonF srcCropPolygon() const { return m_srcCagePolygon; } KisCageTransformWorker::Private *m_d; QPolygonF m_srcCagePolygon; }; QRect KisCageTransformWorker::approxChangeRect(const QRect &rc) { - const int margin = 0.30; + const qreal margin = 0.30; QVector cageSamplePoints; const int minStep = 3; const int maxSamples = 200; const int totalPixels = rc.width() * rc.height(); const int realStep = qMax(minStep, totalPixels / maxSamples); const QPolygonF cagePolygon(m_d->origCage); for (int i = 0; i < totalPixels; i += realStep) { const int x = rc.x() + i % rc.width(); const int y = rc.y() + i / rc.width(); const QPointF pt(x, y); if (cagePolygon.containsPoint(pt, Qt::OddEvenFill)) { cageSamplePoints << pt; } } if (cageSamplePoints.isEmpty()) { return rc; } KisGreenCoordinatesMath cage; cage.precalculateGreenCoordinates(m_d->origCage, cageSamplePoints); cage.generateTransformedCageNormals(m_d->transfCage); const int numValidPoints = cageSamplePoints.size(); QVector transformedPoints(numValidPoints); int failedPoints = 0; for (int i = 0; i < numValidPoints; i++) { transformedPoints[i] = cage.transformedPoint(i, m_d->transfCage); if (qIsNaN(transformedPoints[i].x()) || qIsNaN(transformedPoints[i].y())) { transformedPoints[i] = cageSamplePoints[i]; failedPoints++; } } QRect resultRect = KritaUtils::approximateRectFromPoints(transformedPoints).toAlignedRect(); return KisAlgebra2D::blowRect(resultRect | rc, margin); } QRect KisCageTransformWorker::approxNeedRect(const QRect &rc, const QRect &fullBounds) { Q_UNUSED(rc); return fullBounds; } void KisCageTransformWorker::run() { if (m_d->isGridEmpty()) return; KIS_ASSERT_RECOVER_RETURN(m_d->origCage.size() >= 3); KIS_ASSERT_RECOVER_RETURN(m_d->origCage.size() == m_d->transfCage.size()); QVector transformedPoints = m_d->calculateTransformedPoints(); KisPaintDeviceSP srcDev = new KisPaintDevice(*m_d->dev.data()); KisPaintDeviceSP tempDevice = new KisPaintDevice(m_d->dev->colorSpace()); { KisSelectionSP selection = new KisSelection(); KisPainter painter(selection->pixelSelection()); painter.setPaintColor(KoColor(Qt::black, selection->pixelSelection()->colorSpace())); painter.setAntiAliasPolygonFill(true); painter.setFillStyle(KisPainter::FillStyleForegroundColor); painter.setStrokeStyle(KisPainter::StrokeStyleNone); painter.paintPolygon(m_d->origCage); m_d->dev->clearSelection(selection); } GridIterationTools::PaintDevicePolygonOp polygonOp(srcDev, tempDevice); Private::MapIndexesOp indexesOp(m_d.data()); GridIterationTools::iterateThroughGrid (polygonOp, indexesOp, m_d->gridSize, m_d->validPoints, transformedPoints); QRect rect = tempDevice->extent(); KisPainter gc(m_d->dev); gc.bitBlt(rect.topLeft(), tempDevice, rect); } QImage KisCageTransformWorker::runOnQImage(QPointF *newOffset) { if (m_d->isGridEmpty()) return QImage(); KIS_ASSERT_RECOVER(m_d->origCage.size() >= 3 && m_d->origCage.size() == m_d->transfCage.size()) { return QImage(); } KIS_ASSERT_RECOVER(!m_d->srcImage.isNull()) { return QImage(); } KIS_ASSERT_RECOVER(m_d->srcImage.format() == QImage::Format_ARGB32) { return QImage(); } QVector transformedPoints = m_d->calculateTransformedPoints(); QRectF dstBounds; Q_FOREACH (const QPointF &pt, transformedPoints) { KisAlgebra2D::accumulateBounds(pt, &dstBounds); } const QRectF srcBounds(m_d->srcImageOffset, m_d->srcImage.size()); dstBounds |= srcBounds; QPointF dstQImageOffset = dstBounds.topLeft(); *newOffset = dstQImageOffset; QRect dstBoundsI = dstBounds.toAlignedRect(); QImage dstImage(dstBoundsI.size(), m_d->srcImage.format()); dstImage.fill(0); QImage tempImage(dstImage); { // we shouldn't create too many painters QPainter gc(&dstImage); gc.drawImage(-dstQImageOffset + m_d->srcImageOffset, m_d->srcImage); gc.setBrush(Qt::black); gc.setPen(Qt::black); gc.setCompositionMode(QPainter::CompositionMode_Clear); gc.drawPolygon(QPolygonF(m_d->origCage).translated(-dstQImageOffset)); gc.end(); } GridIterationTools::QImagePolygonOp polygonOp(m_d->srcImage, tempImage, m_d->srcImageOffset, dstQImageOffset); Private::MapIndexesOp indexesOp(m_d.data()); GridIterationTools::iterateThroughGrid (polygonOp, indexesOp, m_d->gridSize, m_d->validPoints, transformedPoints); { QPainter gc(&dstImage); gc.drawImage(QPoint(), tempImage); } return dstImage; } diff --git a/libs/image/kis_circle_mask_generator.cpp b/libs/image/kis_circle_mask_generator.cpp index e2454f6930..23fb790f09 100644 --- a/libs/image/kis_circle_mask_generator.cpp +++ b/libs/image/kis_circle_mask_generator.cpp @@ -1,126 +1,141 @@ /* * Copyright (c) 2004,2007-2009 Cyrille Berger * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2012 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #ifdef HAVE_VC #if defined(__clang__) #pragma GCC diagnostic ignored "-Wundef" #pragma GCC diagnostic ignored "-Wlocal-type-template-args" #endif #if defined _MSC_VER // Lets shut up the "possible loss of data" and "forcing value to bool 'true' or 'false' #pragma warning ( push ) #pragma warning ( disable : 4244 ) #pragma warning ( disable : 4800 ) #endif #include #include #if defined _MSC_VER #pragma warning ( pop ) #endif #endif #include #include "kis_fast_math.h" #include "kis_circle_mask_generator.h" #include "kis_circle_mask_generator_p.h" #include "kis_base_mask_generator.h" #include "kis_brush_mask_applicator_factories.h" #include "kis_brush_mask_applicator_base.h" KisCircleMaskGenerator::KisCircleMaskGenerator(qreal diameter, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges) - : KisMaskGenerator(diameter, ratio, fh, fv, spikes, antialiasEdges, CIRCLE, DefaultId), d(new Private) + : KisMaskGenerator(diameter, ratio, fh, fv, spikes, antialiasEdges, CIRCLE, DefaultId), + d(new Private) { setScale(1.0, 1.0); // store the variable locally to allow vector implementation read it easily d->copyOfAntialiasEdges = antialiasEdges; - d->applicator = createOptimizedClass >(this); + d->applicator.reset(createOptimizedClass >(this)); +} + +KisCircleMaskGenerator::KisCircleMaskGenerator(const KisCircleMaskGenerator &rhs) + : KisMaskGenerator(rhs), + d(new Private(*rhs.d)) +{ + d->applicator.reset(createOptimizedClass >(this)); +} + +KisMaskGenerator* KisCircleMaskGenerator::clone() const +{ + return new KisCircleMaskGenerator(*this); } void KisCircleMaskGenerator::setScale(qreal scaleX, qreal scaleY) { KisMaskGenerator::setScale(scaleX, scaleY); d->xcoef = 2.0 / effectiveSrcWidth(); d->ycoef = 2.0 / effectiveSrcHeight(); d->xfadecoef = (horizontalFade() == 0) ? 1 : (2.0 / (horizontalFade() * effectiveSrcWidth())); d->yfadecoef = (verticalFade() == 0) ? 1 : (2.0 / (verticalFade() * effectiveSrcHeight())); d->transformedFadeX = KisMaskGenerator::softness() * d->xfadecoef; d->transformedFadeY = KisMaskGenerator::softness() * d->yfadecoef; + + d->noFading = !d->copyOfAntialiasEdges && + qFuzzyCompare(d->xcoef, d->transformedFadeX) && + qFuzzyCompare(d->ycoef, d->transformedFadeY); } KisCircleMaskGenerator::~KisCircleMaskGenerator() { - delete d->applicator; - delete d; } bool KisCircleMaskGenerator::shouldSupersample() const { return effectiveSrcWidth() < 10 || effectiveSrcHeight() < 10; } bool KisCircleMaskGenerator::shouldVectorize() const { return !shouldSupersample() && spikes() == 2; } KisBrushMaskApplicatorBase* KisCircleMaskGenerator::applicator() { - return d->applicator; + return d->applicator.data(); } quint8 KisCircleMaskGenerator::valueAt(qreal x, qreal y) const { if (isEmpty()) return 255; qreal xr = (x /*- m_xcenter*/); qreal yr = qAbs(y /*- m_ycenter*/); fixRotation(xr, yr); qreal n = norme(xr * d->xcoef, yr * d->ycoef); if (n > 1.0) return 255; // we add +1.0 to ensure correct antialising on the border if (antialiasEdges()) { xr = qAbs(xr) + 1.0; yr = qAbs(yr) + 1.0; } qreal nf = norme(xr * d->transformedFadeX, yr * d->transformedFadeY); if (nf < 1.0) return 0; return 255 * n * (nf - 1.0) / (nf - n); } void KisCircleMaskGenerator::setSoftness(qreal softness) { KisMaskGenerator::setSoftness(softness); qreal safeSoftnessCoeff = qreal(1.0) / qMax(qreal(0.01), softness); d->transformedFadeX = d->xfadecoef * safeSoftnessCoeff; d->transformedFadeY = d->yfadecoef * safeSoftnessCoeff; } diff --git a/libs/image/kis_circle_mask_generator.h b/libs/image/kis_circle_mask_generator.h index 1141801d64..9fbbe1fba4 100644 --- a/libs/image/kis_circle_mask_generator.h +++ b/libs/image/kis_circle_mask_generator.h @@ -1,60 +1,63 @@ /* * Copyright (c) 2008-2009 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_CIRCLE_MASK_GENERATOR_H_ #define _KIS_CIRCLE_MASK_GENERATOR_H_ #include "kritaimage_export.h" #include "kis_mask_generator.h" +#include /** * Create, serialize and deserialize an elliptical 8-bit mask. */ class KRITAIMAGE_EXPORT KisCircleMaskGenerator : public KisMaskGenerator { public: struct FastRowProcessor; public: KisCircleMaskGenerator(qreal radius, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges); + KisCircleMaskGenerator(const KisCircleMaskGenerator &rhs); virtual ~KisCircleMaskGenerator(); + KisMaskGenerator* clone() const; virtual quint8 valueAt(qreal x, qreal y) const; virtual bool shouldSupersample() const; virtual bool shouldVectorize() const; KisBrushMaskApplicatorBase* applicator(); virtual void setSoftness(qreal softness); virtual void setScale(qreal scaleX, qreal scaleY); private: qreal norme(qreal a, qreal b) const { return a*a + b * b; } private: struct Private; - Private* const d; + const QScopedPointer d; }; #endif diff --git a/libs/image/kis_circle_mask_generator_p.h b/libs/image/kis_circle_mask_generator_p.h index d738583b2d..92fdc4557b 100644 --- a/libs/image/kis_circle_mask_generator_p.h +++ b/libs/image/kis_circle_mask_generator_p.h @@ -1,31 +1,54 @@ /* * Copyright (c) 2008-2009 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_CIRCLE_MASK_GENERATOR_P_H_ #define _KIS_CIRCLE_MASK_GENERATOR_P_H_ struct Q_DECL_HIDDEN KisCircleMaskGenerator::Private { + Private() + : xcoef(0), + ycoef(0), + xfadecoef(0), + yfadecoef(0), + transformedFadeX(0), + transformedFadeY(0), + copyOfAntialiasEdges(false) + { + } + + Private(const Private &rhs) + : xcoef(rhs.xcoef), + ycoef(rhs.ycoef), + xfadecoef(rhs.xfadecoef), + yfadecoef(rhs.yfadecoef), + transformedFadeX(rhs.transformedFadeX), + transformedFadeY(rhs.transformedFadeY), + copyOfAntialiasEdges(rhs.copyOfAntialiasEdges) + { + } + double xcoef, ycoef; double xfadecoef, yfadecoef; double transformedFadeX, transformedFadeY; bool copyOfAntialiasEdges; + bool noFading; - KisBrushMaskApplicatorBase *applicator; + QScopedPointer applicator; }; #endif /* _KIS_CIRCLE_MASK_GENERATOR_P_H_ */ diff --git a/libs/image/kis_convolution_worker_fft.h b/libs/image/kis_convolution_worker_fft.h index e6ff5d07de..32bd6aca9c 100644 --- a/libs/image/kis_convolution_worker_fft.h +++ b/libs/image/kis_convolution_worker_fft.h @@ -1,522 +1,541 @@ /* * Copyright (c) 2010 Edward Apap * Copyright (c) 2011 José Luis Vergara Toloza * * This program is free software; you can redistribute 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_CONVOLUTION_WORKER_FFT_H #define KIS_CONVOLUTION_WORKER_FFT_H #include #include #include "kis_convolution_worker.h" #include "kis_math_toolbox.h" #include #include #include #include #include #include template class KisConvolutionWorkerFFT; class KisConvolutionWorkerFFTLock { private: static QMutex fftwMutex; template friend class KisConvolutionWorkerFFT; }; QMutex KisConvolutionWorkerFFTLock::fftwMutex; template class KisConvolutionWorkerFFT : public KisConvolutionWorker<_IteratorFactory_> { public: KisConvolutionWorkerFFT(KisPainter *painter, KoUpdater *progress) : KisConvolutionWorker<_IteratorFactory_>(painter, progress), m_currentProgress(0), m_kernelFFT(0) { } ~KisConvolutionWorkerFFT() { } virtual void execute(const KisConvolutionKernelSP kernel, const KisPaintDeviceSP src, QPoint srcPos, QPoint dstPos, QSize areaSize, const QRect& dataRect) { // Make the area we cover as small as possible if (this->m_painter->selection()) { QRect r = this->m_painter->selection()->selectedRect().intersect(QRect(srcPos, areaSize)); dstPos += r.topLeft() - srcPos; srcPos = r.topLeft(); areaSize = r.size(); } if (areaSize.width() == 0 || areaSize.height() == 0) return; addToProgress(0); if (isInterrupted()) return; const quint32 halfKernelWidth = (kernel->width() - 1) / 2; const quint32 halfKernelHeight = (kernel->height() - 1) / 2; m_fftWidth = areaSize.width() + 4 * halfKernelWidth; m_fftHeight = areaSize.height() + 2 * halfKernelHeight; /** * FIXME: check whether this "optimization" is needed to * be uncommented. My tests showed about 30% better performance * when the line is commented out (DK). */ //optimumDimensions(m_fftWidth, m_fftHeight); m_fftLength = m_fftHeight * (m_fftWidth / 2 + 1); m_extraMem = (m_fftWidth % 2) ? 1 : 2; // create and fill kernel m_kernelFFT = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * m_fftLength); memset(m_kernelFFT, 0, sizeof(fftw_complex) * m_fftLength); fftFillKernelMatrix(kernel, m_kernelFFT); // find out which channels need convolving QList convChannelList = this->convolvableChannelList(src); m_channelFFT.resize(convChannelList.count()); for (auto i = m_channelFFT.begin(); i != m_channelFFT.end(); ++i) { *i = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * m_fftLength); } const double kernelFactor = kernel->factor() ? kernel->factor() : 1; const double fftScale = 1.0 / (m_fftHeight * m_fftWidth) / kernelFactor; FFTInfo info (fftScale, convChannelList, kernel, this->m_painter->device()->colorSpace()); int cacheRowStride = m_fftWidth + m_extraMem; fillCacheFromDevice(src, QRect(srcPos.x() - halfKernelWidth, srcPos.y() - halfKernelHeight, m_fftWidth, m_fftHeight), cacheRowStride, info, dataRect); addToProgress(10); if (isInterrupted()) return; // calculate number off fft operations required for progress reporting const float progressPerFFT = (100 - 30) / (double)(convChannelList.count() * 2 + 1); // perform FFT fftw_plan fftwPlanForward, fftwPlanBackward; KisConvolutionWorkerFFTLock::fftwMutex.lock(); fftwPlanForward = fftw_plan_dft_r2c_2d(m_fftHeight, m_fftWidth, (double*)m_kernelFFT, m_kernelFFT, FFTW_ESTIMATE); fftwPlanBackward = fftw_plan_dft_c2r_2d(m_fftHeight, m_fftWidth, m_kernelFFT, (double*)m_kernelFFT, FFTW_ESTIMATE); KisConvolutionWorkerFFTLock::fftwMutex.unlock(); fftw_execute(fftwPlanForward); addToProgress(progressPerFFT); if (isInterrupted()) return; for (auto k = m_channelFFT.begin(); k != m_channelFFT.end(); ++k) { fftw_execute_dft_r2c(fftwPlanForward, (double*)(*k), *k); addToProgress(progressPerFFT); if (isInterrupted()) return; fftMultiply(*k, m_kernelFFT); fftw_execute_dft_c2r(fftwPlanBackward, *k, (double*)*k); addToProgress(progressPerFFT); if (isInterrupted()) return; } KisConvolutionWorkerFFTLock::fftwMutex.lock(); fftw_destroy_plan(fftwPlanForward); fftw_destroy_plan(fftwPlanBackward); KisConvolutionWorkerFFTLock::fftwMutex.unlock(); writeResultToDevice(QRect(dstPos.x(), dstPos.y(), areaSize.width(), areaSize.height()), cacheRowStride, halfKernelWidth, halfKernelHeight, info, dataRect); addToProgress(20); cleanUp(); } struct FFTInfo { FFTInfo(qreal _fftScale, - QList _convChannelList, + const QList &_convChannelList, const KisConvolutionKernelSP kernel, const KoColorSpace */*colorSpace*/) : fftScale(_fftScale), convChannelList(_convChannelList), alphaCachePos(-1), alphaRealPos(-1) { KisMathToolbox mathToolbox; for (int i = 0; i < convChannelList.count(); ++i) { minClamp.append(mathToolbox.minChannelValue(convChannelList[i])); maxClamp.append(mathToolbox.maxChannelValue(convChannelList[i])); absoluteOffset.append((maxClamp[i] - minClamp[i]) * kernel->offset()); if (convChannelList[i]->channelType() == KoChannelInfo::ALPHA) { alphaCachePos = i; alphaRealPos = convChannelList[i]->pos(); } } toDoubleFuncPtr.resize(convChannelList.count()); fromDoubleFuncPtr.resize(convChannelList.count()); bool result = mathToolbox.getToDoubleChannelPtr(convChannelList, toDoubleFuncPtr); result &= mathToolbox.getFromDoubleChannelPtr(convChannelList, fromDoubleFuncPtr); KIS_ASSERT(result); } inline int numChannels() const { return convChannelList.size(); } QVector minClamp; QVector maxClamp; QVector absoluteOffset; qreal fftScale; QList convChannelList; QVector toDoubleFuncPtr; QVector fromDoubleFuncPtr; int alphaCachePos; int alphaRealPos; }; void fillCacheFromDevice(KisPaintDeviceSP src, const QRect &rect, const int cacheRowStride, const FFTInfo &info, const QRect &dataRect) { typename _IteratorFactory_::HLineConstIterator hitSrc = _IteratorFactory_::createHLineConstIterator(src, rect.x(), rect.y(), rect.width(), dataRect); - QVector channelPtr(info.numChannels()); + const int channelCount = info.numChannels(); + QVector channelPtr(channelCount); + const auto channelPtrBegin = channelPtr.begin(); + const auto channelPtrEnd = channelPtr.end(); - for (int k = 0; k < channelPtr.size(); ++k) { - channelPtr[k] = (double*)m_channelFFT[k]; + auto iFFt = m_channelFFT.constBegin(); + for (auto i = channelPtrBegin; i != channelPtrEnd; ++i, ++iFFt) { + *i = (double*)*iFFt; } + // prepare cache, reused in all loops + QVector cacheRowStart(channelCount); + const auto cacheRowStartBegin = cacheRowStart.begin(); + for (int y = 0; y < rect.height(); ++y) { - QVector cacheRowStart(channelPtr); + // cache current channelPtr in cacheRowStart + memcpy(cacheRowStart.data(), channelPtr.data(), channelCount * sizeof(double*)); for (int x = 0; x < rect.width(); ++x) { const quint8 *data = hitSrc->oldRawData(); // no alpha is a rare case, so just multiply by 1.0 in that case double alphaValue = info.alphaRealPos >= 0 ? info.toDoubleFuncPtr[info.alphaCachePos](data, info.alphaRealPos) : 1.0; - - for (int k = 0; k < channelPtr.size(); ++k) { + int k = 0; + for (auto i = channelPtrBegin; i != channelPtrEnd; ++i, ++k) { if (k != info.alphaCachePos) { const quint32 channelPos = info.convChannelList[k]->pos(); - *channelPtr[k] = info.toDoubleFuncPtr[k](data, channelPos) * alphaValue; + **i = info.toDoubleFuncPtr[k](data, channelPos) * alphaValue; } else { - *channelPtr[k] = alphaValue; + **i = alphaValue; } - channelPtr[k]++; + ++(*i); } hitSrc->nextPixel(); } - for (int k = 0; k < channelPtr.size(); ++k) { - channelPtr[k] = cacheRowStart[k] + cacheRowStride; + auto iRowStart = cacheRowStartBegin; + for (auto i = channelPtrBegin; i != channelPtrEnd; ++i, ++iRowStart) { + *i = *iRowStart + cacheRowStride; } hitSrc->nextRow(); } } inline void limitValue(qreal *value, qreal lowBound, qreal highBound) { if (*value > highBound) { *value = highBound; } else if (!(*value >= lowBound)) { // value < lowBound or value == NaN // IEEE compliant comparisons with NaN are always false *value = lowBound; } } template inline qreal writeOneChannelFromCache(quint8* dstPtr, const quint32 channel, - const int channelPos, const FFTInfo &info, - const QVector &channelPtr, + double* channelValuePtr, const qreal additionalMultiplier = 0.0) { qreal channelPixelValue; if (additionalMultiplierActive) { - channelPixelValue = (*(channelPtr[channel]) * info.fftScale + info.absoluteOffset[channel]) * additionalMultiplier; + channelPixelValue = (*channelValuePtr * info.fftScale + info.absoluteOffset[channel]) * additionalMultiplier; } else { - channelPixelValue = *(channelPtr[channel]) * info.fftScale + info.absoluteOffset[channel]; + channelPixelValue = *channelValuePtr * info.fftScale + info.absoluteOffset[channel]; } limitValue(&channelPixelValue, info.minClamp[channel], info.maxClamp[channel]); - info.fromDoubleFuncPtr[channel](dstPtr, channelPos, channelPixelValue); + info.fromDoubleFuncPtr[channel](dstPtr, info.convChannelList[channel]->pos(), channelPixelValue); return channelPixelValue; } void writeResultToDevice(const QRect &rect, const int cacheRowStride, const int halfKernelWidth, const int halfKernelHeight, const FFTInfo &info, const QRect &dataRect) { typename _IteratorFactory_::HLineIterator hitDst = _IteratorFactory_::createHLineIterator(this->m_painter->device(), rect.x(), rect.y(), rect.width(), dataRect); int initialOffset = cacheRowStride * halfKernelHeight + halfKernelWidth; - QVector channelPtr(info.numChannels()); + const int channelCount = info.numChannels(); + QVector channelPtr(channelCount); + const auto channelPtrBegin = channelPtr.begin(); + const auto channelPtrEnd = channelPtr.end(); - for (int k = 0; k < channelPtr.size(); ++k) { - channelPtr[k] = (double*)m_channelFFT[k] + initialOffset; + auto iFFt = m_channelFFT.constBegin(); + for (auto i = channelPtrBegin; i != channelPtrEnd; ++i, ++iFFt) { + *i = (double*)*iFFt + initialOffset; } + // prepare cache, reused in all loops + QVector cacheRowStart(channelCount); + const auto cacheRowStartBegin = cacheRowStart.begin(); + for (int y = 0; y < rect.height(); ++y) { - QVector cacheRowStart(channelPtr); + // cache current channelPtr in cacheRowStart + memcpy(cacheRowStart.data(), channelPtr.data(), channelCount * sizeof(double*)); for (int x = 0; x < rect.width(); ++x) { quint8 *dstPtr = hitDst->rawData(); if (info.alphaCachePos >= 0) { qreal alphaValue = writeOneChannelFromCache(dstPtr, info.alphaCachePos, - info.convChannelList[info.alphaCachePos]->pos(), info, - channelPtr); + channelPtr.at(info.alphaCachePos)); if (alphaValue > std::numeric_limits::epsilon()) { qreal alphaValueInv = 1.0 / alphaValue; - for (int k = 0; k < channelPtr.size(); ++k) { + int k = 0; + for (auto i = channelPtrBegin; i != channelPtrEnd; ++i, ++k) { if (k != info.alphaCachePos) { writeOneChannelFromCache(dstPtr, k, - info.convChannelList[k]->pos(), info, - channelPtr, + *i, alphaValueInv); } - ++channelPtr[k]; + ++(*i); } } else { - for (int k = 0; k < channelPtr.size(); ++k) { + int k = 0; + for (auto i = channelPtrBegin; i != channelPtrEnd; ++i, ++k) { if (k != info.alphaCachePos) { info.fromDoubleFuncPtr[k](dstPtr, info.convChannelList[k]->pos(), 0.0); } - ++channelPtr[k]; + ++(*i); } } } else { - for (int k = 0; k < channelPtr.size(); ++k) { + int k = 0; + for (auto i = channelPtrBegin; i != channelPtrEnd; ++i, ++k) { writeOneChannelFromCache(dstPtr, k, - info.convChannelList[k]->pos(), info, - channelPtr); - ++channelPtr[k]; + *i); + ++(*i); } } hitDst->nextPixel(); } - for (int k = 0; k < channelPtr.size(); ++k) { - channelPtr[k] = cacheRowStart[k] + cacheRowStride; + auto iRowStart = cacheRowStartBegin; + for (auto i = channelPtrBegin; i != channelPtrEnd; ++i, ++iRowStart) { + *i = *iRowStart + cacheRowStride; } hitDst->nextRow(); } } private: void fftFillKernelMatrix(const KisConvolutionKernelSP kernel, fftw_complex *m_kernelFFT) { // find central item QPoint offset((kernel->width() - 1) / 2, (kernel->height() - 1) / 2); qint32 xShift = m_fftWidth - offset.x(); qint32 yShift = m_fftHeight - offset.y(); quint32 absXpos, absYpos; for (quint32 y = 0; y < kernel->height(); y++) { absYpos = y + yShift; if (absYpos >= m_fftHeight) absYpos -= m_fftHeight; for (quint32 x = 0; x < kernel->width(); x++) { absXpos = x + xShift; if (absXpos >= m_fftWidth) absXpos -= m_fftWidth; ((double*)m_kernelFFT)[(m_fftWidth + m_extraMem) * absYpos + absXpos] = kernel->data()->coeff(y, x); } } } void fftMultiply(fftw_complex* channel, fftw_complex* kernel) { // perform complex multiplication fftw_complex *channelPtr = channel; fftw_complex *kernelPtr = kernel; fftw_complex tmp; for (quint32 pixelPos = 0; pixelPos < m_fftLength; ++pixelPos) { tmp[0] = ((*channelPtr)[0] * (*kernelPtr)[0]) - ((*channelPtr)[1] * (*kernelPtr)[1]); tmp[1] = ((*channelPtr)[0] * (*kernelPtr)[1]) + ((*channelPtr)[1] * (*kernelPtr)[0]); (*channelPtr)[0] = tmp[0]; (*channelPtr)[1] = tmp[1]; ++channelPtr; ++kernelPtr; } } void optimumDimensions(quint32& w, quint32& h) { // FFTW is most efficient when array size is a factor of 2, 3, 5 or 7 quint32 optW = w, optH = h; while ((optW % 2 != 0) || (optW % 3 != 0) || (optW % 5 != 0) || (optW % 7 != 0)) ++optW; while ((optH % 2 != 0) || (optH % 3 != 0) || (optH % 5 != 0) || (optH % 7 != 0)) ++optH; quint32 optAreaW = optW * h; quint32 optAreaH = optH * w; if (optAreaW < optAreaH) { w = optW; } else { h = optH; } } - void fftLogMatrix(double* channel, QString f) + void fftLogMatrix(double* channel, const QString &f) { KisConvolutionWorkerFFTLock::fftwMutex.lock(); QString filename(QDir::homePath() + "/log_" + f + ".txt"); dbgKrita << "Log File Name: " << filename; QFile file (filename); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { dbgKrita << "Failed"; KisConvolutionWorkerFFTLock::fftwMutex.unlock(); return; } QTextStream in(&file); for (quint32 y = 0; y < m_fftHeight; y++) { for (quint32 x = 0; x < m_fftWidth; x++) { QString num = QString::number(channel[y * m_fftWidth + x]); while (num.length() < 15) num += " "; in << num << " "; } in << "\n"; } KisConvolutionWorkerFFTLock::fftwMutex.unlock(); } void addToProgress(float amount) { m_currentProgress += amount; if (this->m_progress) { this->m_progress->setProgress((int)m_currentProgress); } } bool isInterrupted() { if (this->m_progress && this->m_progress->interrupted()) { cleanUp(); return true; } return false; } void cleanUp() { // free kernel fft data if (m_kernelFFT) { fftw_free(m_kernelFFT); } Q_FOREACH (fftw_complex *channel, m_channelFFT) { fftw_free(channel); } m_channelFFT.clear(); } private: quint32 m_fftWidth, m_fftHeight, m_fftLength, m_extraMem; float m_currentProgress; fftw_complex* m_kernelFFT; QVector m_channelFFT; }; #endif diff --git a/libs/image/kis_curve_circle_mask_generator.cpp b/libs/image/kis_curve_circle_mask_generator.cpp index 9f10c2fb14..4df3be0f07 100644 --- a/libs/image/kis_curve_circle_mask_generator.cpp +++ b/libs/image/kis_curve_circle_mask_generator.cpp @@ -1,160 +1,180 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include //MSVC requires that Vc come first #include #include #include #include #include #include "kis_fast_math.h" #include "kis_base_mask_generator.h" #include "kis_curve_circle_mask_generator.h" #include "kis_cubic_curve.h" #include "kis_antialiasing_fade_maker.h" - struct Q_DECL_HIDDEN KisCurveCircleMaskGenerator::Private { Private(bool enableAntialiasing) : fadeMaker(*this, enableAntialiasing) { } + Private(const Private &rhs) + : xcoef(rhs.xcoef), + ycoef(rhs.ycoef), + curveResolution(rhs.curveResolution), + curveData(rhs.curveData), + curvePoints(rhs.curvePoints), + dirty(true), + fadeMaker(rhs.fadeMaker,*this) + { + } + qreal xcoef, ycoef; qreal curveResolution; QVector curveData; QList curvePoints; bool dirty; KisAntialiasingFadeMaker1D fadeMaker; inline quint8 value(qreal dist) const; }; KisCurveCircleMaskGenerator::KisCurveCircleMaskGenerator(qreal diameter, qreal ratio, qreal fh, qreal fv, int spikes, const KisCubicCurve &curve, bool antialiasEdges) : KisMaskGenerator(diameter, ratio, fh, fv, spikes, antialiasEdges, CIRCLE, SoftId), d(new Private(antialiasEdges)) { // here we set resolution for the maximum size of the brush! d->curveResolution = qRound(qMax(width(), height()) * OVERSAMPLING); d->curveData = curve.floatTransfer(d->curveResolution + 2); d->curvePoints = curve.points(); setCurveString(curve.toString()); d->dirty = false; setScale(1.0, 1.0); } +KisCurveCircleMaskGenerator::KisCurveCircleMaskGenerator(const KisCurveCircleMaskGenerator &rhs) + : KisMaskGenerator(rhs), + d(new Private(*rhs.d)) +{ +} + +KisCurveCircleMaskGenerator::~KisCurveCircleMaskGenerator() +{ +} + +KisMaskGenerator* KisCurveCircleMaskGenerator::clone() const +{ + return new KisCurveCircleMaskGenerator(*this); +} + void KisCurveCircleMaskGenerator::setScale(qreal scaleX, qreal scaleY) { KisMaskGenerator::setScale(scaleX, scaleY); qreal width = effectiveSrcWidth(); qreal height = effectiveSrcHeight(); d->xcoef = 2.0 / width; d->ycoef = 2.0 / height; d->fadeMaker.setSquareNormCoeffs(d->xcoef, d->ycoef); } -KisCurveCircleMaskGenerator::~KisCurveCircleMaskGenerator() -{ - delete d; -} - bool KisCurveCircleMaskGenerator::shouldSupersample() const { return effectiveSrcWidth() < 10 || effectiveSrcHeight() < 10; } inline quint8 KisCurveCircleMaskGenerator::Private::value(qreal dist) const { qreal distance = dist * curveResolution; quint16 alphaValue = distance; qreal alphaValueF = distance - alphaValue; qreal alpha = ( (1.0 - alphaValueF) * curveData.at(alphaValue) + alphaValueF * curveData.at(alphaValue+1)); return (1.0 - alpha) * 255; } quint8 KisCurveCircleMaskGenerator::valueAt(qreal x, qreal y) const { if (isEmpty()) return 255; qreal xr = x; qreal yr = qAbs(y); fixRotation(xr, yr); qreal dist = norme(xr * d->xcoef, yr * d->ycoef); quint8 value; if (d->fadeMaker.needFade(dist, &value)) { return value; } return d->value(dist); } void KisCurveCircleMaskGenerator::toXML(QDomDocument& doc, QDomElement& e) const { KisMaskGenerator::toXML(doc, e); e.setAttribute("softness_curve", curveString()); } void KisCurveCircleMaskGenerator::setSoftness(qreal softness) { // performance if (!d->dirty && softness == 1.0) return; d->dirty = true; KisMaskGenerator::setSoftness(softness); KisCurveCircleMaskGenerator::transformCurveForSoftness(softness,d->curvePoints, d->curveResolution+2, d->curveData); d->dirty = false; } void KisCurveCircleMaskGenerator::transformCurveForSoftness(qreal softness,const QList &points, int curveResolution, QVector< qreal >& result) { QList newList = points; newList.detach(); int size = newList.size(); if (size == 2){ // make place for new point in the centre newList.append(newList.at(1)); newList[1] = (newList.at(0) + newList.at(2)) * 0.5; // transoform it newList[1].setY(qBound(0.0,newList.at(1).y() * softness,1.0)); }else{ // transform all points except first and last for (int i = 1; i < size-1; i++){ newList[i].setY(qBound(0.0,newList.at(i).y() * softness,1.0)); } } // compute the data KisCubicCurve curve(newList); result = curve.floatTransfer( curveResolution ); } diff --git a/libs/image/kis_curve_circle_mask_generator.h b/libs/image/kis_curve_circle_mask_generator.h index e5e5f67e43..7aae27a8d9 100644 --- a/libs/image/kis_curve_circle_mask_generator.h +++ b/libs/image/kis_curve_circle_mask_generator.h @@ -1,68 +1,71 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_CURVE_CIRCLE_MASK_GENERATOR_H_ #define _KIS_CURVE_CIRCLE_MASK_GENERATOR_H_ +#include #include "kritaimage_export.h" #include #include class KisCubicCurve; class QDomElement; class QDomDocument; class QPointF; /** * This mask generator use softness/hardness defined by user curve * It used to be soft brush paintop. */ class KRITAIMAGE_EXPORT KisCurveCircleMaskGenerator : public KisMaskGenerator { public: KisCurveCircleMaskGenerator(qreal radius, qreal ratio, qreal fh, qreal fv, int spikes,const KisCubicCurve& curve, bool antialiasEdges); + KisCurveCircleMaskGenerator(const KisCurveCircleMaskGenerator &rhs); virtual ~KisCurveCircleMaskGenerator(); + KisMaskGenerator* clone() const; virtual quint8 valueAt(qreal x, qreal y) const; void setScale(qreal scaleX, qreal scaleY); bool shouldSupersample() const; virtual void toXML(QDomDocument& , QDomElement&) const; virtual void setSoftness(qreal softness); static void transformCurveForSoftness(qreal softness,const QList &points, int curveResolution, QVector &result); private: qreal norme(qreal a, qreal b) const { return a*a + b*b; } private: struct Private; - Private* const d; + const QScopedPointer d; }; #endif diff --git a/libs/image/kis_curve_rect_mask_generator.cpp b/libs/image/kis_curve_rect_mask_generator.cpp index 6a63f7a42c..7fc9b1d14b 100644 --- a/libs/image/kis_curve_rect_mask_generator.cpp +++ b/libs/image/kis_curve_rect_mask_generator.cpp @@ -1,130 +1,150 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include //MSVC requires that Vc come first #include #include #include #include #include #include "kis_curve_rect_mask_generator.h" #include "kis_cubic_curve.h" #include "kis_antialiasing_fade_maker.h" struct Q_DECL_HIDDEN KisCurveRectangleMaskGenerator::Private { Private(bool enableAntialiasing) : fadeMaker(*this, enableAntialiasing) { } + Private(const Private &rhs) + : xcoeff(rhs.xcoeff), + ycoeff(rhs.ycoeff), + curveResolution(rhs.curveResolution), + curveData(rhs.curveData), + curvePoints(rhs.curvePoints), + dirty(rhs.dirty), + fadeMaker(rhs.fadeMaker, *this) + { + } + + qreal xcoeff, ycoeff; + qreal curveResolution; QVector curveData; QList curvePoints; - int curveResolution; bool dirty; - qreal xcoeff; - qreal ycoeff; - KisAntialiasingFadeMaker2D fadeMaker; quint8 value(qreal xr, qreal yr) const; }; KisCurveRectangleMaskGenerator::KisCurveRectangleMaskGenerator(qreal diameter, qreal ratio, qreal fh, qreal fv, int spikes, const KisCubicCurve &curve, bool antialiasEdges) : KisMaskGenerator(diameter, ratio, fh, fv, spikes, antialiasEdges, RECTANGLE, SoftId), d(new Private(antialiasEdges)) { d->curveResolution = qRound( qMax(width(),height()) * OVERSAMPLING); d->curveData = curve.floatTransfer( d->curveResolution + 1); d->curvePoints = curve.points(); setCurveString(curve.toString()); d->dirty = false; setScale(1.0, 1.0); } +KisCurveRectangleMaskGenerator::KisCurveRectangleMaskGenerator(const KisCurveRectangleMaskGenerator &rhs) + : KisMaskGenerator(rhs), + d(new Private(*rhs.d)) +{ +} + +KisMaskGenerator* KisCurveRectangleMaskGenerator::clone() const +{ + return new KisCurveRectangleMaskGenerator(*this); +} + void KisCurveRectangleMaskGenerator::setScale(qreal scaleX, qreal scaleY) { KisMaskGenerator::setScale(scaleX, scaleY); qreal halfWidth = 0.5 * effectiveSrcWidth(); qreal halfHeight = 0.5 * effectiveSrcHeight(); d->xcoeff = 1.0 / halfWidth; d->ycoeff = 1.0 / halfHeight; d->fadeMaker.setLimits(halfWidth, halfHeight); } KisCurveRectangleMaskGenerator::~KisCurveRectangleMaskGenerator() { delete d; } quint8 KisCurveRectangleMaskGenerator::Private::value(qreal xr, qreal yr) const { xr = qAbs(xr) * xcoeff; yr = qAbs(yr) * ycoeff; int sIndex = qRound(xr * (curveResolution)); int tIndex = qRound(yr * (curveResolution)); int sIndexInverted = curveResolution - sIndex; int tIndexInverted = curveResolution - tIndex; qreal blend = (curveData.at(sIndex) * (1.0 - curveData.at(sIndexInverted)) * curveData.at(tIndex) * (1.0 - curveData.at(tIndexInverted))); return (1.0 - blend) * 255; } quint8 KisCurveRectangleMaskGenerator::valueAt(qreal x, qreal y) const { if (isEmpty()) return 255; qreal xr = x; qreal yr = qAbs(y); fixRotation(xr, yr); quint8 value; if (d->fadeMaker.needFade(xr, yr, &value)) { return value; } return d->value(xr, yr); } void KisCurveRectangleMaskGenerator::toXML(QDomDocument& doc, QDomElement& e) const { KisMaskGenerator::toXML(doc, e); e.setAttribute("softness_curve", curveString()); } void KisCurveRectangleMaskGenerator::setSoftness(qreal softness) { // performance if (!d->dirty && softness == 1.0) return; d->dirty = true; KisMaskGenerator::setSoftness(softness); KisCurveCircleMaskGenerator::transformCurveForSoftness(softness,d->curvePoints, d->curveResolution + 1, d->curveData); d->dirty = false; } diff --git a/libs/image/kis_curve_rect_mask_generator.h b/libs/image/kis_curve_rect_mask_generator.h index 9d9c9e2a91..0b6c3edbc1 100644 --- a/libs/image/kis_curve_rect_mask_generator.h +++ b/libs/image/kis_curve_rect_mask_generator.h @@ -1,54 +1,56 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_CURVE_RECT_MASK_GENERATOR_H_ #define _KIS_CURVE_RECT_MASK_GENERATOR_H_ #include "kritaimage_export.h" class KisCubicCurve; class QDomElement; class QDomDocument; #include "kis_mask_generator.h" /** * Curve based softness for this rectangular mask generator */ class KRITAIMAGE_EXPORT KisCurveRectangleMaskGenerator : public KisMaskGenerator { public: KisCurveRectangleMaskGenerator(qreal radius, qreal ratio, qreal fh, qreal fv, int spikes, const KisCubicCurve& curve, bool antialiasEdges); + KisCurveRectangleMaskGenerator(const KisCurveRectangleMaskGenerator &rhs); virtual ~KisCurveRectangleMaskGenerator(); + KisMaskGenerator* clone() const; virtual quint8 valueAt(qreal x, qreal y) const; void setScale(qreal scaleX, qreal scaleY); virtual void toXML(QDomDocument& , QDomElement&) const; virtual void setSoftness(qreal softness); private: struct Private; Private* const d; }; #endif diff --git a/libs/image/kis_gauss_circle_mask_generator.cpp b/libs/image/kis_gauss_circle_mask_generator.cpp index c80827f091..3af92fc085 100644 --- a/libs/image/kis_gauss_circle_mask_generator.cpp +++ b/libs/image/kis_gauss_circle_mask_generator.cpp @@ -1,105 +1,126 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2011 Geoffry Song * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include //MSVC requires that Vc come first #include #include #include #include #include #include "kis_fast_math.h" #include "kis_base_mask_generator.h" #include "kis_gauss_circle_mask_generator.h" #include "kis_antialiasing_fade_maker.h" #define M_SQRT_2 1.41421356237309504880 #ifdef Q_OS_WIN // on windows we get our erf() from boost #include #define erf(x) boost::math::erf(x) #endif struct Q_DECL_HIDDEN KisGaussCircleMaskGenerator::Private { Private(bool enableAntialiasing) : fadeMaker(*this, enableAntialiasing) { } + Private(const Private &rhs) + : ycoef(rhs.ycoef), + fade(rhs.fade), + center(rhs.center), + distfactor(rhs.distfactor), + alphafactor(rhs.alphafactor), + fadeMaker(rhs.fadeMaker, *this) + { + } + qreal ycoef; qreal fade; qreal center, distfactor, alphafactor; KisAntialiasingFadeMaker1D fadeMaker; inline quint8 value(qreal dist) const; }; KisGaussCircleMaskGenerator::KisGaussCircleMaskGenerator(qreal diameter, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges) - : KisMaskGenerator(diameter, ratio, fh, fv, spikes, antialiasEdges, CIRCLE, GaussId), d(new Private(antialiasEdges)) + : KisMaskGenerator(diameter, ratio, fh, fv, spikes, antialiasEdges, CIRCLE, GaussId), + d(new Private(antialiasEdges)) { d->ycoef = 1.0 / ratio; d->fade = 1.0 - (fh + fv) / 2.0; if (d->fade == 0.0) d->fade = 1e-6; else if (d->fade == 1.0) d->fade = 1.0 - 1e-6; // would become undefined for fade == 0 or 1 d->center = (2.5 * (6761.0*d->fade-10000.0))/(M_SQRT_2*6761.0*d->fade); d->alphafactor = 255.0 / (2.0 * erf(d->center)); } +KisGaussCircleMaskGenerator::KisGaussCircleMaskGenerator(const KisGaussCircleMaskGenerator &rhs) + : KisMaskGenerator(rhs), + d(new Private(*rhs.d)) +{ +} + +KisMaskGenerator* KisGaussCircleMaskGenerator::clone() const +{ + return new KisGaussCircleMaskGenerator(*this); +} + void KisGaussCircleMaskGenerator::setScale(qreal scaleX, qreal scaleY) { KisMaskGenerator::setScale(scaleX, scaleY); d->distfactor = M_SQRT_2 * 12500.0 / (6761.0 * d->fade * effectiveSrcWidth() / 2.0); d->fadeMaker.setRadius(0.5 * effectiveSrcWidth()); } KisGaussCircleMaskGenerator::~KisGaussCircleMaskGenerator() { - delete d; } inline quint8 KisGaussCircleMaskGenerator::Private::value(qreal dist) const { dist *= distfactor; quint8 ret = alphafactor * (erf(dist + center) - erf(dist - center)); return (quint8) 255 - ret; } quint8 KisGaussCircleMaskGenerator::valueAt(qreal x, qreal y) const { if (isEmpty()) return 255; qreal xr = x; qreal yr = qAbs(y); fixRotation(xr, yr); qreal dist = sqrt(norme(xr, yr * d->ycoef)); quint8 value; if (d->fadeMaker.needFade(dist, &value)) { return value; } return d->value(dist); } diff --git a/libs/image/kis_gauss_circle_mask_generator.h b/libs/image/kis_gauss_circle_mask_generator.h index a3e6f2b4ad..4ca5176d4e 100644 --- a/libs/image/kis_gauss_circle_mask_generator.h +++ b/libs/image/kis_gauss_circle_mask_generator.h @@ -1,53 +1,55 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2011 Geoffry Song * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_GAUSS_MASK_GENERATOR_H_ #define _KIS_GAUSS_MASK_GENERATOR_H_ +#include #include "kritaimage_export.h" - /** * This mask generator uses a Gaussian-blurred circle */ class KRITAIMAGE_EXPORT KisGaussCircleMaskGenerator : public KisMaskGenerator { public: KisGaussCircleMaskGenerator(qreal diameter, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges); + KisGaussCircleMaskGenerator(const KisGaussCircleMaskGenerator &rhs); virtual ~KisGaussCircleMaskGenerator(); + KisMaskGenerator* clone() const; virtual quint8 valueAt(qreal x, qreal y) const; void setScale(qreal scaleX, qreal scaleY); private: qreal norme(qreal a, qreal b) const { return a*a + b*b; } private: struct Private; - Private* const d; + const QScopedPointer d; }; #endif diff --git a/libs/image/kis_gauss_rect_mask_generator.cpp b/libs/image/kis_gauss_rect_mask_generator.cpp index 19a8ea3355..1f87715dda 100644 --- a/libs/image/kis_gauss_rect_mask_generator.cpp +++ b/libs/image/kis_gauss_rect_mask_generator.cpp @@ -1,108 +1,128 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2011 Geoffry Song * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include //MSVC requires that Vc come first #include #include #include #include #include #include #include "kis_fast_math.h" #include "kis_base_mask_generator.h" #include "kis_gauss_rect_mask_generator.h" #include "kis_antialiasing_fade_maker.h" #define M_SQRT_2 1.41421356237309504880 #ifdef Q_OS_WIN // on windows we get our erf() from boost #include #define erf(x) boost::math::erf(x) #endif struct Q_DECL_HIDDEN KisGaussRectangleMaskGenerator::Private { Private(bool enableAntialiasing) : fadeMaker(*this, enableAntialiasing) { } + Private(const Private &rhs) + : xfade(rhs.xfade), + yfade(rhs.yfade), + halfWidth(rhs.halfWidth), + halfHeight(rhs.halfHeight), + alphafactor(rhs.alphafactor), + fadeMaker(rhs.fadeMaker, *this) + { + } + qreal xfade, yfade; qreal halfWidth, halfHeight; qreal alphafactor; KisAntialiasingFadeMaker2D fadeMaker; inline quint8 value(qreal x, qreal y) const; }; KisGaussRectangleMaskGenerator::KisGaussRectangleMaskGenerator(qreal diameter, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges) : KisMaskGenerator(diameter, ratio, fh, fv, spikes, antialiasEdges, RECTANGLE, GaussId), d(new Private(antialiasEdges)) { setScale(1.0, 1.0); } +KisGaussRectangleMaskGenerator::KisGaussRectangleMaskGenerator(const KisGaussRectangleMaskGenerator &rhs) + : KisMaskGenerator(rhs), + d(new Private(*rhs.d)) +{ +} + +KisMaskGenerator* KisGaussRectangleMaskGenerator::clone() const +{ + return new KisGaussRectangleMaskGenerator(*this); +} + void KisGaussRectangleMaskGenerator::setScale(qreal scaleX, qreal scaleY) { KisMaskGenerator::setScale(scaleX, scaleY); qreal width = effectiveSrcWidth(); qreal height = effectiveSrcHeight(); qreal xfade = (1.0 - horizontalFade()/2.0) * width * 0.1; qreal yfade = (1.0 - verticalFade()/2.0) * height * 0.1; d->xfade = 1.0 / (M_SQRT_2 * xfade); d->yfade = 1.0 / (M_SQRT_2 * yfade); d->halfWidth = width * 0.5 - 2.5 * xfade; d->halfHeight = height * 0.5 - 2.5 * yfade; d->alphafactor = 255.0 / (4.0 * erf(d->halfWidth * d->xfade) * erf(d->halfHeight * d->yfade)); d->fadeMaker.setLimits(0.5 * width, 0.5 * height); } KisGaussRectangleMaskGenerator::~KisGaussRectangleMaskGenerator() { - delete d; } inline quint8 KisGaussRectangleMaskGenerator::Private::value(qreal xr, qreal yr) const { return (quint8) 255 - (quint8) (alphafactor * (erf((halfWidth + xr) * xfade) + erf((halfWidth - xr) * xfade)) * (erf((halfHeight + yr) * yfade) + erf((halfHeight - yr) * yfade))); } quint8 KisGaussRectangleMaskGenerator::valueAt(qreal x, qreal y) const { if (isEmpty()) return 255; qreal xr = x; qreal yr = qAbs(y); fixRotation(xr, yr); quint8 value; if (d->fadeMaker.needFade(xr, yr, &value)) { return value; } return d->value(xr, yr); } diff --git a/libs/image/kis_gauss_rect_mask_generator.h b/libs/image/kis_gauss_rect_mask_generator.h index 1c49d39161..e0bf7220ab 100644 --- a/libs/image/kis_gauss_rect_mask_generator.h +++ b/libs/image/kis_gauss_rect_mask_generator.h @@ -1,52 +1,48 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2011 Geoffry Song * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_GAUSS_RECT_MASK_GENERATOR_H_ #define _KIS_GAUSS_RECT_MASK_GENERATOR_H_ #include "kritaimage_export.h" /** * This mask generator uses a Gaussian-blurred rectangle */ class KRITAIMAGE_EXPORT KisGaussRectangleMaskGenerator : public KisMaskGenerator { public: KisGaussRectangleMaskGenerator(qreal diameter, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges); + KisGaussRectangleMaskGenerator(const KisGaussRectangleMaskGenerator &rhs); virtual ~KisGaussRectangleMaskGenerator(); + KisMaskGenerator* clone() const; virtual quint8 valueAt(qreal x, qreal y) const; void setScale(qreal scaleX, qreal scaleY); -private: - - qreal norme(qreal a, qreal b) const { - return a*a + b*b; - } - private: struct Private; - Private* const d; + const QScopedPointer d; }; #endif diff --git a/libs/image/kis_image_animation_interface.cpp b/libs/image/kis_image_animation_interface.cpp index fac2800240..4646f44b9c 100644 --- a/libs/image/kis_image_animation_interface.cpp +++ b/libs/image/kis_image_animation_interface.cpp @@ -1,282 +1,292 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_image_animation_interface.h" #include "kis_global.h" #include "kis_image.h" #include "kis_regenerate_frame_stroke_strategy.h" #include "kis_keyframe_channel.h" #include "kis_time_range.h" #include "kis_post_execution_undo_adapter.h" #include "commands_new/kis_switch_current_time_command.h" #include "kis_layer_utils.h" struct KisImageAnimationInterface::Private { Private() : image(0), currentTime(0), currentUITime(0), externalFrameActive(false), frameInvalidationBlocked(false), cachedLastFrameValue(-1) { } KisImage *image; int currentTime; int currentUITime; bool externalFrameActive; bool frameInvalidationBlocked; KisTimeRange fullClipRange; KisTimeRange playbackRange; int framerate; int cachedLastFrameValue; }; KisImageAnimationInterface::KisImageAnimationInterface(KisImage *image) : m_d(new Private) { m_d->image = image; m_d->framerate = 24; m_d->fullClipRange = KisTimeRange::fromTime(0, 100); connect(this, SIGNAL(sigInternalRequestTimeSwitch(int)), SLOT(switchCurrentTimeAsync(int))); } KisImageAnimationInterface::~KisImageAnimationInterface() { } bool KisImageAnimationInterface::hasAnimation() const { bool hasAnimation = false; KisLayerUtils::recursiveApplyNodes( m_d->image->root(), [&hasAnimation](KisNodeSP node) { hasAnimation |= node->isAnimated(); }); return hasAnimation; } int KisImageAnimationInterface::currentTime() const { return m_d->currentTime; } int KisImageAnimationInterface::currentUITime() const { return m_d->currentUITime; } const KisTimeRange& KisImageAnimationInterface::fullClipRange() const { return m_d->fullClipRange; } void KisImageAnimationInterface::setFullClipRange(const KisTimeRange range) { m_d->fullClipRange = range; emit sigFullClipRangeChanged(); } const KisTimeRange& KisImageAnimationInterface::playbackRange() const { return m_d->playbackRange.isValid() ? m_d->playbackRange : m_d->fullClipRange; } void KisImageAnimationInterface::setPlaybackRange(const KisTimeRange range) { m_d->playbackRange = range; emit sigPlaybackRangeChanged(); } int KisImageAnimationInterface::framerate() const { return m_d->framerate; } void KisImageAnimationInterface::setFramerate(int fps) { m_d->framerate = fps; emit sigFramerateChanged(); } KisImageWSP KisImageAnimationInterface::image() const { return m_d->image; } bool KisImageAnimationInterface::externalFrameActive() const { return m_d->externalFrameActive; } void KisImageAnimationInterface::requestTimeSwitchWithUndo(int time) { if (m_d->currentUITime == time) return; KisSwitchCurrentTimeCommand *cmd = new KisSwitchCurrentTimeCommand(m_d->image, time); cmd->redo(); m_d->image->postExecutionUndoAdapter()->addCommand(toQShared(cmd)); } +void KisImageAnimationInterface::setDefaultProjectionColor(const KoColor &color) +{ + int savedTime = 0; + saveAndResetCurrentTime(currentTime(), &savedTime); + + m_d->image->setDefaultProjectionColor(color); + + restoreCurrentTime(&savedTime); +} + void KisImageAnimationInterface::requestTimeSwitchNonGUI(int time) { emit sigInternalRequestTimeSwitch(time); } void KisImageAnimationInterface::switchCurrentTimeAsync(int frameId) { if (m_d->currentUITime == frameId) return; m_d->image->barrierLock(); m_d->currentTime = frameId; m_d->currentUITime = frameId; m_d->image->unlock(); KisStrokeStrategy *strategy = new KisRegenerateFrameStrokeStrategy(this); KisStrokeId stroke = m_d->image->startStroke(strategy); m_d->image->endStroke(stroke); emit sigTimeChanged(frameId); } void KisImageAnimationInterface::requestFrameRegeneration(int frameId, const QRegion &dirtyRegion) { KisStrokeStrategy *strategy = new KisRegenerateFrameStrokeStrategy(frameId, dirtyRegion, this); QList jobs = KisRegenerateFrameStrokeStrategy::createJobsData(m_d->image); KisStrokeId stroke = m_d->image->startStroke(strategy); Q_FOREACH (KisStrokeJobData* job, jobs) { m_d->image->addJob(stroke, job); } m_d->image->endStroke(stroke); } void KisImageAnimationInterface::saveAndResetCurrentTime(int frameId, int *savedValue) { m_d->externalFrameActive = true; *savedValue = m_d->currentTime; m_d->currentTime = frameId; } void KisImageAnimationInterface::restoreCurrentTime(int *savedValue) { m_d->currentTime = *savedValue; m_d->externalFrameActive = false; } void KisImageAnimationInterface::notifyFrameReady() { emit sigFrameReady(m_d->currentTime); } void KisImageAnimationInterface::notifyFrameCancelled() { emit sigFrameCancelled(); } KisUpdatesFacade* KisImageAnimationInterface::updatesFacade() const { return m_d->image; } void KisImageAnimationInterface::notifyNodeChanged(const KisNode *node, const QRect &rect, bool recursive) { if (externalFrameActive() || m_d->frameInvalidationBlocked) return; KisKeyframeChannel *channel = node->getKeyframeChannel(KisKeyframeChannel::Content.id()); if (recursive) { KisTimeRange affectedRange; KisTimeRange::calculateTimeRangeRecursive(node, currentTime(), affectedRange, false); invalidateFrames(affectedRange, rect); } else if (channel) { const int currentTime = m_d->currentTime; invalidateFrames(channel->affectedFrames(currentTime), rect); } else { invalidateFrames(KisTimeRange::infinite(0), rect); } } void KisImageAnimationInterface::invalidateFrames(const KisTimeRange &range, const QRect &rect) { m_d->cachedLastFrameValue = -1; emit sigFramesChanged(range, rect); } void KisImageAnimationInterface::blockFrameInvalidation(bool value) { m_d->frameInvalidationBlocked = value; } int findLastKeyframeTimeRecursive(KisNodeSP node) { int time = 0; KisKeyframeChannel *channel; Q_FOREACH (channel, node->keyframeChannels()) { KisKeyframeSP keyframe = channel->lastKeyframe(); if (keyframe) { time = std::max(time, keyframe->time()); } } KisNodeSP child = node->firstChild(); while (child) { time = std::max(time, findLastKeyframeTimeRecursive(child)); child = child->nextSibling(); } return time; } int KisImageAnimationInterface::totalLength() { if (m_d->cachedLastFrameValue < 0) { m_d->cachedLastFrameValue = findLastKeyframeTimeRecursive(m_d->image->root()); } int lastKey = m_d->cachedLastFrameValue; lastKey = std::max(lastKey, m_d->fullClipRange.end()); lastKey = std::max(lastKey, m_d->currentUITime); return lastKey + 1; } diff --git a/libs/image/kis_image_animation_interface.h b/libs/image/kis_image_animation_interface.h index f808adc925..b59a5e1f48 100644 --- a/libs/image/kis_image_animation_interface.h +++ b/libs/image/kis_image_animation_interface.h @@ -1,154 +1,162 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_IMAGE_ANIMATION_INTERFACE_H #define __KIS_IMAGE_ANIMATION_INTERFACE_H #include #include #include "kis_types.h" #include "kritaimage_export.h" class KisUpdatesFacade; class KisTimeRange; +class KoColor; namespace KisLayerUtils { struct SwitchFrameCommand; } class KRITAIMAGE_EXPORT KisImageAnimationInterface : public QObject { Q_OBJECT public: KisImageAnimationInterface(KisImage *image); ~KisImageAnimationInterface(); /** * Returns true of the image has at least one animated layer */ bool hasAnimation() const; /** * Returns currently active frame of the underlying image. Some strokes * can override this value and it will report a different value. */ int currentTime() const; /** * Same as currentTime, except it isn't changed when background strokes * are running. */ int currentUITime() const; /** * While any non-current frame is being regenerated by the * strategy, the image is kept in a special state, named * 'externalFrameActive'. Is this state the following applies: * * 1) All the animated paint devices switch its state into * frameId() defined by global time. * * 2) All animation-not-capable devices switch to a temporary * content device, which *is in undefined state*. The stroke * should regenerate the image projection manually. */ bool externalFrameActive() const; void requestTimeSwitchWithUndo(int time); void requestTimeSwitchNonGUI(int time); public Q_SLOTS: /** * Switches current frame (synchronously) and starts an * asynchronous regeneration of the entire image. */ void switchCurrentTimeAsync(int frameId); public: /** * Start a backgroud thread that will recalculate some extra frame. * The result will be reported using two types of signals: * * 1) KisImage::sigImageUpdated() will be emitted for every chunk * of updated area. * * 2) sigFrameReady() will be emitted in the end of the operation. * IMPORTANT: to get the result you must connect to this signal * with Qt::DirectConnection and fetch the result from * frameProjection(). After the signal handler is exited, the * data will no longer be available. */ void requestFrameRegeneration(int frameId, const QRegion &dirtyRegion); void notifyNodeChanged(const KisNode *node, const QRect &rect, bool recursive); void invalidateFrames(const KisTimeRange &range, const QRect &rect); + /** + * Changes the default color of the "external frame" projection of + * the image's root layer. Please note that this command should be + * executed from a context of an exclusive job! + */ + void setDefaultProjectionColor(const KoColor &color); + /** * The current time range selected by user. * @return current time range */ const KisTimeRange& fullClipRange() const; void setFullClipRange(const KisTimeRange range); const KisTimeRange &playbackRange() const; void setPlaybackRange(const KisTimeRange range); int framerate() const; public Q_SLOTS: void setFramerate(int fps); public: KisImageWSP image() const; int totalLength(); private: // interface for: friend class KisRegenerateFrameStrokeStrategy; friend class KisAnimationFrameCacheTest; friend struct KisLayerUtils::SwitchFrameCommand; friend class KisImageTest; void saveAndResetCurrentTime(int frameId, int *savedValue); void restoreCurrentTime(int *savedValue); void notifyFrameReady(); void notifyFrameCancelled(); KisUpdatesFacade* updatesFacade() const; void blockFrameInvalidation(bool value); Q_SIGNALS: void sigFrameReady(int time); void sigFrameCancelled(); void sigTimeChanged(int newTime); void sigFramesChanged(const KisTimeRange &range, const QRect &rect); void sigInternalRequestTimeSwitch(int frameId); void sigFramerateChanged(); void sigFullClipRangeChanged(); void sigPlaybackRangeChanged(); private: struct Private; const QScopedPointer m_d; }; #endif /* __KIS_IMAGE_ANIMATION_INTERFACE_H */ diff --git a/libs/image/kis_image_barrier_lock_adapter.h b/libs/image/kis_image_barrier_lock_adapter.h index 2f949e2267..734381a61c 100644 --- a/libs/image/kis_image_barrier_lock_adapter.h +++ b/libs/image/kis_image_barrier_lock_adapter.h @@ -1,63 +1,62 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_IMAGE_BARRIER_LOCK_ADAPTER_H #define __KIS_IMAGE_BARRIER_LOCK_ADAPTER_H template class KisImageBarrierLockAdapterImpl { public: inline KisImageBarrierLockAdapterImpl(ImagePointer image, bool readOnly = false) : m_image(image), m_readOnly(readOnly) { } inline ~KisImageBarrierLockAdapterImpl() { - m_image->unlock(); } inline void lock() { m_image->barrierLock(m_readOnly); } // Qt syntax inline bool tryLock() { return m_image->tryBarrierLock(m_readOnly); } // Boost.Thread syntax inline bool try_lock() { return tryLock(); } inline void unlock() { m_image->unlock(); } private: KisImageBarrierLockAdapterImpl(const KisImageBarrierLockAdapterImpl &rhs); ImagePointer m_image; bool m_readOnly; }; typedef KisImageBarrierLockAdapterImpl KisImageBarrierLockAdapter; typedef KisImageBarrierLockAdapterImpl KisImageBarrierLockAdapterRaw; #endif /* __KIS_IMAGE_BARRIER_LOCK_ADAPTER_H */ diff --git a/libs/image/kis_image_config.cpp b/libs/image/kis_image_config.cpp index ee53bd5f3e..dcbb882026 100644 --- a/libs/image/kis_image_config.cpp +++ b/libs/image/kis_image_config.cpp @@ -1,395 +1,396 @@ /* * Copyright (c) 2010 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_image_config.h" #include #include #include "kis_debug.h" #include #include #include -#include +#include #include "kis_global.h" #include #ifdef Q_OS_MAC #include #endif KisImageConfig::KisImageConfig(bool readOnly) : m_config( KSharedConfig::openConfig()->group("")), m_readOnly(readOnly) { } KisImageConfig::~KisImageConfig() { if (m_readOnly) return; if (qApp->thread() != QThread::currentThread()) { dbgKrita << "KisImageConfig: requested config synchronization from nonGUI thread! Called from" << kisBacktrace(); return; } m_config.sync(); } bool KisImageConfig::enableProgressReporting(bool requestDefault) const { return !requestDefault ? m_config.readEntry("enableProgressReporting", true) : true; } void KisImageConfig::setEnableProgressReporting(bool value) { m_config.writeEntry("enableProgressReporting", value); } bool KisImageConfig::enablePerfLog(bool requestDefault) const { return !requestDefault ? m_config.readEntry("enablePerfLog", false) :false; } void KisImageConfig::setEnablePerfLog(bool value) { m_config.writeEntry("enablePerfLog", value); } qreal KisImageConfig::transformMaskOffBoundsReadArea() const { return m_config.readEntry("transformMaskOffBoundsReadArea", 0.5); } int KisImageConfig::updatePatchHeight() const { return m_config.readEntry("updatePatchHeight", 512); } void KisImageConfig::setUpdatePatchHeight(int value) { m_config.writeEntry("updatePatchHeight", value); } int KisImageConfig::updatePatchWidth() const { return m_config.readEntry("updatePatchWidth", 512); } void KisImageConfig::setUpdatePatchWidth(int value) { m_config.writeEntry("updatePatchWidth", value); } qreal KisImageConfig::maxCollectAlpha() const { return m_config.readEntry("maxCollectAlpha", 2.5); } qreal KisImageConfig::maxMergeAlpha() const { return m_config.readEntry("maxMergeAlpha", 1.); } qreal KisImageConfig::maxMergeCollectAlpha() const { return m_config.readEntry("maxMergeCollectAlpha", 1.5); } qreal KisImageConfig::schedulerBalancingRatio() const { /** * updates-queue-size / strokes-queue-size */ return m_config.readEntry("schedulerBalancingRatio", 100.); } void KisImageConfig::setSchedulerBalancingRatio(qreal value) { m_config.writeEntry("schedulerBalancingRatio", value); } int KisImageConfig::maxSwapSize(bool requestDefault) const { return !requestDefault ? m_config.readEntry("maxSwapSize", 4096) : 4096; // in MiB } void KisImageConfig::setMaxSwapSize(int value) { m_config.writeEntry("maxSwapSize", value); } int KisImageConfig::swapSlabSize() const { return m_config.readEntry("swapSlabSize", 64); // in MiB } void KisImageConfig::setSwapSlabSize(int value) { m_config.writeEntry("swapSlabSize", value); } int KisImageConfig::swapWindowSize() const { return m_config.readEntry("swapWindowSize", 16); // in MiB } void KisImageConfig::setSwapWindowSize(int value) { m_config.writeEntry("swapWindowSize", value); } int KisImageConfig::tilesHardLimit() const { qreal hp = qreal(memoryHardLimitPercent()) / 100.0; qreal pp = qreal(memoryPoolLimitPercent()) / 100.0; return totalRAM() * hp * (1 - pp); } int KisImageConfig::tilesSoftLimit() const { qreal sp = qreal(memorySoftLimitPercent()) / 100.0; return tilesHardLimit() * sp; } int KisImageConfig::poolLimit() const { qreal hp = qreal(memoryHardLimitPercent()) / 100.0; qreal pp = qreal(memoryPoolLimitPercent()) / 100.0; return totalRAM() * hp * pp; } qreal KisImageConfig::memoryHardLimitPercent(bool requestDefault) const { return !requestDefault ? m_config.readEntry("memoryHardLimitPercent", 50.) : 50.; } void KisImageConfig::setMemoryHardLimitPercent(qreal value) { m_config.writeEntry("memoryHardLimitPercent", value); } qreal KisImageConfig::memorySoftLimitPercent(bool requestDefault) const { return !requestDefault ? m_config.readEntry("memorySoftLimitPercent", 2.) : 2.; } void KisImageConfig::setMemorySoftLimitPercent(qreal value) { m_config.writeEntry("memorySoftLimitPercent", value); } qreal KisImageConfig::memoryPoolLimitPercent(bool requestDefault) const { return !requestDefault ? m_config.readEntry("memoryPoolLimitPercent", 2.) : 2.; } void KisImageConfig::setMemoryPoolLimitPercent(qreal value) { m_config.writeEntry("memoryPoolLimitPercent", value); } QString KisImageConfig::swapDir(bool requestDefault) { - QString swap = QStandardPaths::locate(QStandardPaths::TempLocation, "krita", QStandardPaths::LocateDirectory); + QString swap = QDir::tempPath(); return !requestDefault ? m_config.readEntry("swaplocation", swap) : swap; } void KisImageConfig::setSwapDir(const QString &swapDir) { m_config.writeEntry("swaplocation", swapDir); } int KisImageConfig::numberOfOnionSkins() const { return m_config.readEntry("numberOfOnionSkins", 10); } void KisImageConfig::setNumberOfOnionSkins(int value) { m_config.writeEntry("numberOfOnionSkins", value); } int KisImageConfig::onionSkinTintFactor() const { - return m_config.readEntry("onionSkinTintFactor", 0); + return m_config.readEntry("onionSkinTintFactor", 192); } void KisImageConfig::setOnionSkinTintFactor(int value) { m_config.writeEntry("onionSkinTintFactor", value); } int KisImageConfig::onionSkinOpacity(int offset) const { int value = m_config.readEntry("onionSkinOpacity_" + QString::number(offset), -1); if (value < 0) { const int num = numberOfOnionSkins(); const qreal dx = qreal(qAbs(offset)) / num; value = 0.7 * exp(-pow2(dx) / 0.5) * 255; } return value; } void KisImageConfig::setOnionSkinOpacity(int offset, int value) { m_config.writeEntry("onionSkinOpacity_" + QString::number(offset), value); } bool KisImageConfig::onionSkinState(int offset) const { - return m_config.readEntry("onionSkinState_" + QString::number(offset), false); + bool enableByDefault = (qAbs(offset) <= 2); + return m_config.readEntry("onionSkinState_" + QString::number(offset), enableByDefault); } void KisImageConfig::setOnionSkinState(int offset, bool value) { m_config.writeEntry("onionSkinState_" + QString::number(offset), value); } QColor KisImageConfig::onionSkinTintColorBackward() const { return m_config.readEntry("onionSkinTintColorBackward", QColor(Qt::red)); } void KisImageConfig::setOnionSkinTintColorBackward(const QColor &value) { m_config.writeEntry("onionSkinTintColorBackward", value); } QColor KisImageConfig::onionSkinTintColorForward() const { return m_config.readEntry("oninSkinTintColorForward", QColor(Qt::green)); } void KisImageConfig::setOnionSkinTintColorForward(const QColor &value) { m_config.writeEntry("oninSkinTintColorForward", value); } bool KisImageConfig::lazyFrameCreationEnabled(bool requestDefault) const { return !requestDefault ? m_config.readEntry("lazyFrameCreationEnabled", true) : true; } void KisImageConfig::setLazyFrameCreationEnabled(bool value) { m_config.writeEntry("lazyFrameCreationEnabled", value); } #if defined Q_OS_LINUX #include #elif defined Q_OS_FREEBSD || defined Q_OS_NETBSD || defined Q_OS_OPENBSD #include #elif defined Q_OS_WIN #include #elif defined Q_OS_MAC #include #include #endif #include int KisImageConfig::totalRAM() { // let's think that default memory size is 1000MiB int totalMemory = 1000; // MiB int error = 1; #if defined Q_OS_LINUX struct sysinfo info; error = sysinfo(&info); if(!error) { totalMemory = info.totalram * info.mem_unit / (1UL << 20); } #elif defined Q_OS_FREEBSD || defined Q_OS_NETBSD || defined Q_OS_OPENBSD u_long physmem; # if defined HW_PHYSMEM64 // NetBSD only int mib[] = {CTL_HW, HW_PHYSMEM64}; # else int mib[] = {CTL_HW, HW_PHYSMEM}; # endif size_t len = sizeof(physmem); error = sysctl(mib, 2, &physmem, &len, 0, 0); if(!error) { totalMemory = physmem >> 20; } #elif defined Q_OS_WIN MEMORYSTATUSEX status; status.dwLength = sizeof(status); error = !GlobalMemoryStatusEx(&status); if (!error) { totalMemory = status.ullTotalPhys >> 20; } // For 32 bit windows, the total memory available is at max the 2GB per process memory limit. # if defined ENV32BIT totalMemory = qMin(totalMemory, 2000); # endif #elif defined Q_OS_MAC int mib[2] = { CTL_HW, HW_MEMSIZE }; u_int namelen = sizeof(mib) / sizeof(mib[0]); uint64_t size; size_t len = sizeof(size); errno = 0; if (sysctl(mib, namelen, &size, &len, 0, 0) >= 0) { totalMemory = size >> 20; error = 0; } else { dbgKrita << "sysctl(\"hw.memsize\") raised error" << strerror(errno); } #endif if (error) { warnKrita << "Cannot get the size of your RAM. Using 1 GiB by default."; } return totalMemory; } bool KisImageConfig::showAdditionalOnionSkinsSettings(bool requestDefault) const { return !requestDefault ? m_config.readEntry("showAdditionalOnionSkinsSettings", true) : true; } void KisImageConfig::setShowAdditionalOnionSkinsSettings(bool value) { m_config.writeEntry("showAdditionalOnionSkinsSettings", value); } diff --git a/libs/image/kis_layer_utils.cpp b/libs/image/kis_layer_utils.cpp index 828eb1b03e..fb36cf7b60 100644 --- a/libs/image/kis_layer_utils.cpp +++ b/libs/image/kis_layer_utils.cpp @@ -1,1135 +1,1153 @@ /* * 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_layer_utils.h" #include #include #include "kis_painter.h" #include "kis_image.h" #include "kis_node.h" #include "kis_layer.h" #include "kis_paint_layer.h" #include "kis_clone_layer.h" #include "kis_group_layer.h" #include "kis_selection.h" #include "kis_selection_mask.h" #include "kis_meta_data_merge_strategy.h" #include #include "commands/kis_image_layer_add_command.h" #include "commands/kis_image_layer_remove_command.h" #include "commands/kis_image_layer_move_command.h" #include "commands/kis_image_change_layers_command.h" #include "commands_new/kis_activate_selection_mask_command.h" #include "kis_abstract_projection_plane.h" #include "kis_processing_applicator.h" #include "kis_image_animation_interface.h" #include "kis_keyframe_channel.h" #include "kis_command_utils.h" +#include "kis_processing_applicator.h" +#include "commands_new/kis_change_projection_color_command.h" namespace KisLayerUtils { void fetchSelectionMasks(KisNodeList mergedNodes, QVector &selectionMasks) { foreach (KisNodeSP node, mergedNodes) { KisLayerSP layer = dynamic_cast(node.data()); KisSelectionMaskSP mask; if (layer && (mask = layer->selectionMask())) { selectionMasks.append(mask); } } } struct MergeDownInfoBase { MergeDownInfoBase(KisImageSP _image) : image(_image), storage(new SwitchFrameCommand::SharedStorage()) { } virtual ~MergeDownInfoBase() {} KisImageWSP image; QVector selectionMasks; KisNodeSP dstNode; SwitchFrameCommand::SharedStorageSP storage; QSet frames; virtual KisNodeList allSrcNodes() = 0; virtual KisLayerSP dstLayer() { return 0; } }; struct MergeDownInfo : public MergeDownInfoBase { MergeDownInfo(KisImageSP _image, KisLayerSP _prevLayer, KisLayerSP _currLayer) : MergeDownInfoBase(_image), prevLayer(_prevLayer), currLayer(_currLayer) { frames = fetchLayerFramesRecursive(prevLayer) | fetchLayerFramesRecursive(currLayer); } KisLayerSP prevLayer; KisLayerSP currLayer; KisNodeList allSrcNodes() { KisNodeList mergedNodes; mergedNodes << currLayer; mergedNodes << prevLayer; return mergedNodes; } KisLayerSP dstLayer() { return dynamic_cast(dstNode.data()); } }; struct MergeMultipleInfo : public MergeDownInfoBase { MergeMultipleInfo(KisImageSP _image, KisNodeList _mergedNodes) : MergeDownInfoBase(_image), mergedNodes(_mergedNodes) { foreach (KisNodeSP node, mergedNodes) { frames |= fetchLayerFramesRecursive(node); } } KisNodeList mergedNodes; KisNodeList allSrcNodes() { return mergedNodes; } }; typedef QSharedPointer MergeDownInfoBaseSP; typedef QSharedPointer MergeDownInfoSP; typedef QSharedPointer MergeMultipleInfoSP; struct FillSelectionMasks : public KUndo2Command { FillSelectionMasks(MergeDownInfoBaseSP info) : m_info(info) {} void redo() { fetchSelectionMasks(m_info->allSrcNodes(), m_info->selectionMasks); } private: MergeDownInfoBaseSP m_info; }; struct RefreshHiddenAreas : public KUndo2Command { RefreshHiddenAreas(MergeDownInfoBaseSP info) : m_info(info) {} void redo() { KisImageAnimationInterface *interface = m_info->image->animationInterface(); const QRect preparedRect = !interface->externalFrameActive() ? m_info->image->bounds() : QRect(); foreach (KisNodeSP node, m_info->allSrcNodes()) { refreshHiddenAreaAsync(node, preparedRect); } } private: QRect realNodeExactBounds(KisNodeSP rootNode, QRect currentRect = QRect()) { KisNodeSP node = rootNode->firstChild(); while(node) { currentRect |= realNodeExactBounds(node, currentRect); node = node->nextSibling(); } // TODO: it would be better to count up changeRect inside // node's extent() method currentRect |= rootNode->projectionPlane()->changeRect(rootNode->exactBounds()); return currentRect; } void refreshHiddenAreaAsync(KisNodeSP rootNode, const QRect &preparedArea) { QRect realNodeRect = realNodeExactBounds(rootNode); if (!preparedArea.contains(realNodeRect)) { QRegion dirtyRegion = realNodeRect; dirtyRegion -= preparedArea; foreach(const QRect &rc, dirtyRegion.rects()) { m_info->image->refreshGraphAsync(rootNode, rc, realNodeRect); } } } private: MergeDownInfoBaseSP m_info; }; struct KeepMergedNodesSelected : public KisCommandUtils::AggregateCommand { KeepMergedNodesSelected(MergeDownInfoSP info, bool finalizing) : m_singleInfo(info), m_finalizing(finalizing) {} KeepMergedNodesSelected(MergeMultipleInfoSP info, KisNodeSP putAfter, bool finalizing) : m_multipleInfo(info), m_finalizing(finalizing), m_putAfter(putAfter) {} void populateChildCommands() { KisNodeSP prevNode; KisNodeSP nextNode; KisNodeList prevSelection; KisNodeList nextSelection; KisImageSP image; if (m_singleInfo) { prevNode = m_singleInfo->currLayer; nextNode = m_singleInfo->dstNode; image = m_singleInfo->image; } else if (m_multipleInfo) { prevNode = m_putAfter; nextNode = m_multipleInfo->dstNode; prevSelection = m_multipleInfo->allSrcNodes(); image = m_multipleInfo->image; } if (!m_finalizing) { addCommand(new KeepNodesSelectedCommand(prevSelection, KisNodeList(), prevNode, KisNodeSP(), image, false)); } else { addCommand(new KeepNodesSelectedCommand(KisNodeList(), nextSelection, KisNodeSP(), nextNode, image, true)); } } private: MergeDownInfoSP m_singleInfo; MergeMultipleInfoSP m_multipleInfo; bool m_finalizing; KisNodeSP m_putAfter; }; struct CreateMergedLayer : public KisCommandUtils::AggregateCommand { CreateMergedLayer(MergeDownInfoSP info) : m_info(info) {} void populateChildCommands() { // actual merging done by KisLayer::createMergedLayer (or specialized decendant) m_info->dstNode = m_info->currLayer->createMergedLayerTemplate(m_info->prevLayer); if (m_info->frames.size() > 0) { m_info->dstNode->enableAnimation(); m_info->dstNode->getKeyframeChannel(KisKeyframeChannel::Content.id(), true); } } private: MergeDownInfoSP m_info; }; struct CreateMergedLayerMultiple : public KisCommandUtils::AggregateCommand { CreateMergedLayerMultiple(MergeMultipleInfoSP info) : m_info(info) {} void populateChildCommands() { const QString mergedLayerSuffix = i18n("Merged"); QString mergedLayerName = m_info->mergedNodes.first()->name(); if (!mergedLayerName.endsWith(mergedLayerSuffix)) { mergedLayerName = QString("%1 %2") .arg(mergedLayerName).arg(mergedLayerSuffix); } m_info->dstNode = new KisPaintLayer(m_info->image, mergedLayerName, OPACITY_OPAQUE_U8); if (m_info->frames.size() > 0) { m_info->dstNode->enableAnimation(); m_info->dstNode->getKeyframeChannel(KisKeyframeChannel::Content.id(), true); } QString compositeOpId; QBitArray channelFlags; bool compositionVaries = false; foreach (KisNodeSP node, m_info->allSrcNodes()) { if (compositeOpId.isEmpty()) { compositeOpId = node->compositeOpId(); } else if (compositeOpId != node->compositeOpId()) { compositionVaries = true; break; } KisLayerSP layer = dynamic_cast(node.data()); if (layer && layer->layerStyle()) { compositionVaries = true; break; } } if (!compositionVaries) { if (!compositeOpId.isEmpty()) { m_info->dstNode->setCompositeOpId(compositeOpId); } if (m_info->dstLayer() && !channelFlags.isEmpty()) { m_info->dstLayer()->setChannelFlags(channelFlags); } } } private: MergeMultipleInfoSP m_info; }; struct MergeLayers : public KisCommandUtils::AggregateCommand { MergeLayers(MergeDownInfoSP info) : m_info(info) {} void populateChildCommands() { // actual merging done by KisLayer::createMergedLayer (or specialized decendant) m_info->currLayer->fillMergedLayerTemplate(m_info->dstLayer(), m_info->prevLayer); } private: MergeDownInfoSP m_info; }; struct MergeLayersMultiple : public KisCommandUtils::AggregateCommand { MergeLayersMultiple(MergeMultipleInfoSP info) : m_info(info) {} void populateChildCommands() { KisPainter gc(m_info->dstNode->paintDevice()); foreach (KisNodeSP node, m_info->allSrcNodes()) { QRect rc = node->exactBounds() | m_info->image->bounds(); node->projectionPlane()->apply(&gc, rc); } } private: MergeMultipleInfoSP m_info; }; struct MergeMetaData : public KUndo2Command { MergeMetaData(MergeDownInfoSP info, const KisMetaData::MergeStrategy* strategy) : m_info(info), m_strategy(strategy) {} void redo() { QRect layerProjectionExtent = m_info->currLayer->projection()->extent(); QRect prevLayerProjectionExtent = m_info->prevLayer->projection()->extent(); int prevLayerArea = prevLayerProjectionExtent.width() * prevLayerProjectionExtent.height(); int layerArea = layerProjectionExtent.width() * layerProjectionExtent.height(); QList scores; double norm = qMax(prevLayerArea, layerArea); scores.append(prevLayerArea / norm); scores.append(layerArea / norm); QList srcs; srcs.append(m_info->prevLayer->metaData()); srcs.append(m_info->currLayer->metaData()); m_strategy->merge(m_info->dstLayer()->metaData(), srcs, scores); } private: MergeDownInfoSP m_info; const KisMetaData::MergeStrategy *m_strategy; }; KeepNodesSelectedCommand::KeepNodesSelectedCommand(const KisNodeList &selectedBefore, const KisNodeList &selectedAfter, KisNodeSP activeBefore, KisNodeSP activeAfter, KisImageSP image, bool finalize, KUndo2Command *parent) : FlipFlopCommand(finalize, parent), m_selectedBefore(selectedBefore), m_selectedAfter(selectedAfter), m_activeBefore(activeBefore), m_activeAfter(activeAfter), m_image(image) { } void KeepNodesSelectedCommand::end() { KisImageSignalType type; if (isFinalizing()) { type = ComplexNodeReselectionSignal(m_activeAfter, m_selectedAfter); } else { type = ComplexNodeReselectionSignal(m_activeBefore, m_selectedBefore); } m_image->signalRouter()->emitNotification(type); } KisLayerSP constructDefaultLayer(KisImageSP image) { return new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, image->colorSpace()); } RemoveNodeHelper::~RemoveNodeHelper() { } /** * The removal of two nodes in one go may be a bit tricky, because one * of them may be the clone of another. If we remove the source of a * clone layer, it will reincarnate into a paint layer. In this case * the pointer to the second layer will be lost. * * That's why we need to care about the order of the nodes removal: * the clone --- first, the source --- last. */ void RemoveNodeHelper::safeRemoveMultipleNodes(KisNodeList nodes, KisImageSP image) { const bool lastLayer = scanForLastLayer(image, nodes); while (!nodes.isEmpty()) { KisNodeList::iterator it = nodes.begin(); while (it != nodes.end()) { if (!checkIsSourceForClone(*it, nodes)) { KisNodeSP node = *it; addCommandImpl(new KisImageLayerRemoveCommand(image, node, false, true)); it = nodes.erase(it); } else { ++it; } } } if (lastLayer) { KisLayerSP newLayer = constructDefaultLayer(image); addCommandImpl(new KisImageLayerAddCommand(image, newLayer, image->root(), KisNodeSP(), false, false)); } } bool RemoveNodeHelper::checkIsSourceForClone(KisNodeSP src, const KisNodeList &nodes) { foreach (KisNodeSP node, nodes) { if (node == src) continue; KisCloneLayer *clone = dynamic_cast(node.data()); if (clone && KisNodeSP(clone->copyFrom()) == src) { return true; } } return false; } bool RemoveNodeHelper::scanForLastLayer(KisImageWSP image, KisNodeList nodesToRemove) { bool removeLayers = false; Q_FOREACH(KisNodeSP nodeToRemove, nodesToRemove) { if (dynamic_cast(nodeToRemove.data())) { removeLayers = true; break; } } if (!removeLayers) return false; bool lastLayer = true; KisNodeSP node = image->root()->firstChild(); while (node) { if (!nodesToRemove.contains(node) && dynamic_cast(node.data())) { lastLayer = false; break; } node = node->nextSibling(); } return lastLayer; } SimpleRemoveLayers::SimpleRemoveLayers(const KisNodeList &nodes, KisImageSP image) : m_nodes(nodes), m_image(image) { } void SimpleRemoveLayers::populateChildCommands() { if (m_nodes.isEmpty()) return; safeRemoveMultipleNodes(m_nodes, m_image); } void SimpleRemoveLayers::addCommandImpl(KUndo2Command *cmd) { addCommand(cmd); } struct CleanUpNodes : private RemoveNodeHelper, public KisCommandUtils::AggregateCommand { CleanUpNodes(MergeDownInfoBaseSP info, KisNodeSP putAfter) : m_info(info), m_putAfter(putAfter) {} static void findPerfectParent(KisNodeList nodesToDelete, KisNodeSP &putAfter, KisNodeSP &parent) { if (!putAfter) { putAfter = nodesToDelete.last(); } // Add the new merged node on top of the active node -- checking // whether the parent is going to be deleted parent = putAfter->parent(); while (parent && nodesToDelete.contains(parent)) { parent = parent->parent(); } } void populateChildCommands() { KisNodeList nodesToDelete = m_info->allSrcNodes(); KisNodeSP parent; findPerfectParent(nodesToDelete, m_putAfter, parent); if (!parent) { KisNodeSP oldRoot = m_info->image->root(); KisNodeSP newRoot = new KisGroupLayer(m_info->image, "root", OPACITY_OPAQUE_U8); addCommand(new KisImageLayerAddCommand(m_info->image, m_info->dstNode, newRoot, KisNodeSP(), true, false)); addCommand(new KisImageChangeLayersCommand(m_info->image, oldRoot, newRoot)); } else { if (parent == m_putAfter->parent()) { addCommand(new KisImageLayerAddCommand(m_info->image, m_info->dstNode, parent, m_putAfter, true, false)); } else { addCommand(new KisImageLayerAddCommand(m_info->image, m_info->dstNode, parent, parent->lastChild(), true, false)); } reparentSelectionMasks(m_info->image, m_info->dstLayer(), m_info->selectionMasks); safeRemoveMultipleNodes(m_info->allSrcNodes(), m_info->image); } } private: virtual void addCommandImpl(KUndo2Command *cmd) { addCommand(cmd); } void reparentSelectionMasks(KisImageSP image, KisLayerSP newLayer, const QVector &selectionMasks) { foreach (KisSelectionMaskSP mask, selectionMasks) { addCommand(new KisImageLayerMoveCommand(image, mask, newLayer, newLayer->lastChild())); addCommand(new KisActivateSelectionMaskCommand(mask, false)); } } private: MergeDownInfoBaseSP m_info; KisNodeSP m_putAfter; }; SwitchFrameCommand::SharedStorage::~SharedStorage() { } SwitchFrameCommand::SwitchFrameCommand(KisImageSP image, int time, bool finalize, SharedStorageSP storage) : FlipFlopCommand(finalize), m_image(image), m_newTime(time), m_storage(storage) {} SwitchFrameCommand::~SwitchFrameCommand() {} void SwitchFrameCommand::init() { KisImageAnimationInterface *interface = m_image->animationInterface(); const int currentTime = interface->currentTime(); if (currentTime == m_newTime) { m_storage->value = m_newTime; return; } interface->image()->disableUIUpdates(); interface->saveAndResetCurrentTime(m_newTime, &m_storage->value); } void SwitchFrameCommand::end() { KisImageAnimationInterface *interface = m_image->animationInterface(); const int currentTime = interface->currentTime(); if (currentTime == m_storage->value) { return; } interface->restoreCurrentTime(&m_storage->value); interface->image()->enableUIUpdates(); } struct AddNewFrame : public KisCommandUtils::AggregateCommand { AddNewFrame(MergeDownInfoBaseSP info, int frame) : m_info(info), m_frame(frame) {} void populateChildCommands() { KUndo2Command *cmd = new KisCommandUtils::SkipFirstRedoWrapper(); KisKeyframeChannel *channel = m_info->dstNode->getKeyframeChannel(KisKeyframeChannel::Content.id()); channel->addKeyframe(m_frame, cmd); addCommand(cmd); } private: MergeDownInfoBaseSP m_info; int m_frame; }; QSet fetchLayerFrames(KisNodeSP node) { KisKeyframeChannel *channel = node->getKeyframeChannel(KisKeyframeChannel::Content.id()); if (!channel) return QSet(); return channel->allKeyframeIds(); } QSet fetchLayerFramesRecursive(KisNodeSP rootNode) { QSet frames = fetchLayerFrames(rootNode); KisNodeSP node = rootNode->firstChild(); while(node) { frames |= fetchLayerFramesRecursive(node); node = node->nextSibling(); } return frames; } void updateFrameJobs(FrameJobs *jobs, KisNodeSP node) { QSet frames = fetchLayerFrames(node); if (frames.isEmpty()) { (*jobs)[0].insert(node); } else { foreach (int frame, frames) { (*jobs)[frame].insert(node); } } } void updateFrameJobsRecursive(FrameJobs *jobs, KisNodeSP rootNode) { updateFrameJobs(jobs, rootNode); KisNodeSP node = rootNode->firstChild(); while(node) { updateFrameJobsRecursive(jobs, node); node = node->nextSibling(); } } void mergeDown(KisImageSP image, KisLayerSP layer, const KisMetaData::MergeStrategy* strategy) { if (!layer->prevSibling()) return; // XXX: this breaks if we allow free mixing of masks and layers KisLayerSP prevLayer = dynamic_cast(layer->prevSibling().data()); if (!prevLayer) return; if (!layer->visible() && !prevLayer->visible()) { return; } KisImageSignalVector emitSignals; emitSignals << ModifiedSignal; KisProcessingApplicator applicator(image, 0, KisProcessingApplicator::NONE, emitSignals, kundo2_i18n("Merge Down")); if (layer->visible() && prevLayer->visible()) { MergeDownInfoSP info(new MergeDownInfo(image, prevLayer, layer)); applicator.applyCommand(new KeepMergedNodesSelected(info, false)); applicator.applyCommand(new FillSelectionMasks(info)); applicator.applyCommand(new CreateMergedLayer(info), KisStrokeJobData::BARRIER); if (info->frames.size() > 0) { foreach (int frame, info->frames) { applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage)); applicator.applyCommand(new AddNewFrame(info, frame)); applicator.applyCommand(new RefreshHiddenAreas(info)); applicator.applyCommand(new MergeLayers(info), KisStrokeJobData::BARRIER); applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage)); } } else { applicator.applyCommand(new RefreshHiddenAreas(info)); applicator.applyCommand(new MergeLayers(info), KisStrokeJobData::BARRIER); } applicator.applyCommand(new MergeMetaData(info, strategy), KisStrokeJobData::BARRIER); applicator.applyCommand(new CleanUpNodes(info, layer), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); applicator.applyCommand(new KeepMergedNodesSelected(info, true)); } else if (layer->visible()) { applicator.applyCommand(new KeepNodesSelectedCommand(KisNodeList(), KisNodeList(), layer, KisNodeSP(), image, false)); applicator.applyCommand( new SimpleRemoveLayers(KisNodeList() << prevLayer, image), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); applicator.applyCommand(new KeepNodesSelectedCommand(KisNodeList(), KisNodeList(), KisNodeSP(), layer, image, true)); } else if (prevLayer->visible()) { applicator.applyCommand(new KeepNodesSelectedCommand(KisNodeList(), KisNodeList(), layer, KisNodeSP(), image, false)); applicator.applyCommand( new SimpleRemoveLayers(KisNodeList() << layer, image), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); applicator.applyCommand(new KeepNodesSelectedCommand(KisNodeList(), KisNodeList(), KisNodeSP(), prevLayer, image, true)); } applicator.end(); } bool checkIsChildOf(KisNodeSP node, const KisNodeList &parents) { KisNodeList nodeParents; KisNodeSP parent = node->parent(); while (parent) { nodeParents << parent; parent = parent->parent(); } foreach(KisNodeSP perspectiveParent, parents) { if (nodeParents.contains(perspectiveParent)) { return true; } } return false; } bool checkIsCloneOf(KisNodeSP node, const KisNodeList &nodes) { bool result = false; KisCloneLayer *clone = dynamic_cast(node.data()); if (clone) { KisNodeSP cloneSource = KisNodeSP(clone->copyFrom()); Q_FOREACH(KisNodeSP subtree, nodes) { result = recursiveFindNode(subtree, [cloneSource](KisNodeSP node) -> bool { return node == cloneSource; }); if (!result) { result = checkIsCloneOf(cloneSource, nodes); } if (result) { break; } } } return result; } void filterMergableNodes(KisNodeList &nodes, bool allowMasks) { KisNodeList::iterator it = nodes.begin(); while (it != nodes.end()) { if ((!allowMasks && !dynamic_cast(it->data())) || checkIsChildOf(*it, nodes)) { qDebug() << "Skipping node" << ppVar((*it)->name()); it = nodes.erase(it); } else { ++it; } } } void sortMergableNodes(KisNodeSP root, KisNodeList &inputNodes, KisNodeList &outputNodes) { KisNodeList::iterator it = std::find(inputNodes.begin(), inputNodes.end(), root); if (it != inputNodes.end()) { outputNodes << *it; inputNodes.erase(it); } if (inputNodes.isEmpty()) { return; } KisNodeSP child = root->firstChild(); while (child) { sortMergableNodes(child, inputNodes, outputNodes); child = child->nextSibling(); } /** * By the end of recursion \p inputNodes must be empty */ KIS_ASSERT_RECOVER_NOOP(root->parent() || inputNodes.isEmpty()); } KisNodeList sortMergableNodes(KisNodeSP root, KisNodeList nodes) { KisNodeList result; sortMergableNodes(root, nodes, result); return result; } KisNodeList sortAndFilterMergableInternalNodes(KisNodeList nodes, bool allowMasks) { KIS_ASSERT_RECOVER(!nodes.isEmpty()) { return nodes; } KisNodeSP root; Q_FOREACH(KisNodeSP node, nodes) { KisNodeSP localRoot = node; while (localRoot->parent()) { localRoot = localRoot->parent(); } if (!root) { root = localRoot; } KIS_ASSERT_RECOVER(root == localRoot) { return nodes; } } KisNodeList result; sortMergableNodes(root, nodes, result); filterMergableNodes(result, allowMasks); return result; } void addCopyOfNameTag(KisNodeSP node) { const QString prefix = i18n("Copy of"); QString newName = node->name(); if (!newName.startsWith(prefix)) { newName = QString("%1 %2").arg(prefix).arg(newName); node->setName(newName); } } KisNodeList findNodesWithProps(KisNodeSP root, const KoProperties &props, bool excludeRoot) { KisNodeList nodes; if ((!excludeRoot || root->parent()) && root->check(props)) { nodes << root; } KisNodeSP node = root->firstChild(); while (node) { nodes += findNodesWithProps(node, props, excludeRoot); node = node->nextSibling(); } return nodes; } KisNodeList filterInvisibleNodes(const KisNodeList &nodes, KisNodeList *invisibleNodes, KisNodeSP *putAfter) { KIS_ASSERT_RECOVER(invisibleNodes) { return nodes; } KIS_ASSERT_RECOVER(putAfter) { return nodes; } KisNodeList visibleNodes; int putAfterIndex = -1; Q_FOREACH(KisNodeSP node, nodes) { if (node->visible()) { visibleNodes << node; } else { *invisibleNodes << node; if (node == *putAfter) { putAfterIndex = visibleNodes.size() - 1; } } } if (!visibleNodes.isEmpty() && putAfterIndex >= 0) { putAfterIndex = qBound(0, putAfterIndex, visibleNodes.size() - 1); *putAfter = visibleNodes[putAfterIndex]; } return visibleNodes; } + void changeImageDefaultProjectionColor(KisImageSP image, const KoColor &color) + { + KisImageSignalVector emitSignals; + emitSignals << ModifiedSignal; + + KisProcessingApplicator applicator(image, + image->root(), + KisProcessingApplicator::RECURSIVE, + emitSignals, + kundo2_i18n("Change projection color"), + 0, + 142857 + 1); + applicator.applyCommand(new KisChangeProjectionColorCommand(image, color), KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE); + applicator.end(); + } + void mergeMultipleLayersImpl(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter, bool flattenSingleLayer, const KUndo2MagicString &actionName) { filterMergableNodes(mergedNodes); { KisNodeList tempNodes; qSwap(mergedNodes, tempNodes); sortMergableNodes(image->root(), tempNodes, mergedNodes); } if (mergedNodes.size() <= 1 && (!flattenSingleLayer && mergedNodes.size() == 1)) return; KisImageSignalVector emitSignals; emitSignals << ModifiedSignal; emitSignals << ComplexNodeReselectionSignal(KisNodeSP(), KisNodeList(), KisNodeSP(), mergedNodes); KisProcessingApplicator applicator(image, 0, KisProcessingApplicator::NONE, emitSignals, actionName); KisNodeList originalNodes = mergedNodes; KisNodeList invisibleNodes; mergedNodes = filterInvisibleNodes(originalNodes, &invisibleNodes, &putAfter); if (!invisibleNodes.isEmpty()) { applicator.applyCommand( new SimpleRemoveLayers(invisibleNodes, image), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); } if (mergedNodes.size() > 1 || invisibleNodes.isEmpty()) { MergeMultipleInfoSP info(new MergeMultipleInfo(image, mergedNodes)); applicator.applyCommand(new KeepMergedNodesSelected(info, putAfter, false)); applicator.applyCommand(new FillSelectionMasks(info)); applicator.applyCommand(new CreateMergedLayerMultiple(info), KisStrokeJobData::BARRIER); if (info->frames.size() > 0) { foreach (int frame, info->frames) { applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage)); applicator.applyCommand(new AddNewFrame(info, frame)); applicator.applyCommand(new RefreshHiddenAreas(info)); applicator.applyCommand(new MergeLayersMultiple(info), KisStrokeJobData::BARRIER); applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage)); } } else { applicator.applyCommand(new RefreshHiddenAreas(info)); applicator.applyCommand(new MergeLayersMultiple(info), KisStrokeJobData::BARRIER); } //applicator.applyCommand(new MergeMetaData(info, strategy), KisStrokeJobData::BARRIER); applicator.applyCommand(new CleanUpNodes(info, putAfter), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); applicator.applyCommand(new KeepMergedNodesSelected(info, putAfter, true)); } applicator.end(); } void mergeMultipleLayers(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter) { mergeMultipleLayersImpl(image, mergedNodes, putAfter, false, kundo2_i18n("Merge Selected Nodes")); } struct MergeSelectionMasks : public KisCommandUtils::AggregateCommand { MergeSelectionMasks(MergeDownInfoBaseSP info, KisNodeSP putAfter) : m_info(info), m_putAfter(putAfter){} void populateChildCommands() { KisNodeSP parent; CleanUpNodes::findPerfectParent(m_info->allSrcNodes(), m_putAfter, parent); KisLayerSP parentLayer; do { parentLayer = dynamic_cast(parent.data()); parent = parent->parent(); } while(!parentLayer && parent); KisSelectionSP selection = new KisSelection(); foreach (KisNodeSP node, m_info->allSrcNodes()) { KisMaskSP mask = dynamic_cast(node.data()); if (!mask) continue; selection->pixelSelection()->applySelection( mask->selection()->pixelSelection(), SELECTION_ADD); } KisSelectionMaskSP mergedMask = new KisSelectionMask(m_info->image); mergedMask->initSelection(parentLayer); mergedMask->setSelection(selection); m_info->dstNode = mergedMask; } private: MergeDownInfoBaseSP m_info; KisNodeSP m_putAfter; }; struct ActivateSelectionMask : public KisCommandUtils::AggregateCommand { ActivateSelectionMask(MergeDownInfoBaseSP info) : m_info(info) {} void populateChildCommands() { KisSelectionMaskSP mergedMask = dynamic_cast(m_info->dstNode.data()); addCommand(new KisActivateSelectionMaskCommand(mergedMask, true)); } private: MergeDownInfoBaseSP m_info; }; bool tryMergeSelectionMasks(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter) { QList selectionMasks; for (auto it = mergedNodes.begin(); it != mergedNodes.end(); /*noop*/) { KisSelectionMaskSP mask = dynamic_cast(it->data()); if (!mask) { it = mergedNodes.erase(it); } else { selectionMasks.append(mask); ++it; } } if (mergedNodes.isEmpty()) return false; KisLayerSP parentLayer = dynamic_cast(selectionMasks.first()->parent().data()); KIS_ASSERT_RECOVER(parentLayer) { return 0; } KisImageSignalVector emitSignals; emitSignals << ModifiedSignal; KisProcessingApplicator applicator(image, 0, KisProcessingApplicator::NONE, emitSignals, kundo2_i18n("Merge Selection Masks")); MergeMultipleInfoSP info(new MergeMultipleInfo(image, mergedNodes)); applicator.applyCommand(new MergeSelectionMasks(info, putAfter)); applicator.applyCommand(new CleanUpNodes(info, putAfter), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); applicator.applyCommand(new ActivateSelectionMask(info)); applicator.end(); return true; } void flattenLayer(KisImageSP image, KisLayerSP layer) { if (!layer->childCount() && !layer->layerStyle()) return; KisNodeList mergedNodes; mergedNodes << layer; mergeMultipleLayersImpl(image, mergedNodes, layer, true, kundo2_i18n("Flatten Layer")); } void flattenImage(KisImageSP image) { KisNodeList mergedNodes; mergedNodes << image->root(); mergeMultipleLayersImpl(image, mergedNodes, 0, true, kundo2_i18n("Flatten Image")); } KisSimpleUpdateCommand::KisSimpleUpdateCommand(KisNodeList nodes, bool finalize, KUndo2Command *parent) : FlipFlopCommand(finalize, parent), m_nodes(nodes) { } void KisSimpleUpdateCommand::end() { updateNodes(m_nodes); } void KisSimpleUpdateCommand::updateNodes(const KisNodeList &nodes) { Q_FOREACH(KisNodeSP node, nodes) { node->setDirty(node->extent()); } } void recursiveApplyNodes(KisNodeSP node, std::function func) { func(node); node = node->firstChild(); while (node) { recursiveApplyNodes(node, func); node = node->nextSibling(); } } KisNodeSP recursiveFindNode(KisNodeSP node, std::function func) { if (func(node)) { return node; } node = node->firstChild(); while (node) { KisNodeSP resultNode = recursiveFindNode(node, func); if (resultNode) { return resultNode; } node = node->nextSibling(); } return 0; } } diff --git a/libs/image/kis_layer_utils.h b/libs/image/kis_layer_utils.h index 9c76df9bea..46550d86c6 100644 --- a/libs/image/kis_layer_utils.h +++ b/libs/image/kis_layer_utils.h @@ -1,193 +1,196 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_LAYER_UTILS_H #define __KIS_LAYER_UTILS_H #include #include "kundo2command.h" #include "kis_types.h" #include "kritaimage_export.h" #include "kis_command_utils.h" class KoProperties; +class KoColor; namespace KisMetaData { class MergeStrategy; } namespace KisLayerUtils { KRITAIMAGE_EXPORT void sortMergableNodes(KisNodeSP root, QList &inputNodes, QList &outputNodes); KRITAIMAGE_EXPORT KisNodeList sortMergableNodes(KisNodeSP root, KisNodeList nodes); KRITAIMAGE_EXPORT void filterMergableNodes(KisNodeList &nodes, bool allowMasks = false); KRITAIMAGE_EXPORT bool checkIsChildOf(KisNodeSP node, const KisNodeList &parents); /** * Returns true if: * o \p node is a clone of some layer in \p nodes * o \p node is a clone any child layer of any layer in \p nodes * o \p node is a clone of a clone of a ..., that in the end points * to any layer in \p nodes of their children. */ KRITAIMAGE_EXPORT bool checkIsCloneOf(KisNodeSP node, const KisNodeList &nodes); KRITAIMAGE_EXPORT KisNodeList sortAndFilterMergableInternalNodes(KisNodeList nodes, bool allowMasks = false); KRITAIMAGE_EXPORT void mergeDown(KisImageSP image, KisLayerSP layer, const KisMetaData::MergeStrategy* strategy); KRITAIMAGE_EXPORT QSet fetchLayerFrames(KisNodeSP node); KRITAIMAGE_EXPORT QSet fetchLayerFramesRecursive(KisNodeSP rootNode); KRITAIMAGE_EXPORT void mergeMultipleLayers(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter); KRITAIMAGE_EXPORT bool tryMergeSelectionMasks(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter); KRITAIMAGE_EXPORT void flattenLayer(KisImageSP image, KisLayerSP layer); KRITAIMAGE_EXPORT void flattenImage(KisImageSP image); KRITAIMAGE_EXPORT void addCopyOfNameTag(KisNodeSP node); KRITAIMAGE_EXPORT KisNodeList findNodesWithProps(KisNodeSP root, const KoProperties &props, bool excludeRoot); + KRITAIMAGE_EXPORT void changeImageDefaultProjectionColor(KisImageSP image, const KoColor &color); + typedef QMap > FrameJobs; void updateFrameJobs(FrameJobs *jobs, KisNodeSP node); void updateFrameJobsRecursive(FrameJobs *jobs, KisNodeSP rootNode); struct SwitchFrameCommand : public KisCommandUtils::FlipFlopCommand { struct SharedStorage { /** * For some reason the absence of a destructor in the SharedStorage * makes Krita crash on exit. Seems like some compiler weirdness... (DK) */ ~SharedStorage(); int value; }; typedef QSharedPointer SharedStorageSP; public: SwitchFrameCommand(KisImageSP image, int time, bool finalize, SharedStorageSP storage); ~SwitchFrameCommand(); private: void init(); void end(); private: KisImageWSP m_image; int m_newTime; SharedStorageSP m_storage; }; /** * A command to keep correct set of selected/active nodes thoroughout * the action. */ class KRITAIMAGE_EXPORT KeepNodesSelectedCommand : public KisCommandUtils::FlipFlopCommand { public: KeepNodesSelectedCommand(const KisNodeList &selectedBefore, const KisNodeList &selectedAfter, KisNodeSP activeBefore, KisNodeSP activeAfter, KisImageSP image, bool finalize, KUndo2Command *parent = 0); void end(); private: KisNodeList m_selectedBefore; KisNodeList m_selectedAfter; KisNodeSP m_activeBefore; KisNodeSP m_activeAfter; KisImageWSP m_image; }; KRITAIMAGE_EXPORT KisLayerSP constructDefaultLayer(KisImageSP image); class KRITAIMAGE_EXPORT RemoveNodeHelper { public: virtual ~RemoveNodeHelper(); protected: virtual void addCommandImpl(KUndo2Command *cmd) = 0; void safeRemoveMultipleNodes(KisNodeList nodes, KisImageSP image); private: bool checkIsSourceForClone(KisNodeSP src, const KisNodeList &nodes); static bool scanForLastLayer(KisImageWSP image, KisNodeList nodesToRemove); }; struct SimpleRemoveLayers : private KisLayerUtils::RemoveNodeHelper, public KisCommandUtils::AggregateCommand { SimpleRemoveLayers(const KisNodeList &nodes, KisImageSP image); void populateChildCommands(); protected: virtual void addCommandImpl(KUndo2Command *cmd); private: KisNodeList m_nodes; KisImageSP m_image; KisNodeList m_selectedNodes; KisNodeSP m_activeNode; }; class KRITAIMAGE_EXPORT KisSimpleUpdateCommand : public KisCommandUtils::FlipFlopCommand { public: KisSimpleUpdateCommand(KisNodeList nodes, bool finalize, KUndo2Command *parent = 0); void end(); static void updateNodes(const KisNodeList &nodes); private: KisNodeList m_nodes; }; template bool checkNodesDiffer(KisNodeList nodes, std::function checkerFunc) { bool valueDiffers = false; bool initialized = false; T currentValue; Q_FOREACH (KisNodeSP node, nodes) { if (!initialized) { currentValue = checkerFunc(node); initialized = true; } else if (currentValue != checkerFunc(node)) { valueDiffers = true; break; } } return valueDiffers; } /** * Applies \p func to \p node and all its children recursively */ void KRITAIMAGE_EXPORT recursiveApplyNodes(KisNodeSP node, std::function func); /** * Walks through \p node and all its children recursively until * \p func returns true. When \p func returns true, the node is * considered to be found, the search is stopped and the found * node is returned to the caller. */ KisNodeSP KRITAIMAGE_EXPORT recursiveFindNode(KisNodeSP node, std::function func); }; #endif /* __KIS_LAYER_UTILS_H */ diff --git a/libs/image/kis_liquify_transform_worker.cpp b/libs/image/kis_liquify_transform_worker.cpp index 18a197b6b2..f50d7fe29b 100644 --- a/libs/image/kis_liquify_transform_worker.cpp +++ b/libs/image/kis_liquify_transform_worker.cpp @@ -1,592 +1,592 @@ /* * 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_liquify_transform_worker.h" #include "kis_grid_interpolation_tools.h" #include "kis_dom_utils.h" #include "krita_utils.h" struct Q_DECL_HIDDEN KisLiquifyTransformWorker::Private { Private(const QRect &_srcBounds, KoUpdater *_progress, int _pixelPrecision) : srcBounds(_srcBounds), progress(_progress), pixelPrecision(_pixelPrecision) { } const QRect srcBounds; QVector originalPoints; QVector transformedPoints; KoUpdater *progress; int pixelPrecision; QSize gridSize; void preparePoints(); struct MapIndexesOp; template void processTransformedPixelsBuildUp(ProcessOp op, const QPointF &base, qreal sigma); template void processTransformedPixelsWash(ProcessOp op, const QPointF &base, qreal sigma, qreal flow); template void processTransformedPixels(ProcessOp op, const QPointF &base, qreal sigma, bool useWashMode, qreal flow); }; KisLiquifyTransformWorker::KisLiquifyTransformWorker(const QRect &srcBounds, KoUpdater *progress, int pixelPrecision) : m_d(new Private(srcBounds, progress, pixelPrecision)) { KIS_ASSERT_RECOVER_RETURN(!srcBounds.isEmpty()); // TODO: implement 'progress' stuff m_d->preparePoints(); } KisLiquifyTransformWorker::KisLiquifyTransformWorker(const KisLiquifyTransformWorker &rhs) : m_d(new Private(*rhs.m_d.data())) { } KisLiquifyTransformWorker::~KisLiquifyTransformWorker() { } bool KisLiquifyTransformWorker::operator==(const KisLiquifyTransformWorker &other) const { return m_d->srcBounds == other.m_d->srcBounds && m_d->originalPoints == other.m_d->originalPoints && m_d->transformedPoints == other.m_d->transformedPoints && m_d->pixelPrecision == other.m_d->pixelPrecision && m_d->gridSize == other.m_d->gridSize; } int KisLiquifyTransformWorker::pointToIndex(const QPoint &cellPt) { return GridIterationTools::pointToIndex(cellPt, m_d->gridSize); } QSize KisLiquifyTransformWorker::gridSize() const { return m_d->gridSize; } const QVector& KisLiquifyTransformWorker::originalPoints() const { return m_d->originalPoints; } QVector& KisLiquifyTransformWorker::transformedPoints() { return m_d->transformedPoints; } struct AllPointsFetcherOp { AllPointsFetcherOp(QRectF srcRect) : m_srcRect(srcRect) {} inline void processPoint(int col, int row, int prevCol, int prevRow, int colIndex, int rowIndex) { Q_UNUSED(prevCol); Q_UNUSED(prevRow); Q_UNUSED(colIndex); Q_UNUSED(rowIndex); QPointF pt(col, row); m_points << pt; } inline void nextLine() { } QVector m_points; QRectF m_srcRect; }; void KisLiquifyTransformWorker::Private::preparePoints() { gridSize = GridIterationTools::calcGridSize(srcBounds, pixelPrecision); AllPointsFetcherOp pointsOp(srcBounds); GridIterationTools::processGrid(pointsOp, srcBounds, pixelPrecision); const int numPoints = pointsOp.m_points.size(); KIS_ASSERT_RECOVER_RETURN(numPoints == gridSize.width() * gridSize.height()); originalPoints = pointsOp.m_points; transformedPoints = pointsOp.m_points; } void KisLiquifyTransformWorker::translate(const QPointF &offset) { QVector::iterator it = m_d->transformedPoints.begin(); QVector::iterator end = m_d->transformedPoints.end(); QVector::iterator refIt = m_d->originalPoints.begin(); KIS_ASSERT_RECOVER_RETURN(m_d->originalPoints.size() == m_d->transformedPoints.size()); for (; it != end; ++it, ++refIt) { *it += offset; *refIt += offset; } } void KisLiquifyTransformWorker::undoPoints(const QPointF &base, qreal amount, qreal sigma) { const qreal maxDistCoeff = 3.0; const qreal maxDist = maxDistCoeff * sigma; QRectF clipRect(base.x() - maxDist, base.y() - maxDist, 2 * maxDist, 2 * maxDist); QVector::iterator it = m_d->transformedPoints.begin(); QVector::iterator end = m_d->transformedPoints.end(); QVector::iterator refIt = m_d->originalPoints.begin(); KIS_ASSERT_RECOVER_RETURN(m_d->originalPoints.size() == m_d->transformedPoints.size()); for (; it != end; ++it, ++refIt) { if (!clipRect.contains(*it)) continue; QPointF diff = *it - base; qreal dist = KisAlgebra2D::norm(diff); if (dist > maxDist) continue; qreal lambda = exp(-0.5 * pow2(dist / sigma)); lambda *= amount; *it = *refIt * lambda + *it * (1.0 - lambda); } } template void KisLiquifyTransformWorker::Private:: processTransformedPixelsBuildUp(ProcessOp op, const QPointF &base, qreal sigma) { const qreal maxDist = ProcessOp::maxDistCoeff * sigma; QRectF clipRect(base.x() - maxDist, base.y() - maxDist, 2 * maxDist, 2 * maxDist); QVector::iterator it = transformedPoints.begin(); QVector::iterator end = transformedPoints.end(); for (; it != end; ++it) { if (!clipRect.contains(*it)) continue; QPointF diff = *it - base; qreal dist = KisAlgebra2D::norm(diff); if (dist > maxDist) continue; const qreal lambda = exp(-0.5 * pow2(dist / sigma)); *it = op(*it, base, diff, lambda); } } template void KisLiquifyTransformWorker::Private:: processTransformedPixelsWash(ProcessOp op, const QPointF &base, qreal sigma, qreal flow) { const qreal maxDist = ProcessOp::maxDistCoeff * sigma; QRectF clipRect(base.x() - maxDist, base.y() - maxDist, 2 * maxDist, 2 * maxDist); QVector::iterator it = transformedPoints.begin(); QVector::iterator end = transformedPoints.end(); QVector::iterator refIt = originalPoints.begin(); KIS_ASSERT_RECOVER_RETURN(originalPoints.size() == transformedPoints.size()); for (; it != end; ++it, ++refIt) { if (!clipRect.contains(*it)) continue; QPointF diff = *refIt - base; qreal dist = KisAlgebra2D::norm(diff); if (dist > maxDist) continue; const qreal lambda = exp(-0.5 * pow2(dist / sigma)); QPointF dstPt = op(*refIt, base, diff, lambda); if (kisDistance(dstPt, *refIt) > kisDistance(*it, *refIt)) { *it = (1.0 - flow) * (*it) + flow * dstPt; } } } template void KisLiquifyTransformWorker::Private:: processTransformedPixels(ProcessOp op, const QPointF &base, qreal sigma, bool useWashMode, qreal flow) { if (useWashMode) { processTransformedPixelsWash(op, base, sigma, flow); } else { processTransformedPixelsBuildUp(op, base, sigma); } } struct TranslateOp { TranslateOp(const QPointF &offset) : m_offset(offset) {} QPointF operator() (const QPointF &pt, const QPointF &base, const QPointF &diff, qreal lambda) { Q_UNUSED(base); Q_UNUSED(diff); return pt + lambda * m_offset; } static const qreal maxDistCoeff; QPointF m_offset; }; const qreal TranslateOp::maxDistCoeff = 3.0; struct ScaleOp { ScaleOp(qreal scale) : m_scale(scale) {} QPointF operator() (const QPointF &pt, const QPointF &base, const QPointF &diff, qreal lambda) { Q_UNUSED(pt); Q_UNUSED(diff); return base + (1.0 + m_scale * lambda) * diff; } static const qreal maxDistCoeff; qreal m_scale; }; const qreal ScaleOp::maxDistCoeff = 3.0; struct RotateOp { RotateOp(qreal angle) : m_angle(angle) {} QPointF operator() (const QPointF &pt, const QPointF &base, const QPointF &diff, qreal lambda) { Q_UNUSED(pt); const qreal angle = m_angle * lambda; const qreal sinA = std::sin(angle); const qreal cosA = std::cos(angle); qreal x = cosA * diff.x() + sinA * diff.y(); qreal y = -sinA * diff.x() + cosA * diff.y(); return base + QPointF(x, y); } static const qreal maxDistCoeff; qreal m_angle; }; const qreal RotateOp::maxDistCoeff = 3.0; void KisLiquifyTransformWorker::translatePoints(const QPointF &base, const QPointF &offset, qreal sigma, bool useWashMode, qreal flow) { TranslateOp op(offset); m_d->processTransformedPixels(op, base, sigma, useWashMode, flow); } void KisLiquifyTransformWorker::scalePoints(const QPointF &base, qreal scale, qreal sigma, bool useWashMode, qreal flow) { ScaleOp op(scale); m_d->processTransformedPixels(op, base, sigma, useWashMode, flow); } void KisLiquifyTransformWorker::rotatePoints(const QPointF &base, qreal angle, qreal sigma, bool useWashMode, qreal flow) { RotateOp op(angle); m_d->processTransformedPixels(op, base, sigma, useWashMode, flow); } struct KisLiquifyTransformWorker::Private::MapIndexesOp { MapIndexesOp(KisLiquifyTransformWorker::Private *d) : m_d(d) { } inline QVector calculateMappedIndexes(int col, int row, int *numExistingPoints) const { *numExistingPoints = 4; QVector cellIndexes = GridIterationTools::calculateCellIndexes(col, row, m_d->gridSize); return cellIndexes; } inline int tryGetValidIndex(const QPoint &cellPt) const { Q_UNUSED(cellPt); KIS_ASSERT_RECOVER_NOOP(0 && "Not applicable"); return -1; } inline QPointF getSrcPointForce(const QPoint &cellPt) const { Q_UNUSED(cellPt); KIS_ASSERT_RECOVER_NOOP(0 && "Not applicable"); return QPointF(); } inline const QPolygonF srcCropPolygon() const { KIS_ASSERT_RECOVER_NOOP(0 && "Not applicable"); return QPolygonF(); } KisLiquifyTransformWorker::Private *m_d; }; void KisLiquifyTransformWorker::run(KisPaintDeviceSP device) { KisPaintDeviceSP srcDev = new KisPaintDevice(*device.data()); device->clear(); using namespace GridIterationTools; PaintDevicePolygonOp polygonOp(srcDev, device); Private::MapIndexesOp indexesOp(m_d.data()); iterateThroughGrid(polygonOp, indexesOp, m_d->gridSize, m_d->originalPoints, m_d->transformedPoints); } QRect KisLiquifyTransformWorker::approxChangeRect(const QRect &rc) { - const int margin = 0.05; + const qreal margin = 0.05; /** * Here we just return the full area occupied by the transformed grid. * We sample grid points for not doing too much work. */ const int maxSamplePoints = 200; const int minStep = 3; const int step = qMax(minStep, m_d->transformedPoints.size() / maxSamplePoints); QVector samplePoints; for (int i = 0; i < m_d->transformedPoints.size(); i += step) { samplePoints << m_d->transformedPoints[i].toPoint(); } QRect resultRect = KritaUtils::approximateRectFromPoints(samplePoints); return KisAlgebra2D::blowRect(resultRect | rc, margin); } QRect KisLiquifyTransformWorker::approxNeedRect(const QRect &rc, const QRect &fullBounds) { Q_UNUSED(rc); return fullBounds; } #include #include using PointMapFunction = std::function; PointMapFunction bindPointMapTransform(const QTransform &transform) { using namespace std::placeholders; typedef QPointF (QTransform::*MapFuncType)(const QPointF&) const; return std::bind(static_cast(&QTransform::map), &transform, _1); } QImage KisLiquifyTransformWorker::runOnQImage(const QImage &srcImage, const QPointF &srcImageOffset, const QTransform &imageToThumbTransform, QPointF *newOffset) { KIS_ASSERT_RECOVER(m_d->originalPoints.size() == m_d->transformedPoints.size()) { return QImage(); } KIS_ASSERT_RECOVER(!srcImage.isNull()) { return QImage(); } KIS_ASSERT_RECOVER(srcImage.format() == QImage::Format_ARGB32) { return QImage(); } QVector originalPointsLocal(m_d->originalPoints); QVector transformedPointsLocal(m_d->transformedPoints); PointMapFunction mapFunc = bindPointMapTransform(imageToThumbTransform); std::transform(originalPointsLocal.begin(), originalPointsLocal.end(), originalPointsLocal.begin(), mapFunc); std::transform(transformedPointsLocal.begin(), transformedPointsLocal.end(), transformedPointsLocal.begin(), mapFunc); QRectF dstBounds; Q_FOREACH (const QPointF &pt, transformedPointsLocal) { KisAlgebra2D::accumulateBounds(pt, &dstBounds); } const QRectF srcBounds(srcImageOffset, srcImage.size()); dstBounds |= srcBounds; QPointF dstQImageOffset = dstBounds.topLeft(); *newOffset = dstQImageOffset; QRect dstBoundsI = dstBounds.toAlignedRect(); QImage dstImage(dstBoundsI.size(), srcImage.format()); dstImage.fill(0); GridIterationTools::QImagePolygonOp polygonOp(srcImage, dstImage, srcImageOffset, dstQImageOffset); Private::MapIndexesOp indexesOp(m_d.data()); GridIterationTools::iterateThroughGrid (polygonOp, indexesOp, m_d->gridSize, originalPointsLocal, transformedPointsLocal); return dstImage; } void KisLiquifyTransformWorker::toXML(QDomElement *e) const { QDomDocument doc = e->ownerDocument(); QDomElement liqEl = doc.createElement("liquify_points"); e->appendChild(liqEl); KisDomUtils::saveValue(&liqEl, "srcBounds", m_d->srcBounds); KisDomUtils::saveValue(&liqEl, "originalPoints", m_d->originalPoints); KisDomUtils::saveValue(&liqEl, "transformedPoints", m_d->transformedPoints); KisDomUtils::saveValue(&liqEl, "pixelPrecision", m_d->pixelPrecision); KisDomUtils::saveValue(&liqEl, "gridSize", m_d->gridSize); } KisLiquifyTransformWorker* KisLiquifyTransformWorker::fromXML(const QDomElement &e) { QDomElement liquifyEl; QRect srcBounds; QVector originalPoints; QVector transformedPoints; int pixelPrecision; QSize gridSize; bool result = false; result = KisDomUtils::findOnlyElement(e, "liquify_points", &liquifyEl) && KisDomUtils::loadValue(liquifyEl, "srcBounds", &srcBounds) && KisDomUtils::loadValue(liquifyEl, "originalPoints", &originalPoints) && KisDomUtils::loadValue(liquifyEl, "transformedPoints", &transformedPoints) && KisDomUtils::loadValue(liquifyEl, "pixelPrecision", &pixelPrecision) && KisDomUtils::loadValue(liquifyEl, "gridSize", &gridSize); if (!result) { warnKrita << "WARNING: Failed to load liquify worker from XML"; return new KisLiquifyTransformWorker(QRect(0,0,1024, 1024), 0, 8); } KisLiquifyTransformWorker *worker = new KisLiquifyTransformWorker(srcBounds, 0, pixelPrecision); const int numPoints = originalPoints.size(); if (numPoints != transformedPoints.size() || numPoints != worker->m_d->originalPoints.size() || gridSize != worker->m_d->gridSize) { warnKrita << "WARNING: Inconsistent number of points!"; warnKrita << ppVar(originalPoints.size()); warnKrita << ppVar(transformedPoints.size()); warnKrita << ppVar(gridSize); warnKrita << ppVar(worker->m_d->originalPoints.size()); warnKrita << ppVar(worker->m_d->transformedPoints.size()); warnKrita << ppVar(worker->m_d->gridSize); return worker; } for (int i = 0; i < numPoints; i++) { worker->m_d->originalPoints[i] = originalPoints[i]; worker->m_d->transformedPoints[i] = transformedPoints[i]; } return worker; } diff --git a/libs/image/kis_node.cpp b/libs/image/kis_node.cpp index 3b658086cf..9e4a605708 100644 --- a/libs/image/kis_node.cpp +++ b/libs/image/kis_node.cpp @@ -1,628 +1,629 @@ /* * Copyright (c) 2007 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_node.h" #include #include #include #include #include #include #include #include #include "kis_global.h" #include "kis_node_graph_listener.h" #include "kis_node_visitor.h" #include "kis_processing_visitor.h" #include "kis_node_progress_proxy.h" #include "kis_busy_progress_indicator.h" #include "kis_clone_layer.h" #include "kis_safe_read_list.h" typedef KisSafeReadList KisSafeReadNodeList; #include "kis_abstract_projection_plane.h" #include "kis_projection_leaf.h" #include "kis_undo_adapter.h" #include "kis_keyframe_channel.h" /** *The link between KisProjection ans KisImageUpdater *uses queued signals with an argument of KisNodeSP type, *so we should register it beforehand */ struct KisNodeSPStaticRegistrar { KisNodeSPStaticRegistrar() { qRegisterMetaType("KisNodeSP"); } }; static KisNodeSPStaticRegistrar __registrar1; struct KisNodeListStaticRegistrar { KisNodeListStaticRegistrar() { qRegisterMetaType("KisNodeList"); } }; static KisNodeListStaticRegistrar __registrar2; /** * Note about "thread safety" of KisNode * * 1) One can *read* any information about node and node graph in any * number of threads concurrently. This operation is safe because * of the usage of KisSafeReadNodeList and will run concurrently * (lock-free). * * 2) One can *write* any information into the node or node graph in a * single thread only! Changing the graph concurrently is *not* * sane and therefore not supported. * * 3) One can *read and write* information about the node graph * concurrently, given that there is only *one* writer thread and * any number of reader threads. Please note that in this case the * node's code is just guaranteed *not to crash*, which is ensured * by nodeSubgraphLock. You need to ensure the sanity of the data * read by the reader threads yourself! */ struct Q_DECL_HIDDEN KisNode::Private { public: Private(KisNode *node) : graphListener(0) , nodeProgressProxy(0) , busyProgressIndicator(0) , projectionLeaf(new KisProjectionLeaf(node)) { } KisNodeWSP parent; KisNodeGraphListener *graphListener; KisSafeReadNodeList nodes; KisNodeProgressProxy *nodeProgressProxy; KisBusyProgressIndicator *busyProgressIndicator; QReadWriteLock nodeSubgraphLock; KisProjectionLeafSP projectionLeaf; const KisNode* findSymmetricClone(const KisNode *srcRoot, const KisNode *dstRoot, const KisNode *srcTarget); void processDuplicatedClones(const KisNode *srcDuplicationRoot, const KisNode *dstDuplicationRoot, KisNode *node); }; /** * Finds the layer in \p dstRoot subtree, which has the same path as * \p srcTarget has in \p srcRoot */ const KisNode* KisNode::Private::findSymmetricClone(const KisNode *srcRoot, const KisNode *dstRoot, const KisNode *srcTarget) { if (srcRoot == srcTarget) return dstRoot; KisSafeReadNodeList::const_iterator srcIter = srcRoot->m_d->nodes.constBegin(); KisSafeReadNodeList::const_iterator dstIter = dstRoot->m_d->nodes.constBegin(); for (; srcIter != srcRoot->m_d->nodes.constEnd(); srcIter++, dstIter++) { KIS_ASSERT_RECOVER_RETURN_VALUE((srcIter != srcRoot->m_d->nodes.constEnd()) == (dstIter != dstRoot->m_d->nodes.constEnd()), 0); const KisNode *node = findSymmetricClone(srcIter->data(), dstIter->data(), srcTarget); if (node) return node; } return 0; } /** * This function walks through a subtrees of old and new layers and * searches for clone layers. For each clone layer it checks whether * its copyFrom() lays inside the old subtree, and if it is so resets * it to the corresponding layer in the new subtree. * * That is needed when the user duplicates a group layer with all its * layer subtree. In such a case all the "internal" clones must stay * "internal" and not point to the layers of the older group. */ void KisNode::Private::processDuplicatedClones(const KisNode *srcDuplicationRoot, const KisNode *dstDuplicationRoot, KisNode *node) { if (KisCloneLayer *clone = dynamic_cast(node)) { KIS_ASSERT_RECOVER_RETURN(clone->copyFrom()); const KisNode *newCopyFrom = findSymmetricClone(srcDuplicationRoot, dstDuplicationRoot, clone->copyFrom()); if (newCopyFrom) { KisLayer *newCopyFromLayer = dynamic_cast(const_cast(newCopyFrom)); KIS_ASSERT_RECOVER_RETURN(newCopyFromLayer); clone->setCopyFrom(newCopyFromLayer); } } KisSafeReadNodeList::const_iterator iter; FOREACH_SAFE(iter, node->m_d->nodes) { KisNode *child = const_cast((*iter).data()); processDuplicatedClones(srcDuplicationRoot, dstDuplicationRoot, child); } } KisNode::KisNode() : m_d(new Private(this)) { m_d->parent = 0; m_d->graphListener = 0; moveToThread(qApp->thread()); } KisNode::KisNode(const KisNode & rhs) : KisBaseNode(rhs) , m_d(new Private(this)) { m_d->parent = 0; m_d->graphListener = 0; moveToThread(qApp->thread()); // NOTE: the nodes are not supposed to be added/removed while // creation of another node, so we do *no* locking here! KisSafeReadNodeList::const_iterator iter; FOREACH_SAFE(iter, rhs.m_d->nodes) { KisNodeSP child = (*iter)->clone(); child->createNodeProgressProxy(); m_d->nodes.append(child); child->setParent(this); } m_d->processDuplicatedClones(&rhs, this, this); } KisNode::~KisNode() { if (m_d->busyProgressIndicator) { - m_d->busyProgressIndicator->endUpdatesBeforeDestroying(); + m_d->busyProgressIndicator->prepareDestroying(); m_d->busyProgressIndicator->deleteLater(); } if (m_d->nodeProgressProxy) { + m_d->nodeProgressProxy->prepareDestroying(); m_d->nodeProgressProxy->deleteLater(); } { QWriteLocker l(&m_d->nodeSubgraphLock); m_d->nodes.clear(); } delete m_d; } QRect KisNode::needRect(const QRect &rect, PositionToFilthy pos) const { Q_UNUSED(pos); return rect; } QRect KisNode::changeRect(const QRect &rect, PositionToFilthy pos) const { Q_UNUSED(pos); return rect; } QRect KisNode::accessRect(const QRect &rect, PositionToFilthy pos) const { Q_UNUSED(pos); return rect; } KisAbstractProjectionPlaneSP KisNode::projectionPlane() const { KIS_ASSERT_RECOVER_NOOP(0 && "KisNode::projectionPlane() is not defined!"); static KisAbstractProjectionPlaneSP plane = toQShared(new KisDumbProjectionPlane()); return plane; } KisProjectionLeafSP KisNode::projectionLeaf() const { return m_d->projectionLeaf; } bool KisNode::accept(KisNodeVisitor &v) { return v.visit(this); } void KisNode::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) { return visitor.visit(this, undoAdapter); } int KisNode::graphSequenceNumber() const { return m_d->graphListener ? m_d->graphListener->graphSequenceNumber() : -1; } KisNodeGraphListener *KisNode::graphListener() const { return m_d->graphListener; } void KisNode::setGraphListener(KisNodeGraphListener *graphListener) { m_d->graphListener = graphListener; QReadLocker l(&m_d->nodeSubgraphLock); KisSafeReadNodeList::const_iterator iter; FOREACH_SAFE(iter, m_d->nodes) { KisNodeSP child = (*iter); child->setGraphListener(graphListener); } } void KisNode::setParent(KisNodeWSP parent) { QWriteLocker l(&m_d->nodeSubgraphLock); m_d->parent = parent; } KisNodeSP KisNode::parent() const { QReadLocker l(&m_d->nodeSubgraphLock); return m_d->parent.isValid() ? KisNodeSP(m_d->parent) : 0; } KisBaseNodeSP KisNode::parentCallback() const { return parent(); } void KisNode::notifyParentVisibilityChanged(bool value) { QReadLocker l(&m_d->nodeSubgraphLock); KisSafeReadNodeList::const_iterator iter; FOREACH_SAFE(iter, m_d->nodes) { KisNodeSP child = (*iter); child->notifyParentVisibilityChanged(value); } } void KisNode::baseNodeChangedCallback() { if(m_d->graphListener) { m_d->graphListener->nodeChanged(this); } } void KisNode::baseNodeInvalidateAllFramesCallback() { if(m_d->graphListener) { m_d->graphListener->invalidateAllFrames(); } } void KisNode::addKeyframeChannel(KisKeyframeChannel *channel) { channel->setNode(this); KisBaseNode::addKeyframeChannel(channel); } KisNodeSP KisNode::firstChild() const { QReadLocker l(&m_d->nodeSubgraphLock); return !m_d->nodes.isEmpty() ? m_d->nodes.first() : 0; } KisNodeSP KisNode::lastChild() const { QReadLocker l(&m_d->nodeSubgraphLock); return !m_d->nodes.isEmpty() ? m_d->nodes.last() : 0; } KisNodeSP KisNode::prevChildImpl(KisNodeSP child) { /** * Warning: mind locking policy! * * The graph locks must be *always* taken in descending * order. That is if you want to (or it implicitly happens that * you) take a lock of a parent and a chil, you must first take * the lock of a parent, and only after that ask a child to do the * same. Otherwise you'll get a deadlock. */ QReadLocker l(&m_d->nodeSubgraphLock); int i = m_d->nodes.indexOf(child) - 1; return i >= 0 ? m_d->nodes.at(i) : 0; } KisNodeSP KisNode::nextChildImpl(KisNodeSP child) { /** * See a comment in KisNode::prevChildImpl() */ QReadLocker l(&m_d->nodeSubgraphLock); int i = m_d->nodes.indexOf(child) + 1; return i > 0 && i < m_d->nodes.size() ? m_d->nodes.at(i) : 0; } KisNodeSP KisNode::prevSibling() const { KisNodeSP parentNode = parent(); return parentNode ? parentNode->prevChildImpl(const_cast(this)) : 0; } KisNodeSP KisNode::nextSibling() const { KisNodeSP parentNode = parent(); return parentNode ? parentNode->nextChildImpl(const_cast(this)) : 0; } quint32 KisNode::childCount() const { QReadLocker l(&m_d->nodeSubgraphLock); return m_d->nodes.size(); } KisNodeSP KisNode::at(quint32 index) const { QReadLocker l(&m_d->nodeSubgraphLock); if (!m_d->nodes.isEmpty() && index < (quint32)m_d->nodes.size()) { return m_d->nodes.at(index); } return 0; } int KisNode::index(const KisNodeSP node) const { QReadLocker l(&m_d->nodeSubgraphLock); return m_d->nodes.indexOf(node); } QList KisNode::childNodes(const QStringList & nodeTypes, const KoProperties & properties) const { QReadLocker l(&m_d->nodeSubgraphLock); QList nodes; KisSafeReadNodeList::const_iterator iter; FOREACH_SAFE(iter, m_d->nodes) { if (*iter) { if (properties.isEmpty() || (*iter)->check(properties)) { bool rightType = true; if(!nodeTypes.isEmpty()) { rightType = false; Q_FOREACH (const QString &nodeType, nodeTypes) { if ((*iter)->inherits(nodeType.toLatin1())) { rightType = true; break; } } } if (rightType) { nodes.append(*iter); } } } } return nodes; } KisNodeSP KisNode::findChildByName(const QString &name) { KisNodeSP child = firstChild(); while (child) { if (child->name() == name) { return child; } if (child->childCount() > 0) { KisNodeSP grandChild = child->findChildByName(name); if (grandChild) { return grandChild; } } child = child->nextSibling(); } return 0; } bool KisNode::add(KisNodeSP newNode, KisNodeSP aboveThis) { Q_ASSERT(newNode); if (!newNode) return false; if (aboveThis && aboveThis->parent().data() != this) return false; if (!allowAsChild(newNode)) return false; if (newNode->parent()) return false; if (index(newNode) >= 0) return false; int idx = aboveThis ? this->index(aboveThis) + 1 : 0; // threoretical race condition may happen here ('idx' may become // deprecated until the write lock will be held). But we ignore // it, because it is not supported to add/remove nodes from two // concurrent threads simultaneously if (m_d->graphListener) { m_d->graphListener->aboutToAddANode(this, idx); } { QWriteLocker l(&m_d->nodeSubgraphLock); newNode->createNodeProgressProxy(); m_d->nodes.insert(idx, newNode); newNode->setParent(this); newNode->setGraphListener(m_d->graphListener); } if (m_d->graphListener) { m_d->graphListener->nodeHasBeenAdded(this, idx); } return true; } bool KisNode::remove(quint32 index) { if (index < childCount()) { KisNodeSP removedNode = at(index); if (m_d->graphListener) { m_d->graphListener->aboutToRemoveANode(this, index); } { QWriteLocker l(&m_d->nodeSubgraphLock); removedNode->setGraphListener(0); removedNode->setParent(0); // after calling aboutToRemoveANode or then the model get broken according to TT's modeltest m_d->nodes.removeAt(index); } if (m_d->graphListener) { m_d->graphListener->nodeHasBeenRemoved(this, index); } return true; } return false; } bool KisNode::remove(KisNodeSP node) { return node->parent().data() == this ? remove(index(node)) : false; } KisNodeProgressProxy* KisNode::nodeProgressProxy() const { if (m_d->nodeProgressProxy) { return m_d->nodeProgressProxy; } else if (parent()) { return parent()->nodeProgressProxy(); } return 0; } KisBusyProgressIndicator* KisNode::busyProgressIndicator() const { if (m_d->busyProgressIndicator) { return m_d->busyProgressIndicator; } else if (parent()) { return parent()->busyProgressIndicator(); } return 0; } void KisNode::createNodeProgressProxy() { if (!m_d->nodeProgressProxy) { m_d->nodeProgressProxy = new KisNodeProgressProxy(this); m_d->busyProgressIndicator = new KisBusyProgressIndicator(m_d->nodeProgressProxy); m_d->nodeProgressProxy->moveToThread(this->thread()); m_d->busyProgressIndicator->moveToThread(this->thread()); } } void KisNode::setDirty() { setDirty(extent()); } void KisNode::setDirty(const QVector &rects) { Q_FOREACH (const QRect &rc, rects) { setDirty(rc); } } void KisNode::setDirty(const QRegion ®ion) { setDirty(region.rects()); } void KisNode::setDirty(const QRect & rect) { if(m_d->graphListener) { m_d->graphListener->requestProjectionUpdate(this, rect); } } void KisNode::invalidateFrames(const KisTimeRange &range, const QRect &rect) { if(m_d->graphListener) { m_d->graphListener->invalidateFrames(range, rect); } } void KisNode::requestTimeSwitch(int time) { if(m_d->graphListener) { m_d->graphListener->requestTimeSwitch(time); } } void KisNode::syncLodCache() { // noop. everything is done by getLodCapableDevices() } KisPaintDeviceList KisNode::getLodCapableDevices() const { KisPaintDeviceList list; KisPaintDeviceSP device = paintDevice(); if (device) { list << device; } KisPaintDeviceSP originalDevice = original(); if (originalDevice && originalDevice != device) { list << originalDevice; } list << projectionPlane()->getLodCapableDevices(); return list; } diff --git a/libs/image/kis_node_progress_proxy.cpp b/libs/image/kis_node_progress_proxy.cpp index d6ef64db75..f5efc554ee 100644 --- a/libs/image/kis_node_progress_proxy.cpp +++ b/libs/image/kis_node_progress_proxy.cpp @@ -1,100 +1,101 @@ /* * Copyright (c) 2008 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_node_progress_proxy.h" #include #include "kis_node.h" struct Q_DECL_HIDDEN KisNodeProgressProxy::Private { Private() : minimum(0) , maximum(100) , value(100) , percentage(-1) { } KisNodeWSP node; int minimum; int maximum; int value; int percentage; bool computePercentage() { int old_percentage = percentage; if (value == maximum) { percentage = -1; } else if (minimum == maximum && minimum == 0) { percentage = 0; } else { percentage = (100 * (value - minimum)) / (maximum - minimum); percentage = qBound(0, percentage, 100); } return old_percentage != percentage; } }; KisNodeProgressProxy::KisNodeProgressProxy(KisNode* _node) : d(new Private) { d->node = _node; } KisNodeProgressProxy::~KisNodeProgressProxy() { delete d; } -const KisNodeSP KisNodeProgressProxy::node() const +void KisNodeProgressProxy::prepareDestroying() { - return d->node; + d->node = 0; } + int KisNodeProgressProxy::maximum() const { return d->maximum; } int KisNodeProgressProxy::percentage() const { return d->percentage; } void KisNodeProgressProxy::setValue(int _value) { d->value = _value; if (d->node && d->computePercentage()) { emit(percentageChanged(d->percentage, d->node)); } } void KisNodeProgressProxy::setRange(int _minimum, int _maximum) { d->minimum = _minimum; d->maximum = _maximum; if (d->node && d->computePercentage()) { emit(percentageChanged(d->percentage, d->node)); } } void KisNodeProgressProxy::setFormat(const QString & _format) { Q_UNUSED(_format); } diff --git a/libs/image/kis_node_progress_proxy.h b/libs/image/kis_node_progress_proxy.h index 21051903c7..852769481b 100644 --- a/libs/image/kis_node_progress_proxy.h +++ b/libs/image/kis_node_progress_proxy.h @@ -1,69 +1,73 @@ /* * Copyright (c) 2008 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_NODE_PROGRESS_PROXY_H_ #define _KIS_NODE_PROGRESS_PROXY_H_ #include #include #include #include "kritaimage_export.h" /** * This class implements \ref KoProgressProxy and allows node to report progress. */ class KRITAIMAGE_EXPORT KisNodeProgressProxy : public QObject, public KoProgressProxy { Q_OBJECT friend class KisNode; /** * Create a proxy to report progress when processing, this proxy is associated * with a node, it will report progress in the node progress bar. This proxy * will be deleted when @p _node is deleted. */ - KisNodeProgressProxy(KisNode* _node); + explicit KisNodeProgressProxy(KisNode* _node); ~KisNodeProgressProxy(); public: - /** - * @return the node associated with this proxy. - */ - const KisNodeSP node() const; virtual int maximum() const ; virtual void setValue(int value); virtual void setRange(int minimum, int maximum); virtual void setFormat(const QString & format); /** * @return the current percentage (return -1 if no progress) */ int percentage() const; Q_SIGNALS: /** * Emitted when the percentage of the proxy is changed. * @param _percentage is the progress value in percent * @param _node is the node that own this \ref KisNodeProgressProxy */ void percentageChanged(int _percentage, const KisNodeSP& _node); + +private: + /** + * To be called when the node is and will be no longer available + * and this object is going to be deleted as well. + */ + void prepareDestroying(); + private: struct Private; Private* const d; }; #endif diff --git a/libs/image/kis_onion_skin_cache.cpp b/libs/image/kis_onion_skin_cache.cpp index 69b2af6f2c..5f760d2031 100644 --- a/libs/image/kis_onion_skin_cache.cpp +++ b/libs/image/kis_onion_skin_cache.cpp @@ -1,133 +1,132 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_onion_skin_cache.h" #include #include #include #include "kis_paint_device.h" #include "kis_onion_skin_compositor.h" #include "kis_default_bounds.h" #include "kis_image.h" #include "kis_raster_keyframe_channel.h" struct KisOnionSkinCache::Private { KisPaintDeviceSP cachedProjection; int cacheTime = 0; int cacheConfigSeqNo = 0; int framesHash = 0; QReadWriteLock lock; bool checkCacheValid(KisPaintDeviceSP source, KisOnionSkinCompositor *compositor) { const KisRasterKeyframeChannel *keyframes = source->keyframeChannel(); const int time = source->defaultBounds()->currentTime(); const int seqNo = compositor->configSeqNo(); const int hash = keyframes->framesHash(); return time == cacheTime && cacheConfigSeqNo == seqNo && framesHash == hash; } void updateCacheMetrics(KisPaintDeviceSP source, KisOnionSkinCompositor *compositor) { const KisRasterKeyframeChannel *keyframes = source->keyframeChannel(); const int time = source->defaultBounds()->currentTime(); const int seqNo = compositor->configSeqNo(); const int hash = keyframes->framesHash(); cacheTime = time; cacheConfigSeqNo = seqNo; framesHash = hash; } }; KisOnionSkinCache::KisOnionSkinCache() : m_d(new Private) { } KisOnionSkinCache::~KisOnionSkinCache() { } KisPaintDeviceSP KisOnionSkinCache::projection(KisPaintDeviceSP source) { KisOnionSkinCompositor *compositor = KisOnionSkinCompositor::instance(); KisPaintDeviceSP cachedProjection; QReadLocker readLocker(&m_d->lock); cachedProjection = m_d->cachedProjection; if (!cachedProjection || !m_d->checkCacheValid(source, compositor)) { readLocker.unlock(); QWriteLocker writeLocker(&m_d->lock); cachedProjection = m_d->cachedProjection; if (!cachedProjection || !m_d->checkCacheValid(source, compositor)) { if (!cachedProjection) { cachedProjection = new KisPaintDevice(source->colorSpace()); } else { + cachedProjection->setDefaultBounds(new KisDefaultBounds()); cachedProjection->clear(); } - cachedProjection->setDefaultBounds(new KisDefaultBounds()); - const QRect extent = compositor->calculateExtent(source); compositor->composite(source, cachedProjection, extent); cachedProjection->setDefaultBounds(source->defaultBounds()); /** * It might happen that the lod planes has already been * generated for all the devices, so we should cold-init them * for the onion skins. */ const int lod = source->defaultBounds()->currentLevelOfDetail(); if (lod > 0) { KisPaintDevice::LodDataStruct *data = cachedProjection->createLodDataStruct(lod); cachedProjection->updateLodDataStruct(data, extent); cachedProjection->uploadLodDataStruct(data); } m_d->updateCacheMetrics(source, compositor); m_d->cachedProjection = cachedProjection; } } return cachedProjection; } void KisOnionSkinCache::reset() { QWriteLocker writeLocker(&m_d->lock); m_d->cachedProjection = 0; } KisPaintDeviceSP KisOnionSkinCache::lodCapableDevice() const { return m_d->cachedProjection; } diff --git a/libs/image/kis_paint_device.cc b/libs/image/kis_paint_device.cc index 8b3e4355dc..68b0816c58 100644 --- a/libs/image/kis_paint_device.cc +++ b/libs/image/kis_paint_device.cc @@ -1,1892 +1,1897 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_paint_device.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_image.h" #include "kis_random_sub_accessor.h" #include "kis_selection.h" #include "kis_node.h" #include "kis_datamanager.h" #include "kis_paint_device_writer.h" #include "kis_selection_component.h" #include "kis_pixel_selection.h" #include "kis_repeat_iterators_pixel.h" #include "kis_fixed_paint_device.h" #include "tiles3/kis_hline_iterator.h" #include "tiles3/kis_vline_iterator.h" #include "tiles3/kis_random_accessor.h" #include "kis_default_bounds.h" #include "kis_lod_transform.h" #include "kis_raster_keyframe_channel.h" #include "kis_paint_device_cache.h" #include "kis_paint_device_data.h" #include "kis_paint_device_frames_interface.h" struct KisPaintDevice::Private { /** * Used when the paint device is loading to ensure no lod/animation * interferes the process. */ static const KisDefaultBoundsSP transitionalDefaultBounds; public: class KisPaintDeviceStrategy; class KisPaintDeviceWrappedStrategy; Private(KisPaintDevice *paintDevice); ~Private(); KisPaintDevice *q; KisNodeWSP parent; QScopedPointer contentChannel; KisDefaultBoundsBaseSP defaultBounds; QScopedPointer basicStrategy; QScopedPointer wrappedStrategy; QScopedPointer framesInterface; bool isProjectionDevice; KisPaintDeviceStrategy* currentStrategy(); void init(const KoColorSpace *cs, const quint8 *defaultPixel); KUndo2Command* convertColorSpace(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags); bool assignProfile(const KoColorProfile * profile); inline const KoColorSpace* colorSpace() const { return currentData()->colorSpace(); } inline KisDataManagerSP dataManager() const { return currentData()->dataManager(); } inline qint32 x() const {return currentData()->x();} inline qint32 y() const {return currentData()->y();} inline void setX(qint32 x) { currentData()->setX(x); } inline void setY(qint32 y) { currentData()->setY(y); } inline KisPaintDeviceCache* cache() { return currentData()->cache(); } void cloneAllDataObjects(Private *rhs, bool copyFrames) { m_lodData.reset(); m_externalFrameData.reset(); if (!m_frames.isEmpty()) { m_frames.clear(); } if (!copyFrames) { if (m_data) { m_data->prepareClone(rhs->currentNonLodData(), true); } else { m_data = toQShared(new KisPaintDeviceData(rhs->currentNonLodData(), true)); } } else { if (m_data && !rhs->m_data) { m_data.clear(); } else if (!m_data && rhs->m_data) { m_data = toQShared(new KisPaintDeviceData(rhs->m_data.data(), true)); } else if (m_data && rhs->m_data) { m_data->prepareClone(rhs->m_data.data(), true); } if (!rhs->m_frames.isEmpty()) { FramesHash::const_iterator it = rhs->m_frames.constBegin(); FramesHash::const_iterator end = rhs->m_frames.constEnd(); for (; it != end; ++it) { DataSP data = toQShared(new KisPaintDeviceData(it.value().data(), true)); m_frames.insert(it.key(), data); } } } if (rhs->m_lodData) { m_lodData.reset(new KisPaintDeviceData(rhs->m_lodData.data(), true)); } } void prepareClone(KisPaintDeviceSP src) { prepareCloneImpl(src, src->m_d->currentData()); Q_ASSERT(fastBitBltPossible(src)); } bool fastBitBltPossible(KisPaintDeviceSP src) { return fastBitBltPossibleImpl(src->m_d->currentData()); } int currentFrameId() const { KIS_ASSERT_RECOVER(contentChannel) { return -1; } return !defaultBounds->currentLevelOfDetail() ? contentChannel->frameIdAt(defaultBounds->currentTime()) : -1; } KisDataManagerSP frameDataManager(int frameId) const { DataSP data = m_frames[frameId]; return data->dataManager(); } void invalidateFrameCache(int frameId) { DataSP data = m_frames[frameId]; return data->cache()->invalidate(); } private: typedef KisPaintDeviceData Data; typedef QSharedPointer DataSP; typedef QHash FramesHash; class FrameInsertionCommand : public KUndo2Command { public: FrameInsertionCommand(FramesHash *hash, DataSP data, int frameId, bool insert, KUndo2Command *parentCommand) : KUndo2Command(parentCommand), m_hash(hash), m_data(data), m_frameId(frameId), m_insert(insert) { } void redo() { doSwap(m_insert); } void undo() { doSwap(!m_insert); } private: void doSwap(bool insert) { if (insert) { m_hash->insert(m_frameId, m_data); } else { DataSP deletedData = m_hash->take(m_frameId); } } private: FramesHash *m_hash; DataSP m_data; int m_frameId; bool m_insert; }; public: int createFrame(bool copy, int copySrc, const QPoint &offset, KUndo2Command *parentCommand) { KIS_ASSERT_RECOVER(parentCommand) { return -1; } DataSP data; bool initialFrame = false; if (m_frames.isEmpty()) { /** * Here we move the contents of the paint device to the * new frame and clear m_data to make the "background" for * the areas where there is no frame at all. */ data = toQShared(new Data(m_data.data(), true)); m_data->dataManager()->clear(); m_data->cache()->invalidate(); initialFrame = true; } else if (copy) { DataSP srcData = m_frames[copySrc]; data = toQShared(new Data(srcData.data(), true)); } else { DataSP srcData = m_frames.begin().value(); data = toQShared(new Data(srcData.data(), false)); } if (!initialFrame && !copy) { data->setX(offset.x()); data->setY(offset.y()); } int frameId = nextFreeFrameId++; KUndo2Command *cmd = new FrameInsertionCommand(&m_frames, data, frameId, true, parentCommand); cmd->redo(); return frameId; } void deleteFrame(int frame, KUndo2Command *parentCommand) { KIS_ASSERT_RECOVER_RETURN(m_frames.contains(frame)); KIS_ASSERT_RECOVER_RETURN(parentCommand); DataSP deletedData = m_frames[frame]; KUndo2Command *cmd = new FrameInsertionCommand(&m_frames, deletedData, frame, false, parentCommand); cmd->redo(); } QRect frameBounds(int frameId) { DataSP data = m_frames[frameId]; QRect extent = data->dataManager()->extent(); extent.translate(data->x(), data->y()); return extent; } QPoint frameOffset(int frameId) const { DataSP data = m_frames[frameId]; return QPoint(data->x(), data->y()); } void setFrameOffset(int frameId, const QPoint &offset) { DataSP data = m_frames[frameId]; data->setX(offset.x()); data->setY(offset.y()); } const QList frameIds() const { return m_frames.keys(); } bool readFrame(QIODevice *stream, int frameId) { bool retval = false; DataSP data = m_frames[frameId]; retval = data->dataManager()->read(stream); data->cache()->invalidate(); return retval; } bool writeFrame(KisPaintDeviceWriter &store, int frameId) { DataSP data = m_frames[frameId]; return data->dataManager()->write(store); } void setFrameDefaultPixel(const quint8 *defPixel, int frameId) { DataSP data = m_frames[frameId]; data->dataManager()->setDefaultPixel(defPixel); } const quint8* frameDefaultPixel(int frameId) const { DataSP data = m_frames[frameId]; return data->dataManager()->defaultPixel(); } void fetchFrame(int frameId, KisPaintDeviceSP targetDevice); void uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice); void uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice); void uploadFrameData(DataSP srcData, DataSP dstData); - struct LodDataStruct; + struct LodDataStructImpl; LodDataStruct* createLodDataStruct(int lod); void updateLodDataStruct(LodDataStruct *dst, const QRect &srcRect); void uploadLodDataStruct(LodDataStruct *dst); QRegion regionForLodSyncing() const; void tesingFetchLodDevice(KisPaintDeviceSP targetDevice); private: QRegion syncWholeDevice(Data *srcData); inline DataSP currentFrameData() const { DataSP data; const int numberOfFrames = contentChannel->keyframeCount(); if (numberOfFrames > 1) { int frameId = contentChannel->frameIdAt(defaultBounds->currentTime()); KIS_ASSERT_RECOVER(m_frames.contains(frameId)) { return m_frames.begin().value(); } data = m_frames[frameId]; } else if (numberOfFrames == 1) { data = m_frames.begin().value(); } else { data = m_data; } return data; } inline Data* currentNonLodData() const { Data *data = m_data.data(); if (contentChannel) { data = currentFrameData().data(); } else if (isProjectionDevice && defaultBounds->externalFrameActive()) { if (!m_externalFrameData) { QMutexLocker l(&m_dataSwitchLock); if (!m_externalFrameData) { m_externalFrameData.reset(new Data(m_data.data(), false)); } } data = m_externalFrameData.data(); } return data; } - inline void ensureLodDataPreset() const { + inline void ensureLodDataPresent() const { if (!m_lodData) { Data *srcData = currentNonLodData(); QMutexLocker l(&m_dataSwitchLock); if (!m_lodData) { m_lodData.reset(new Data(srcData, false)); } } } inline Data* currentData() const { Data *data = m_data.data(); if (defaultBounds->currentLevelOfDetail()) { - ensureLodDataPreset(); + ensureLodDataPresent(); data = m_lodData.data(); } else { data = currentNonLodData(); } return data; } void prepareCloneImpl(KisPaintDeviceSP src, Data *srcData) { currentData()->prepareClone(srcData); q->setDefaultPixel(srcData->dataManager()->defaultPixel()); q->setDefaultBounds(src->defaultBounds()); } bool fastBitBltPossibleImpl(Data *srcData) { return x() == srcData->x() && y() == srcData->y() && *colorSpace() == *srcData->colorSpace(); } QList allDataObjects() const { QList dataObjects; if (m_frames.isEmpty()) { dataObjects << m_data.data(); } dataObjects << m_lodData.data(); dataObjects << m_externalFrameData.data(); Q_FOREACH (DataSP value, m_frames.values()) { dataObjects << value.data(); } return dataObjects; } void transferFromData(Data *data, KisPaintDeviceSP targetDevice); struct Q_DECL_HIDDEN StrategyPolicy; typedef KisSequentialIteratorBase, StrategyPolicy> InternalSequentialConstIterator; typedef KisSequentialIteratorBase, StrategyPolicy> InternalSequentialIterator; private: friend class KisPaintDeviceFramesInterface; private: DataSP m_data; mutable QScopedPointer m_lodData; mutable QScopedPointer m_externalFrameData; mutable QMutex m_dataSwitchLock; FramesHash m_frames; int nextFreeFrameId; }; const KisDefaultBoundsSP KisPaintDevice::Private::transitionalDefaultBounds = new KisDefaultBounds(); #include "kis_paint_device_strategies.h" KisPaintDevice::Private::Private(KisPaintDevice *paintDevice) : q(paintDevice), basicStrategy(new KisPaintDeviceStrategy(paintDevice, this)), isProjectionDevice(false), m_data(new Data(paintDevice)), nextFreeFrameId(0) { } KisPaintDevice::Private::~Private() { m_frames.clear(); } KisPaintDevice::Private::KisPaintDeviceStrategy* KisPaintDevice::Private::currentStrategy() { if (!defaultBounds->wrapAroundMode()) { return basicStrategy.data(); } QRect wrapRect = defaultBounds->bounds(); if (!wrappedStrategy || wrappedStrategy->wrapRect() != wrapRect) { wrappedStrategy.reset(new KisPaintDeviceWrappedStrategy(wrapRect, q, this)); } return wrappedStrategy.data(); } struct KisPaintDevice::Private::StrategyPolicy { StrategyPolicy(KisPaintDevice::Private::KisPaintDeviceStrategy *strategy, KisDataManager *dataManager, qint32 offsetX, qint32 offsetY) : m_strategy(strategy), m_dataManager(dataManager), m_offsetX(offsetX), m_offsetY(offsetY) { } KisHLineConstIteratorSP createConstIterator(const QRect &rect) { return m_strategy->createHLineConstIteratorNG(m_dataManager, rect.x(), rect.y(), rect.width(), m_offsetX, m_offsetY); } KisHLineIteratorSP createIterator(const QRect &rect) { return m_strategy->createHLineIteratorNG(m_dataManager, rect.x(), rect.y(), rect.width(), m_offsetX, m_offsetY); } int pixelSize() const { return m_dataManager->pixelSize(); } KisPaintDeviceStrategy *m_strategy; KisDataManager *m_dataManager; int m_offsetX; int m_offsetY; }; -struct KisPaintDevice::Private::LodDataStruct +struct KisPaintDevice::Private::LodDataStructImpl : public KisPaintDevice::LodDataStruct { - LodDataStruct(Data *_lodData) : lodData(_lodData) {} - Data *lodData; + LodDataStructImpl(Data *_lodData) : lodData(_lodData) {} + QScopedPointer lodData; }; QRegion KisPaintDevice::Private::regionForLodSyncing() const { Data *srcData = currentNonLodData(); return srcData->dataManager()->region().translated(srcData->x(), srcData->y()); } -KisPaintDevice::Private::LodDataStruct* KisPaintDevice::Private::createLodDataStruct(int newLod) +KisPaintDevice::LodDataStruct* KisPaintDevice::Private::createLodDataStruct(int newLod) { Data *srcData = currentNonLodData(); Data *lodData = new Data(srcData, false); - LodDataStruct *lodStruct = new LodDataStruct(lodData); + LodDataStruct *lodStruct = new LodDataStructImpl(lodData); int expectedX = KisLodTransform::coordToLodCoord(srcData->x(), newLod); int expectedY = KisLodTransform::coordToLodCoord(srcData->y(), newLod); /** * We compare color spaces as pure pointers, because they must be * exactly the same, since they come from the common source. */ if (lodData->levelOfDetail() != newLod || lodData->colorSpace() != srcData->colorSpace() || lodData->x() != expectedX || lodData->y() != expectedY) { lodData->prepareClone(srcData); lodData->setLevelOfDetail(newLod); lodData->setX(expectedX); lodData->setY(expectedY); // FIXME: different kind of synchronization } //QRegion dirtyRegion = syncWholeDevice(srcData); lodData->cache()->invalidate(); return lodStruct; } -void KisPaintDevice::Private::updateLodDataStruct(LodDataStruct *dst, const QRect &originalRect) +void KisPaintDevice::Private::updateLodDataStruct(LodDataStruct *_dst, const QRect &originalRect) { - Data *lodData = dst->lodData; + LodDataStructImpl *dst = dynamic_cast(_dst); + KIS_SAFE_ASSERT_RECOVER_RETURN(dst); + + Data *lodData = dst->lodData.data(); Data *srcData = currentNonLodData(); const int lod = lodData->levelOfDetail(); const int srcStepSize = 1 << lod; const QRect srcRect = KisLodTransform::alignedRect(originalRect, lod); const QRect dstRect = KisLodTransform::scaledRect(srcRect, lod); if (!srcRect.isValid() || !dstRect.isValid()) return; KIS_ASSERT_RECOVER_NOOP(srcRect.width() / srcStepSize == dstRect.width()); const int pixelSize = srcData->dataManager()->pixelSize(); int rowsAccumulated = 0; int columnsAccumulated = 0; KoMixColorsOp *mixOp = colorSpace()->mixColorsOp(); QScopedArrayPointer blendData(new quint8[srcStepSize * srcRect.width() * pixelSize]); quint8 *blendDataPtr = blendData.data(); int blendDataOffset = 0; const int srcCellSize = srcStepSize * srcStepSize; const int srcCellStride = srcCellSize * pixelSize; const int srcStepStride = srcStepSize * pixelSize; const int srcColumnStride = (srcStepSize - 1) * srcStepStride; QScopedArrayPointer weights(new qint16[srcCellSize]); { const qint16 averageWeight = qCeil(255.0 / srcCellSize); const qint16 extraWeight = averageWeight * srcCellSize - 255; KIS_ASSERT_RECOVER_NOOP(extraWeight == 1); for (int i = 0; i < srcCellSize - 1; i++) { weights[i] = averageWeight; } weights[srcCellSize - 1] = averageWeight - extraWeight; } InternalSequentialConstIterator srcIntIt(StrategyPolicy(currentStrategy(), srcData->dataManager().data(), srcData->x(), srcData->y()), srcRect); InternalSequentialIterator dstIntIt(StrategyPolicy(currentStrategy(), lodData->dataManager().data(), lodData->x(), lodData->y()), dstRect); int rowsRemaining = srcRect.height(); while (rowsRemaining > 0) { int colsRemaining = srcRect.width(); while (colsRemaining > 0) { memcpy(blendDataPtr, srcIntIt.rawDataConst(), pixelSize); blendDataPtr += pixelSize; columnsAccumulated++; if (columnsAccumulated >= srcStepSize) { blendDataPtr += srcColumnStride; columnsAccumulated = 0; } srcIntIt.nextPixel(); colsRemaining--; } rowsAccumulated++; if (rowsAccumulated >= srcStepSize) { // blend and write the final data blendDataPtr = blendData.data(); for (int i = 0; i < dstRect.width(); i++) { mixOp->mixColors(blendDataPtr, weights.data(), srcCellSize, dstIntIt.rawData()); blendDataPtr += srcCellStride; dstIntIt.nextPixel(); } // reset counters rowsAccumulated = 0; blendDataPtr = blendData.data(); blendDataOffset = 0; } else { blendDataOffset += srcStepStride; blendDataPtr = blendData.data() + blendDataOffset; } rowsRemaining--; } } -void KisPaintDevice::Private::uploadLodDataStruct(LodDataStruct *dst) +void KisPaintDevice::Private::uploadLodDataStruct(LodDataStruct *_dst) { - KIS_ASSERT_RECOVER_RETURN( + LodDataStructImpl *dst = dynamic_cast(_dst); + KIS_SAFE_ASSERT_RECOVER_RETURN(dst); + + KIS_SAFE_ASSERT_RECOVER_RETURN( dst->lodData->levelOfDetail() == defaultBounds->currentLevelOfDetail()); - ensureLodDataPreset(); + ensureLodDataPresent(); - m_lodData->prepareClone(dst->lodData); + m_lodData->prepareClone(dst->lodData.data()); m_lodData->dataManager()->bitBltRough(dst->lodData->dataManager(), dst->lodData->dataManager()->extent()); } void KisPaintDevice::Private::transferFromData(Data *data, KisPaintDeviceSP targetDevice) { QRect extent = data->dataManager()->extent(); extent.translate(data->x(), data->y()); targetDevice->m_d->prepareCloneImpl(q, data); targetDevice->m_d->currentStrategy()->fastBitBltRough(data->dataManager(), extent); } void KisPaintDevice::Private::fetchFrame(int frameId, KisPaintDeviceSP targetDevice) { DataSP data = m_frames[frameId]; transferFromData(data.data(), targetDevice); } void KisPaintDevice::Private::uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice) { DataSP dstData = m_frames[dstFrameId]; KIS_ASSERT_RECOVER_RETURN(dstData); DataSP srcData = srcDevice->m_d->m_frames[srcFrameId]; KIS_ASSERT_RECOVER_RETURN(srcData); uploadFrameData(srcData, dstData); } void KisPaintDevice::Private::uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice) { DataSP dstData = m_frames[dstFrameId]; KIS_ASSERT_RECOVER_RETURN(dstData); DataSP srcData = srcDevice->m_d->m_data; KIS_ASSERT_RECOVER_RETURN(srcData); uploadFrameData(srcData, dstData); } void KisPaintDevice::Private::uploadFrameData(DataSP srcData, DataSP dstData) { if (srcData->colorSpace() != dstData->colorSpace() && !(*srcData->colorSpace() == *dstData->colorSpace())) { KUndo2Command tempCommand; srcData = toQShared(new Data(srcData.data(), true)); srcData->convertDataColorSpace(dstData->colorSpace(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags(), &tempCommand); } dstData->dataManager()->clear(); dstData->cache()->invalidate(); const QRect rect = srcData->dataManager()->extent(); dstData->dataManager()->bitBltRough(srcData->dataManager(), rect); } void KisPaintDevice::Private::tesingFetchLodDevice(KisPaintDeviceSP targetDevice) { Data *data = m_lodData.data(); Q_ASSERT(data); transferFromData(data, targetDevice); } KUndo2Command* KisPaintDevice::Private::convertColorSpace(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { class DeviceChangeColorSpaceCommand : public KUndo2Command { public: DeviceChangeColorSpaceCommand(KisPaintDeviceSP device) : m_firstRun(true), m_device(device) { } void emitNotifications() { m_device->emitColorSpaceChanged(); m_device->setDirty(); } void redo() { KUndo2Command::redo(); if (!m_firstRun) { m_firstRun = false; return; } emitNotifications(); } void undo() { KUndo2Command::undo(); emitNotifications(); } private: bool m_firstRun; KisPaintDeviceSP m_device; }; KUndo2Command *parentCommand = new DeviceChangeColorSpaceCommand(q); QList dataObjects = allDataObjects();; Q_FOREACH (Data *data, dataObjects) { if (!data) continue; data->convertDataColorSpace(dstColorSpace, renderingIntent, conversionFlags, parentCommand); } if (!parentCommand->childCount()) { delete parentCommand; parentCommand = 0; } else { q->emitColorSpaceChanged(); } return parentCommand; } bool KisPaintDevice::Private::assignProfile(const KoColorProfile * profile) { if (!profile) return false; const KoColorSpace *dstColorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile); if (!dstColorSpace) return false; QList dataObjects = allDataObjects();; Q_FOREACH (Data *data, dataObjects) { if (!data) continue; data->assignColorSpace(dstColorSpace); } q->emitProfileChanged(); // no undo information is provided here return true; } void KisPaintDevice::Private::init(const KoColorSpace *cs, const quint8 *defaultPixel) { QList dataObjects = allDataObjects();; Q_FOREACH (Data *data, dataObjects) { if (!data) continue; KisDataManagerSP dataManager = new KisDataManager(cs->pixelSize(), defaultPixel); data->init(cs, dataManager); } } KisPaintDevice::KisPaintDevice(const KoColorSpace * colorSpace, const QString& name) : QObject(0) , m_d(new Private(this)) { init(colorSpace, new KisDefaultBounds(), 0, name); } KisPaintDevice::KisPaintDevice(KisNodeWSP parent, const KoColorSpace * colorSpace, KisDefaultBoundsBaseSP defaultBounds, const QString& name) : QObject(0) , m_d(new Private(this)) { init(colorSpace, defaultBounds, parent, name); } void KisPaintDevice::init(const KoColorSpace *colorSpace, KisDefaultBoundsBaseSP defaultBounds, KisNodeWSP parent, const QString& name) { Q_ASSERT(colorSpace); setObjectName(name); // temporary def. bounds object for the initialization phase only m_d->defaultBounds = m_d->transitionalDefaultBounds; if (!defaultBounds) { // Reuse transitionalDefaultBounds here. Change if you change // semantics of transitionalDefaultBounds defaultBounds = m_d->transitionalDefaultBounds; } QScopedArrayPointer defaultPixel(new quint8[colorSpace->pixelSize()]); colorSpace->fromQColor(Qt::transparent, defaultPixel.data()); m_d->init(colorSpace, defaultPixel.data()); Q_ASSERT(m_d->colorSpace()); setDefaultBounds(defaultBounds); setParentNode(parent); } KisPaintDevice::KisPaintDevice(const KisPaintDevice& rhs, bool copyFrames, KisNode *newParentNode) : QObject() , KisShared() , m_d(new Private(this)) { if (this != &rhs) { // temporary def. bounds object for the initialization phase only m_d->defaultBounds = m_d->transitionalDefaultBounds; // copy data objects with or without frames m_d->cloneAllDataObjects(rhs.m_d, copyFrames); if (copyFrames) { KIS_ASSERT_RECOVER_RETURN(rhs.m_d->framesInterface); KIS_ASSERT_RECOVER_RETURN(rhs.m_d->contentChannel); m_d->framesInterface.reset(new KisPaintDeviceFramesInterface(this)); m_d->contentChannel.reset(new KisRasterKeyframeChannel(*rhs.m_d->contentChannel.data(), newParentNode, this)); } setDefaultBounds(rhs.m_d->defaultBounds); setParentNode(0); } } KisPaintDevice::~KisPaintDevice() { delete m_d; } void KisPaintDevice::setProjectionDevice(bool value) { m_d->isProjectionDevice = value; } void KisPaintDevice::prepareClone(KisPaintDeviceSP src) { m_d->prepareClone(src); Q_ASSERT(fastBitBltPossible(src)); } void KisPaintDevice::makeCloneFrom(KisPaintDeviceSP src, const QRect &rect) { prepareClone(src); // we guarantee that *this is totally empty, so copy pixels that // are areally present on the source image only const QRect optimizedRect = rect & src->extent(); fastBitBlt(src, optimizedRect); } void KisPaintDevice::makeCloneFromRough(KisPaintDeviceSP src, const QRect &minimalRect) { prepareClone(src); // we guarantee that *this is totally empty, so copy pixels that // are areally present on the source image only const QRect optimizedRect = minimalRect & src->extent(); fastBitBltRough(src, optimizedRect); } void KisPaintDevice::setDirty() { m_d->cache()->invalidate(); if (m_d->parent.isValid()) m_d->parent->setDirty(); } void KisPaintDevice::setDirty(const QRect & rc) { m_d->cache()->invalidate(); if (m_d->parent.isValid()) m_d->parent->setDirty(rc); } void KisPaintDevice::setDirty(const QRegion & region) { m_d->cache()->invalidate(); if (m_d->parent.isValid()) m_d->parent->setDirty(region); } void KisPaintDevice::setDirty(const QVector rects) { m_d->cache()->invalidate(); if (m_d->parent.isValid()) m_d->parent->setDirty(rects); } void KisPaintDevice::requestTimeSwitch(int time) { if (m_d->parent.isValid()) { m_d->parent->requestTimeSwitch(time); } } void KisPaintDevice::setParentNode(KisNodeWSP parent) { m_d->parent = parent; } // for testing purposes only KisNodeWSP KisPaintDevice::parentNode() const { return m_d->parent; } void KisPaintDevice::setDefaultBounds(KisDefaultBoundsBaseSP defaultBounds) { m_d->defaultBounds = defaultBounds; m_d->cache()->invalidate(); } KisDefaultBoundsBaseSP KisPaintDevice::defaultBounds() const { return m_d->defaultBounds; } void KisPaintDevice::move(const QPoint &pt) { m_d->currentStrategy()->move(pt); + m_d->cache()->invalidate(); } void KisPaintDevice::move(qint32 x, qint32 y) { move(QPoint(x, y)); } void KisPaintDevice::setX(qint32 x) { move(QPoint(x, m_d->y())); } void KisPaintDevice::setY(qint32 y) { move(QPoint(m_d->x(), y)); } qint32 KisPaintDevice::x() const { return m_d->x(); } qint32 KisPaintDevice::y() const { return m_d->y(); } void KisPaintDevice::extent(qint32 &x, qint32 &y, qint32 &w, qint32 &h) const { QRect rc = extent(); x = rc.x(); y = rc.y(); w = rc.width(); h = rc.height(); } QRect KisPaintDevice::extent() const { return m_d->currentStrategy()->extent(); } QRegion KisPaintDevice::region() const { return m_d->currentStrategy()->region(); } QRect KisPaintDevice::nonDefaultPixelArea() const { return m_d->cache()->nonDefaultPixelArea(); } QRect KisPaintDevice::exactBounds() const { return m_d->cache()->exactBounds(); } QRect KisPaintDevice::exactBoundsAmortized() const { return m_d->cache()->exactBoundsAmortized(); } namespace Impl { struct CheckFullyTransparent { CheckFullyTransparent(const KoColorSpace *colorSpace) : m_colorSpace(colorSpace) { } bool isPixelEmpty(const quint8 *pixelData) { return m_colorSpace->opacityU8(pixelData) == OPACITY_TRANSPARENT_U8; } private: const KoColorSpace *m_colorSpace; }; struct CheckNonDefault { CheckNonDefault(int pixelSize, const quint8 *defaultPixel) : m_pixelSize(pixelSize), m_defaultPixel(defaultPixel) { } bool isPixelEmpty(const quint8 *pixelData) { return memcmp(m_defaultPixel, pixelData, m_pixelSize) == 0; } private: int m_pixelSize; const quint8 *m_defaultPixel; }; template QRect calculateExactBoundsImpl(const KisPaintDevice *device, const QRect &startRect, const QRect &endRect, ComparePixelOp compareOp) { if (startRect == endRect) return startRect; // Solution n°2 int x, y, w, h; int boundLeft, boundTop, boundRight, boundBottom; int endDirN, endDirE, endDirS, endDirW; startRect.getRect(&x, &y, &w, &h); if (endRect.isEmpty()) { endDirS = startRect.bottom(); endDirN = startRect.top(); endDirE = startRect.right(); endDirW = startRect.left(); startRect.getCoords(&boundLeft, &boundTop, &boundRight, &boundBottom); } else { endDirS = endRect.top() - 1; endDirN = endRect.bottom() + 1; endDirE = endRect.left() - 1; endDirW = endRect.right() + 1; endRect.getCoords(&boundLeft, &boundTop, &boundRight, &boundBottom); } // XXX: a small optimization is possible by using H/V line iterators in the first // and third cases, at the cost of making the code a bit more complex KisRandomConstAccessorSP accessor = device->createRandomConstAccessorNG(x, y); bool found = false; { for (qint32 y2 = y; y2 <= endDirS; ++y2) { for (qint32 x2 = x; x2 < x + w || found; ++ x2) { accessor->moveTo(x2, y2); if (!compareOp.isPixelEmpty(accessor->rawDataConst())) { boundTop = y2; found = true; break; } } if (found) break; } } /** * If the first pass hasn't found any opaque pixel, there is no * reason to check that 3 more times. They will not appear in the * meantime. Just return an empty bounding rect. */ if (!found && endRect.isEmpty()) { return QRect(); } found = false; for (qint32 y2 = y + h - 1; y2 >= endDirN ; --y2) { for (qint32 x2 = x + w - 1; x2 >= x || found; --x2) { accessor->moveTo(x2, y2); if (!compareOp.isPixelEmpty(accessor->rawDataConst())) { boundBottom = y2; found = true; break; } } if (found) break; } found = false; { for (qint32 x2 = x; x2 <= endDirE ; ++x2) { for (qint32 y2 = y; y2 < y + h || found; ++y2) { accessor->moveTo(x2, y2); if (!compareOp.isPixelEmpty(accessor->rawDataConst())) { boundLeft = x2; found = true; break; } } if (found) break; } } found = false; // Look for right edge ) { for (qint32 x2 = x + w - 1; x2 >= endDirW; --x2) { for (qint32 y2 = y + h -1; y2 >= y || found; --y2) { accessor->moveTo(x2, y2); if (!compareOp.isPixelEmpty(accessor->rawDataConst())) { boundRight = x2; found = true; break; } } if (found) break; } } return QRect(boundLeft, boundTop, boundRight - boundLeft + 1, boundBottom - boundTop + 1); } } QRect KisPaintDevice::calculateExactBounds(bool nonDefaultOnly) const { QRect startRect = extent(); QRect endRect; quint8 defaultOpacity = m_d->colorSpace()->opacityU8(defaultPixel()); if(defaultOpacity != OPACITY_TRANSPARENT_U8) { if (!nonDefaultOnly) { /** * We will calculate exact bounds only outside of the * image bounds, and that'll be nondefault area only. */ endRect = defaultBounds()->bounds(); nonDefaultOnly = true; } else { startRect = region().boundingRect(); } } if (nonDefaultOnly) { Impl::CheckNonDefault compareOp(pixelSize(), defaultPixel()); endRect = Impl::calculateExactBoundsImpl(this, startRect, endRect, compareOp); } else { Impl::CheckFullyTransparent compareOp(m_d->colorSpace()); endRect = Impl::calculateExactBoundsImpl(this, startRect, endRect, compareOp); } return endRect; } void KisPaintDevice::crop(qint32 x, qint32 y, qint32 w, qint32 h) { crop(QRect(x, y, w, h)); } void KisPaintDevice::crop(const QRect &rect) { m_d->currentStrategy()->crop(rect); } void KisPaintDevice::purgeDefaultPixels() { KisDataManagerSP dm = m_d->dataManager(); dm->purge(dm->extent()); } void KisPaintDevice::setDefaultPixel(const quint8 *defPixel) { m_d->dataManager()->setDefaultPixel(defPixel); m_d->cache()->invalidate(); } const quint8 *KisPaintDevice::defaultPixel() const { return m_d->dataManager()->defaultPixel(); } void KisPaintDevice::clear() { m_d->dataManager()->clear(); m_d->cache()->invalidate(); } void KisPaintDevice::clear(const QRect & rc) { m_d->currentStrategy()->clear(rc); } void KisPaintDevice::fill(const QRect & rc, const KoColor &color) { Q_ASSERT(*color.colorSpace() == *colorSpace()); m_d->currentStrategy()->fill(rc, color.data()); } void KisPaintDevice::fill(qint32 x, qint32 y, qint32 w, qint32 h, const quint8 *fillPixel) { m_d->currentStrategy()->fill(QRect(x, y, w, h), fillPixel); } bool KisPaintDevice::write(KisPaintDeviceWriter &store) { return m_d->dataManager()->write(store); } bool KisPaintDevice::read(QIODevice *stream) { bool retval; retval = m_d->dataManager()->read(stream); m_d->cache()->invalidate(); return retval; } void KisPaintDevice::emitColorSpaceChanged() { emit colorSpaceChanged(m_d->colorSpace()); } void KisPaintDevice::emitProfileChanged() { emit profileChanged(m_d->colorSpace()->profile()); } KUndo2Command* KisPaintDevice::convertTo(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { KUndo2Command *command = m_d->convertColorSpace(dstColorSpace, renderingIntent, conversionFlags); return command; } bool KisPaintDevice::setProfile(const KoColorProfile * profile) { return m_d->assignProfile(profile); } KisDataManagerSP KisPaintDevice::dataManager() const { return m_d->dataManager(); } void KisPaintDevice::convertFromQImage(const QImage& _image, const KoColorProfile *profile, qint32 offsetX, qint32 offsetY) { QImage image = _image; if (image.format() != QImage::Format_ARGB32) { image = image.convertToFormat(QImage::Format_ARGB32); } // Don't convert if not no profile is given and both paint dev and qimage are rgba. if (!profile && colorSpace()->id() == "RGBA") { writeBytes(image.constBits(), offsetX, offsetY, image.width(), image.height()); } else { try { quint8 * dstData = new quint8[image.width() * image.height() * pixelSize()]; KoColorSpaceRegistry::instance() ->colorSpace(RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), profile) ->convertPixelsTo(image.constBits(), dstData, colorSpace(), image.width() * image.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); writeBytes(dstData, offsetX, offsetY, image.width(), image.height()); delete[] dstData; } catch (std::bad_alloc) { warnKrita << "KisPaintDevice::convertFromQImage: Could not allocate" << image.width() * image.height() * pixelSize() << "bytes"; return; } } m_d->cache()->invalidate(); } QImage KisPaintDevice::convertToQImage(const KoColorProfile *dstProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { qint32 x1; qint32 y1; qint32 w; qint32 h; QRect rc = exactBounds(); x1 = rc.x(); y1 = rc.y(); w = rc.width(); h = rc.height(); return convertToQImage(dstProfile, x1, y1, w, h, renderingIntent, conversionFlags); } QImage KisPaintDevice::convertToQImage(const KoColorProfile *dstProfile, const QRect &rc, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { return convertToQImage(dstProfile, rc.x(), rc.y(), rc.width(), rc.height(), renderingIntent, conversionFlags); } QImage KisPaintDevice::convertToQImage(const KoColorProfile * dstProfile, qint32 x1, qint32 y1, qint32 w, qint32 h, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { if (w < 0) return QImage(); if (h < 0) return QImage(); quint8 *data = 0; try { data = new quint8 [w * h * pixelSize()]; } catch (std::bad_alloc) { warnKrita << "KisPaintDevice::convertToQImage std::bad_alloc for " << w << " * " << h << " * " << pixelSize(); //delete[] data; // data is not allocated, so don't free it return QImage(); } Q_CHECK_PTR(data); // XXX: Is this really faster than converting line by line and building the QImage directly? // This copies potentially a lot of data. readBytes(data, x1, y1, w, h); QImage image = colorSpace()->convertToQImage(data, w, h, dstProfile, renderingIntent, conversionFlags); delete[] data; return image; } KisPaintDeviceSP KisPaintDevice::createThumbnailDevice(qint32 w, qint32 h, QRect rect) const { KisPaintDeviceSP thumbnail = new KisPaintDevice(colorSpace()); int srcWidth, srcHeight; int srcX0, srcY0; QRect e = rect.isValid() ? rect : extent(); e.getRect(&srcX0, &srcY0, &srcWidth, &srcHeight); if (w > srcWidth) { w = srcWidth; h = qint32(double(srcWidth) / w * h); } if (h > srcHeight) { h = srcHeight; w = qint32(double(srcHeight) / h * w); } if (srcWidth > srcHeight) h = qint32(double(srcHeight) / srcWidth * w); else if (srcHeight > srcWidth) w = qint32(double(srcWidth) / srcHeight * h); const qint32 pixelSize = this->pixelSize(); KisRandomConstAccessorSP iter = createRandomConstAccessorNG(0, 0); KisRandomAccessorSP dstIter = thumbnail->createRandomAccessorNG(0, 0); for (qint32 y = 0; y < h; ++y) { qint32 iY = srcY0 + (y * srcHeight) / h; for (qint32 x = 0; x < w; ++x) { qint32 iX = srcX0 + (x * srcWidth) / w; iter->moveTo(iX, iY); dstIter->moveTo(x, y); memcpy(dstIter->rawData(), iter->rawDataConst(), pixelSize); } } return thumbnail; } QImage KisPaintDevice::createThumbnail(qint32 w, qint32 h, QRect rect, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { KisPaintDeviceSP dev = createThumbnailDevice(w, h, rect); QImage thumbnail = dev->convertToQImage(KoColorSpaceRegistry::instance()->rgb8()->profile(), 0, 0, w, h, renderingIntent, conversionFlags); return thumbnail; } QImage KisPaintDevice::createThumbnail(qint32 w, qint32 h, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { return m_d->cache()->createThumbnail(w, h, renderingIntent, conversionFlags); } KisHLineIteratorSP KisPaintDevice::createHLineIteratorNG(qint32 x, qint32 y, qint32 w) { m_d->cache()->invalidate(); return m_d->currentStrategy()->createHLineIteratorNG(m_d->dataManager().data(), x, y, w, m_d->x(), m_d->y()); } KisHLineConstIteratorSP KisPaintDevice::createHLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const { return m_d->currentStrategy()->createHLineConstIteratorNG(m_d->dataManager().data(), x, y, w, m_d->x(), m_d->y()); } KisVLineIteratorSP KisPaintDevice::createVLineIteratorNG(qint32 x, qint32 y, qint32 w) { m_d->cache()->invalidate(); return m_d->currentStrategy()->createVLineIteratorNG(x, y, w); } KisVLineConstIteratorSP KisPaintDevice::createVLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const { return m_d->currentStrategy()->createVLineConstIteratorNG(x, y, w); } KisRepeatHLineConstIteratorSP KisPaintDevice::createRepeatHLineConstIterator(qint32 x, qint32 y, qint32 w, const QRect& _dataWidth) const { return new KisRepeatHLineConstIteratorNG(m_d->dataManager().data(), x, y, w, m_d->x(), m_d->y(), _dataWidth); } KisRepeatVLineConstIteratorSP KisPaintDevice::createRepeatVLineConstIterator(qint32 x, qint32 y, qint32 h, const QRect& _dataWidth) const { return new KisRepeatVLineConstIteratorNG(m_d->dataManager().data(), x, y, h, m_d->x(), m_d->y(), _dataWidth); } KisRandomAccessorSP KisPaintDevice::createRandomAccessorNG(qint32 x, qint32 y) { m_d->cache()->invalidate(); return m_d->currentStrategy()->createRandomAccessorNG(x, y); } KisRandomConstAccessorSP KisPaintDevice::createRandomConstAccessorNG(qint32 x, qint32 y) const { return m_d->currentStrategy()->createRandomConstAccessorNG(x, y); } KisRandomSubAccessorSP KisPaintDevice::createRandomSubAccessor() const { KisPaintDevice* pd = const_cast(this); return new KisRandomSubAccessor(pd); } void KisPaintDevice::clearSelection(KisSelectionSP selection) { const KoColorSpace *colorSpace = m_d->colorSpace(); QRect r = selection->selectedExactRect() & m_d->defaultBounds->bounds(); if (r.isValid()) { KisHLineIteratorSP devIt = createHLineIteratorNG(r.x(), r.y(), r.width()); KisHLineConstIteratorSP selectionIt = selection->projection()->createHLineConstIteratorNG(r.x(), r.y(), r.width()); const quint8* defaultPixel_ = defaultPixel(); bool transparentDefault = (colorSpace->opacityU8(defaultPixel_) == OPACITY_TRANSPARENT_U8); for (qint32 y = 0; y < r.height(); y++) { do { // XXX: Optimize by using stretches colorSpace->applyInverseAlphaU8Mask(devIt->rawData(), selectionIt->rawDataConst(), 1); if (transparentDefault && colorSpace->opacityU8(devIt->rawData()) == OPACITY_TRANSPARENT_U8) { memcpy(devIt->rawData(), defaultPixel_, colorSpace->pixelSize()); } } while (devIt->nextPixel() && selectionIt->nextPixel()); devIt->nextRow(); selectionIt->nextRow(); } m_d->dataManager()->purge(r.translated(-m_d->x(), -m_d->y())); setDirty(r); } } bool KisPaintDevice::pixel(qint32 x, qint32 y, QColor *c) const { KisHLineConstIteratorSP iter = createHLineConstIteratorNG(x, y, 1); const quint8 *pix = iter->rawDataConst(); if (!pix) return false; colorSpace()->toQColor(pix, c); return true; } bool KisPaintDevice::pixel(qint32 x, qint32 y, KoColor * kc) const { KisHLineConstIteratorSP iter = createHLineConstIteratorNG(x, y, 1); const quint8 *pix = iter->rawDataConst(); if (!pix) return false; kc->setColor(pix, m_d->colorSpace()); return true; } bool KisPaintDevice::setPixel(qint32 x, qint32 y, const QColor& c) { KisHLineIteratorSP iter = createHLineIteratorNG(x, y, 1); colorSpace()->fromQColor(c, iter->rawData()); m_d->cache()->invalidate(); return true; } bool KisPaintDevice::setPixel(qint32 x, qint32 y, const KoColor& kc) { const quint8 * pix; KisHLineIteratorSP iter = createHLineIteratorNG(x, y, 1); if (kc.colorSpace() != m_d->colorSpace()) { KoColor kc2(kc, m_d->colorSpace()); pix = kc2.data(); memcpy(iter->rawData(), pix, m_d->colorSpace()->pixelSize()); } else { pix = kc.data(); memcpy(iter->rawData(), pix, m_d->colorSpace()->pixelSize()); } m_d->cache()->invalidate(); return true; } bool KisPaintDevice::fastBitBltPossible(KisPaintDeviceSP src) { return m_d->fastBitBltPossible(src); } void KisPaintDevice::fastBitBlt(KisPaintDeviceSP src, const QRect &rect) { m_d->currentStrategy()->fastBitBlt(src, rect); } void KisPaintDevice::fastBitBltOldData(KisPaintDeviceSP src, const QRect &rect) { m_d->currentStrategy()->fastBitBltOldData(src, rect); } void KisPaintDevice::fastBitBltRough(KisPaintDeviceSP src, const QRect &rect) { m_d->currentStrategy()->fastBitBltRough(src, rect); } void KisPaintDevice::fastBitBltRoughOldData(KisPaintDeviceSP src, const QRect &rect) { m_d->currentStrategy()->fastBitBltRoughOldData(src, rect); } void KisPaintDevice::readBytes(quint8 * data, qint32 x, qint32 y, qint32 w, qint32 h) const { readBytes(data, QRect(x, y, w, h)); } void KisPaintDevice::readBytes(quint8 *data, const QRect &rect) const { m_d->currentStrategy()->readBytes(data, rect); } void KisPaintDevice::writeBytes(const quint8 *data, qint32 x, qint32 y, qint32 w, qint32 h) { writeBytes(data, QRect(x, y, w, h)); } void KisPaintDevice::writeBytes(const quint8 *data, const QRect &rect) { m_d->currentStrategy()->writeBytes(data, rect); } QVector KisPaintDevice::readPlanarBytes(qint32 x, qint32 y, qint32 w, qint32 h) const { return m_d->currentStrategy()->readPlanarBytes(x, y, w, h); } void KisPaintDevice::writePlanarBytes(QVector planes, qint32 x, qint32 y, qint32 w, qint32 h) { m_d->currentStrategy()->writePlanarBytes(planes, x, y, w, h); } quint32 KisPaintDevice::pixelSize() const { quint32 _pixelSize = m_d->colorSpace()->pixelSize(); Q_ASSERT(_pixelSize > 0); return _pixelSize; } quint32 KisPaintDevice::channelCount() const { quint32 _channelCount = m_d->colorSpace()->channelCount(); Q_ASSERT(_channelCount > 0); return _channelCount; } KisRasterKeyframeChannel *KisPaintDevice::createKeyframeChannel(const KoID &id) { Q_ASSERT(!m_d->framesInterface); m_d->framesInterface.reset(new KisPaintDeviceFramesInterface(this)); Q_ASSERT(!m_d->contentChannel); m_d->contentChannel.reset(new KisRasterKeyframeChannel(id, this, m_d->defaultBounds)); // Raster channels always have at least one frame (representing a static image) KUndo2Command tempParentCommand; m_d->contentChannel->addKeyframe(0, &tempParentCommand); return m_d->contentChannel.data(); } KisRasterKeyframeChannel* KisPaintDevice::keyframeChannel() const { Q_ASSERT(m_d->contentChannel); return m_d->contentChannel.data(); } const KoColorSpace* KisPaintDevice::colorSpace() const { Q_ASSERT(m_d->colorSpace() != 0); return m_d->colorSpace(); } KisPaintDeviceSP KisPaintDevice::createCompositionSourceDevice() const { KisPaintDeviceSP device = new KisPaintDevice(compositionSourceColorSpace()); device->setDefaultBounds(defaultBounds()); return device; } KisPaintDeviceSP KisPaintDevice::createCompositionSourceDevice(KisPaintDeviceSP cloneSource) const { KisPaintDeviceSP clone = new KisPaintDevice(*cloneSource); clone->setDefaultBounds(defaultBounds()); clone->convertTo(compositionSourceColorSpace(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); return clone; } KisPaintDeviceSP KisPaintDevice::createCompositionSourceDevice(KisPaintDeviceSP cloneSource, const QRect roughRect) const { KisPaintDeviceSP clone = new KisPaintDevice(colorSpace()); clone->setDefaultBounds(defaultBounds()); clone->makeCloneFromRough(cloneSource, roughRect); clone->convertTo(compositionSourceColorSpace(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); return clone; } KisFixedPaintDeviceSP KisPaintDevice::createCompositionSourceDeviceFixed() const { return new KisFixedPaintDevice(compositionSourceColorSpace()); } const KoColorSpace* KisPaintDevice::compositionSourceColorSpace() const { return colorSpace(); } QVector KisPaintDevice::channelSizes() const { QVector sizes; QList channels = colorSpace()->channels(); qSort(channels); Q_FOREACH (KoChannelInfo * channelInfo, channels) { sizes.append(channelInfo->size()); } return sizes; } KisPaintDevice::MemoryReleaseObject::~MemoryReleaseObject() { KisDataManager::releaseInternalPools(); } KisPaintDevice::MemoryReleaseObject* KisPaintDevice::createMemoryReleaseObject() { return new MemoryReleaseObject(); } -struct KisPaintDevice::LodDataStruct { - LodDataStruct(Private::LodDataStruct *_lodData) : lodData(_lodData) {} - Private::LodDataStruct *lodData; -}; +KisPaintDevice::LodDataStruct::~LodDataStruct() +{ +} QRegion KisPaintDevice::regionForLodSyncing() const { return m_d->regionForLodSyncing(); } KisPaintDevice::LodDataStruct* KisPaintDevice::createLodDataStruct(int lod) { - Private::LodDataStruct *lodData = m_d->createLodDataStruct(lod); - return new LodDataStruct(lodData); + return m_d->createLodDataStruct(lod); } void KisPaintDevice::updateLodDataStruct(LodDataStruct *dst, const QRect &srcRect) { - m_d->updateLodDataStruct(dst->lodData, srcRect); + m_d->updateLodDataStruct(dst, srcRect); } void KisPaintDevice::uploadLodDataStruct(LodDataStruct *dst) { - m_d->uploadLodDataStruct(dst->lodData); + m_d->uploadLodDataStruct(dst); } KisPaintDeviceFramesInterface* KisPaintDevice::framesInterface() { return m_d->framesInterface.data(); } /******************************************************************/ /* KisPaintDeviceFramesInterface */ /******************************************************************/ KisPaintDeviceFramesInterface::KisPaintDeviceFramesInterface(KisPaintDevice *parentDevice) : q(parentDevice) { } QList KisPaintDeviceFramesInterface::frames() { return q->m_d->frameIds(); } int KisPaintDeviceFramesInterface::createFrame(bool copy, int copySrc, const QPoint &offset, KUndo2Command *parentCommand) { return q->m_d->createFrame(copy, copySrc, offset, parentCommand); } void KisPaintDeviceFramesInterface::deleteFrame(int frame, KUndo2Command *parentCommand) { return q->m_d->deleteFrame(frame, parentCommand); } void KisPaintDeviceFramesInterface::fetchFrame(int frameId, KisPaintDeviceSP targetDevice) { q->m_d->fetchFrame(frameId, targetDevice); } void KisPaintDeviceFramesInterface::uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice) { q->m_d->uploadFrame(srcFrameId, dstFrameId, srcDevice); } void KisPaintDeviceFramesInterface::uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice) { q->m_d->uploadFrame(dstFrameId, srcDevice); } QRect KisPaintDeviceFramesInterface::frameBounds(int frameId) { return q->m_d->frameBounds(frameId); } QPoint KisPaintDeviceFramesInterface::frameOffset(int frameId) const { return q->m_d->frameOffset(frameId); } void KisPaintDeviceFramesInterface::setFrameDefaultPixel(const quint8 *defPixel, int frameId) { KIS_ASSERT_RECOVER_RETURN(frameId >= 0); q->m_d->setFrameDefaultPixel(defPixel, frameId); } const quint8* KisPaintDeviceFramesInterface::frameDefaultPixel(int frameId) const { KIS_ASSERT_RECOVER(frameId >= 0) { return (quint8*)"deadbeef"; } return q->m_d->frameDefaultPixel(frameId); } bool KisPaintDeviceFramesInterface::writeFrame(KisPaintDeviceWriter &store, int frameId) { KIS_ASSERT_RECOVER(frameId >= 0) { return false; } return q->m_d->writeFrame(store, frameId); } bool KisPaintDeviceFramesInterface::readFrame(QIODevice *stream, int frameId) { KIS_ASSERT_RECOVER(frameId >= 0) { return false; } return q->m_d->readFrame(stream, frameId); } int KisPaintDeviceFramesInterface::currentFrameId() const { return q->m_d->currentFrameId(); } KisDataManagerSP KisPaintDeviceFramesInterface::frameDataManager(int frameId) const { KIS_ASSERT_RECOVER(frameId >= 0) { return q->m_d->dataManager(); } return q->m_d->frameDataManager(frameId); } void KisPaintDeviceFramesInterface::invalidateFrameCache(int frameId) { KIS_ASSERT_RECOVER_RETURN(frameId >= 0); return q->m_d->invalidateFrameCache(frameId); } void KisPaintDeviceFramesInterface::setFrameOffset(int frameId, const QPoint &offset) { KIS_ASSERT_RECOVER_RETURN(frameId >= 0); return q->m_d->setFrameOffset(frameId, offset); } KisPaintDeviceFramesInterface::TestingDataObjects KisPaintDeviceFramesInterface::testingGetDataObjects() const { TestingDataObjects objects; objects.m_data = q->m_d->m_data.data(); objects.m_lodData = q->m_d->m_lodData.data(); objects.m_externalFrameData = q->m_d->m_externalFrameData.data(); typedef KisPaintDevice::Private::FramesHash FramesHash; FramesHash::const_iterator it = q->m_d->m_frames.constBegin(); FramesHash::const_iterator end = q->m_d->m_frames.constEnd(); for (; it != end; ++it) { objects.m_frames.insert(it.key(), it.value().data()); } objects.m_currentData = q->m_d->currentData(); return objects; } QList KisPaintDeviceFramesInterface::testingGetDataObjectsList() const { return q->m_d->allDataObjects(); } void KisPaintDevice::tesingFetchLodDevice(KisPaintDeviceSP targetDevice) { m_d->tesingFetchLodDevice(targetDevice); } diff --git a/libs/image/kis_paint_device.h b/libs/image/kis_paint_device.h index 1b84c482d2..013fb897bb 100644 --- a/libs/image/kis_paint_device.h +++ b/libs/image/kis_paint_device.h @@ -1,846 +1,849 @@ /* * Copyright (c) 2002 patrick julien * 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_PAINT_DEVICE_IMPL_H_ #define KIS_PAINT_DEVICE_IMPL_H_ #include #include #include #include "kis_debug.h" #include #include "kis_types.h" #include "kis_shared.h" #include "kis_default_bounds_base.h" #include class KUndo2Command; class QRect; class QImage; class QPoint; class QString; class QColor; class QIODevice; class KoColor; class KoColorSpace; class KoColorProfile; class KisDataManager; class KisPaintDeviceWriter; class KisKeyframe; class KisRasterKeyframeChannel; class KisPaintDeviceFramesInterface; typedef KisSharedPtr KisDataManagerSP; /** * A paint device contains the actual pixel data and offers methods * to read and write pixels. A paint device has an integer x,y position * (i.e., are not positioned on the image with sub-pixel accuracy). * A KisPaintDevice doesn't have any fixed size, the size changes dynamically * when pixels are accessed by an iterator. */ class KRITAIMAGE_EXPORT KisPaintDevice : public QObject , public KisShared { Q_OBJECT public: /** * Create a new paint device with the specified colorspace. * * @param colorSpace the colorspace of this paint device * @param name for debugging purposes */ explicit KisPaintDevice(const KoColorSpace * colorSpace, const QString& name = QString()); /** * Create a new paint device with the specified colorspace. The * parent node will be notified of changes to this paint device. * * @param parent the node that contains this paint device * @param colorSpace the colorspace of this paint device * @param defaultBounds boundaries of the device in case it is empty * @param name for debugging purposes */ KisPaintDevice(KisNodeWSP parent, const KoColorSpace * colorSpace, KisDefaultBoundsBaseSP defaultBounds = 0, const QString& name = QString()); /** * Creates a copy of this device. * * If \p copyFrames is false, the newly created device clones the * current frame of \p rhs only (default and efficient * behavior). If \p copyFrames is true, the new device is a deep * copy of the source with all the frames included. */ KisPaintDevice(const KisPaintDevice& rhs, bool copyFrames = false, KisNode *newParentNode = 0); virtual ~KisPaintDevice(); protected: /** * A special constructor for usage in KisPixelSelection. It allows * two paint devices to share a data manager. * * @param explicitDataManager data manager to use inside paint device * @param src source paint device to copy parameters from * @param name for debugging purposes */ KisPaintDevice(KisDataManagerSP explicitDataManager, KisPaintDeviceSP src, const QString& name = QString()); public: /** * Write the pixels of this paint device into the specified file store. */ bool write(KisPaintDeviceWriter &store); /** * Fill this paint device with the pixels from the specified file store. */ bool read(QIODevice *stream); public: /** * set the parent node of the paint device */ void setParentNode(KisNodeWSP parent); /** * set the default bounds for the paint device when * the default pixel in not completely transarent */ void setDefaultBounds(KisDefaultBoundsBaseSP bounds); /** * the default bounds rect of the paint device */ KisDefaultBoundsBaseSP defaultBounds() const; /** * Moves the device to these new coordinates (so no incremental move or so) */ void move(qint32 x, qint32 y); /** * Convenience method for the above. */ virtual void move(const QPoint& pt); /** * The X offset of the paint device */ qint32 x() const; /** * The Y offset of the paint device */ qint32 y() const; /** * set the X offset of the paint device */ void setX(qint32 x); /** * set the Y offset of the paint device */ void setY(qint32 y); /** * Retrieve the bounds of the paint device. The size is not exact, * but may be larger if the underlying datamanager works that way. * For instance, the tiled datamanager keeps the extent to the nearest * multiple of 64. * * If default pixel is not transparent, then the actual extent * rect is united with the defaultBounds()->bounds() value * (the size of the image, usually). */ QRect extent() const; /// Convience method for the above void extent(qint32 &x, qint32 &y, qint32 &w, qint32 &h) const; /** * Get the exact bounds of this paint device. The real solution is * very slow because it does a linear scanline search, but it * uses caching, so calling to this function without changing * the device is quite cheap. * * Exactbounds follows these rules: * *
    *
  • if default pixel is transparent, then exact bounds * of actual pixel data are returned *
  • if default pixel is not transparent, then the union * (defaultBounds()->bounds() | nonDefaultPixelArea()) is * returned *
* \see calculateExactBounds() */ QRect exactBounds() const; /** * Relaxed version of the exactBounds() that can be used in tight * loops. If the exact bounds value is present in the paint * device cache, returns this value. If the cache is invalidated, * returns extent() and tries to recalculate the exact bounds not * faster than once in 1000 ms. */ QRect exactBoundsAmortized() const; /** * Retuns exact rectangle of the paint device that contains * non-default pixels. For paint devices with fully transparent * default pixel is equivalent to exactBounds(). * * nonDefaultPixelArea() follows these rules: * *
    *
  • if default pixel is transparent, then exact bounds * of actual pixel data are returned. The same as exactBounds() *
  • if default pixel is not transparent, then calculates the * rectangle of non-default pixels. May be smaller or greater * than image bounds *
* \see calculateExactBounds() */ QRect nonDefaultPixelArea() const; /** * Returns a rough approximation of region covered by device. * For tiled data manager, it region will consist of a number * of rects each corresponding to a tile. */ QRegion region() const; /** * Cut the paint device down to the specified rect. If the crop * area is bigger than the paint device, nothing will happen. */ void crop(qint32 x, qint32 y, qint32 w, qint32 h); /// Convience method for the above void crop(const QRect & r); /** * Complete erase the current paint device. Its size will become 0. This * does not take the selection into account. */ virtual void clear(); /** * Clear the given rectangle to transparent black. The paint device will expand to * contain the given rect. */ void clear(const QRect & rc); /** * Frees the memory occupied by the pixels containing default * values. The extents() and exactBounds() of the image will * probably also shrink */ void purgeDefaultPixels(); /** * Sets the default pixel. New data will be initialised with this pixel. The pixel is copied: the * caller still owns the pointer and needs to delete it to avoid memory leaks. * If frame ID is given, set default pixel for that frame. Otherwise use active frame. */ void setDefaultPixel(const quint8 *defPixel); /** * Get a pointer to the default pixel. * If the frame parameter is given, get the default pixel of * specified frame. Otherwise use currently active frame. */ const quint8 *defaultPixel() const; /** * Fill the given rectangle with the given pixel. The paint device will expand to * contain the given rect. */ void fill(const QRect & rc, const KoColor &color); /** * Overloaded function. For legacy purposes only. * Please use fill(const QRect & rc, const KoColor &color) instead */ void fill(qint32 x, qint32 y, qint32 w, qint32 h, const quint8 *fillPixel); public: /** * Prepares the device for fastBitBlt opreration. It clears * the device, switches x,y shifts and colorspace if needed. * After this call fastBitBltPossible will return true. * May be used for initialization of temporary devices. */ void prepareClone(KisPaintDeviceSP src); /** * Make this device to become a clone of \a src. It will have the same * x,y shifts, colorspace and will share pixels inside \a rect. * After calling this function: * (this->extent() >= this->exactBounds() == rect). * * Rule of thumb: * * "Use makeCloneFrom() or makeCloneFromRough() if and only if you * are the only owner of the destination paint device and you are * 100% sure no other thread has access to it" */ void makeCloneFrom(KisPaintDeviceSP src, const QRect &rect); /** * Make this device to become a clone of \a src. It will have the same * x,y shifts, colorspace and will share pixels inside \a rect. * Be careful, this function will copy *at least* \a rect * of pixels. Actual copy area will be a bigger - it will * be aligned by tiles borders. So after calling this function: * (this->extent() == this->exactBounds() >= rect). * * Rule of thumb: * * "Use makeCloneFrom() or makeCloneFromRough() if and only if you * are the only owner of the destination paint device and you are * 100% sure no other thread has access to it" */ void makeCloneFromRough(KisPaintDeviceSP src, const QRect &minimalRect); protected: friend class KisPaintDeviceTest; friend class DataReaderThread; /** * Checks whether a src paint device can be used as source * of fast bitBlt operation. The result of the check may * depend on whether color spaces coinside, whether there is * any shift of tiles between the devices and etc. * * WARNING: This check must be done before performing any * fast bitBlt operation! * * \see fastBitBlt * \see fastBitBltRough */ bool fastBitBltPossible(KisPaintDeviceSP src); /** * Clones rect from another paint device. The cloned area will be * shared between both paint devices as much as possible using * copy-on-write. Parts of the rect that cannot be shared * (cross tiles) are deep-copied, * * \see fastBitBltPossible * \see fastBitBltRough */ void fastBitBlt(KisPaintDeviceSP src, const QRect &rect); /** * The same as \ref fastBitBlt() but reads old data */ void fastBitBltOldData(KisPaintDeviceSP src, const QRect &rect); /** * Clones rect from another paint device in a rough and fast way. * All the tiles touched by rect will be shared, between both * devices, that means it will copy a bigger area than was * requested. This method is supposed to be used for bitBlt'ing * into temporary paint devices. * * \see fastBitBltPossible * \see fastBitBlt */ void fastBitBltRough(KisPaintDeviceSP src, const QRect &rect); /** * The same as \ref fastBitBltRough() but reads old data */ void fastBitBltRoughOldData(KisPaintDeviceSP src, const QRect &rect); public: /** * Read the bytes representing the rectangle described by x, y, w, h into * data. If data is not big enough, Krita will gladly overwrite the rest * of your precious memory. * * Since this is a copy, you need to make sure you have enough memory. * * Reading from areas not previously initialized will read the default * pixel value into data but not initialize that region. */ void readBytes(quint8 * data, qint32 x, qint32 y, qint32 w, qint32 h) const; /** * Read the bytes representing the rectangle rect into * data. If data is not big enough, Krita will gladly overwrite the rest * of your precious memory. * * Since this is a copy, you need to make sure you have enough memory. * * Reading from areas not previously initialized will read the default * pixel value into data but not initialize that region. * @param data The address of the memory to receive the bytes read * @param rect The rectangle in the paint device to read from */ void readBytes(quint8 * data, const QRect &rect) const; /** * Copy the bytes in data into the rect specified by x, y, w, h. If the * data is too small or uninitialized, Krita will happily read parts of * memory you never wanted to be read. * * If the data is written to areas of the paint device not previously initialized, * the paint device will grow. */ void writeBytes(const quint8 * data, qint32 x, qint32 y, qint32 w, qint32 h); /** * Copy the bytes in data into the rectangle rect. If the * data is too small or uninitialized, Krita will happily read parts of * memory you never wanted to be read. * * If the data is written to areas of the paint device not previously initialized, * the paint device will grow. * @param data The address of the memory to write bytes from * @param rect The rectangle in the paint device to write to */ void writeBytes(const quint8 * data, const QRect &rect); /** * Copy the bytes in the paint device into a vector of arrays of bytes, * where the number of arrays is the number of channels in the * paint device. If the specified area is larger than the paint * device's extent, the default pixel will be read. */ QVector readPlanarBytes(qint32 x, qint32 y, qint32 w, qint32 h) const; /** * Write the data in the separate arrays to the channes. If there * are less vectors than channels, the remaining channels will not * be copied. If any of the arrays points to 0, the channel in * that location will not be touched. If the specified area is * larger than the paint device, the paint device will be * extended. There are no guards: if the area covers more pixels * than there are bytes in the arrays, krita will happily fill * your paint device with areas of memory you never wanted to be * read. Krita may also crash. * * XXX: what about undo? */ void writePlanarBytes(QVector planes, qint32 x, qint32 y, qint32 w, qint32 h); /** * Converts the paint device to a different colorspace * * @return a command that can be used to undo the conversion. */ KUndo2Command* convertTo(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags()); /** * Changes the profile of the colorspace of this paint device to the given * profile. If the given profile is 0, nothing happens. */ bool setProfile(const KoColorProfile * profile); /** * Fill this paint device with the data from image; starting at (offsetX, offsetY) * @param srcProfileName name of the RGB profile to interpret the image as. 0 is interpreted as sRGB */ void convertFromQImage(const QImage& image, const KoColorProfile *profile, qint32 offsetX = 0, qint32 offsetY = 0); /** * Create an RGBA QImage from a rectangle in the paint device. * * @param x Left coordinate of the rectangle * @param y Top coordinate of the rectangle * @param w Width of the rectangle in pixels * @param h Height of the rectangle in pixels * @param dstProfile RGB profile to use in conversion. May be 0, in which * case it's up to the color strategy to choose a profile (most * like sRGB). */ virtual QImage convertToQImage(const KoColorProfile *dstProfile, qint32 x, qint32 y, qint32 w, qint32 h, KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags()) const; /** * Overridden method for convenience */ QImage convertToQImage(const KoColorProfile *dstProfile, const QRect &rc, KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags()) const; /** * Create an RGBA QImage from a rectangle in the paint device. The * rectangle is defined by the parent image's bounds. * * @param dstProfile RGB profile to use in conversion. May be 0, in which * case it's up to the color strategy to choose a profile (most * like sRGB). */ QImage convertToQImage(const KoColorProfile * dstProfile, KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags()) const; /** * Creates a paint device thumbnail of the paint device, retaining * the aspect ratio. The width and height of the returned device * won't exceed \p maxw and \p maxw, but they may be smaller. * * @param maxw: maximum width * @param maxh: maximum height * @param rect: only this rect will be used for the thumbnail * */ KisPaintDeviceSP createThumbnailDevice(qint32 w, qint32 h, QRect rect = QRect()) const; /** * Creates a thumbnail of the paint device, retaining the aspect ratio. * The width and height of the returned QImage won't exceed \p maxw and \p maxw, but they may be smaller. * The colors are not corrected for display! * * @param maxw: maximum width * @param maxh: maximum height * @param rect: only this rect will be used for the thumbnail */ QImage createThumbnail(qint32 maxw, qint32 maxh, QRect rect, KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags()); /** * Cached version of createThumbnail(qint32 maxw, qint32 maxh, const KisSelection *selection, QRect rect) */ QImage createThumbnail(qint32 maxw, qint32 maxh, KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags()); /** * Fill c and opacity with the values found at x and y. * * The color values will be transformed from the profile of * this paint device to the display profile. * * @return true if the operation was successful. */ bool pixel(qint32 x, qint32 y, QColor *c) const; /** * Fill kc with the values found at x and y. This method differs * from the above in using KoColor, which can be of any colorspace * * The color values will be transformed from the profile of * this paint device to the display profile. * * @return true if the operation was successful. */ bool pixel(qint32 x, qint32 y, KoColor * kc) const; /** * Set the specified pixel to the specified color. Note that this * bypasses KisPainter. the PaintDevice is here used as an equivalent * to QImage, not QPixmap. This means that this is not undoable; also, * there is no compositing with an existing value at this location. * * The color values will be transformed from the display profile to * the paint device profile. * * Note that this will use 8-bit values and may cause a significant * degradation when used on 16-bit or hdr quality images. * * @return true if the operation was successful */ bool setPixel(qint32 x, qint32 y, const QColor& c); /// Convience method for the above bool setPixel(qint32 x, qint32 y, const KoColor& kc); /** * @return the colorspace of the pixels in this paint device */ const KoColorSpace* colorSpace() const; /** * There is quite a common technique in Krita. It is used in * cases, when we want to paint something over a paint device * using the composition, opacity or selection. E.g. painting a * dab in a paint op, filling the selection in the Fill Tool. * Such work is usually done in the following way: * * 1) Create a paint device * * 2) Fill it with the desired color or data * * 3) Create a KisPainter and set all the properties of the * trasaction: selection, compositeOp, opacity and etc. * * 4) Paint a newly created paint device over the destination * device. * * The following two methods (createCompositionSourceDevice() or * createCompositionSourceDeviceFixed())should be used for the * accomplishing the step 1). The point is that the desired color * space of the temporary device may not coincide with the color * space of the destination. That is the case, for example, for * the alpha8() colorspace used in the selections. So for such * devices the temporary target would have a different (grayscale) * color space. * * So there are two rules of thumb: * * 1) If you need a temporary device which you are going to fill * with some data and then paint over the paint device, create * it with either createCompositionSourceDevice() or * createCompositionSourceDeviceFixed(). * * 2) Do *not* expect that the color spaces of the destination and * the temporary device would coincide. If you need to copy a * single pixel from one device to another, you can use * KisCrossDeviceColorPicker class, that will handle all the * necessary conversions for you. * * \see createCompositionSourceDeviceFixed() * \see compositionSourceColorSpace() * \see KisCrossDeviceColorPicker * \see KisCrossDeviceColorPickerInt */ KisPaintDeviceSP createCompositionSourceDevice() const; /** * The same as createCompositionSourceDevice(), but initializes the * newly created device with the content of \p cloneSource * * \see createCompositionSourceDevice() */ KisPaintDeviceSP createCompositionSourceDevice(KisPaintDeviceSP cloneSource) const; /** * The same as createCompositionSourceDevice(), but initializes * the newly created device with the *rough* \p roughRect of * \p cloneSource. * * "Rough rect" means that it may copy a bit more than * requested. It is expected that the caller will not use the area * outside \p roughRect. * * \see createCompositionSourceDevice() */ KisPaintDeviceSP createCompositionSourceDevice(KisPaintDeviceSP cloneSource, const QRect roughRect) const; /** * This is a convenience method for createCompositionSourceDevice() * * \see createCompositionSourceDevice() */ KisFixedPaintDeviceSP createCompositionSourceDeviceFixed() const; /** * This is a lowlevel method for the principle used in * createCompositionSourceDevice(). In most of the cases the paint * device creation methods should be used instead of this function. * * \see createCompositionSourceDevice() * \see createCompositionSourceDeviceFixed() */ virtual const KoColorSpace* compositionSourceColorSpace() const; /** * @return the internal datamanager that keeps the pixels. */ KisDataManagerSP dataManager() const; /** * Replace the pixel data, color strategy, and profile. */ void setDataManager(KisDataManagerSP data, const KoColorSpace * colorSpace = 0); /** * Return the number of bytes a pixel takes. */ quint32 pixelSize() const; /** * Return the number of channels a pixel takes */ quint32 channelCount() const; /** * Create a keyframe channel for the content on this device. * @param id identifier for the channel * @param node the parent node for the channel * @return keyframe channel */ KisRasterKeyframeChannel *createKeyframeChannel(const KoID &id); KisRasterKeyframeChannel* keyframeChannel() const; /** * An interface to modify/load/save frames stored inside this device */ KisPaintDeviceFramesInterface* framesInterface(); public: /** * Add the specified rect to the parent layer's set of dirty rects * (if there is a parent layer) */ void setDirty(const QRect & rc); /** * Add the specified region to the parent layer's dirty region * (if there is a parent layer) */ void setDirty(const QRegion & region); /** * Set the parent layer completely dirty, if this paint device has * as parent layer. */ void setDirty(); void setDirty(const QVector rects); /** * Called by KisTransactionData when it thinks current time should * be changed. And the requests is forwarded to the image if * needed. */ void requestTimeSwitch(int time); public: KisHLineIteratorSP createHLineIteratorNG(qint32 x, qint32 y, qint32 w); KisHLineConstIteratorSP createHLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const; KisVLineIteratorSP createVLineIteratorNG(qint32 x, qint32 y, qint32 h); KisVLineConstIteratorSP createVLineConstIteratorNG(qint32 x, qint32 y, qint32 h) const; KisRandomAccessorSP createRandomAccessorNG(qint32 x, qint32 y); KisRandomConstAccessorSP createRandomConstAccessorNG(qint32 x, qint32 y) const; /** * Create an iterator that will "artificially" extend the paint device with the * value of the border when trying to access values outside the range of data. * * @param rc indicates the rectangle that truly contains data */ KisRepeatHLineConstIteratorSP createRepeatHLineConstIterator(qint32 x, qint32 y, qint32 w, const QRect& _dataWidth) const; /** * Create an iterator that will "artificially" extend the paint device with the * value of the border when trying to access values outside the range of data. * * @param rc indicates the rectangle that trully contains data */ KisRepeatVLineConstIteratorSP createRepeatVLineConstIterator(qint32 x, qint32 y, qint32 h, const QRect& _dataWidth) const; /** * This function create a random accessor which can easily access to sub pixel values. * @param selection an up-to-date selection that has the same origin as the paint device */ KisRandomSubAccessorSP createRandomSubAccessor() const; /** Clear the selected pixels from the paint device */ void clearSelection(KisSelectionSP selection); Q_SIGNALS: void profileChanged(const KoColorProfile * profile); void colorSpaceChanged(const KoColorSpace *colorspace); public: friend class PaintDeviceCache; /** * Caclculates exact bounds of the device. Used internally * by a transparent caching system. The solution is very slow * because it does a linear scanline search. So the complexity * is n*n at worst. * * \see exactBounds(), nonDefaultPixelArea() */ QRect calculateExactBounds(bool nonDefaultOnly) const; public: struct MemoryReleaseObject : public QObject { ~MemoryReleaseObject(); }; static MemoryReleaseObject* createMemoryReleaseObject(); public: - struct LodDataStruct; + struct LodDataStruct { + virtual ~LodDataStruct(); + }; + QRegion regionForLodSyncing() const; LodDataStruct* createLodDataStruct(int lod); void updateLodDataStruct(LodDataStruct *dst, const QRect &srcRect); void uploadLodDataStruct(LodDataStruct *dst); void setProjectionDevice(bool value); void tesingFetchLodDevice(KisPaintDeviceSP targetDevice); private: KisPaintDevice& operator=(const KisPaintDevice&); void init(const KoColorSpace *colorSpace, KisDefaultBoundsBaseSP defaultBounds, KisNodeWSP parent, const QString& name); // Only KisPainter is allowed to have access to these low-level methods friend class KisPainter; /** * Return a vector with in order the size in bytes of the channels * in the colorspace of this paint device. */ QVector channelSizes() const; void emitColorSpaceChanged(); void emitProfileChanged(); private: friend class KisPaintDeviceFramesInterface; protected: friend class KisSelectionTest; KisNodeWSP parentNode() const; private: struct Private; Private * const m_d; }; #endif // KIS_PAINT_DEVICE_IMPL_H_ diff --git a/libs/image/kis_paint_device_cache.h b/libs/image/kis_paint_device_cache.h index f72370fe67..4ae8d28adc 100644 --- a/libs/image/kis_paint_device_cache.h +++ b/libs/image/kis_paint_device_cache.h @@ -1,163 +1,159 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_PAINT_DEVICE_CACHE_H #define __KIS_PAINT_DEVICE_CACHE_H #include "kis_lock_free_cache.h" #include class KisPaintDeviceCache { public: KisPaintDeviceCache(KisPaintDevice *paintDevice) : m_paintDevice(paintDevice), m_exactBoundsCache(paintDevice), m_nonDefaultPixelAreaCache(paintDevice), m_regionCache(paintDevice) { } KisPaintDeviceCache(const KisPaintDeviceCache &rhs) : m_paintDevice(rhs.m_paintDevice), m_exactBoundsCache(rhs.m_paintDevice), m_nonDefaultPixelAreaCache(rhs.m_paintDevice), m_regionCache(rhs.m_paintDevice) { } void setupCache() { invalidate(); } void invalidate() { m_thumbnailsValid = false; m_exactBoundsCache.invalidate(); m_nonDefaultPixelAreaCache.invalidate(); m_regionCache.invalidate(); } QRect exactBounds() { return m_exactBoundsCache.getValue(); } QRect exactBoundsAmortized() { QRect bounds; bool result = m_exactBoundsCache.tryGetValue(bounds); - const int msecThreshold = 1000; - if (!result) { - if (!m_lastCalculatedExactBounds.isValid() || - m_lastCalculatedExactBounds.elapsed() > msecThreshold) { - - m_lastCalculatedExactBounds.restart(); - bounds = exactBounds(); - } else { - bounds = m_paintDevice->extent(); - } + /** + * The calculation of the exact bounds might be too slow + * in some special cases, e.g. for an empty canvas of 7k + * by 6k. So we just always return extent, when the exact + * bounds is not available. + */ + bounds = m_paintDevice->extent(); } return bounds; } QRect nonDefaultPixelArea() { return m_nonDefaultPixelAreaCache.getValue(); } QRegion region() { return m_regionCache.getValue(); } QImage createThumbnail(qint32 w, qint32 h, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { QImage thumbnail; if(m_thumbnailsValid) { thumbnail = findThumbnail(w, h); } else { m_thumbnails.clear(); m_thumbnailsValid = true; } if(thumbnail.isNull()) { thumbnail = m_paintDevice->createThumbnail(w, h, QRect(), renderingIntent, conversionFlags); cacheThumbnail(w, h, thumbnail); } Q_ASSERT(!thumbnail.isNull() || m_paintDevice->extent().isEmpty()); return thumbnail; } private: inline QImage findThumbnail(qint32 w, qint32 h) { QImage resultImage; if (m_thumbnails.contains(w) && m_thumbnails[w].contains(h)) { resultImage = m_thumbnails[w][h]; } return resultImage; } inline void cacheThumbnail(qint32 w, qint32 h, QImage image) { m_thumbnails[w][h] = image; } private: KisPaintDevice *m_paintDevice; struct ExactBoundsCache : KisLockFreeCache { ExactBoundsCache(KisPaintDevice *paintDevice) : m_paintDevice(paintDevice) {} QRect calculateNewValue() const { return m_paintDevice->calculateExactBounds(false); } private: KisPaintDevice *m_paintDevice; }; struct NonDefaultPixelCache : KisLockFreeCache { NonDefaultPixelCache(KisPaintDevice *paintDevice) : m_paintDevice(paintDevice) {} QRect calculateNewValue() const { return m_paintDevice->calculateExactBounds(true); } private: KisPaintDevice *m_paintDevice; }; struct RegionCache : KisLockFreeCache { RegionCache(KisPaintDevice *paintDevice) : m_paintDevice(paintDevice) {} QRegion calculateNewValue() const { return m_paintDevice->dataManager()->region(); } private: KisPaintDevice *m_paintDevice; }; ExactBoundsCache m_exactBoundsCache; NonDefaultPixelCache m_nonDefaultPixelAreaCache; RegionCache m_regionCache; - QElapsedTimer m_lastCalculatedExactBounds; bool m_thumbnailsValid; QMap > m_thumbnails; }; #endif /* __KIS_PAINT_DEVICE_CACHE_H */ diff --git a/libs/image/kis_rect_mask_generator.cpp b/libs/image/kis_rect_mask_generator.cpp index 34dd98996a..52f8c00119 100644 --- a/libs/image/kis_rect_mask_generator.cpp +++ b/libs/image/kis_rect_mask_generator.cpp @@ -1,118 +1,128 @@ /* * Copyright (c) 2004,2007,2008,2009.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 //MSVC requires that Vc come first #include #include #include "kis_fast_math.h" #include "kis_rect_mask_generator.h" #include "kis_base_mask_generator.h" #include struct Q_DECL_HIDDEN KisRectangleMaskGenerator::Private { double m_c; qreal xcoeff; qreal ycoeff; qreal xfadecoeff; qreal yfadecoeff; qreal transformedFadeX; qreal transformedFadeY; }; KisRectangleMaskGenerator::KisRectangleMaskGenerator(qreal radius, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges) : KisMaskGenerator(radius, ratio, fh, fv, spikes, antialiasEdges, RECTANGLE, DefaultId), d(new Private) { if (fv == 0 && fh == 0) { d->m_c = 0; } else { d->m_c = (fv / fh); Q_ASSERT(!qIsNaN(d->m_c)); } setScale(1.0, 1.0); } +KisRectangleMaskGenerator::KisRectangleMaskGenerator(const KisRectangleMaskGenerator &rhs) + : KisMaskGenerator(rhs), + d(new Private(*rhs.d)) +{ +} + +KisMaskGenerator* KisRectangleMaskGenerator::clone() const +{ + return new KisRectangleMaskGenerator(*this); +} + +KisRectangleMaskGenerator::~KisRectangleMaskGenerator() +{ +} + void KisRectangleMaskGenerator::setScale(qreal scaleX, qreal scaleY) { KisMaskGenerator::setScale(scaleX, scaleY); d->xcoeff = 2.0 / effectiveSrcWidth(); d->ycoeff = 2.0 / effectiveSrcHeight(); d->xfadecoeff = (horizontalFade() == 0) ? 1 : (2.0 / (horizontalFade() * effectiveSrcWidth())); d->yfadecoeff = (verticalFade() == 0) ? 1 : (2.0 / (verticalFade() * effectiveSrcHeight())); setSoftness(this->softness()); } void KisRectangleMaskGenerator::setSoftness(qreal softness) { KisMaskGenerator::setSoftness(softness); qreal safeSoftnessCoeff = qreal(1.0) / qMax(qreal(0.01), softness); d->transformedFadeX = d->xfadecoeff * safeSoftnessCoeff; d->transformedFadeY = d->yfadecoeff * safeSoftnessCoeff; } -KisRectangleMaskGenerator::~KisRectangleMaskGenerator() -{ - delete d; -} - bool KisRectangleMaskGenerator::shouldSupersample() const { return effectiveSrcWidth() < 10 || effectiveSrcHeight() < 10; } quint8 KisRectangleMaskGenerator::valueAt(qreal x, qreal y) const { if (isEmpty()) return 255; qreal xr = qAbs(x /*- m_xcenter*/); qreal yr = qAbs(y /*- m_ycenter*/); fixRotation(xr, yr); xr = qAbs(xr); yr = qAbs(yr); qreal nxr = xr * d->xcoeff; qreal nyr = yr * d->ycoeff; if (nxr > 1.0 || nyr > 1.0) return 255; if (antialiasEdges()) { xr += 1.0; yr += 1.0; } qreal fxr = xr * d->transformedFadeX; qreal fyr = yr * d->transformedFadeY; if (fxr > 1.0 && (fxr > fyr || fyr < 1.0)) { return 255 * nxr * (fxr - 1.0) / (fxr - nxr); } if (fyr > 1.0 && (fyr > fxr || fxr < 1.0)) { return 255 * nyr * (fyr - 1.0) / (fyr - nyr); } return 0; } diff --git a/libs/image/kis_rect_mask_generator.h b/libs/image/kis_rect_mask_generator.h index f1d027a9b4..3d29d612d0 100644 --- a/libs/image/kis_rect_mask_generator.h +++ b/libs/image/kis_rect_mask_generator.h @@ -1,48 +1,52 @@ /* * Copyright (c) 2008-2009 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_RECT_MASK_GENERATOR_H_ #define _KIS_RECT_MASK_GENERATOR_H_ +#include #include "kritaimage_export.h" #include "kis_mask_generator.h" /** * Represent, serialize and deserialize a rectangular 8-bit mask. */ class KRITAIMAGE_EXPORT KisRectangleMaskGenerator : public KisMaskGenerator { public: KisRectangleMaskGenerator(qreal radius, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges); + KisRectangleMaskGenerator(const KisRectangleMaskGenerator &rhs); virtual ~KisRectangleMaskGenerator(); + KisMaskGenerator* clone() const; + virtual bool shouldSupersample() const; virtual quint8 valueAt(qreal x, qreal y) const; void setScale(qreal scaleX, qreal scaleY); void setSoftness(qreal softness); private: struct Private; - Private* const d; + const QScopedPointer d; }; #endif diff --git a/libs/image/kis_scalar_keyframe_channel.cpp b/libs/image/kis_scalar_keyframe_channel.cpp index 4984589f55..b3bb216535 100644 --- a/libs/image/kis_scalar_keyframe_channel.cpp +++ b/libs/image/kis_scalar_keyframe_channel.cpp @@ -1,220 +1,220 @@ /* * 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_scalar_keyframe_channel.h" #include "kis_node.h" #include "kundo2command.h" #include "kis_time_range.h" -#include "kis_global.h" - +#include +#include struct KisScalarKeyframeChannel::Private { public: Private(qreal min, qreal max) : minValue(min), maxValue(max), firstFreeIndex(0) {} qreal minValue; qreal maxValue; QMap values; int firstFreeIndex; struct InsertValueCommand; struct SetValueCommand; }; KisScalarKeyframeChannel::KisScalarKeyframeChannel(const KoID &id, qreal minValue, qreal maxValue, KisDefaultBoundsBaseSP defaultBounds) : KisKeyframeChannel(id, defaultBounds), m_d(new Private(minValue, maxValue)) { } KisScalarKeyframeChannel::~KisScalarKeyframeChannel() {} bool KisScalarKeyframeChannel::hasScalarValue() const { return true; } qreal KisScalarKeyframeChannel::minScalarValue() const { return m_d->minValue; } qreal KisScalarKeyframeChannel::maxScalarValue() const { return m_d->maxValue; } qreal KisScalarKeyframeChannel::scalarValue(const KisKeyframeSP keyframe) const { return m_d->values[keyframe->value()]; } struct KisScalarKeyframeChannel::Private::SetValueCommand : public KUndo2Command { SetValueCommand(KisScalarKeyframeChannel *channel, KisKeyframeSP keyframe, qreal oldValue, qreal newValue, KUndo2Command *parentCommand) : KUndo2Command(parentCommand), m_channel(channel), m_keyframe(keyframe), m_oldValue(oldValue), m_newValue(newValue) { } void redo() { m_channel->setScalarValueImpl(m_keyframe, m_newValue); } void undo() { m_channel->setScalarValueImpl(m_keyframe, m_oldValue); } private: KisScalarKeyframeChannel *m_channel; KisKeyframeSP m_keyframe; qreal m_oldValue; qreal m_newValue; }; void KisScalarKeyframeChannel::setScalarValueImpl(KisKeyframeSP keyframe, qreal value) { int index = keyframe->value(); m_d->values[index] = value; QRect rect = affectedRect(keyframe); KisTimeRange range = affectedFrames(keyframe->time()); requestUpdate(range, rect); } void KisScalarKeyframeChannel::setScalarValue(KisKeyframeSP keyframe, qreal value, KUndo2Command *parentCommand) { QScopedPointer tempCommand; if (!parentCommand) { tempCommand.reset(new KUndo2Command()); parentCommand = tempCommand.data(); } int index = keyframe->value(); KUndo2Command *cmd = new Private::SetValueCommand(this, keyframe, m_d->values[index], value, parentCommand); cmd->redo(); } struct KisScalarKeyframeChannel::Private::InsertValueCommand : public KUndo2Command { InsertValueCommand(KisScalarKeyframeChannel::Private *d, int index, qreal value, bool insert, KUndo2Command *parentCommand) : KUndo2Command(parentCommand), m_d(d), m_index(index), m_value(value), m_insert(insert) { } void redo() { doSwap(m_insert); } void undo() { doSwap(!m_insert); } private: void doSwap(bool insert) { if (insert) { m_d->values[m_index] = m_value; } else { m_d->values.remove(m_index); } } private: KisScalarKeyframeChannel::Private *m_d; int m_index; qreal m_value; bool m_insert; }; KisKeyframeSP KisScalarKeyframeChannel::createKeyframe(int time, const KisKeyframeSP copySrc, KUndo2Command *parentCommand) { qreal value = (copySrc.isNull() ? 0 : scalarValue(copySrc)); int index = m_d->firstFreeIndex++; KUndo2Command *cmd = new Private::InsertValueCommand(m_d.data(), index, value, true, parentCommand); cmd->redo(); return toQShared(new KisKeyframe(this, time, index)); } void KisScalarKeyframeChannel::destroyKeyframe(KisKeyframeSP key, KUndo2Command *parentCommand) { int index = key->value(); KIS_ASSERT_RECOVER_RETURN(m_d->values.contains(index)); KUndo2Command *cmd = new Private::InsertValueCommand(m_d.data(), index, m_d->values[index], false, parentCommand); cmd->redo(); } void KisScalarKeyframeChannel::uploadExternalKeyframe(KisKeyframeChannel *srcChannel, int srcTime, KisKeyframeSP dstFrame) { KisScalarKeyframeChannel *srcScalarChannel = dynamic_cast(srcChannel); KIS_ASSERT_RECOVER_RETURN(srcScalarChannel); KisKeyframeSP srcFrame = srcScalarChannel->keyframeAt(srcTime); KIS_ASSERT_RECOVER_RETURN(srcFrame); const qreal newValue = scalarValue(srcFrame); const int dstId = dstFrame->value(); KIS_ASSERT_RECOVER_RETURN(m_d->values.contains(dstId)); m_d->values[dstId] = newValue; } QRect KisScalarKeyframeChannel::affectedRect(KisKeyframeSP key) { Q_UNUSED(key); if (node()) { return node()->extent(); } else { return QRect(); } } void KisScalarKeyframeChannel::saveKeyframe(KisKeyframeSP keyframe, QDomElement keyframeElement, const QString &layerFilename) { Q_UNUSED(layerFilename); - keyframeElement.setAttribute("value", m_d->values[keyframe->value()]); + keyframeElement.setAttribute("value", KisDomUtils::toString(m_d->values[keyframe->value()])); } KisKeyframeSP KisScalarKeyframeChannel::loadKeyframe(const QDomElement &keyframeNode) { int time = keyframeNode.toElement().attribute("time").toUInt(); - QVariant value = keyframeNode.toElement().attribute("value"); + qreal value = KisDomUtils::toDouble(keyframeNode.toElement().attribute("value")); KUndo2Command tempParentCommand; KisKeyframeSP keyframe = createKeyframe(time, KisKeyframeSP(), &tempParentCommand); - setScalarValue(keyframe, value.toReal()); + setScalarValue(keyframe, value); return keyframe; } diff --git a/libs/image/kis_selection_based_layer.cpp b/libs/image/kis_selection_based_layer.cpp index 198814878e..50fb3c8a58 100644 --- a/libs/image/kis_selection_based_layer.cpp +++ b/libs/image/kis_selection_based_layer.cpp @@ -1,290 +1,306 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2005 C. Boemann * Copyright (c) 2009 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_selection_based_layer.h" #include #include "kis_debug.h" #include #include "kis_image.h" #include "kis_painter.h" #include "kis_default_bounds.h" #include "kis_selection.h" #include "kis_pixel_selection.h" #include "filter/kis_filter_configuration.h" #include "filter/kis_filter_registry.h" #include "filter/kis_filter.h" #include "kis_raster_keyframe_channel.h" struct Q_DECL_HIDDEN KisSelectionBasedLayer::Private { public: Private() : useSelectionInProjection(true) {} KisSelectionSP selection; KisPaintDeviceSP paintDevice; bool useSelectionInProjection; }; KisSelectionBasedLayer::KisSelectionBasedLayer(KisImageWSP image, const QString &name, KisSelectionSP selection, KisFilterConfiguration *filterConfig, bool useGeneratorRegistry) : KisLayer(image.data(), name, OPACITY_OPAQUE_U8), KisNodeFilterInterface(filterConfig, useGeneratorRegistry), m_d(new Private()) { if (!selection) initSelection(); else setInternalSelection(selection); m_d->paintDevice = new KisPaintDevice(this, image->colorSpace(), new KisDefaultBounds(image)); + connect(image.data(), SIGNAL(sigSizeChanged(QPointF,QPointF)), SLOT(slotImageSizeChanged())); } KisSelectionBasedLayer::KisSelectionBasedLayer(const KisSelectionBasedLayer& rhs) : KisLayer(rhs) , KisIndirectPaintingSupport() , KisNodeFilterInterface(rhs) , m_d(new Private()) { setInternalSelection(rhs.m_d->selection); m_d->paintDevice = new KisPaintDevice(*rhs.m_d->paintDevice.data()); } KisSelectionBasedLayer::~KisSelectionBasedLayer() { delete m_d; } void KisSelectionBasedLayer::initSelection() { m_d->selection = new KisSelection(new KisDefaultBounds(image())); - m_d->selection->pixelSelection()->select(image()->bounds()); + quint8 newDefaultPixel = MAX_SELECTED; + m_d->selection->pixelSelection()->setDefaultPixel(&newDefaultPixel); m_d->selection->setParentNode(this); m_d->selection->updateProjection(); } +void KisSelectionBasedLayer::slotImageSizeChanged() +{ + if (m_d->selection) { + /** + * Make sure exactBounds() of the selection got recalculated after + * the image changed + */ + m_d->selection->pixelSelection()->setDirty(); + setDirty(); + } +} + void KisSelectionBasedLayer::setImage(KisImageWSP image) { m_d->paintDevice->setDefaultBounds(new KisDefaultBounds(image)); KisLayer::setImage(image); + + connect(image.data(), SIGNAL(sigSizeChanged(QPointF,QPointF)), SLOT(slotImageSizeChanged())); } bool KisSelectionBasedLayer::allowAsChild(KisNodeSP node) const { return node->inherits("KisMask"); } KisPaintDeviceSP KisSelectionBasedLayer::original() const { return m_d->paintDevice; } KisPaintDeviceSP KisSelectionBasedLayer::paintDevice() const { return m_d->selection->pixelSelection(); } bool KisSelectionBasedLayer::needProjection() const { return m_d->selection; } void KisSelectionBasedLayer::setUseSelectionInProjection(bool value) const { m_d->useSelectionInProjection = value; } KisSelectionSP KisSelectionBasedLayer::fetchComposedInternalSelection(const QRect &rect) const { if (!m_d->selection) return 0; m_d->selection->updateProjection(rect); KisSelectionSP tempSelection = m_d->selection; lockTemporaryTarget(); if (hasTemporaryTarget()) { /** * Cloning a selection with COW * FIXME: check whether it's faster than usual bitBlt'ing */ tempSelection = new KisSelection(*tempSelection); KisPainter gc2(tempSelection->pixelSelection()); setupTemporaryPainter(&gc2); gc2.bitBlt(rect.topLeft(), temporaryTarget(), rect); } unlockTemporaryTarget(); return tempSelection; } void KisSelectionBasedLayer::copyOriginalToProjection(const KisPaintDeviceSP original, KisPaintDeviceSP projection, const QRect& rect) const { KisSelectionSP tempSelection; if (m_d->useSelectionInProjection) { tempSelection = fetchComposedInternalSelection(rect); } projection->clear(rect); KisPainter::copyAreaOptimized(rect.topLeft(), original, projection, rect, tempSelection); } QRect KisSelectionBasedLayer::cropChangeRectBySelection(const QRect &rect) const { return m_d->selection ? rect & m_d->selection->selectedRect() : rect; } QRect KisSelectionBasedLayer::needRect(const QRect &rect, PositionToFilthy pos) const { Q_UNUSED(pos); return rect; } void KisSelectionBasedLayer::resetCache(const KoColorSpace *colorSpace) { if (!colorSpace) colorSpace = image()->colorSpace(); if (!m_d->paintDevice || !(*m_d->paintDevice->colorSpace() == *colorSpace)) { m_d->paintDevice = new KisPaintDevice(this, colorSpace, new KisDefaultBounds(image())); } else { m_d->paintDevice->clear(); } } KisSelectionSP KisSelectionBasedLayer::internalSelection() const { return m_d->selection; } void KisSelectionBasedLayer::setInternalSelection(KisSelectionSP selection) { if (selection) { m_d->selection = new KisSelection(*selection.data()); m_d->selection->setParentNode(this); m_d->selection->updateProjection(); } else { m_d->selection = 0; } if (selection->pixelSelection()->defaultBounds()->bounds() != image()->bounds()) { qWarning() << "WARNING: KisSelectionBasedLayer::setInternalSelection" << "New selection has suspicious default bounds"; qWarning() << "WARNING:" << ppVar(selection->pixelSelection()->defaultBounds()->bounds()); qWarning() << "WARNING:" << ppVar(image()->bounds()); } } qint32 KisSelectionBasedLayer::x() const { return m_d->selection ? m_d->selection->x() : 0; } qint32 KisSelectionBasedLayer::y() const { return m_d->selection ? m_d->selection->y() : 0; } void KisSelectionBasedLayer::setX(qint32 x) { if (m_d->selection) { m_d->selection->setX(x); } } void KisSelectionBasedLayer::setY(qint32 y) { if (m_d->selection) { m_d->selection->setY(y); } } void KisSelectionBasedLayer::setDirty(const QRect & rect) { KisLayer::setDirty(rect); } KisKeyframeChannel *KisSelectionBasedLayer::requestKeyframeChannel(const QString &id) { if (id == KisKeyframeChannel::Content.id()) { KisRasterKeyframeChannel *contentChannel = m_d->selection->pixelSelection()->createKeyframeChannel(KisKeyframeChannel::Content); contentChannel->setFilenameSuffix(".pixelselection"); return contentChannel; } return KisLayer::requestKeyframeChannel(id); } void KisSelectionBasedLayer::setDirty() { Q_ASSERT(image()); setDirty(image()->bounds()); } QRect KisSelectionBasedLayer::extent() const { Q_ASSERT(image()); return m_d->selection ? m_d->selection->selectedRect() : image()->bounds(); } QRect KisSelectionBasedLayer::exactBounds() const { Q_ASSERT(image()); return m_d->selection ? m_d->selection->selectedExactRect() : image()->bounds(); } QImage KisSelectionBasedLayer::createThumbnail(qint32 w, qint32 h) { KisSelectionSP originalSelection = internalSelection(); KisPaintDeviceSP originalDevice = original(); return originalDevice && originalSelection ? originalDevice->createThumbnail(w, h, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) : QImage(); } diff --git a/libs/image/kis_selection_based_layer.h b/libs/image/kis_selection_based_layer.h index 9ad7451381..b9ab24bb64 100644 --- a/libs/image/kis_selection_based_layer.h +++ b/libs/image/kis_selection_based_layer.h @@ -1,210 +1,211 @@ /* * Copyright (c) 2006 Boudewijn Rempt * (c) 2009 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ #ifndef KIS_SELECTION_BASED_LAYER_H_ #define KIS_SELECTION_BASED_LAYER_H_ #include #include "kis_types.h" #include "kis_layer.h" #include "kis_indirect_painting_support.h" #include #include "kis_node_filter_interface.h" class KisFilterConfiguration; /** * @class KisSelectionBasedLayer describes base behaviour for * selection base classes like KisAdjustmentLayer and KisGeneratorLayer. * These classes should have a persistent selection that controls * the area where filter/generators are applied. The area outside * this selection is not affected by the layer */ class KRITAIMAGE_EXPORT KisSelectionBasedLayer : public KisLayer, public KisIndirectPaintingSupport, public KisNodeFilterInterface { Q_OBJECT public: /** * creates a new layer with the given selection. * Note that the selection will be _copied_ (with COW, though). * @param image the image to set this layer to * @param name name of the layer * @param selection is a mask used by the layer to know * where to apply the filter/generator. */ KisSelectionBasedLayer(KisImageWSP image, const QString &name, KisSelectionSP selection, KisFilterConfiguration *filterConfig, bool useGeneratorRegistry = false); KisSelectionBasedLayer(const KisSelectionBasedLayer& rhs); virtual ~KisSelectionBasedLayer(); /** * tells whether the @node can be a child of this layer * @param node to be connected node * @return tells if to be connected is a child of KisMask */ bool allowAsChild(KisNodeSP node) const; void setImage(KisImageWSP image); KisPaintDeviceSP original() const; KisPaintDeviceSP paintDevice() const; bool needProjection() const; /** * resets cached projection of lower layer to a new device * @return void */ void resetCache(const KoColorSpace *colorSpace = 0); /** * for KisLayer::setDirty(const QRegion&) */ using KisLayer::setDirty; /** * Mark a layer as dirty. We can't use KisLayer's one * as our extent() function doesn't fit for this */ void setDirty(); void setDirty(const QRect & rect); public: /** * Returns the selection of the layer * * Do not mix it with selection() which returns * the currently active selection of the image */ KisSelectionSP internalSelection() const; /** * sets the selection of this layer to a copy of * selection * @param selection the selection to set * @return void */ void setInternalSelection(KisSelectionSP selection); /** * When painted in indirect painting mode, the internal selection * might not contain actual selection, because a part of it is * stored on an indirect painting device. This method returns the * merged copy of the real selection. The area in \p rect only is * guaranteed to be prepared. The content of the rest of the * selection is undefined. */ KisSelectionSP fetchComposedInternalSelection(const QRect &rect) const; /** * gets this layer's x coordinate, taking selection into account * @return x-coordinate value */ qint32 x() const; /** * gets this layer's y coordinate, taking selection into account * @return y-coordinate value */ qint32 y() const; /** * sets this layer's y coordinate, taking selection into account * @param x x coordinate */ void setX(qint32 x); /** * sets this layer's y coordinate, taking selection into account * @param y y coordinate */ void setY(qint32 y); public: /** * gets an approximation of where the bounds on actual data * are in this layer, taking selection into account */ QRect extent() const; /** * returns the exact bounds of where the actual data resides * in this layer, taking selection into account */ QRect exactBounds() const; /** * copies the image and reformats it to thumbnail size * and returns the new thumbnail image. * @param w width of the thumbnail to create * @param h height of the thumbnail to create * @return the thumbnail image created. */ QImage createThumbnail(qint32 w, qint32 h); protected: // override from KisLayer void copyOriginalToProjection(const KisPaintDeviceSP original, KisPaintDeviceSP projection, const QRect& rect) const; // override from KisNode QRect needRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const; protected: void initSelection(); QRect cropChangeRectBySelection(const QRect &rect) const; /** * Sets if the selection should be used in * copyOriginalToProjection() method. * * Default value is 'true'. The descendants should override it to * get desired behaviour. * * Must be called only once in the child's constructor */ void setUseSelectionInProjection(bool value) const; KisKeyframeChannel *requestKeyframeChannel(const QString &id); public Q_SLOTS: + void slotImageSizeChanged(); /** * gets this layer. Overriddes function in * KisIndirectPaintingSupport * @return this AdjustmentLayer */ KisLayer* layer() { return this; } private: struct Private; Private * const m_d; }; #endif /* KIS_SELECTION_BASED_LAYER_H_ */ diff --git a/libs/image/kis_sync_lod_cache_stroke_strategy.cpp b/libs/image/kis_sync_lod_cache_stroke_strategy.cpp index e1627e152c..5715b7cd0f 100644 --- a/libs/image/kis_sync_lod_cache_stroke_strategy.cpp +++ b/libs/image/kis_sync_lod_cache_stroke_strategy.cpp @@ -1,170 +1,171 @@ /* * 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_sync_lod_cache_stroke_strategy.h" #include #include #include "krita_utils.h" #include "kis_layer_utils.h" struct KisSyncLodCacheStrokeStrategy::Private { KisImageWSP image; QHash dataObjects; ~Private() { qDeleteAll(dataObjects); dataObjects.clear(); } class InitData : public KisStrokeJobData { public: InitData(KisPaintDeviceSP _device) : KisStrokeJobData(SEQUENTIAL), device(_device) {} KisPaintDeviceSP device; }; class ProcessData : public KisStrokeJobData { public: ProcessData(KisPaintDeviceSP _device, const QRect &_rect) : KisStrokeJobData(CONCURRENT), device(_device), rect(_rect) {} KisPaintDeviceSP device; QRect rect; }; class AdditionalProcessNode : public KisStrokeJobData { public: AdditionalProcessNode(KisNodeSP _node) : KisStrokeJobData(SEQUENTIAL), node(_node) {} KisNodeSP node; }; }; KisSyncLodCacheStrokeStrategy::KisSyncLodCacheStrokeStrategy(KisImageWSP image, bool forgettable) : KisSimpleStrokeStrategy("SyncLodCacheStroke", kundo2_i18n("Instant Preview")), m_d(new Private) { m_d->image = image; /** * We shouldn't start syncing before all the updates are * done. Otherwise we might get artifacts! */ enableJob(KisSimpleStrokeStrategy::JOB_INIT, true, KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE); enableJob(KisSimpleStrokeStrategy::JOB_DOSTROKE); enableJob(KisSimpleStrokeStrategy::JOB_FINISH); enableJob(KisSimpleStrokeStrategy::JOB_CANCEL, true, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); setRequestsOtherStrokesToEnd(false); setClearsRedoOnStart(false); setCanForgetAboutMe(forgettable); } KisSyncLodCacheStrokeStrategy::~KisSyncLodCacheStrokeStrategy() { } void KisSyncLodCacheStrokeStrategy::doStrokeCallback(KisStrokeJobData *data) { Private::InitData *initData = dynamic_cast(data); Private::ProcessData *processData = dynamic_cast(data); Private::AdditionalProcessNode *additionalProcessNode = dynamic_cast(data); if (initData) { KisPaintDeviceSP dev = initData->device; const int lod = dev->defaultBounds()->currentLevelOfDetail(); m_d->dataObjects.insert(dev, dev->createLodDataStruct(lod)); } else if (processData) { KisPaintDeviceSP dev = processData->device; KIS_ASSERT(m_d->dataObjects.contains(dev)); KisPaintDevice::LodDataStruct *data = m_d->dataObjects.value(dev); dev->updateLodDataStruct(data, processData->rect); } else if (additionalProcessNode) { additionalProcessNode->node->syncLodCache(); } } void KisSyncLodCacheStrokeStrategy::finishStrokeCallback() { auto it = m_d->dataObjects.begin(); auto end = m_d->dataObjects.end(); for (; it != end; ++it) { KisPaintDeviceSP dev = it.key(); dev->uploadLodDataStruct(it.value()); } + qDeleteAll(m_d->dataObjects); m_d->dataObjects.clear(); } void KisSyncLodCacheStrokeStrategy::cancelStrokeCallback() { qDeleteAll(m_d->dataObjects); m_d->dataObjects.clear(); } QList KisSyncLodCacheStrokeStrategy::createJobsData(KisImageWSP _image) { using KisLayerUtils::recursiveApplyNodes; using KritaUtils::splitRegionIntoPatches; using KritaUtils::optimalPatchSize; KisImageSP image = _image; KisPaintDeviceList deviceList; QList jobsData; recursiveApplyNodes(image->root(), [&deviceList](KisNodeSP node) { deviceList << node->getLodCapableDevices(); }); KritaUtils::makeContainerUnique(deviceList); Q_FOREACH (KisPaintDeviceSP device, deviceList) { jobsData << new Private::InitData(device); } Q_FOREACH (KisPaintDeviceSP device, deviceList) { QRegion region = device->regionForLodSyncing(); QVector rects = splitRegionIntoPatches(region, optimalPatchSize()); Q_FOREACH (const QRect &rc, rects) { jobsData << new Private::ProcessData(device, rc); } } recursiveApplyNodes(image->root(), [&jobsData](KisNodeSP node) { jobsData << new Private::AdditionalProcessNode(node); }); return jobsData; } diff --git a/libs/image/kis_timed_signal_threshold.cpp b/libs/image/kis_timed_signal_threshold.cpp index 98a9244e51..d764f1fce4 100644 --- a/libs/image/kis_timed_signal_threshold.cpp +++ b/libs/image/kis_timed_signal_threshold.cpp @@ -1,82 +1,91 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_timed_signal_threshold.h" #include #include "kis_debug.h" struct KisTimedSignalThreshold::Private { Private(int _delay, int _cancelDelay) : delay(_delay), - cancelDelay(_cancelDelay), + cancelDelay(0), enabled(true) { - if (cancelDelay < 0) { - cancelDelay = 2 * delay; - } + setCancelDelay(_cancelDelay); + } + + void setCancelDelay(int value) { + cancelDelay = value >= 0 ? value : 2 * delay; } QElapsedTimer timer; int delay; int cancelDelay; bool enabled; }; KisTimedSignalThreshold::KisTimedSignalThreshold(int delay, int cancelDelay, QObject *parent) : QObject(parent), m_d(new Private(delay, cancelDelay)) { } KisTimedSignalThreshold::~KisTimedSignalThreshold() { } void KisTimedSignalThreshold::forceDone() { stop(); emit timeout(); } void KisTimedSignalThreshold::start() { + if (!m_d->enabled) return; + if (!m_d->timer.isValid()) { m_d->timer.start(); - } else if (m_d->timer.elapsed() > 2 * m_d->delay) { + } else if (m_d->timer.elapsed() > m_d->cancelDelay) { stop(); } else if (m_d->timer.elapsed() > m_d->delay) { forceDone(); } } void KisTimedSignalThreshold::stop() { m_d->timer.invalidate(); } void KisTimedSignalThreshold::setEnabled(bool value) { m_d->enabled = value; if (!m_d->enabled) { stop(); } } +void KisTimedSignalThreshold::setDelayThreshold(int delay, int cancelDelay) +{ + m_d->delay = delay; + m_d->setCancelDelay(cancelDelay); +} diff --git a/libs/image/kis_timed_signal_threshold.h b/libs/image/kis_timed_signal_threshold.h index 55051790a7..242cb63022 100644 --- a/libs/image/kis_timed_signal_threshold.h +++ b/libs/image/kis_timed_signal_threshold.h @@ -1,71 +1,77 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_TIMED_SIGNAL_THRESHOLD_H #define __KIS_TIMED_SIGNAL_THRESHOLD_H #include "kritaimage_export.h" #include #include /** * Emits the timeout() signal if and only if the flow of start() * events has been coming for a consecutive \p delay of milliseconds. * If the events were not coming for \p cancelDelay of milliseconds the * counting is dropped and the new period is started. */ class KRITAIMAGE_EXPORT KisTimedSignalThreshold : public QObject { Q_OBJECT public: KisTimedSignalThreshold(int delay, int cancelDelay = -1, QObject *parent = 0); ~KisTimedSignalThreshold(); public Q_SLOTS: /** * Stops counting and emits the signal forcefully */ void forceDone(); /** * Start/continue counting and if the signal flow is stable enough * (longer than \p delay and shorter than \p cancelDelay), the * timeout signal in emitted. */ void start(); /** * Stops counting the signals flow */ void stop(); /** * Enable or disable emitting the signal */ void setEnabled(bool value); + + /** + * The peiod of time, after which the the signal will be emitted + */ + void setDelayThreshold(int delay, int cancelDelay = -1); + Q_SIGNALS: void timeout(); private: struct Private; const QScopedPointer m_d; }; #endif /* __KIS_TIMED_SIGNAL_THRESHOLD_H */ diff --git a/libs/image/recorder/kis_recorded_paint_action.cpp b/libs/image/recorder/kis_recorded_paint_action.cpp index 869381e9bb..473f589942 100644 --- a/libs/image/recorder/kis_recorded_paint_action.cpp +++ b/libs/image/recorder/kis_recorded_paint_action.cpp @@ -1,467 +1,468 @@ /* * Copyright (c) 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 //MSVC requires that Vc come first #include "recorder/kis_recorded_paint_action.h" #include #include #include #include #include #include #include "kis_node.h" #include "kis_mask_generator.h" #include "kis_painter.h" #include #include "kis_paintop_registry.h" #include "kis_transaction.h" #include "kis_undo_adapter.h" #include #include #include "kis_paint_device.h" #include "kis_image.h" #include "kis_layer.h" #include "kis_play_info.h" #include "kis_node_query_path.h" #include // Recorder #include "kis_recorded_action_factory_registry.h" #include "kis_recorded_action_load_context.h" #include "kis_recorded_action_save_context.h" #include #include #include #include #include + struct Q_DECL_HIDDEN KisRecordedPaintAction::Private { KisPaintOpPresetSP paintOpPreset; KoColor foregroundColor; KoColor backgroundColor; qreal opacity; ///< opacity in the range 0.0 -> 100.0 bool paintIncremental; QString compositeOp; KisPainter::StrokeStyle strokeStyle; KisPainter::FillStyle fillStyle; const KoPattern* pattern; const KoAbstractGradient* gradient; const KisFilterConfiguration* generator; }; KisRecordedPaintAction::KisRecordedPaintAction(const QString & id, const QString & name, const KisNodeQueryPath& path, const KisPaintOpPresetSP paintOpPreset) : KisRecordedNodeAction(id, name, path) , d(new Private) { if (paintOpPreset) { d->paintOpPreset = paintOpPreset; } d->opacity = 1.0; d->paintIncremental = true; d->compositeOp = COMPOSITE_OVER; d->strokeStyle = KisPainter::StrokeStyleBrush; d->fillStyle = KisPainter::FillStyleNone; d->pattern = 0; d->gradient = 0; d->generator = 0; } KisRecordedPaintAction::KisRecordedPaintAction(const KisRecordedPaintAction& rhs) : KisRecordedNodeAction(rhs), d(new Private(*rhs.d)) { } KisRecordedPaintAction::~KisRecordedPaintAction() { delete d; } void KisRecordedPaintAction::toXML(QDomDocument& doc, QDomElement& elt, KisRecordedActionSaveContext* context) const { KisRecordedAction::toXML(doc, elt, context); // Paint op presset if (d->paintOpPreset) { QDomElement paintopPressetElt = doc.createElement("PaintopPreset"); d->paintOpPreset->toXML(doc, paintopPressetElt); elt.appendChild(paintopPressetElt); } // ForegroundColor QDomElement foregroundColorElt = doc.createElement("ForegroundColor"); d->foregroundColor.toXML(doc, foregroundColorElt); elt.appendChild(foregroundColorElt); // BackgroundColor QDomElement backgroundColorElt = doc.createElement("BackgroundColor"); d->backgroundColor.toXML(doc, backgroundColorElt); elt.appendChild(backgroundColorElt); // Opacity - elt.setAttribute("opacity", d->opacity); + elt.setAttribute("opacity", KisDomUtils::toString(d->opacity)); // paintIncremental elt.setAttribute("paintIncremental", d->paintIncremental); // compositeOp elt.setAttribute("compositeOp", d->compositeOp); // Save stroke style switch(d->strokeStyle) { case KisPainter::StrokeStyleNone: elt.setAttribute("strokeStyle", "None"); break; case KisPainter::StrokeStyleBrush: elt.setAttribute("strokeStyle", "Brush"); break; } // Save fill style switch(d->fillStyle) { case KisPainter::FillStyleNone: elt.setAttribute("fillStyle", "None"); break; case KisPainter::FillStyleForegroundColor: elt.setAttribute("fillStyle", "PaintColor"); break; case KisPainter::FillStyleBackgroundColor: elt.setAttribute("fillStyle", "AlternativeColor"); break; case KisPainter::FillStylePattern: elt.setAttribute("fillStyle", "Pattern"); context->savePattern(d->pattern); elt.setAttribute("pattern", d->pattern->name()); break; case KisPainter::FillStyleGradient: elt.setAttribute("fillStyle", "Gradient"); context->saveGradient(d->gradient); elt.setAttribute("gradient", d->gradient->name()); break; case KisPainter::FillStyleStrokes: elt.setAttribute("fillStyle", "Strokes"); break; case KisPainter::FillStyleGenerator: elt.setAttribute("fillStyle", "Generator"); if (d->generator) { elt.setAttribute("generator", d->generator->name()); QDomElement filterConfigElt = doc.createElement("Generator"); d->generator->toXML(doc, filterConfigElt); elt.appendChild(filterConfigElt); } break; } } KisPaintOpPresetSP KisRecordedPaintAction::paintOpPreset() const { return d->paintOpPreset; } void KisRecordedPaintAction::setPaintOpPreset(KisPaintOpPresetSP preset) { d->paintOpPreset = preset; } qreal KisRecordedPaintAction::opacity() const { return d->opacity; } void KisRecordedPaintAction::setOpacity(qreal opacity) { d->opacity = opacity; } KoColor KisRecordedPaintAction::paintColor() const { return d->foregroundColor; } void KisRecordedPaintAction::setPaintColor(const KoColor& color) { d->foregroundColor = color; } KoColor KisRecordedPaintAction::backgroundColor() const { return d->backgroundColor; } void KisRecordedPaintAction::setBackgroundColor(const KoColor& color) { d->backgroundColor = color; } QString KisRecordedPaintAction::compositeOp() { return d->compositeOp; } void KisRecordedPaintAction::setCompositeOp(const QString& id) { d->compositeOp = id; } void KisRecordedPaintAction::setPaintIncremental(bool v) { d->paintIncremental = v; } void KisRecordedPaintAction::setStrokeStyle(KisPainter::StrokeStyle strokeStyle) { d->strokeStyle = strokeStyle; } void KisRecordedPaintAction::setFillStyle(KisPainter::FillStyle fillStyle) { d->fillStyle = fillStyle; } KisPainter::FillStyle KisRecordedPaintAction::fillStyle() const { return d->fillStyle; } void KisRecordedPaintAction::setPattern(const KoPattern* pattern) { d->pattern = pattern; } void KisRecordedPaintAction::setGradient(const KoAbstractGradient* gradient) { d->gradient = gradient; } void KisRecordedPaintAction::setGenerator(const KisFilterConfiguration * generator) { d->generator = generator; } void KisRecordedPaintAction::play(KisNodeSP node, const KisPlayInfo& info, KoUpdater* _updater) const { dbgUI << "Play recorded paint action on node : " << node->name() ; KisTransaction transaction(node->paintDevice()); KisPaintDeviceSP target = 0; if (d->paintIncremental) { target = node->paintDevice(); } else { target = node->paintDevice()->createCompositionSourceDevice(); } KisPainter* painter = createPainter(target); painter->setProgress(_updater); if (d->paintIncremental) { painter->setCompositeOp(d->compositeOp); painter->setOpacity(d->opacity * 255); } else { painter->setCompositeOp(node->paintDevice()->colorSpace()->compositeOp(COMPOSITE_ALPHA_DARKEN)); painter->setOpacity(OPACITY_OPAQUE_U8); } painter->setPaintColor(d->foregroundColor); painter->setBackgroundColor(d->backgroundColor); painter->setStrokeStyle(d->strokeStyle); painter->setFillStyle(d->fillStyle); painter->setPattern(d->pattern); painter->setGradient(d->gradient); painter->setGenerator(d->generator); if (d->paintOpPreset) { painter->setPaintOpPreset(d->paintOpPreset, node, info.image()); } playPaint(info, painter); if (!d->paintIncremental) { KisPainter painter2(node->paintDevice()); painter2.setCompositeOp(d->compositeOp); painter2.setOpacity(d->opacity * 255); QVector dirtyRects = painter->takeDirtyRegion(); Q_FOREACH (const QRect &rc, dirtyRects) { painter2.bitBlt(rc.topLeft(), target, rc); } node->setDirty(painter2.takeDirtyRegion()); } else { node->setDirty(painter->takeDirtyRegion()); } delete painter; transaction.commit(info.undoAdapter()); } KisPainter* KisRecordedPaintAction::createPainter(KisPaintDeviceSP device) const { return new KisPainter(device); } void KisRecordedPaintActionFactory::setupPaintAction(KisRecordedPaintAction* action, const QDomElement& elt, const KisRecordedActionLoadContext* context) { QString name = elt.attribute("name"); qreal opacity = opacityFromXML(elt); dbgKrita << ppVar(opacity); bool paintIncremental = paintIncrementalFromXML(elt); QString compositeOp = compositeOpFromXML(elt); // Decode colors KoColor bC = backgroundColorFromXML(elt); KoColor fC = paintColorFromXML(elt); action->setName(name); action->setBackgroundColor(bC); action->setPaintColor(fC); action->setOpacity(opacity); action->setPaintIncremental(paintIncremental); action->setCompositeOp(compositeOp); // Load stroke style QString strokeAttr = elt.attribute("strokeStyle", "None"); if (strokeAttr == "Brush" ) { action->setStrokeStyle(KisPainter::StrokeStyleBrush); } else { // "None" action->setStrokeStyle(KisPainter::StrokeStyleNone); } // Save fill style QString fillAttr = elt.attribute("fillStyle", "None"); if (fillAttr == "PaintColor") { action->setFillStyle(KisPainter::FillStyleForegroundColor); } else if(fillAttr == "AlternativeColor") { action->setFillStyle(KisPainter::FillStyleBackgroundColor); } else if(fillAttr == "Pattern") { const KoPattern* pattern = context->pattern(elt.attribute("pattern")); if (pattern) { action->setFillStyle(KisPainter::FillStylePattern); action->setPattern(pattern); } else { action->setFillStyle(KisPainter::FillStyleNone); } } else if(fillAttr == "Gradient") { const KoAbstractGradient* gradient = context->gradient(elt.attribute("gradient")); if (gradient) { action->setFillStyle(KisPainter::FillStyleGradient); action->setGradient(gradient); } else { action->setFillStyle(KisPainter::FillStyleNone); } } else if(fillAttr == "Strokes") { action->setFillStyle(KisPainter::FillStyleStrokes); } else if(fillAttr == "Generator") { KisGeneratorSP g = KisGeneratorRegistry::instance()->value(elt.attribute("generator")); KisFilterConfiguration* config = 0; if (g) { config = g->defaultConfiguration(0); QDomElement paramsElt = elt.firstChildElement("Generator"); if (config && !paramsElt.isNull()) { config->fromXML(paramsElt); } } if(config) { action->setFillStyle(KisPainter::FillStyleGenerator); action->setGenerator(config); } else { action->setFillStyle(KisPainter::FillStyleNone); } } } KisPaintOpPresetSP KisRecordedPaintActionFactory::paintOpPresetFromXML(const QDomElement& elt) { QDomElement settingsElt = elt.firstChildElement("PaintopPreset"); if (!settingsElt.isNull()) { KisPaintOpPresetSP settings = new KisPaintOpPreset; settings->fromXML(settingsElt); return settings; } else { errImage << "No found"; return 0; } } KoColor KisRecordedPaintActionFactory::paintColorFromXML(const QDomElement& elt) { return colorFromXML(elt, "ForegroundColor"); } KoColor KisRecordedPaintActionFactory::backgroundColorFromXML(const QDomElement& elt) { return colorFromXML(elt, "BackgroundColor"); } KoColor KisRecordedPaintActionFactory::colorFromXML(const QDomElement& elt, const QString& elementName) { QDomElement colorElt = elt.firstChildElement(elementName); KoColor bC; if (!colorElt.isNull()) { bC = KoColor::fromXML(colorElt.firstChildElement(), Integer8BitsColorDepthID.id(), QHash()); bC.setOpacity(quint8(255)); dbgImage << elementName << " color : " << bC.toQColor(); } else { dbgImage << "Warning: no <" << elementName << " /> found"; } return bC; } qreal KisRecordedPaintActionFactory::opacityFromXML(const QDomElement& elt) { return KisDomUtils::toDouble(elt.attribute("opacity", "1.0")); } bool KisRecordedPaintActionFactory::paintIncrementalFromXML(const QDomElement& elt) { return KisDomUtils::toInt(elt.attribute("paintIncremental", "1")); } QString KisRecordedPaintActionFactory::compositeOpFromXML(const QDomElement& elt) { return elt.attribute("compositeOp", COMPOSITE_OVER); } KisNodeQueryPath KisRecordedPaintActionFactory::nodeQueryPathFromXML(const QDomElement& elt) { return KisNodeQueryPath::fromString(elt.attribute("path")); } diff --git a/libs/image/recorder/kis_recorded_path_paint_action.cpp b/libs/image/recorder/kis_recorded_path_paint_action.cpp index abc15be983..db57127038 100644 --- a/libs/image/recorder/kis_recorded_path_paint_action.cpp +++ b/libs/image/recorder/kis_recorded_path_paint_action.cpp @@ -1,260 +1,260 @@ /* * Copyright (c) 2007 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 //MSVC requires that Vc come first #include "recorder/kis_recorded_path_paint_action.h" #include #include #include #include #include #include "kis_node.h" #include "kis_mask_generator.h" #include "kis_painter.h" #include #include "kis_paintop_registry.h" #include "recorder/kis_recorded_action_factory_registry.h" #include "kis_transaction.h" #include "kis_undo_adapter.h" #include #include #include "kis_paint_device.h" #include "kis_image.h" #include "kis_layer.h" #include "kis_node_query_path.h" #include struct Q_DECL_HIDDEN KisRecordedPathPaintAction::Private { struct BezierCurveSlice { enum Type { Point, Line, Curve }; Type type; KisPaintInformation point1; QPointF control1; QPointF control2; KisPaintInformation point2; }; QList curveSlices; }; KisRecordedPathPaintAction::KisRecordedPathPaintAction( const KisNodeQueryPath& path, const KisPaintOpPresetSP preset) : KisRecordedPaintAction("PathPaintAction", i18n("Path"), path, preset) , d(new Private) { } KisRecordedPathPaintAction::KisRecordedPathPaintAction(const KisRecordedPathPaintAction& rhs) : KisRecordedPaintAction(rhs), d(new Private(*rhs.d)) { } KisRecordedPathPaintAction::~KisRecordedPathPaintAction() { delete d; } void KisRecordedPathPaintAction::addPoint(const KisPaintInformation& info) { Private::BezierCurveSlice slice; slice.type = Private::BezierCurveSlice::Point; slice.point1 = info; d->curveSlices.append(slice); } void KisRecordedPathPaintAction::addLine(const KisPaintInformation& point1, const KisPaintInformation& point2) { Private::BezierCurveSlice slice; slice.type = Private::BezierCurveSlice::Line; slice.point1 = point1; slice.point2 = point2; d->curveSlices.append(slice); } void KisRecordedPathPaintAction::addPolyLine(const QList& points) { QPointF previousPoint = points[0]; for(int i = 1; i < points.size(); ++i) { QPointF pt = points[i]; addLine(KisPaintInformation(previousPoint), KisPaintInformation(pt)); previousPoint = pt; } } void KisRecordedPathPaintAction::addCurve(const KisPaintInformation& point1, const QPointF& control1, const QPointF& control2, const KisPaintInformation& point2) { Private::BezierCurveSlice slice; slice.type = Private::BezierCurveSlice::Curve; slice.point1 = point1; slice.control1 = control1; slice.control2 = control2; slice.point2 = point2; d->curveSlices.append(slice); } void KisRecordedPathPaintAction::playPaint(const KisPlayInfo&, KisPainter* painter) const { dbgImage << "play path paint action with " << d->curveSlices.size() << " slices"; if (d->curveSlices.size() <= 0) return; KisDistanceInformation savedDist; Q_FOREACH (const Private::BezierCurveSlice &slice, d->curveSlices) { switch(slice.type) { case Private::BezierCurveSlice::Point: painter->paintAt(slice.point1, &savedDist); break; case Private::BezierCurveSlice::Line: painter->paintLine(slice.point1, slice.point2, &savedDist); break; case Private::BezierCurveSlice::Curve: painter->paintBezierCurve(slice.point1, slice.control1, slice.control2, slice.point2, &savedDist); break; } } } void KisRecordedPathPaintAction::toXML(QDomDocument& doc, QDomElement& elt, KisRecordedActionSaveContext* context) const { KisRecordedPaintAction::toXML(doc, elt, context); QDomElement waypointsElt = doc.createElement("Slices"); Q_FOREACH (const Private::BezierCurveSlice & slice, d->curveSlices) { switch(slice.type) { case Private::BezierCurveSlice::Point: { QDomElement infoElt = doc.createElement("Point"); slice.point1.toXML(doc, infoElt); waypointsElt.appendChild(infoElt); break; } case Private::BezierCurveSlice::Line: { QDomElement infoElt = doc.createElement("Line"); // Point1 QDomElement point1Elt = doc.createElement("Point1"); slice.point1.toXML(doc, point1Elt); infoElt.appendChild(point1Elt); // Point2 QDomElement point2Elt = doc.createElement("Point2"); slice.point2.toXML(doc, point2Elt); infoElt.appendChild(point2Elt); waypointsElt.appendChild(infoElt); break; } case Private::BezierCurveSlice::Curve: { QDomElement infoElt = doc.createElement("Curve"); // Point1 QDomElement point1Elt = doc.createElement("Point1"); slice.point1.toXML(doc, point1Elt); infoElt.appendChild(point1Elt); // Control1 QDomElement control1Elt = doc.createElement("Control1"); - control1Elt.setAttribute("x", slice.control1.x()); - control1Elt.setAttribute("y", slice.control1.y()); + control1Elt.setAttribute("x", KisDomUtils::toString(slice.control1.x())); + control1Elt.setAttribute("y", KisDomUtils::toString(slice.control1.y())); infoElt.appendChild(control1Elt); // Control2 QDomElement control2Elt = doc.createElement("Control2"); - control2Elt.setAttribute("x", slice.control2.x()); - control2Elt.setAttribute("y", slice.control2.y()); + control2Elt.setAttribute("x", KisDomUtils::toString(slice.control2.x())); + control2Elt.setAttribute("y", KisDomUtils::toString(slice.control2.y())); infoElt.appendChild(control2Elt); // Point2 QDomElement point2Elt = doc.createElement("Point2"); slice.point2.toXML(doc, point2Elt); infoElt.appendChild(point2Elt); waypointsElt.appendChild(infoElt); } } } elt.appendChild(waypointsElt); } KisRecordedAction* KisRecordedPathPaintAction::clone() const { return new KisRecordedPathPaintAction(*this); } KisRecordedPathPaintActionFactory::KisRecordedPathPaintActionFactory() : KisRecordedPaintActionFactory("PathPaintAction") { } KisRecordedPathPaintActionFactory::~KisRecordedPathPaintActionFactory() { } KisRecordedAction* KisRecordedPathPaintActionFactory::fromXML(const QDomElement& elt, const KisRecordedActionLoadContext* context) { KisNodeQueryPath pathnode = nodeQueryPathFromXML(elt); // Decode pressets KisPaintOpPresetSP paintOpPreset = paintOpPresetFromXML(elt); KisRecordedPathPaintAction* rplpa = new KisRecordedPathPaintAction(pathnode, paintOpPreset); setupPaintAction(rplpa, elt, context); QDomElement wpElt = elt.firstChildElement("Slices"); if (!wpElt.isNull()) { QDomNode nWp = wpElt.firstChild(); while (!nWp.isNull()) { QDomElement eWp = nWp.toElement(); if (!eWp.isNull()) { if( eWp.tagName() == "Point") { rplpa->addPoint(KisPaintInformation::fromXML(eWp)); } else if(eWp.tagName() == "Line") { rplpa->addLine(KisPaintInformation::fromXML(eWp.firstChildElement("Point1")), KisPaintInformation::fromXML(eWp.firstChildElement("Point2"))); } else if( eWp.tagName() == "Curve") { QDomElement control1Elt = eWp.firstChildElement("Control1"); QDomElement control2Elt = eWp.firstChildElement("Control2"); rplpa->addCurve(KisPaintInformation::fromXML(eWp.firstChildElement("Point1")), QPointF(KisDomUtils::toDouble(control1Elt.attribute("x", "0.0")), KisDomUtils::toDouble(control1Elt.attribute("y", "0.0"))), QPointF(KisDomUtils::toDouble(control2Elt.attribute("x", "0.0")), KisDomUtils::toDouble(control2Elt.attribute("y", "0.0"))), KisPaintInformation::fromXML(eWp.firstChildElement("Point2"))); } else { dbgImage << "Unsupported <" << eWp.tagName() << " /> element"; } } nWp = nWp.nextSibling(); } } else { dbgImage << "Warning: no found"; } return rplpa; } diff --git a/libs/image/recorder/kis_recorded_shape_paint_action.cpp b/libs/image/recorder/kis_recorded_shape_paint_action.cpp index cfab4e26b1..ea48b426d8 100644 --- a/libs/image/recorder/kis_recorded_shape_paint_action.cpp +++ b/libs/image/recorder/kis_recorded_shape_paint_action.cpp @@ -1,169 +1,169 @@ /* * Copyright (c) 2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include //MSVC requires that Vc come first #include "recorder/kis_recorded_shape_paint_action.h" #include #include #include #include #include #include "kis_node.h" #include "kis_mask_generator.h" #include "kis_painter.h" #include #include "kis_paintop_registry.h" #include "recorder/kis_recorded_action_factory_registry.h" #include "kis_transaction.h" #include "kis_undo_adapter.h" #include #include #include "kis_paint_device.h" #include "kis_image.h" #include "kis_layer.h" #include "kis_node_query_path.h" #include struct Q_DECL_HIDDEN KisRecordedShapePaintAction::Private { Shape shape; QRectF rectangle; }; QString selectName(KisRecordedShapePaintAction::Shape s) { switch(s) { case KisRecordedShapePaintAction::Ellipse: return i18n("Ellipse"); case KisRecordedShapePaintAction::Rectangle: return i18n("Rectangle"); } return QString(); } KisRecordedShapePaintAction::KisRecordedShapePaintAction( const KisNodeQueryPath& path, const KisPaintOpPresetSP preset, Shape shape, const QRectF& rect) : KisRecordedPaintAction("ShapePaintAction", selectName(shape), path, preset) , d(new Private) { d->shape = shape; d->rectangle = rect; } KisRecordedShapePaintAction::KisRecordedShapePaintAction(const KisRecordedShapePaintAction& rhs) : KisRecordedPaintAction(rhs), d(new Private(*rhs.d)) { } KisRecordedShapePaintAction::~KisRecordedShapePaintAction() { delete d; } void KisRecordedShapePaintAction::playPaint(const KisPlayInfo&, KisPainter* painter) const { switch(d->shape) { case Ellipse: painter->paintEllipse(d->rectangle); break; case Rectangle: painter->paintRect(d->rectangle); break; } } void KisRecordedShapePaintAction::toXML(QDomDocument& doc, QDomElement& elt, KisRecordedActionSaveContext* context) const { KisRecordedPaintAction::toXML(doc, elt, context); QDomElement rectangleElt = doc.createElement("Rectangle"); - rectangleElt.setAttribute("x", d->rectangle.x()); - rectangleElt.setAttribute("y", d->rectangle.y()); - rectangleElt.setAttribute("width", d->rectangle.width()); - rectangleElt.setAttribute("height", d->rectangle.height()); + rectangleElt.setAttribute("x", KisDomUtils::toString(d->rectangle.x())); + rectangleElt.setAttribute("y", KisDomUtils::toString(d->rectangle.y())); + rectangleElt.setAttribute("width", KisDomUtils::toString(d->rectangle.width())); + rectangleElt.setAttribute("height", KisDomUtils::toString(d->rectangle.height())); elt.appendChild(rectangleElt); switch(d->shape) { case Ellipse: elt.setAttribute("shape", "Ellipse"); break; case Rectangle: elt.setAttribute("shape", "Rectangle"); break; } } KisRecordedAction* KisRecordedShapePaintAction::clone() const { return new KisRecordedShapePaintAction(*this); } KisRecordedShapePaintActionFactory::KisRecordedShapePaintActionFactory() : KisRecordedPaintActionFactory("ShapePaintAction") { } KisRecordedShapePaintActionFactory::~KisRecordedShapePaintActionFactory() { } KisRecordedAction* KisRecordedShapePaintActionFactory::fromXML(const QDomElement& elt, const KisRecordedActionLoadContext* context) { KisNodeQueryPath pathnode = nodeQueryPathFromXML(elt); // Decode pressets KisPaintOpPresetSP paintOpPreset = paintOpPresetFromXML(elt); QDomElement ellipseElt = elt.firstChildElement("Rectangle"); qreal x, y, width, height; if (!ellipseElt.isNull()) { x = KisDomUtils::toDouble(ellipseElt.attribute("x", "0.0")); y = KisDomUtils::toDouble(ellipseElt.attribute("y", "0.0")); width = KisDomUtils::toDouble(ellipseElt.attribute("width", "0.0")); height = KisDomUtils::toDouble(ellipseElt.attribute("height", "0.0")); } else { x = 0; y = 0; width = 0; height = 0; dbgImage << "Warning: no found"; } KisRecordedShapePaintAction::Shape shape = KisRecordedShapePaintAction::Ellipse; QString shapeStr = elt.attribute("shape", "Ellipse"); if (shapeStr == "Ellipse") { shape = KisRecordedShapePaintAction::Ellipse; } else { // shapeStr == "Rectangle" shape = KisRecordedShapePaintAction::Rectangle; } KisRecordedShapePaintAction* rplpa = new KisRecordedShapePaintAction(pathnode, paintOpPreset, shape, QRectF(x, y, width, height)); setupPaintAction(rplpa, elt, context); return rplpa; } diff --git a/libs/image/tests/kis_keyframing_test.cpp b/libs/image/tests/kis_keyframing_test.cpp index 3b82fe275c..0e995dbbc6 100644 --- a/libs/image/tests/kis_keyframing_test.cpp +++ b/libs/image/tests/kis_keyframing_test.cpp @@ -1,442 +1,441 @@ /* * 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_keyframing_test.h" #include #include #include "kis_paint_device_frames_interface.h" #include "kis_keyframe_channel.h" #include "kis_scalar_keyframe_channel.h" #include "kis_raster_keyframe_channel.h" #include "kis_node.h" #include "kis_time_range.h" #include "kundo2command.h" #include #include "testing_timed_default_bounds.h" void KisKeyframingTest::initTestCase() { cs = KoColorSpaceRegistry::instance()->rgb8(); red = new quint8[cs->pixelSize()]; green = new quint8[cs->pixelSize()]; blue = new quint8[cs->pixelSize()]; cs->fromQColor(Qt::red, red); cs->fromQColor(Qt::green, green); cs->fromQColor(Qt::blue, blue); } void KisKeyframingTest::cleanupTestCase() { delete[] red; delete[] green; delete[] blue; } void KisKeyframingTest::testScalarChannel() { KisScalarKeyframeChannel *channel = new KisScalarKeyframeChannel(KoID("", ""), -17, 31, 0); KisKeyframeSP key; bool ok; QCOMPARE(channel->hasScalarValue(), true); QCOMPARE(channel->minScalarValue(), -17.0); QCOMPARE(channel->maxScalarValue(), 31.0); QVERIFY(channel->keyframeAt(0) == 0); // Adding new keyframe key = channel->addKeyframe(42); channel->setScalarValue(key, 7.0); key = channel->keyframeAt(42); QCOMPARE(channel->scalarValue(key), 7.0); // Copying a keyframe KisKeyframeSP key2 = channel->copyKeyframe(key, 13); QVERIFY(key2 != 0); QVERIFY(channel->keyframeAt(13) == key2); QCOMPARE(channel->scalarValue(key2), 7.0); // Adding a keyframe where one exists key2 = channel->addKeyframe(13); QVERIFY(key2 != key); QCOMPARE(channel->keyframeCount(), 2); // Moving keyframes ok = channel->moveKeyframe(key, 10); QCOMPARE(ok, true); QVERIFY(channel->keyframeAt(42) == 0); key = channel->keyframeAt(10); QCOMPARE(channel->scalarValue(key), 7.0); // Moving a keyframe where another one exists ok = channel->moveKeyframe(key, 13); QCOMPARE(ok, true); QVERIFY(channel->keyframeAt(13) != key2); // Deleting a keyframe channel->deleteKeyframe(key); QVERIFY(channel->keyframeAt(10) == 0); QCOMPARE(channel->keyframeCount(), 0); delete channel; } void KisKeyframingTest::testScalarChannelUndoRedo() { KisScalarKeyframeChannel *channel = new KisScalarKeyframeChannel(KoID("", ""), -17, 31, 0); KisKeyframeSP key; QCOMPARE(channel->hasScalarValue(), true); QCOMPARE(channel->minScalarValue(), -17.0); QCOMPARE(channel->maxScalarValue(), 31.0); QVERIFY(channel->keyframeAt(0) == 0); // Adding new keyframe KUndo2Command addCmd; key = channel->addKeyframe(42, &addCmd); channel->setScalarValue(key, 7.0, &addCmd); key = channel->keyframeAt(42); QCOMPARE(channel->scalarValue(key), 7.0); addCmd.undo(); KisKeyframeSP newKey; newKey = channel->keyframeAt(42); QVERIFY(!newKey); addCmd.redo(); newKey = channel->keyframeAt(42); QVERIFY(newKey); QCOMPARE(channel->scalarValue(key), 7.0); QCOMPARE(channel->scalarValue(newKey), 7.0); delete channel; } void KisKeyframingTest::testRasterChannel() { TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds(); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->setDefaultBounds(bounds); KisRasterKeyframeChannel * channel = dev->createKeyframeChannel(KoID()); QCOMPARE(channel->hasScalarValue(), false); QCOMPARE(channel->keyframeCount(), 1); QCOMPARE(dev->framesInterface()->frames().count(), 1); QCOMPARE(channel->frameIdAt(0), 0); QVERIFY(channel->keyframeAt(0) != 0); KisKeyframeSP key_0 = channel->keyframeAt(0); // New keyframe KisKeyframeSP key_10 = channel->addKeyframe(10); QCOMPARE(channel->keyframeCount(), 2); QCOMPARE(dev->framesInterface()->frames().count(), 2); QVERIFY(channel->frameIdAt(10) != 0); dev->fill(0, 0, 512, 512, red); QImage thumb1a = dev->createThumbnail(50, 50); bounds->testingSetTime(10); dev->fill(0, 0, 512, 512, green); QImage thumb2a = dev->createThumbnail(50, 50); bounds->testingSetTime(0); QImage thumb1b = dev->createThumbnail(50, 50); QVERIFY(thumb2a != thumb1a); QVERIFY(thumb1b == thumb1a); // Duplicate keyframe KisKeyframeSP key_20 = channel->copyKeyframe(key_0, 20); bounds->testingSetTime(20); QImage thumb3a = dev->createThumbnail(50, 50); QVERIFY(thumb3a == thumb1b); dev->fill(0, 0, 512, 512, blue); QImage thumb3b = dev->createThumbnail(50, 50); bounds->testingSetTime(0); QImage thumb1c = dev->createThumbnail(50, 50); QVERIFY(thumb3b != thumb3a); QVERIFY(thumb1c == thumb1b); // Delete keyrame QCOMPARE(channel->keyframeCount(), 3); QCOMPARE(dev->framesInterface()->frames().count(), 3); channel->deleteKeyframe(key_0); QCOMPARE(channel->keyframeCount(), 2); QCOMPARE(dev->framesInterface()->frames().count(), 2); QVERIFY(channel->keyframeAt(0) == 0); channel->deleteKeyframe(key_20); QCOMPARE(channel->keyframeCount(), 1); QCOMPARE(dev->framesInterface()->frames().count(), 1); QVERIFY(channel->keyframeAt(20) == 0); // Last remaining keyframe cannot be deleted channel->deleteKeyframe(key_10); QCOMPARE(channel->keyframeCount(), 1); QCOMPARE(dev->framesInterface()->frames().count(), 1); QVERIFY(channel->keyframeAt(10) != 0); // Fetching current keyframe before the first one should // return the first keyframe QCOMPARE(channel->frameIdAt(0), (int)key_10->value()); } void KisKeyframingTest::testChannelSignals() { KisScalarKeyframeChannel *channel = new KisScalarKeyframeChannel(KoID("", ""), -17, 31, 0); KisKeyframeSP key; KisKeyframe *resKey; qRegisterMetaType("KisKeyframePtr"); QSignalSpy spyPreAdd(channel, SIGNAL(sigKeyframeAboutToBeAdded(KisKeyframe*))); QSignalSpy spyPostAdd(channel, SIGNAL(sigKeyframeAdded(KisKeyframe*))); QSignalSpy spyPreRemove(channel, SIGNAL(sigKeyframeAboutToBeRemoved(KisKeyframe*))); QSignalSpy spyPostRemove(channel, SIGNAL(sigKeyframeRemoved(KisKeyframe*))); QSignalSpy spyPreMove(channel, SIGNAL(sigKeyframeAboutToBeMoved(KisKeyframe*,int))); QSignalSpy spyPostMove(channel, SIGNAL(sigKeyframeMoved(KisKeyframe*, int))); QVERIFY(spyPreAdd.isValid()); QVERIFY(spyPostAdd.isValid()); QVERIFY(spyPreRemove.isValid()); QVERIFY(spyPostRemove.isValid()); QVERIFY(spyPreMove.isValid()); QVERIFY(spyPostMove.isValid()); // Adding a keyframe QCOMPARE(spyPreAdd.count(), 0); QCOMPARE(spyPostAdd.count(), 0); key = channel->addKeyframe(10); QCOMPARE(spyPreAdd.count(), 1); QCOMPARE(spyPostAdd.count(), 1); resKey = spyPreAdd.at(0).at(0).value(); QVERIFY(resKey == key.data()); resKey = spyPostAdd.at(0).at(0).value(); QVERIFY(resKey == key.data()); // Moving a keyframe QCOMPARE(spyPreMove.count(), 0); QCOMPARE(spyPostMove.count(), 0); channel->moveKeyframe(key, 15); QCOMPARE(spyPreMove.count(), 1); QCOMPARE(spyPostMove.count(), 1); resKey = spyPreMove.at(0).at(0).value(); QVERIFY(resKey == key.data()); QCOMPARE(spyPreMove.at(0).at(1).toInt(), 15); resKey = spyPostMove.at(0).at(0).value(); QVERIFY(resKey == key.data()); // No-op move (no signals) channel->moveKeyframe(key, 15); QCOMPARE(spyPreMove.count(), 1); QCOMPARE(spyPostMove.count(), 1); // Deleting a keyframe QCOMPARE(spyPreRemove.count(), 0); QCOMPARE(spyPostRemove.count(), 0); channel->deleteKeyframe(key); QCOMPARE(spyPreRemove.count(), 1); QCOMPARE(spyPostRemove.count(), 1); delete channel; } void KisKeyframingTest::testRasterFrameFetching() { TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds(); KisPaintDeviceSP dev = new KisPaintDevice(cs); KisPaintDeviceSP devTarget = new KisPaintDevice(cs); dev->setDefaultBounds(bounds); KisRasterKeyframeChannel * channel = dev->createKeyframeChannel(KoID()); channel->addKeyframe(0); channel->addKeyframe(10); channel->addKeyframe(50); bounds->testingSetTime(0); dev->fill(0, 0, 512, 512, red); QImage frame1 = dev->createThumbnail(50, 50); bounds->testingSetTime(10); dev->fill(0, 256, 512, 512, green); QImage frame2 = dev->createThumbnail(50, 50); bounds->testingSetTime(50); dev->fill(0, 0, 256, 512, blue); QImage frame3 = dev->createThumbnail(50, 50); bounds->testingSetTime(10); KisKeyframeSP keyframe = channel->activeKeyframeAt(0); channel->fetchFrame(keyframe, devTarget); QImage fetched1 = devTarget->createThumbnail(50, 50); keyframe = channel->activeKeyframeAt(10); channel->fetchFrame(keyframe, devTarget); QImage fetched2 = devTarget->createThumbnail(50, 50); keyframe = channel->activeKeyframeAt(50); channel->fetchFrame(keyframe, devTarget); QImage fetched3 = devTarget->createThumbnail(50, 50); QVERIFY(fetched1 == frame1); QVERIFY(fetched2 == frame2); QVERIFY(fetched3 == frame3); } void KisKeyframingTest::testDeleteFirstRasterChannel() { // Test Plan: // // delete // undo delete // move // undo move TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds(); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->setDefaultBounds(bounds); KisRasterKeyframeChannel * channel = dev->createKeyframeChannel(KoID()); QCOMPARE(channel->hasScalarValue(), false); QCOMPARE(channel->keyframeCount(), 1); QCOMPARE(dev->framesInterface()->frames().count(), 1); QCOMPARE(channel->frameIdAt(0), 0); QVERIFY(channel->keyframeAt(0) != 0); KisKeyframeSP key_0 = channel->keyframeAt(0); { KUndo2Command cmd; bool deleteResult = channel->deleteKeyframe(key_0, &cmd); QVERIFY(deleteResult); QCOMPARE(dev->framesInterface()->frames().count(), 1); QVERIFY(channel->frameIdAt(0) != 0); QVERIFY(channel->keyframeAt(0)); QVERIFY(channel->keyframeAt(0) != key_0); cmd.undo(); QCOMPARE(dev->framesInterface()->frames().count(), 1); QVERIFY(channel->frameIdAt(0) == 0); QVERIFY(channel->keyframeAt(0)); QVERIFY(channel->keyframeAt(0) == key_0); } { KUndo2Command cmd; bool moveResult = channel->moveKeyframe(key_0, 1, &cmd); QVERIFY(moveResult); QCOMPARE(dev->framesInterface()->frames().count(), 2); QVERIFY(channel->frameIdAt(0) != 0); QVERIFY(channel->frameIdAt(1) == 0); QVERIFY(channel->keyframeAt(0)); QVERIFY(channel->keyframeAt(1)); QVERIFY(channel->keyframeAt(0) != key_0); QVERIFY(channel->keyframeAt(1) == key_0); cmd.undo(); QCOMPARE(dev->framesInterface()->frames().count(), 1); QVERIFY(channel->frameIdAt(0) == 0); QVERIFY(channel->keyframeAt(0)); QVERIFY(channel->keyframeAt(0) == key_0); } } void KisKeyframingTest::testAffectedFrames() { KisScalarKeyframeChannel *channel = new KisScalarKeyframeChannel(KoID("", ""), -17, 31, 0); KisTimeRange range; channel->addKeyframe(10); channel->addKeyframe(20); channel->addKeyframe(30); // At a keyframe range = channel->affectedFrames(20); QCOMPARE(range.start(), 20); QCOMPARE(range.end(), 29); QCOMPARE(range.isInfinite(), false); // Between frames range = channel->affectedFrames(25); QCOMPARE(range.start(), 20); QCOMPARE(range.end(), 29); QCOMPARE(range.isInfinite(), false); // Before first frame range = channel->affectedFrames(5); QCOMPARE(range.start(), 0); QCOMPARE(range.end(), 19); QCOMPARE(range.isInfinite(), false); // After last frame range = channel->affectedFrames(35); QCOMPARE(range.start(), 30); QCOMPARE(range.isInfinite(), true); } QTEST_MAIN(KisKeyframingTest) -#include "kis_keyframing_test.moc" diff --git a/libs/image/tests/kis_mask_generator_test.cpp b/libs/image/tests/kis_mask_generator_test.cpp index 09423a20d7..159b604f2c 100644 --- a/libs/image/tests/kis_mask_generator_test.cpp +++ b/libs/image/tests/kis_mask_generator_test.cpp @@ -1,106 +1,185 @@ /* * Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org * Copyright (c) 2008 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 //MSVC requires that Vc come first #include "kis_mask_generator_test.h" #include #include "kis_mask_generator.h" #include #include QImage createQImageFromMask(const KisMaskGenerator& generator) { QImage image(10, 10, QImage::Format_ARGB32); for (int i = 0; i < 10; ++i) { for (int j = 0; j < 10; ++j) { quint8 c = generator.valueAt(i, j); image.setPixel(i, j, qRgb(c, c, c)); } } return image; } void KisMaskGeneratorTest::testCircleSerialisation() { KisCircleMaskGenerator cmg(10.0 * rand() / RAND_MAX, rand() / RAND_MAX, rand() / RAND_MAX, rand() / RAND_MAX, 4, false); QDomDocument doc = QDomDocument("cmg"); QDomElement root = doc.createElement("cmg"); doc.appendChild(root); cmg.toXML(doc, root); KisMaskGenerator* cmg2 = KisMaskGenerator::fromXML(root); createQImageFromMask(cmg).save("circle1.png"); createQImageFromMask(*cmg2).save("circle2.png"); for (int i = 0; i < 10; ++i) { for (int j = 0; j < 10; ++j) { QVERIFY(cmg.valueAt(i, j) == cmg2->valueAt(i, j)); } } delete cmg2; } void KisMaskGeneratorTest::testSquareSerialisation() { // check consistency KisRectangleMaskGenerator cmg_1(5, 5, 5, 5, 5, false); KisRectangleMaskGenerator cmg_2(5, 5, 5, 5, 5, false); for (int i = 0; i < 10; ++i) { for (int j = 0; j < 10; ++j) { QVERIFY(cmg_1.valueAt(i, j) == cmg_2.valueAt(i, j)); } } KisRectangleMaskGenerator cmg(10.0 * rand() / RAND_MAX, rand() / float(RAND_MAX), rand() / float(RAND_MAX), rand() / float(RAND_MAX), 4, false); QDomDocument doc = QDomDocument("cmg"); QDomElement root = doc.createElement("cmg"); doc.appendChild(root); cmg.toXML(doc, root); // check consistency with itself for (int i = 0; i < 10; ++i) { for (int j = 0; j < 10; ++j) { QVERIFY(cmg.valueAt(i, j) == cmg.valueAt(i, j)); } } KisMaskGenerator* cmg2 = KisMaskGenerator::fromXML(root); QDomDocument doc2 = QDomDocument("cmg"); QDomElement root2 = doc2.createElement("cmg"); doc2.appendChild(root2); cmg.toXML(doc2, root2); // check serialization QCOMPARE(doc.toString(), doc2.toString()); createQImageFromMask(cmg).save("square1.png"); createQImageFromMask(*cmg2).save("square2.png"); for (int i = 0; i < 10; ++i) { for (int j = 0; j < 10; ++j) { QVERIFY(cmg.valueAt(i, j) == cmg2->valueAt(i, j)); } } delete cmg2; } +#include "kis_random_source.h" + +void testCopyCtor(KisMaskGenerator *gen1) +{ + QScopedPointer gen2(gen1->clone()); + + const int halfWidth = gen1->width() / 2; + const int halfHeight = gen1->height() / 2; + const int numSamples = qMax(100.0, 0.1 * gen1->width() * gen1->height()); + + KisRandomSource random; + for (int i = 0; i < numSamples; i++) { + const int x = random.generate(-halfWidth, halfWidth); + const int y = random.generate(-halfHeight, halfHeight); + + const quint8 v1 = gen1->valueAt(x, y); + const quint8 v2 = gen2->valueAt(x, y); + + if (v1 != v2) { + qDebug() << ppVar(i) << ppVar(x) << ppVar(y) << ppVar(v2) << ppVar(v1); + } + + QCOMPARE(v2, v1); + } +} + +void KisMaskGeneratorTest::testCopyCtorCircle() +{ + KisCircleMaskGenerator gen(50.0 * rand() / RAND_MAX, rand() / RAND_MAX, rand() / RAND_MAX, rand() / RAND_MAX, 4, true); + testCopyCtor(&gen); +} + +void KisMaskGeneratorTest::testCopyCtorRect() +{ + KisRectangleMaskGenerator gen(50.0 * rand() / RAND_MAX, rand() / RAND_MAX, rand() / RAND_MAX, rand() / RAND_MAX, 4, true); + testCopyCtor(&gen); +} + +#include "kis_cubic_curve.h" + +void KisMaskGeneratorTest::testCopyCtorCurveCircle() +{ + KisCurveCircleMaskGenerator gen(50, 0.8, + 0.75, 0.85, + 2, + KisCubicCurve(), // linear + true); + testCopyCtor(&gen); +} + +void KisMaskGeneratorTest::testCopyCtorCurveRect() +{ + KisCurveRectangleMaskGenerator gen(50, 0.8, + 0.75, 0.85, + 2, + KisCubicCurve(), // linear + true); + testCopyCtor(&gen); +} + +void KisMaskGeneratorTest::testCopyCtorGaussCircle() +{ + KisGaussCircleMaskGenerator gen(50, 0.8, + 0.75, 0.85, + 2, + true); + testCopyCtor(&gen); +} + +void KisMaskGeneratorTest::testCopyCtorGaussRect() +{ + KisGaussRectangleMaskGenerator gen(50, 0.8, + 0.75, 0.85, + 2, + true); + testCopyCtor(&gen); +} + + QTEST_MAIN(KisMaskGeneratorTest) diff --git a/libs/image/tests/kis_mask_generator_test.h b/libs/image/tests/kis_mask_generator_test.h index 97f129ad03..e4b0c5c51b 100644 --- a/libs/image/tests/kis_mask_generator_test.h +++ b/libs/image/tests/kis_mask_generator_test.h @@ -1,33 +1,42 @@ /* * Copyright (c) 2008 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_MASK_GENERATOR_TEST_H #define KIS_MASK_GENERATOR_TEST_H #include class KisMaskGeneratorTest : public QObject { Q_OBJECT private Q_SLOTS: void testCircleSerialisation(); void testSquareSerialisation(); + + void testCopyCtorCircle(); + void testCopyCtorRect(); + + void testCopyCtorCurveCircle(); + void testCopyCtorCurveRect(); + + void testCopyCtorGaussCircle(); + void testCopyCtorGaussRect(); }; #endif diff --git a/libs/image/tests/kis_onion_skin_compositor_test.cpp b/libs/image/tests/kis_onion_skin_compositor_test.cpp index 09f7af2b75..ff2035ed14 100644 --- a/libs/image/tests/kis_onion_skin_compositor_test.cpp +++ b/libs/image/tests/kis_onion_skin_compositor_test.cpp @@ -1,192 +1,191 @@ /* * 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_test.h" #include #include "kis_onion_skin_compositor.h" #include "kis_paint_device.h" #include "kis_raster_keyframe_channel.h" #include "kis_image_animation_interface.h" #include "testutil.h" #include "KoColor.h" #include "kis_image_config.h" void KisOnionSkinCompositorTest::testComposite() { KisImageConfig config; config.setOnionSkinTintFactor(64); config.setOnionSkinTintColorBackward(Qt::blue); config.setOnionSkinTintColorForward(Qt::red); config.setNumberOfOnionSkins(1); config.setOnionSkinOpacity(-1, 128); config.setOnionSkinOpacity(1, 128); KisOnionSkinCompositor *compositor = KisOnionSkinCompositor::instance(); TestUtil::MaskParent p; KisImageAnimationInterface *i = p.image->animationInterface(); KisPaintDeviceSP paintDevice = p.layer->paintDevice(); KisKeyframeChannel *keyframes = paintDevice->keyframeChannel(); keyframes->addKeyframe(0); keyframes->addKeyframe(10); keyframes->addKeyframe(20); paintDevice->fill(QRect(0,0,256,512), KoColor(Qt::red, paintDevice->colorSpace())); i->switchCurrentTimeAsync(10); p.image->waitForDone(); paintDevice->fill(QRect(0,0,512,256), KoColor(Qt::green, paintDevice->colorSpace())); i->switchCurrentTimeAsync(20); p.image->waitForDone(); paintDevice->fill(QRect(0,256,512,256), KoColor(Qt::blue, paintDevice->colorSpace())); KisPaintDeviceSP compositeDevice = new KisPaintDevice(p.image->colorSpace()); KisPaintDeviceSP expectedComposite = new KisPaintDevice(p.image->colorSpace()); // Frame 0 i->switchCurrentTimeAsync(0); p.image->waitForDone(); compositor->composite(paintDevice, compositeDevice, QRect(0,0,512,512)); expectedComposite->fill(QRect(256,0,256,256), KoColor(QColor(64, 191, 0, 128), paintDevice->colorSpace())); expectedComposite->fill(QRect(0,0,256,512), KoColor(Qt::red, paintDevice->colorSpace())); QImage result = compositeDevice->createThumbnail(64, 64); QImage expected = expectedComposite->createThumbnail(64, 64); QVERIFY(result == expected); // Frame 10 i->switchCurrentTimeAsync(10); p.image->waitForDone(); compositor->composite(paintDevice, compositeDevice, QRect(0,0,512,512)); expectedComposite->clear(); expectedComposite->fill(QRect(0,256,256,256), KoColor(QColor(106, 0, 149, 192), paintDevice->colorSpace())); expectedComposite->fill(QRect(256,256,256,256), KoColor(QColor(64, 0, 191, 128), paintDevice->colorSpace())); expectedComposite->fill(QRect(0,0,512,256), KoColor(Qt::green, paintDevice->colorSpace())); result = compositeDevice->createThumbnail(64, 64); expected = expectedComposite->createThumbnail(64, 64); QVERIFY(result == expected); // Frame 20 i->switchCurrentTimeAsync(20); p.image->waitForDone(); compositor->composite(paintDevice, compositeDevice, QRect(0,0,512,512)); expectedComposite->clear(); expectedComposite->fill(QRect(0,0,512,256), KoColor(QColor(0, 191, 64, 128), paintDevice->colorSpace())); expectedComposite->fill(QRect(0,256,512,256), KoColor(Qt::blue, paintDevice->colorSpace())); result = compositeDevice->createThumbnail(64, 64); expected = expectedComposite->createThumbnail(64, 64); QVERIFY(result == expected); } void KisOnionSkinCompositorTest::testSettings() { KisOnionSkinCompositor *compositor = KisOnionSkinCompositor::instance(); TestUtil::MaskParent p; KisImageAnimationInterface *i = p.image->animationInterface(); KisPaintDeviceSP paintDevice = p.layer->paintDevice(); KisKeyframeChannel *keyframes = paintDevice->keyframeChannel(); keyframes->addKeyframe(0); keyframes->addKeyframe(1); keyframes->addKeyframe(2); keyframes->addKeyframe(3); paintDevice->fill(QRect(0,0,512,512), KoColor(Qt::red, paintDevice->colorSpace())); i->switchCurrentTimeAsync(2); p.image->waitForDone(); paintDevice->fill(QRect(0,0,512,512), KoColor(Qt::green, paintDevice->colorSpace())); i->switchCurrentTimeAsync(3); p.image->waitForDone(); paintDevice->fill(QRect(0,0,512,512), KoColor(Qt::blue, paintDevice->colorSpace())); i->switchCurrentTimeAsync(1); p.image->waitForDone(); KisImageConfig config; config.setOnionSkinOpacity(-1, 32); config.setOnionSkinOpacity(1, 192); config.setOnionSkinOpacity(2, 64); config.setOnionSkinTintFactor(0); KisPaintDeviceSP compositeDevice = new KisPaintDevice(p.image->colorSpace()); KisPaintDeviceSP expectedComposite = new KisPaintDevice(p.image->colorSpace()); config.setNumberOfOnionSkins(1); compositor->configChanged(); expectedComposite->clear(); expectedComposite->fill(QRect(0,0,512,512), KoColor(QColor(10, 245, 0, 200), paintDevice->colorSpace())); QImage expected = expectedComposite->createThumbnail(64, 64); compositor->composite(paintDevice, compositeDevice, QRect(0,0,512,512)); QImage result = compositeDevice->createThumbnail(64, 64); QVERIFY(result == expected); config.setNumberOfOnionSkins(2); compositor->configChanged(); expectedComposite->fill(QRect(0,0,512,512), KoColor(QColor(9, 229, 16, 214), paintDevice->colorSpace())); expected = expectedComposite->createThumbnail(64, 64); compositor->composite(paintDevice, compositeDevice, QRect(0,0,512,512)); result = compositeDevice->createThumbnail(64, 64); QVERIFY(result == expected); // Test tint options config.setNumberOfOnionSkins(1); config.setOnionSkinTintFactor(64); config.setOnionSkinTintColorBackward(Qt::blue); config.setOnionSkinTintColorForward(Qt::red); compositor->configChanged(); compositor->composite(paintDevice, compositeDevice, QRect(0,0,512,512)); result = compositeDevice->createThumbnail(64, 64); expectedComposite->fill(QRect(0,0,512,512), KoColor(QColor(69, 183, 3, 200), paintDevice->colorSpace())); expected = expectedComposite->createThumbnail(64, 64); QVERIFY(result == expected); } QTEST_MAIN(KisOnionSkinCompositorTest) -#include "kis_onion_skin_compositor_test.moc" diff --git a/libs/image/tests/kis_update_scheduler_test.cpp b/libs/image/tests/kis_update_scheduler_test.cpp index 0b51215516..e3fad86c70 100644 --- a/libs/image/tests/kis_update_scheduler_test.cpp +++ b/libs/image/tests/kis_update_scheduler_test.cpp @@ -1,415 +1,433 @@ /* * Copyright (c) 2010 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_update_scheduler_test.h" #include #include #include #include "kis_group_layer.h" #include "kis_paint_layer.h" #include "kis_adjustment_layer.h" #include "filter/kis_filter.h" #include "filter/kis_filter_configuration.h" #include "filter/kis_filter_registry.h" #include "kis_selection.h" #include "scheduler_utils.h" #include "kis_update_scheduler.h" #include "kis_updater_context.h" #include "kis_update_job_item.h" #include "kis_simple_update_queue.h" #include "../../sdk/tests/testutil.h" KisImageSP KisUpdateSchedulerTest::buildTestingImage() { QImage sourceImage1(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); QImage sourceImage2(QString(FILES_DATA_DIR) + QDir::separator() + "inverted_hakonepa.png"); QRect imageRect = QRect(QPoint(0,0), sourceImage1.size()); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), cs, "merge test"); KisFilterSP filter = KisFilterRegistry::instance()->value("blur"); Q_ASSERT(filter); KisFilterConfiguration *configuration = filter->defaultConfiguration(0); Q_ASSERT(configuration); KisPaintLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8); KisPaintLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8 / 3); KisLayerSP blur1 = new KisAdjustmentLayer(image, "blur1", configuration, 0); paintLayer1->paintDevice()->convertFromQImage(sourceImage1, 0, 0, 0); paintLayer2->paintDevice()->convertFromQImage(sourceImage2, 0, 0, 0); image->lock(); image->addNode(paintLayer1); image->addNode(paintLayer2); image->addNode(blur1); image->unlock(); return image; } void KisUpdateSchedulerTest::testMerge() { KisImageSP image = buildTestingImage(); QRect imageRect = image->bounds(); KisNodeSP rootLayer = image->rootLayer(); KisNodeSP paintLayer1 = rootLayer->firstChild(); QCOMPARE(paintLayer1->name(), QString("paint1")); KisUpdateScheduler scheduler(image.data()); /** * Test synchronous Full Refresh */ scheduler.fullRefresh(rootLayer, image->bounds(), image->bounds()); QCOMPARE(rootLayer->exactBounds(), image->bounds()); QImage resultFRProjection = rootLayer->projection()->convertToQImage(0); resultFRProjection.save(QString(FILES_OUTPUT_DIR) + QDir::separator() + "scheduler_fr_merge_result.png"); /** * Test incremental updates */ rootLayer->projection()->clear(); const qint32 num = 4; qint32 width = imageRect.width() / num; qint32 lastWidth = imageRect.width() - width; QVector dirtyRects(num); for(qint32 i = 0; i < num-1; i++) { dirtyRects[i] = QRect(width*i, 0, width, imageRect.height()); } dirtyRects[num-1] = QRect(width*(num-1), 0, lastWidth, imageRect.height()); for(qint32 i = 0; i < num; i+=2) { scheduler.updateProjection(paintLayer1, dirtyRects[i], image->bounds()); } for(qint32 i = 1; i < num; i+=2) { scheduler.updateProjection(paintLayer1, dirtyRects[i], image->bounds()); } scheduler.waitForDone(); QCOMPARE(rootLayer->exactBounds(), image->bounds()); QImage resultDirtyProjection = rootLayer->projection()->convertToQImage(0); resultDirtyProjection.save(QString(FILES_OUTPUT_DIR) + QDir::separator() + "scheduler_dp_merge_result.png"); QPoint pt; QVERIFY(TestUtil::compareQImages(pt, resultFRProjection, resultDirtyProjection)); } void KisUpdateSchedulerTest::benchmarkOverlappedMerge() { KisImageSP image = buildTestingImage(); KisNodeSP rootLayer = image->rootLayer(); KisNodeSP paintLayer1 = rootLayer->firstChild(); QRect imageRect = image->bounds(); QCOMPARE(paintLayer1->name(), QString("paint1")); QCOMPARE(imageRect, QRect(0,0,640,441)); KisUpdateScheduler scheduler(image.data()); const qint32 xShift = 10; const qint32 yShift = 0; const qint32 numShifts = 64; QBENCHMARK{ QRect dirtyRect(0, 0, 200, imageRect.height()); for(int i = 0; i < numShifts; i++) { // dbgKrita << dirtyRect; scheduler.updateProjection(paintLayer1, dirtyRect, image->bounds()); dirtyRect.translate(xShift, yShift); } scheduler.waitForDone(); } } void KisUpdateSchedulerTest::testLocking() { KisImageSP image = buildTestingImage(); KisNodeSP rootLayer = image->rootLayer(); KisNodeSP paintLayer1 = rootLayer->firstChild(); QRect imageRect = image->bounds(); QCOMPARE(paintLayer1->name(), QString("paint1")); QCOMPARE(imageRect, QRect(0,0,640,441)); KisTestableUpdateScheduler scheduler(image.data(), 2); QRect dirtyRect1(0,0,50,100); QRect dirtyRect2(0,0,100,100); QRect dirtyRect3(50,0,50,100); QRect dirtyRect4(150,150,50,50); KisTestableUpdaterContext *context = scheduler.updaterContext(); QVector jobs; scheduler.updateProjection(paintLayer1, imageRect, imageRect); jobs = context->getJobs(); QCOMPARE(jobs[0]->isRunning(), true); QCOMPARE(jobs[1]->isRunning(), false); QVERIFY(checkWalker(jobs[0]->walker(), imageRect)); context->clear(); scheduler.lock(); scheduler.updateProjection(paintLayer1, dirtyRect1, imageRect); scheduler.updateProjection(paintLayer1, dirtyRect2, imageRect); scheduler.updateProjection(paintLayer1, dirtyRect3, imageRect); scheduler.updateProjection(paintLayer1, dirtyRect4, imageRect); jobs = context->getJobs(); QCOMPARE(jobs[0]->isRunning(), false); QCOMPARE(jobs[1]->isRunning(), false); scheduler.unlock(); jobs = context->getJobs(); QCOMPARE(jobs[0]->isRunning(), true); QCOMPARE(jobs[1]->isRunning(), true); QVERIFY(checkWalker(jobs[0]->walker(), dirtyRect2)); QVERIFY(checkWalker(jobs[1]->walker(), dirtyRect4)); } void KisUpdateSchedulerTest::testExclusiveStrokes() { KisImageSP image = buildTestingImage(); KisNodeSP rootLayer = image->rootLayer(); KisNodeSP paintLayer1 = rootLayer->firstChild(); QRect imageRect = image->bounds(); QCOMPARE(paintLayer1->name(), QString("paint1")); QCOMPARE(imageRect, QRect(0,0,640,441)); QRect dirtyRect1(0,0,50,100); KisTestableUpdateScheduler scheduler(image.data(), 2); KisTestableUpdaterContext *context = scheduler.updaterContext(); QVector jobs; scheduler.updateProjection(paintLayer1, dirtyRect1, imageRect); jobs = context->getJobs(); QCOMPARE(jobs[0]->isRunning(), true); QCOMPARE(jobs[1]->isRunning(), false); QVERIFY(checkWalker(jobs[0]->walker(), dirtyRect1)); KisStrokeId id = scheduler.startStroke(new KisTestingStrokeStrategy("excl_", true, false)); jobs = context->getJobs(); QCOMPARE(jobs[0]->isRunning(), true); QCOMPARE(jobs[1]->isRunning(), false); QVERIFY(checkWalker(jobs[0]->walker(), dirtyRect1)); context->clear(); scheduler.endStroke(id); jobs = context->getJobs(); QCOMPARE(jobs[0]->isRunning(), true); QCOMPARE(jobs[1]->isRunning(), false); COMPARE_NAME(jobs[0], "excl_init"); scheduler.updateProjection(paintLayer1, dirtyRect1, imageRect); jobs = context->getJobs(); QCOMPARE(jobs[0]->isRunning(), true); QCOMPARE(jobs[1]->isRunning(), false); COMPARE_NAME(jobs[0], "excl_init"); context->clear(); scheduler.processQueues(); jobs = context->getJobs(); QCOMPARE(jobs[0]->isRunning(), true); QCOMPARE(jobs[1]->isRunning(), false); COMPARE_NAME(jobs[0], "excl_finish"); context->clear(); scheduler.processQueues(); jobs = context->getJobs(); QCOMPARE(jobs[0]->isRunning(), true); QCOMPARE(jobs[1]->isRunning(), false); QVERIFY(checkWalker(jobs[0]->walker(), dirtyRect1)); } void KisUpdateSchedulerTest::testEmptyStroke() { KisImageSP image = buildTestingImage(); KisStrokeId id = image->startStroke(new KisStrokeStrategy()); image->addJob(id, 0); image->endStroke(id); image->waitForDone(); } #include "kis_lazy_wait_condition.h" void KisUpdateSchedulerTest::testLazyWaitCondition() { { dbgKrita << "Not initialized"; KisLazyWaitCondition condition; QVERIFY(!condition.wait(50)); } { dbgKrita << "Initialized, not awake"; KisLazyWaitCondition condition; condition.initWaiting(); QVERIFY(!condition.wait(50)); condition.endWaiting(); } { dbgKrita << "Initialized, awake"; KisLazyWaitCondition condition; condition.initWaiting(); condition.wakeAll(); QVERIFY(condition.wait(50)); condition.endWaiting(); } { dbgKrita << "Initialized, not awake, then awake"; KisLazyWaitCondition condition; condition.initWaiting(); QVERIFY(!condition.wait(50)); condition.wakeAll(); QVERIFY(condition.wait(50)); condition.endWaiting(); } { dbgKrita << "Doublewait"; KisLazyWaitCondition condition; condition.initWaiting(); condition.initWaiting(); QVERIFY(!condition.wait(50)); condition.wakeAll(); QVERIFY(condition.wait(50)); QVERIFY(condition.wait(50)); condition.endWaiting(); } } #define NUM_THREADS 10 #define NUM_CYCLES 500 #define NTH_CHECK 3 class UpdatesBlockTester : public QRunnable { public: UpdatesBlockTester(KisUpdateScheduler *scheduler, KisNodeSP node) : m_scheduler(scheduler), m_node(node) { } void run() { for (int i = 0; i < NUM_CYCLES; i++) { if(i % NTH_CHECK == 0) { m_scheduler->blockUpdates(); QTest::qSleep(1); // a bit of salt for crashiness ;) Q_ASSERT(!m_scheduler->haveUpdatesRunning()); m_scheduler->unblockUpdates(); } else { QRect updateRect(0,0,100,100); updateRect.moveTopLeft(QPoint((i%10)*100, (i%10)*100)); m_scheduler->updateProjection(m_node, updateRect, QRect(0,0,1100,1100)); } } } private: KisUpdateScheduler *m_scheduler; KisNodeSP m_node; }; void KisUpdateSchedulerTest::testBlockUpdates() { KisImageSP image = buildTestingImage(); KisNodeSP rootLayer = image->rootLayer(); KisNodeSP paintLayer1 = rootLayer->firstChild(); KisUpdateScheduler scheduler(image.data()); QThreadPool threadPool; threadPool.setMaxThreadCount(NUM_THREADS); for(int i = 0; i< NUM_THREADS; i++) { UpdatesBlockTester *tester = new UpdatesBlockTester(&scheduler, paintLayer1); threadPool.start(tester); } threadPool.waitForDone(); } #include "kis_update_time_monitor.h" void KisUpdateSchedulerTest::testTimeMonitor() { QVector dirtyRects; KisUpdateTimeMonitor::instance()->startStrokeMeasure(); KisUpdateTimeMonitor::instance()->reportMouseMove(QPointF(100, 0)); KisUpdateTimeMonitor::instance()->reportJobStarted((void*) 10); QTest::qSleep(300); KisUpdateTimeMonitor::instance()->reportJobStarted((void*) 20); QTest::qSleep(100); dirtyRects << QRect(10,10,10,10); KisUpdateTimeMonitor::instance()->reportJobFinished((void*) 10, dirtyRects); QTest::qSleep(100); dirtyRects.clear(); dirtyRects << QRect(30,30,10,10); KisUpdateTimeMonitor::instance()->reportJobFinished((void*) 20, dirtyRects); QTest::qSleep(500); KisUpdateTimeMonitor::instance()->reportUpdateFinished(QRect(10,10,10,10)); QTest::qSleep(300); KisUpdateTimeMonitor::instance()->reportUpdateFinished(QRect(30,30,10,10)); KisUpdateTimeMonitor::instance()->reportMouseMove(QPointF(130, 0)); KisUpdateTimeMonitor::instance()->endStrokeMeasure(); } +void KisUpdateSchedulerTest::testLodSync() +{ + KisImageSP image = buildTestingImage(); + QRect imageRect = image->bounds(); + KisNodeSP rootLayer = image->root(); + KisNodeSP paintLayer1 = rootLayer->firstChild(); + + QCOMPARE(paintLayer1->name(), QString("paint1")); + + + image->setLevelOfDetailBlocked(false); + image->setDesiredLevelOfDetail(2); + + image->explicitRegenerateLevelOfDetail(); + + image->waitForDone(); +} + QTEST_MAIN(KisUpdateSchedulerTest) diff --git a/libs/image/tests/kis_update_scheduler_test.h b/libs/image/tests/kis_update_scheduler_test.h index 41e5552c79..65a960aa92 100644 --- a/libs/image/tests/kis_update_scheduler_test.h +++ b/libs/image/tests/kis_update_scheduler_test.h @@ -1,46 +1,48 @@ /* * Copyright (c) 2010 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_UPDATE_SCHEDULER_TEST_H #define KIS_UPDATE_SCHEDULER_TEST_H #include #include "kis_types.h" class KisUpdateSchedulerTest : public QObject { Q_OBJECT private: KisImageSP buildTestingImage(); private Q_SLOTS: void testMerge(); void benchmarkOverlappedMerge(); void testLocking(); void testExclusiveStrokes(); void testEmptyStroke(); void testLazyWaitCondition(); void testBlockUpdates(); void testTimeMonitor(); + + void testLodSync(); }; #endif /* KIS_UPDATE_SCHEDULER_TEST_H */ diff --git a/libs/image/tiles3/tests/CMakeLists.txt b/libs/image/tiles3/tests/CMakeLists.txt index ca584f1192..7c3e4aee61 100644 --- a/libs/image/tiles3/tests/CMakeLists.txt +++ b/libs/image/tiles3/tests/CMakeLists.txt @@ -1,77 +1,75 @@ set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) include_directories( ${CMAKE_BINARY_DIR}/libs/image - ${CMAKE_CURRENT_SOURCE_DIR}/tiles3 - ${CMAKE_CURRENT_SOURCE_DIR}/tile4/SWAP ) include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR} ) macro_add_unittest_definitions() ########### next target ############### set(kis_tiled_data_manager_test_SRCS kis_tiled_data_manager_test.cpp ) kde4_add_unit_test(KisTiledDataManagerTest TESTNAME krita-image-KisTiledDataManagerTest ${kis_tiled_data_manager_test_SRCS}) target_link_libraries(KisTiledDataManagerTest kritaimage Qt5::Test) ########### next target ############### set(kis_low_memory_tests_SRCS kis_low_memory_tests.cpp ) kde4_add_unit_test(KisLowMemoryTests TESTNAME krita-image-KisLowMemoryTests ${kis_low_memory_tests_SRCS}) target_link_libraries(KisLowMemoryTests kritaimage Qt5::Test) set_tests_properties(krita-image-KisLowMemoryTests PROPERTIES TIMEOUT 180) ########### next target ############### #kisdoc #set(kis_tile_compressors_test_SRCS kis_tile_compressors_test.cpp ) #kde4_add_unit_test(KisTileCompressorsTest TESTNAME krita-image-KisTileCompressorsTest ${kis_tile_compressors_test_SRCS}) #target_link_libraries(KisTileCompressorsTest kritaodf kritaimage Qt5::Test) ########### next target ############### #kisdoc #set(kis_compression_tests_SRCS kis_compression_tests.cpp ) #kde4_add_unit_test(KisCompressionTests TESTNAME krita-image-KisCompressionTests ${kis_compression_tests_SRCS}) #target_link_libraries(KisCompressionTests kritaimage Qt5::Test) ########### next target ############### set(kis_lockless_stack_test_SRCS kis_lockless_stack_test.cpp ) kde4_add_unit_test(KisLocklessStackTest TESTNAME krita-image-KisLocklessStackTest ${kis_lockless_stack_test_SRCS}) target_link_libraries(KisLocklessStackTest kritaimage Qt5::Test) ########### next target ############### set(kis_memory_pool_test_SRCS kis_memory_pool_test.cpp ) kde4_add_unit_test(KisMemoryPoolTest TESTNAME krita-image-KisMemoryPoolTest ${kis_memory_pool_test_SRCS}) target_link_libraries(KisMemoryPoolTest kritaglobal Qt5::Test) ########### next target ############### set(kis_chunk_allocator_test_SRCS kis_chunk_allocator_test.cpp ../swap/kis_chunk_allocator.cpp) kde4_add_unit_test(KisChunkAllocatorTest TESTNAME krita-image-KisChunkAllocatorTest ${kis_chunk_allocator_test_SRCS}) target_link_libraries(KisChunkAllocatorTest kritaglobal Qt5::Test) ########### next target ############### set(kis_memory_window_test_SRCS kis_memory_window_test.cpp ../swap/kis_memory_window.cpp) kde4_add_unit_test(KisMemoryWindowTest TESTNAME krita-image-KisMemoryWindowTest ${kis_memory_window_test_SRCS}) target_link_libraries(KisMemoryWindowTest kritaglobal Qt5::Test) ########### next target ############### set(kis_swapped_data_store_test_SRCS kis_swapped_data_store_test.cpp ../kis_tile_data.cc) kde4_add_unit_test(KisSwappedDataStoreTest TESTNAME krita-image-KisSwappedDataStoreTest ${kis_swapped_data_store_test_SRCS}) target_link_libraries(KisSwappedDataStoreTest kritaimage Qt5::Test ${Boost_SYSTEM_LIBRARY}) ########### next target ############### set(kis_tile_data_store_test_SRCS kis_tile_data_store_test.cpp ../kis_tile_data.cc) kde4_add_broken_unit_test(KisTileDataStoreTest TESTNAME krita-image-KisTileDataStoreTest ${kis_tile_data_store_test_SRCS}) target_link_libraries(KisTileDataStoreTest kritaimage Qt5::Test ${Boost_SYSTEM_LIBRARY}) ########### next target ############### set(kis_store_limits_test_SRCS kis_store_limits_test.cpp ../kis_tile_data.cc ) kde4_add_broken_unit_test(KisStoreLimitsTest TESTNAME krita-image-KisStoreLimitsTest ${kis_store_limits_test_SRCS}) target_link_libraries(KisStoreLimitsTest kritaimage Qt5::Test ${Boost_SYSTEM_LIBRARY}) ########### next target ############### set(kis_tile_data_pooler_test_SRCS kis_tile_data_pooler_test.cpp ../kis_tile_data.cc ../kis_tile_data_pooler.cc ) kde4_add_unit_test(KisTileDataPoolerTest TESTNAME krita-image-KisTileDataPoolerTest ${kis_tile_data_pooler_test_SRCS}) target_link_libraries(KisTileDataPoolerTest kritaimage Qt5::Test ${Boost_SYSTEM_LIBRARY}) diff --git a/libs/koplugin/KisMimeDatabase.cpp b/libs/koplugin/KisMimeDatabase.cpp index 3420727013..72eb8f1097 100644 --- a/libs/koplugin/KisMimeDatabase.cpp +++ b/libs/koplugin/KisMimeDatabase.cpp @@ -1,258 +1,264 @@ /* * Copyright (c) 2016 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisMimeDatabase.h" #include #include #include #include #include QList KisMimeDatabase::s_mimeDatabase; QString KisMimeDatabase::mimeTypeForFile(const QString &file) { fillMimeData(); QFileInfo fi(file); QString suffix = fi.suffix(); Q_FOREACH(const KisMimeDatabase::KisMimeType &mimeType, s_mimeDatabase) { if (mimeType.suffixes.contains("*." + suffix)) { debugPlugin << "mimeTypeForFile(). KisMimeDatabase returned" << mimeType.mimeType << "for" << file; return mimeType.mimeType; } } QMimeDatabase db; QMimeType mime = db.mimeTypeForFile(file); if (mime.name() != "application/octet-stream") { debugPlugin << "mimeTypeForFile(). QMimeDatabase returned" << mime.name() << "for" << file; return mime.name(); } return ""; } QString KisMimeDatabase::mimeTypeForSuffix(const QString &suffix) { fillMimeData(); QMimeDatabase db; QString s = suffix; if (!s.startsWith("*.")) { s = "*." + s; } Q_FOREACH(const KisMimeDatabase::KisMimeType &mimeType, s_mimeDatabase) { if (mimeType.suffixes.contains(s)) { debugPlugin << "mimeTypeForSuffix(). KisMimeDatabase returned" << mimeType.mimeType << "for" << s; return mimeType.mimeType; } } QMimeType mime = db.mimeTypeForFile(s); if (mime.name() != "application/octet-stream") { debugPlugin << "mimeTypeForSuffix(). QMimeDatabase returned" << mime.name() << "for" << s; return mime.name(); } return ""; } QString KisMimeDatabase::mimeTypeForData(const QByteArray ba) { QMimeDatabase db; QMimeType mtp = db.mimeTypeForData(ba); debugPlugin << "mimeTypeForData(). QMimeDatabase returned" << mtp.name(); return mtp.name(); } QString KisMimeDatabase::descriptionForMimeType(const QString &mimeType) { fillMimeData(); Q_FOREACH(const KisMimeDatabase::KisMimeType &m, s_mimeDatabase) { if (m.mimeType == mimeType) { debugPlugin << "descriptionForMimeType. KisMimeDatabase returned" << m.description << "for" << mimeType; return m.description; } } QMimeDatabase db; QMimeType mime = db.mimeTypeForName(mimeType); if (mime.name() != "application/octet-stream") { debugPlugin << "descriptionForMimeType. QMimeDatabase returned" << mime.comment() << "for" << mimeType; return mime.comment(); } return mimeType; } QStringList KisMimeDatabase::suffixesForMimeType(const QString &mimeType) { fillMimeData(); Q_FOREACH(const KisMimeDatabase::KisMimeType &m, s_mimeDatabase) { if (m.mimeType == mimeType) { debugPlugin << "suffixesForMimeType. KisMimeDatabase returned" << m.suffixes; return m.suffixes; } } QMimeDatabase db; QMimeType mime = db.mimeTypeForName(mimeType); if (mime.name() != "application/octet-stream" && !mime.suffixes().isEmpty()) { QString preferredSuffix = mime.preferredSuffix(); QStringList suffixes = mime.suffixes(); if (preferredSuffix != suffixes.first()) { suffixes.removeAll(preferredSuffix); suffixes.prepend(preferredSuffix); } debugPlugin << "suffixesForMimeType. QMimeDatabase returned" << suffixes; return suffixes; } return QStringList(); } QString KisMimeDatabase::iconNameForMimeType(const QString &mimeType) { QMimeDatabase db; QMimeType mime = db.mimeTypeForName(mimeType); debugPlugin << "iconNameForMimeType" << mime.iconName(); return mime.iconName(); } void KisMimeDatabase::fillMimeData() { // This should come from the import/export plugins, but the json files aren't translated, // which is bad for the description field if (s_mimeDatabase.isEmpty()) { KisMimeType mimeType; mimeType.mimeType = "image/x-gimp-brush"; mimeType.description = i18nc("description of a file type", "Gimp Brush"); mimeType.suffixes = QStringList() << "*.gbr" << "*.vbr"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/x-gimp-brush-animated"; mimeType.description = i18nc("description of a file type", "Gimp Image Hose Brush"); mimeType.suffixes = QStringList() << "*.gih"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/x-adobe-brushlibrary"; mimeType.description = i18nc("description of a file type", "Adobe Brush Library"); mimeType.suffixes = QStringList() << "*.abr"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-krita-paintoppreset"; mimeType.description = i18nc("description of a file type", "Krita Brush Preset"); mimeType.suffixes = QStringList() << "*.kpp"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-krita-assistant"; mimeType.description = i18nc("description of a file type", "Krita Assistant"); mimeType.suffixes = QStringList() << "*.paintingassistant"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/x-r16"; mimeType.description = i18nc("description of a file type", "R16 Heightmap"); mimeType.suffixes = QStringList() << "*.r16"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/x-r8"; mimeType.description = i18nc("description of a file type", "R8 Heightmap"); mimeType.suffixes = QStringList() << "*.r8"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-spriter"; mimeType.description = i18nc("description of a file type", "Spriter SCML"); mimeType.suffixes = QStringList() << "*.scml"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/x-svm"; mimeType.description = i18nc("description of a file type", "Starview Metafile"); mimeType.suffixes = QStringList() << "*.svm"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/openraster"; mimeType.description = i18nc("description of a file type", "OpenRaster Image"); mimeType.suffixes = QStringList() << "*.ora"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-photoshop-style-library"; mimeType.description = i18nc("description of a file type", "Photoshop Layer Style Library"); mimeType.suffixes = QStringList() << "*.asl"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-gimp-color-palette"; mimeType.description = i18nc("description of a file type", "Gimp Color Palette"); mimeType.suffixes = QStringList() << "*.gpl" << "*.pal" << "*.act" << "*.aco" << "*.colors"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-opencolorio-configuration"; mimeType.description = i18nc("description of a file type", "OpenColorIO Configuration"); mimeType.suffixes = QStringList() << "*.ocio"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-krita-recorded-macro"; mimeType.description = i18nc("description of a file type", "Krita Recorded Action"); mimeType.suffixes = QStringList() << "*.krarec"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-gimp-gradient"; mimeType.description = i18nc("description of a file type", "GIMP Gradients"); mimeType.suffixes = QStringList() << "*.ggr"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-gimp-pattern"; mimeType.description = i18nc("description of a file type", "GIMP Patterns"); mimeType.suffixes = QStringList() << "*.pat"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-karbon-gradient"; mimeType.description = i18nc("description of a file type", "Karbon Gradients"); mimeType.suffixes = QStringList() << "*.kgr"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-krita-bundle"; mimeType.description = i18nc("description of a file type", "Krita Resource Bundle"); mimeType.suffixes = QStringList() << "*.bundle"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-krita-workspace"; mimeType.description = i18nc("description of a file type", "Krita Workspace"); + mimeType.suffixes = QStringList() << "*.kws"; + s_mimeDatabase << mimeType; + + mimeType.mimeType = "application/x-krita-taskset"; + mimeType.description = i18nc("description of a file type", "Krita Taskset"); mimeType.suffixes = QStringList() << "*.kts"; s_mimeDatabase << mimeType; + mimeType.mimeType = "image/x-krita-raw"; mimeType.description = i18nc("description of a file type", "Camera Raw Files"); mimeType.suffixes = QStringList() << "*.nef" << "*.cr2" << "*.sr2" << "*.crw" << "*.pef" << "*.x3f" << "*.kdc" << "*.mrw" << "*.arw" << "*.k25" << "*.dcr" << "*.orf" << "*.raw" << "*.raw" << "*.raf" << "*.srf" << "*.dng"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-extension-exr"; mimeType.description = i18nc("description of a file type", "OpenEXR (Extended)"); mimeType.suffixes = QStringList() << "*.exr"; s_mimeDatabase << mimeType; debugPlugin << "Filled mimedatabase with" << s_mimeDatabase.count() << "special mimetypes"; } } diff --git a/libs/pigment/CMakeLists.txt b/libs/pigment/CMakeLists.txt index 0db69f65c2..f72e516bf3 100644 --- a/libs/pigment/CMakeLists.txt +++ b/libs/pigment/CMakeLists.txt @@ -1,123 +1,126 @@ project(kritapigment) # we have to repeat platform specifics from top-level if (WIN32) include_directories(${CMAKE_SOURCE_DIR}/winquirks) add_definitions(-D_USE_MATH_DEFINES) add_definitions(-DNOMINMAX) set(WIN32_PLATFORM_NET_LIBS ws2_32.lib netapi32.lib) endif () include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/resources ${CMAKE_CURRENT_SOURCE_DIR}/compositeops) include_directories(SYSTEM ${Boost_INCLUDE_DIRS} ) set(FILE_OPENEXR_SOURCES) set(LINK_OPENEXR_LIB) if(OPENEXR_FOUND) include_directories(SYSTEM ${OPENEXR_INCLUDE_DIR}) set(LINK_OPENEXR_LIB ${OPENEXR_LIBRARIES}) add_definitions(${OPENEXR_DEFINITIONS}) endif() set(LINK_VC_LIB) if(HAVE_VC) include_directories(SYSTEM ${Vc_INCLUDE_DIR}) set(LINK_VC_LIB ${Vc_LIBRARIES}) ko_compile_for_all_implementations_no_scalar(__per_arch_factory_objs compositeops/KoOptimizedCompositeOpFactoryPerArch.cpp) message("Following objects are generated from the per-arch lib") message(${__per_arch_factory_objs}) endif() add_subdirectory(tests) add_subdirectory(benchmarks) set(kritapigment_SRCS DebugPigment.cpp KoBasicHistogramProducers.cpp KoColor.cpp KoColorDisplayRendererInterface.cpp KoColorConversionAlphaTransformation.cpp KoColorConversionCache.cpp KoColorConversions.cpp KoColorConversionSystem.cpp KoColorConversionTransformation.cpp KoColorConversionTransformationFactory.cpp KoColorModelStandardIds.cpp KoColorProfile.cpp KoColorSpace.cpp KoColorSpaceEngine.cpp KoColorSpaceFactory.cpp KoColorSpaceMaths.cpp KoColorSpaceRegistry.cpp KoColorTransformation.cpp KoColorTransformationFactory.cpp KoColorTransformationFactoryRegistry.cpp KoCompositeColorTransformation.cpp KoCompositeOp.cpp KoCompositeOpRegistry.cpp KoCopyColorConversionTransformation.cpp KoFallBackColorTransformation.cpp KoHistogramProducer.cpp KoMultipleColorConversionTransformation.cpp KoUniqueNumberForIdServer.cpp colorspaces/KoAlphaColorSpace.cpp colorspaces/KoLabColorSpace.cpp colorspaces/KoRgbU16ColorSpace.cpp colorspaces/KoRgbU8ColorSpace.cpp colorspaces/KoSimpleColorSpaceEngine.cpp compositeops/KoOptimizedCompositeOpFactory.cpp compositeops/KoOptimizedCompositeOpFactoryPerArch_Scalar.cpp ${__per_arch_factory_objs} colorprofiles/KoDummyColorProfile.cpp resources/KoAbstractGradient.cpp resources/KoColorSet.cpp resources/KoPattern.cpp resources/KoResource.cpp resources/KoMD5Generator.cpp resources/KoHashGeneratorProvider.cpp resources/KoStopGradient.cpp resources/KoSegmentGradient.cpp ) set (EXTRA_LIBRARIES ${LINK_OPENEXR_LIB} ${LINK_VC_LIB}) if(MSVC OR (WIN32 AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")) # avoid "cannot open file 'LIBC.lib'" error set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB:LIBC.LIB") endif() add_library(kritapigment SHARED ${kritapigment_SRCS}) generate_export_header(kritapigment) if (HAVE_VC) target_link_libraries(kritapigment PUBLIC KF5::I18n KF5::ConfigCore) + if (NOT PACKAGERS_BUILD) + set_property(TARGET kritapigment APPEND PROPERTY COMPILE_OPTIONS "${Vc_ARCHITECTURE_FLAGS}") + endif() endif() target_include_directories( kritapigment PUBLIC $ $ ) target_link_libraries( kritapigment PUBLIC kritaplugin ${EXTRA_LIBRARIES} KF5::I18n Qt5::Core Qt5::Gui Qt5::Xml ${WIN32_PLATFORM_NET_LIBS} ) set_target_properties(kritapigment PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritapigment ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/libs/pigment/KoColor.cpp b/libs/pigment/KoColor.cpp index 1729502f6a..f79648caa2 100644 --- a/libs/pigment/KoColor.cpp +++ b/libs/pigment/KoColor.cpp @@ -1,330 +1,331 @@ /* * Copyright (c) 2005 Boudewijn Rempt * Copyright (C) 2007 Thomas Zander * Copyright (C) 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. */ #include "KoColor.h" #include #include #include "DebugPigment.h" #include "KoColorModelStandardIds.h" #include "KoColorProfile.h" #include "KoColorSpace.h" #include "KoColorSpaceRegistry.h" #include "KoChannelInfo.h" class Q_DECL_HIDDEN KoColor::Private { public: Private() : data(0), colorSpace(0) {} ~Private() { delete [] data; } quint8 * data; const KoColorSpace * colorSpace; }; KoColor::KoColor() : d(new Private()) { d->colorSpace = KoColorSpaceRegistry::instance()->rgb16(0); d->data = new quint8[d->colorSpace->pixelSize()]; d->colorSpace->fromQColor(Qt::black, d->data); d->colorSpace->setOpacity(d->data, OPACITY_OPAQUE_U8, 1); } KoColor::KoColor(const KoColorSpace * colorSpace) : d(new Private()) { Q_ASSERT(colorSpace); d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); d->data = new quint8[d->colorSpace->pixelSize()]; memset(d->data, 0, d->colorSpace->pixelSize()); } KoColor::~KoColor() { delete d; } KoColor::KoColor(const QColor & color, const KoColorSpace * colorSpace) : d(new Private()) { Q_ASSERT(color.isValid()); Q_ASSERT(colorSpace); d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); d->data = new quint8[colorSpace->pixelSize()]; memset(d->data, 0, d->colorSpace->pixelSize()); d->colorSpace->fromQColor(color, d->data); } KoColor::KoColor(const quint8 * data, const KoColorSpace * colorSpace) : d(new Private()) { Q_ASSERT(colorSpace); Q_ASSERT(data); d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); d->data = new quint8[colorSpace->pixelSize()]; memset(d->data, 0, d->colorSpace->pixelSize()); memmove(d->data, data, colorSpace->pixelSize()); } KoColor::KoColor(const KoColor &src, const KoColorSpace * colorSpace) : d(new Private()) { Q_ASSERT(colorSpace); d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); d->data = new quint8[colorSpace->pixelSize()]; memset(d->data, 0, d->colorSpace->pixelSize()); src.colorSpace()->convertPixelsTo(src.d->data, d->data, colorSpace, 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); } KoColor::KoColor(const KoColor & rhs) : d(new Private()) { d->colorSpace = rhs.colorSpace(); Q_ASSERT(*d->colorSpace == *KoColorSpaceRegistry::instance()->permanentColorspace(d->colorSpace)); if (d->colorSpace && rhs.d->data) { d->data = new quint8[d->colorSpace->pixelSize()]; memcpy(d->data, rhs.d->data, d->colorSpace->pixelSize()); } } KoColor & KoColor::operator=(const KoColor & rhs) { if (this == &rhs) return *this; delete [] d->data; d->data = 0; d->colorSpace = rhs.colorSpace(); if (rhs.d->colorSpace && rhs.d->data) { Q_ASSERT(d->colorSpace == KoColorSpaceRegistry::instance()->permanentColorspace(d->colorSpace)); // here we want to do a check on pointer, since d->colorSpace is supposed to already be a permanent one d->data = new quint8[d->colorSpace->pixelSize()]; memcpy(d->data, rhs.d->data, d->colorSpace->pixelSize()); } return * this; } bool KoColor::operator==(const KoColor &other) const { if (!(*colorSpace() == *other.colorSpace())) return false; return memcmp(d->data, other.d->data, d->colorSpace->pixelSize()) == 0; } void KoColor::convertTo(const KoColorSpace * cs, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { //dbgPigment <<"Our colormodel:" << d->colorSpace->id().name() // << ", new colormodel: " << cs->id().name() << "\n"; if (*d->colorSpace == *cs) return; quint8 * data = new quint8[cs->pixelSize()]; memset(data, 0, cs->pixelSize()); d->colorSpace->convertPixelsTo(d->data, data, cs, 1, renderingIntent, conversionFlags); delete [] d->data; d->data = data; d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(cs); } void KoColor::convertTo(const KoColorSpace * cs) { convertTo(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); } void KoColor::setColor(const quint8 * data, const KoColorSpace * colorSpace) { Q_ASSERT(data); Q_ASSERT(colorSpace); if(d->colorSpace->pixelSize() != colorSpace->pixelSize()) { delete [] d->data; d->data = new quint8[colorSpace->pixelSize()]; } memcpy(d->data, data, colorSpace->pixelSize()); d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); } // To save the user the trouble of doing color->colorSpace()->toQColor(color->data(), &c, &a, profile void KoColor::toQColor(QColor *c) const { Q_ASSERT(c); if (d->colorSpace && d->data) { d->colorSpace->toQColor(d->data, c); } } QColor KoColor::toQColor() const { QColor c; toQColor(&c); return c; } void KoColor::fromQColor(const QColor& c) const { if (d->colorSpace && d->data) { d->colorSpace->fromQColor(c, d->data); } } #ifndef NDEBUG void KoColor::dump() const { dbgPigment <<"KoColor (" << this <<")," << d->colorSpace->id() <<""; QList channels = d->colorSpace->channels(); QList::const_iterator begin = channels.constBegin(); QList::const_iterator end = channels.constEnd(); for (QList::const_iterator it = begin; it != end; ++it) { KoChannelInfo * ch = (*it); // XXX: setNum always takes a byte. if (ch->size() == sizeof(quint8)) { // Byte dbgPigment <<"Channel (byte):" << ch->name() <<":" << QString().setNum(d->data[ch->pos()]) <<""; } else if (ch->size() == sizeof(quint16)) { // Short (may also by an nvidia half) dbgPigment <<"Channel (short):" << ch->name() <<":" << QString().setNum(*((const quint16 *)(d->data+ch->pos()))) <<""; } else if (ch->size() == sizeof(quint32)) { // Integer (may also be float... Find out how to distinguish these!) dbgPigment <<"Channel (int):" << ch->name() <<":" << QString().setNum(*((const quint32 *)(d->data+ch->pos()))) <<""; } } } #endif void KoColor::fromKoColor(const KoColor& src) { src.colorSpace()->convertPixelsTo(src.d->data, d->data, colorSpace(), 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); } const KoColorProfile * KoColor::profile() const { return d->colorSpace->profile(); } quint8 * KoColor::data() { return d->data; } const quint8 * KoColor::data() const { return d->data; } const KoColorSpace * KoColor::colorSpace() const { return d->colorSpace; } void KoColor::toXML(QDomDocument& doc, QDomElement& colorElt) const { d->colorSpace->colorToXML(d->data, doc, colorElt); } void KoColor::setOpacity(quint8 alpha) { d->colorSpace->setOpacity(d->data, alpha, 1); } void KoColor::setOpacity(qreal alpha) { d->colorSpace->setOpacity(d->data, alpha, 1); } quint8 KoColor::opacityU8() const { return d->colorSpace->opacityU8(d->data); } qreal KoColor::opacityF() const { return d->colorSpace->opacityF(d->data); } KoColor KoColor::fromXML(const QDomElement& elt, const QString & bitDepthId, const QHash & aliases) { QString modelId; if (elt.tagName() == "CMYK") { modelId = CMYKAColorModelID.id(); } else if (elt.tagName() == "RGB") { modelId = RGBAColorModelID.id(); } else if (elt.tagName() == "sRGB") { modelId = RGBAColorModelID.id(); } else if (elt.tagName() == "Lab") { modelId = LABAColorModelID.id(); } else if (elt.tagName() == "XYZ") { modelId = XYZAColorModelID.id(); } else if (elt.tagName() == "Gray") { modelId = GrayAColorModelID.id(); } else if (elt.tagName() == "YCbCr") { modelId = YCbCrAColorModelID.id(); } QString profileName; if (elt.tagName() != "sRGB") { profileName = elt.attribute("space", ""); - if (aliases.contains(profileName)) { - profileName = aliases.value(profileName); + const QHash::ConstIterator it = aliases.find(profileName); + if (it != aliases.end()) { + profileName = it.value(); } if (!KoColorSpaceRegistry::instance()->profileByName(profileName)) { profileName.clear(); } } const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(modelId, bitDepthId, profileName); if (cs == 0) { QList list = KoColorSpaceRegistry::instance()->colorDepthList(modelId, KoColorSpaceRegistry::AllColorSpaces); if (!list.empty()) { cs = KoColorSpaceRegistry::instance()->colorSpace(modelId, list[0].id(), profileName); } } if (cs) { KoColor c(cs); cs->colorFromXML(c.data(), elt); return c; } else { return KoColor(); } } QString KoColor::toQString(const KoColor &color) { QStringList ls; Q_FOREACH (KoChannelInfo *channel, KoChannelInfo::displayOrderSorted(color.colorSpace()->channels())) { int realIndex = KoChannelInfo::displayPositionToChannelIndex(channel->displayPosition(), color.colorSpace()->channels()); ls << channel->name(); ls << color.colorSpace()->channelValueText(color.data(), realIndex); } return ls.join(" "); } diff --git a/libs/pigment/KoColorConversionSystem.cpp b/libs/pigment/KoColorConversionSystem.cpp index 1b2f7ba0f7..e90b385fd1 100644 --- a/libs/pigment/KoColorConversionSystem.cpp +++ b/libs/pigment/KoColorConversionSystem.cpp @@ -1,518 +1,521 @@ /* * 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 "KoColorConversionSystem.h" #include "KoColorConversionSystem_p.h" #include #include #include "KoColorConversionAlphaTransformation.h" #include "KoColorConversionTransformation.h" #include "KoColorProfile.h" #include "KoColorSpace.h" #include "KoCopyColorConversionTransformation.h" #include "KoMultipleColorConversionTransformation.h" KoColorConversionSystem::KoColorConversionSystem() : d(new Private) { // Create the Alpha 8bit d->alphaNode = new Node; d->alphaNode->modelId = AlphaColorModelID.id(); d->alphaNode->depthId = Integer8BitsColorDepthID.id(); d->alphaNode->crossingCost = 1000000; d->alphaNode->isInitialized = true; d->alphaNode->isGray = true; // <- FIXME: it's a little bit hacky as alpha doesn't really have color information - d->graph[ NodeKey(d->alphaNode->modelId, d->alphaNode->depthId, "default")] = d->alphaNode; + d->graph.insert(NodeKey(d->alphaNode->modelId, d->alphaNode->depthId, "default"), d->alphaNode); Vertex* v = createVertex(d->alphaNode, d->alphaNode); v->setFactoryFromSrc(new KoCopyColorConversionTransformationFactory(AlphaColorModelID.id(), Integer8BitsColorDepthID.id(), "default")); } KoColorConversionSystem::~KoColorConversionSystem() { qDeleteAll(d->graph); qDeleteAll(d->vertexes); delete d; } void KoColorConversionSystem::connectToEngine(Node* _node, Node* _engine) { Vertex* v1 = createVertex(_node, _engine); Vertex* v2 = createVertex(_engine, _node); v1->conserveColorInformation = !_node->isGray; v2->conserveColorInformation = !_node->isGray; v1->conserveDynamicRange = _engine->isHdr; v2->conserveDynamicRange = _engine->isHdr; } KoColorConversionSystem::Node* KoColorConversionSystem::insertEngine(const KoColorSpaceEngine* engine) { NodeKey key(engine->id(), engine->id(), engine->id()); Node* n = new Node; n->modelId = engine->id(); n->depthId = engine->id(); n->profileName = engine->id(); n->referenceDepth = 64; // engine don't have reference depth, - d->graph[ key ] = n; + d->graph.insert(key, n); n->init(engine); return n; } void KoColorConversionSystem::insertColorSpace(const KoColorSpaceFactory* csf) { dbgPigment << "Inserting color space " << csf->name() << " (" << csf->id() << ") Model: " << csf->colorModelId() << " Depth: " << csf->colorDepthId() << " into the CCS"; - QList profiles = KoColorSpaceRegistry::instance()->profilesFor(csf); + const QList profiles = KoColorSpaceRegistry::instance()->profilesFor(csf); QString modelId = csf->colorModelId().id(); QString depthId = csf->colorDepthId().id(); if (profiles.isEmpty()) { // There is no profile for this CS, create a node without profile name if the color engine isn't icc-based if (csf->colorSpaceEngine() != "icc") { Node* n = nodeFor(modelId, depthId, "default"); n->init(csf); } else { dbgPigment << "Cannot add node for " << csf->name() << ", since there are no profiles available"; } } else { // Initialise the nodes Q_FOREACH (const KoColorProfile* profile, profiles) { Node* n = nodeFor(modelId, depthId, profile->name()); n->init(csf); if (!csf->colorSpaceEngine().isEmpty()) { KoColorSpaceEngine* engine = KoColorSpaceEngineRegistry::instance()->get(csf->colorSpaceEngine()); Q_ASSERT(engine); NodeKey engineKey(engine->id(), engine->id(), engine->id()); Node* engineNode = 0; - if (d->graph.contains(engineKey)) { - engineNode = d->graph[engineKey]; + QHash::ConstIterator it = d->graph.constFind(engineKey); + if (it != d->graph.constEnd()) { + engineNode = it.value(); } else { engineNode = insertEngine(engine); } connectToEngine(n, engineNode); } } } // Construct a link for "custom" transformation - QList cctfs = csf->colorConversionLinks(); + const QList cctfs = csf->colorConversionLinks(); Q_FOREACH (KoColorConversionTransformationFactory* cctf, cctfs) { Node* srcNode = nodeFor(cctf->srcColorModelId(), cctf->srcColorDepthId(), cctf->srcProfile()); Q_ASSERT(srcNode); Node* dstNode = nodeFor(cctf->dstColorModelId(), cctf->dstColorDepthId(), cctf->dstProfile()); Q_ASSERT(dstNode); // Check if the two nodes are already connected Vertex* v = vertexBetween(srcNode, dstNode); // If the vertex doesn't already exist, then create it if (!v) { v = createVertex(srcNode, dstNode); } Q_ASSERT(v); // we should have one now if (dstNode->modelId == modelId && dstNode->depthId == depthId) { v->setFactoryFromDst(cctf); } if (srcNode->modelId == modelId && srcNode->depthId == depthId) { v->setFactoryFromSrc(cctf); } } } void KoColorConversionSystem::insertColorProfile(const KoColorProfile* _profile) { dbgPigmentCCS << _profile->name(); const QList< const KoColorSpaceFactory* >& factories = KoColorSpaceRegistry::instance()->colorSpacesFor(_profile); Q_FOREACH (const KoColorSpaceFactory* factory, factories) { QString modelId = factory->colorModelId().id(); QString depthId = factory->colorDepthId().id(); Node* n = nodeFor(modelId, depthId, _profile->name()); n->init(factory); if (!factory->colorSpaceEngine().isEmpty()) { KoColorSpaceEngine* engine = KoColorSpaceEngineRegistry::instance()->get(factory->colorSpaceEngine()); Q_ASSERT(engine); Node* engineNode = d->graph[ NodeKey(engine->id(), engine->id(), engine->id())]; Q_ASSERT(engineNode); connectToEngine(n, engineNode); } - QList cctfs = factory->colorConversionLinks(); + const QList cctfs = factory->colorConversionLinks(); Q_FOREACH (KoColorConversionTransformationFactory* cctf, cctfs) { Node* srcNode = nodeFor(cctf->srcColorModelId(), cctf->srcColorDepthId(), cctf->srcProfile()); Q_ASSERT(srcNode); Node* dstNode = nodeFor(cctf->dstColorModelId(), cctf->dstColorDepthId(), cctf->dstProfile()); Q_ASSERT(dstNode); if (srcNode == n || dstNode == n) { // Check if the two nodes are already connected Vertex* v = vertexBetween(srcNode, dstNode); // If the vertex doesn't already exist, then create it if (!v) { v = createVertex(srcNode, dstNode); } Q_ASSERT(v); // we should have one now if (dstNode->modelId == modelId && dstNode->depthId == depthId) { v->setFactoryFromDst(cctf); } if (srcNode->modelId == modelId && srcNode->depthId == depthId) { v->setFactoryFromSrc(cctf); } } } } } const KoColorSpace* KoColorConversionSystem::defaultColorSpaceForNode(const Node* node) const { return KoColorSpaceRegistry::instance()->colorSpace(node->modelId, node->depthId, node->profileName); } KoColorConversionSystem::Node* KoColorConversionSystem::createNode(const QString& _modelId, const QString& _depthId, const QString& _profileName) { Node* n = new Node; n->modelId = _modelId; n->depthId = _depthId; n->profileName = _profileName; - d->graph[ NodeKey(_modelId, _depthId, _profileName)] = n; + d->graph.insert(NodeKey(_modelId, _depthId, _profileName), n); Q_ASSERT(vertexBetween(d->alphaNode, n) == 0); // The two color spaces should not be connected yet Vertex* vFromAlpha = createVertex(d->alphaNode, n); vFromAlpha->setFactoryFromSrc(new KoColorConversionFromAlphaTransformationFactory(_modelId, _depthId, _profileName)); Q_ASSERT(vertexBetween(n, d->alphaNode) == 0); // The two color spaces should not be connected yet Vertex* vToAlpha = createVertex(n, d->alphaNode); vToAlpha->setFactoryFromDst(new KoColorConversionToAlphaTransformationFactory(_modelId, _depthId, _profileName)); return n; } const KoColorConversionSystem::Node* KoColorConversionSystem::nodeFor(const KoColorSpace* _colorSpace) const { const KoColorProfile* profile = _colorSpace->profile(); return nodeFor(_colorSpace->colorModelId().id(), _colorSpace->colorDepthId().id(), profile ? profile->name() : "default"); } const KoColorConversionSystem::Node* KoColorConversionSystem::nodeFor(const QString& _colorModelId, const QString& _colorDepthId, const QString& _profileName) const { dbgPigmentCCS << "Look for node: " << _colorModelId << " " << _colorDepthId << " " << _profileName; return nodeFor(NodeKey(_colorModelId, _colorDepthId, _profileName)); } const KoColorConversionSystem::Node* KoColorConversionSystem::nodeFor(const NodeKey& key) const { dbgPigmentCCS << "Look for node: " << key.modelId << " " << key.depthId << " " << key.profileName << " " << d->graph.value(key); return d->graph.value(key); } KoColorConversionSystem::Node* KoColorConversionSystem::nodeFor(const QString& _colorModelId, const QString& _colorDepthId, const QString& _profileName) { return nodeFor(NodeKey(_colorModelId, _colorDepthId, _profileName)); } KoColorConversionSystem::Node* KoColorConversionSystem::nodeFor(const KoColorConversionSystem::NodeKey& key) { - if (d->graph.contains(key)) { - return d->graph.value(key); + QHash::ConstIterator it = d->graph.constFind(key); + if (it != d->graph.constEnd()) { + return it.value(); } else { return createNode(key.modelId, key.depthId, key.profileName); } } QList KoColorConversionSystem::nodesFor(const QString& _modelId, const QString& _depthId) { QList nodes; Q_FOREACH (Node* node, d->graph) { if (node->modelId == _modelId && node->depthId == _depthId) { nodes << node; } } return nodes; } KoColorConversionTransformation* KoColorConversionSystem::createColorConverter(const KoColorSpace * srcColorSpace, const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { if (*srcColorSpace == *dstColorSpace) { return new KoCopyColorConversionTransformation(srcColorSpace); } Q_ASSERT(srcColorSpace); Q_ASSERT(dstColorSpace); dbgPigmentCCS << srcColorSpace->id() << (srcColorSpace->profile() ? srcColorSpace->profile()->name() : "default"); dbgPigmentCCS << dstColorSpace->id() << (dstColorSpace->profile() ? dstColorSpace->profile()->name() : "default"); Path path = findBestPath( nodeFor(srcColorSpace), nodeFor(dstColorSpace)); Q_ASSERT(path.length() > 0); KoColorConversionTransformation* transfo = createTransformationFromPath(path, srcColorSpace, dstColorSpace, renderingIntent, conversionFlags); Q_ASSERT(*transfo->srcColorSpace() == *srcColorSpace); Q_ASSERT(*transfo->dstColorSpace() == *dstColorSpace); Q_ASSERT(transfo); return transfo; } -void KoColorConversionSystem::createColorConverters(const KoColorSpace* colorSpace, QList< QPair >& possibilities, KoColorConversionTransformation*& fromCS, KoColorConversionTransformation*& toCS) const +void KoColorConversionSystem::createColorConverters(const KoColorSpace* colorSpace, const QList< QPair >& possibilities, KoColorConversionTransformation*& fromCS, KoColorConversionTransformation*& toCS) const { // TODO This function currently only select the best conversion only based on the transformation // from colorSpace to one of the color spaces in the list, but not the other way around // it might be worth to look also the return path. const Node* csNode = nodeFor(colorSpace); PathQualityChecker pQC(csNode->referenceDepth, !csNode->isHdr, !csNode->isGray); // Look for a color conversion Path bestPath; typedef QPair KoID2KoID; Q_FOREACH (const KoID2KoID & possibility, possibilities) { const KoColorSpaceFactory* csf = KoColorSpaceRegistry::instance()->colorSpaceFactory(KoColorSpaceRegistry::instance()->colorSpaceId(possibility.first.id(), possibility.second.id())); if (csf) { Path path = findBestPath(csNode, nodeFor(csf->colorModelId().id(), csf->colorDepthId().id(), csf->defaultProfile())); Q_ASSERT(path.length() > 0); path.isGood = pQC.isGoodPath(path); if (bestPath.isEmpty()) { bestPath = path; } else if ((!bestPath.isGood && path.isGood) || pQC.lessWorseThan(path, bestPath)) { bestPath = path; } } } Q_ASSERT(!bestPath.isEmpty()); const KoColorSpace* endColorSpace = defaultColorSpaceForNode(bestPath.endNode()); fromCS = createTransformationFromPath(bestPath, colorSpace, endColorSpace, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); Path returnPath = findBestPath(bestPath.endNode(), csNode); Q_ASSERT(!returnPath.isEmpty()); toCS = createTransformationFromPath(returnPath, endColorSpace, colorSpace, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); Q_ASSERT(*toCS->dstColorSpace() == *fromCS->srcColorSpace()); Q_ASSERT(*fromCS->dstColorSpace() == *toCS->srcColorSpace()); } KoColorConversionTransformation* KoColorConversionSystem::createTransformationFromPath(const Path &path, const KoColorSpace * srcColorSpace, const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { Q_ASSERT(srcColorSpace->colorModelId().id() == path.startNode()->modelId); Q_ASSERT(srcColorSpace->colorDepthId().id() == path.startNode()->depthId); Q_ASSERT(dstColorSpace->colorModelId().id() == path.endNode()->modelId); Q_ASSERT(dstColorSpace->colorDepthId().id() == path.endNode()->depthId); KoColorConversionTransformation* transfo; - QList< Path::node2factory > pathOfNode = path.compressedPath(); + const QList< Path::node2factory > pathOfNode = path.compressedPath(); if (pathOfNode.size() == 2) { // Direct connection transfo = pathOfNode[1].second->createColorTransformation(srcColorSpace, dstColorSpace, renderingIntent, conversionFlags); } else { KoMultipleColorConversionTransformation* mccTransfo = new KoMultipleColorConversionTransformation(srcColorSpace, dstColorSpace, renderingIntent, conversionFlags); transfo = mccTransfo; // Get the first intermediary color space dbgPigmentCCS << pathOfNode[ 0 ].first->id() << " to " << pathOfNode[ 1 ].first->id(); const KoColorSpace* intermCS = defaultColorSpaceForNode(pathOfNode[1].first); mccTransfo->appendTransfo(pathOfNode[1].second->createColorTransformation(srcColorSpace, intermCS, renderingIntent, conversionFlags)); for (int i = 2; i < pathOfNode.size() - 1; i++) { dbgPigmentCCS << pathOfNode[ i - 1 ].first->id() << " to " << pathOfNode[ i ].first->id(); const KoColorSpace* intermCS2 = defaultColorSpaceForNode(pathOfNode[i].first); Q_ASSERT(intermCS2); mccTransfo->appendTransfo(pathOfNode[i].second->createColorTransformation(intermCS, intermCS2, renderingIntent, conversionFlags)); intermCS = intermCS2; } dbgPigmentCCS << pathOfNode[ pathOfNode.size() - 2 ].first->id() << " to " << pathOfNode[ pathOfNode.size() - 1 ].first->id(); mccTransfo->appendTransfo(pathOfNode.last().second->createColorTransformation(intermCS, dstColorSpace, renderingIntent, conversionFlags)); } return transfo; } KoColorConversionSystem::Vertex* KoColorConversionSystem::vertexBetween(KoColorConversionSystem::Node* srcNode, KoColorConversionSystem::Node* dstNode) { Q_FOREACH (Vertex* oV, srcNode->outputVertexes) { if (oV->dstNode == dstNode) { return oV; } } return 0; } KoColorConversionSystem::Vertex* KoColorConversionSystem::createVertex(Node* srcNode, Node* dstNode) { Vertex* v = new Vertex(srcNode, dstNode); srcNode->outputVertexes.append(v); d->vertexes.append(v); return v; } // -- Graph visualization functions -- -QString KoColorConversionSystem::vertexToDot(KoColorConversionSystem::Vertex* v, QString options) const +QString KoColorConversionSystem::vertexToDot(KoColorConversionSystem::Vertex* v, const QString &options) const { return QString(" \"%1\" -> \"%2\" %3\n").arg(v->srcNode->id()).arg(v->dstNode->id()).arg(options); } QString KoColorConversionSystem::toDot() const { QString dot = "digraph CCS {\n"; Q_FOREACH (Vertex* oV, d->vertexes) { dot += vertexToDot(oV, "default") ; } dot += "}\n"; return dot; } bool KoColorConversionSystem::existsPath(const QString& srcModelId, const QString& srcDepthId, const QString& srcProfileName, const QString& dstModelId, const QString& dstDepthId, const QString& dstProfileName) const { dbgPigmentCCS << "srcModelId = " << srcModelId << " srcDepthId = " << srcDepthId << " srcProfileName = " << srcProfileName << " dstModelId = " << dstModelId << " dstDepthId = " << dstDepthId << " dstProfileName = " << dstProfileName; const Node* srcNode = nodeFor(srcModelId, srcDepthId, srcProfileName); const Node* dstNode = nodeFor(dstModelId, dstDepthId, dstProfileName); if (srcNode == dstNode) return true; if (!srcNode) return false; if (!dstNode) return false; Path path = findBestPath(srcNode, dstNode); bool exist = !path.isEmpty(); return exist; } bool KoColorConversionSystem::existsGoodPath(const QString& srcModelId, const QString& srcDepthId, const QString& srcProfileName, const QString& dstModelId, const QString& dstDepthId, const QString& dstProfileName) const { const Node* srcNode = nodeFor(srcModelId, srcDepthId, srcProfileName); const Node* dstNode = nodeFor(dstModelId, dstDepthId, dstProfileName); if (srcNode == dstNode) return true; if (!srcNode) return false; if (!dstNode) return false; Path path = findBestPath(srcNode, dstNode); bool existAndGood = path.isGood; return existAndGood; } QString KoColorConversionSystem::bestPathToDot(const QString& srcKey, const QString& dstKey) const { const Node* srcNode = 0; const Node* dstNode = 0; Q_FOREACH (Node* node, d->graph) { if (node->id() == srcKey) { srcNode = node; } if (node->id() == dstKey) { dstNode = node; } } Path p = findBestPath(srcNode, dstNode); Q_ASSERT(!p.isEmpty()); QString dot = "digraph CCS {\n" + QString(" \"%1\" [color=red]\n").arg(srcNode->id()) + QString(" \"%1\" [color=red]\n").arg(dstNode->id()); Q_FOREACH (Vertex* oV, d->vertexes) { QString options; if (p.vertexes.contains(oV)) { options = "[color=red]"; } dot += vertexToDot(oV, options) ; } dot += "}\n"; return dot; } inline KoColorConversionSystem::Path KoColorConversionSystem::findBestPathImpl2(const KoColorConversionSystem::Node* srcNode, const KoColorConversionSystem::Node* dstNode, bool ignoreHdr, bool ignoreColorCorrectness) const { PathQualityChecker pQC(qMin(srcNode->referenceDepth, dstNode->referenceDepth), ignoreHdr, ignoreColorCorrectness); Node2PathHash node2path; // current best path to reach a given node QList possiblePaths; // list of all paths // Generate the initial list of paths Q_FOREACH (Vertex* v, srcNode->outputVertexes) { if (v->dstNode->isInitialized) { Path p; p.appendVertex(v); - Node* endNode = p.endNode(); + Node* endNode = v->dstNode; if (endNode == dstNode) { Q_ASSERT(pQC.isGoodPath(p)); // <- it's a direct link, it has to be a good path p.isGood = true; return p; } else { Q_ASSERT(!node2path.contains(endNode)); // That would be a total fuck up if there are two vertexes between two nodes - node2path[ endNode ] = p; + node2path.insert(endNode, p); possiblePaths.append(p); } } } Path currentBestPath; // Continue while there are any possibilities remaining while (possiblePaths.size() > 0) { // Loop through all paths and explore one step further - QList currentPaths = possiblePaths; - for (Path p : currentPaths) { - Node* endNode = p.endNode(); + const QList currentPaths = possiblePaths; + for (const Path &p : currentPaths) { + const Node* endNode = p.endNode(); for (Vertex* v : endNode->outputVertexes) { - if (!p.contains(v->dstNode) && v->dstNode->isInitialized) { + if (v->dstNode->isInitialized && !p.contains(v->dstNode)) { Path newP = p; // Candidate newP.appendVertex(v); - Node* newEndNode = newP.endNode(); + Node* newEndNode = v->dstNode; if (newEndNode == dstNode) { if (pQC.isGoodPath(newP)) { // Victory newP.isGood = true; return newP; } else if (pQC.lessWorseThan(newP, currentBestPath)) { Q_ASSERT(newP.startNode()->id() == currentBestPath.startNode()->id()); Q_ASSERT(newP.endNode()->id() == currentBestPath.endNode()->id()); // Can we do better than dumping memory values??? // warnPigment << pQC.lessWorseThan(newP, currentBestPath) << " " << newP << " " << currentBestPath; currentBestPath = newP; } } else { // This is an incomplete path. Check if there's a better way to get to its endpoint. - if (node2path.contains(newEndNode)) { - Path p2 = node2path[newEndNode]; + Node2PathHash::Iterator it = node2path.find(newEndNode); + if (it != node2path.end()) { + Path &p2 = it.value(); if (pQC.lessWorseThan(newP, p2)) { - node2path[ newEndNode ] = newP; + p2 = newP; possiblePaths.append(newP); } } else { - node2path[ newEndNode ] = newP; + node2path.insert(newEndNode, newP); possiblePaths.append(newP); } } } } possiblePaths.removeAll(p); // Remove from list of remaining paths } } if (!currentBestPath.isEmpty()) { warnPigment << "No good path from " << srcNode->id() << " to " << dstNode->id() << " found : length = " << currentBestPath.length() << " cost = " << currentBestPath.cost << " referenceDepth = " << currentBestPath.referenceDepth << " respectColorCorrectness = " << currentBestPath.respectColorCorrectness << " isGood = " << currentBestPath.isGood ; return currentBestPath; } errorPigment << "No path from " << srcNode->id() << " to " << dstNode->id() << " found not "; return currentBestPath; } inline KoColorConversionSystem::Path KoColorConversionSystem::findBestPathImpl(const KoColorConversionSystem::Node* srcNode, const KoColorConversionSystem::Node* dstNode, bool ignoreHdr) const { Q_ASSERT(srcNode); Q_ASSERT(dstNode); return findBestPathImpl2(srcNode, dstNode, ignoreHdr, (srcNode->isGray || dstNode->isGray)); } KoColorConversionSystem::Path KoColorConversionSystem::findBestPath(const KoColorConversionSystem::Node* srcNode, const KoColorConversionSystem::Node* dstNode) const { Q_ASSERT(srcNode); Q_ASSERT(dstNode); dbgPigmentCCS << "Find best path between " << srcNode->id() << " and " << dstNode->id(); if (srcNode->isHdr && dstNode->isHdr) { return findBestPathImpl(srcNode, dstNode, false); } else { return findBestPathImpl(srcNode, dstNode, true); } } diff --git a/libs/pigment/KoColorConversionSystem.h b/libs/pigment/KoColorConversionSystem.h index aa1529a25f..086d6795b9 100644 --- a/libs/pigment/KoColorConversionSystem.h +++ b/libs/pigment/KoColorConversionSystem.h @@ -1,180 +1,180 @@ /* * 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. */ #ifndef _KO_COLOR_CONVERSION_SYSTEM_H_ #define _KO_COLOR_CONVERSION_SYSTEM_H_ class KoColorProfile; class KoColorSpace; class KoColorSpaceFactory; class KoColorSpaceEngine; class KoID; #include "KoColorConversionTransformation.h" #include #include #include "kritapigment_export.h" /** * This class hold the logic related to pigment's Color Conversion System. It's * basically a graph containing all the possible color transformation between * the color spaces. The most useful functions are createColorConverter to create * a color conversion between two color spaces, and insertColorSpace which is called * by KoColorSpaceRegistry each time a new color space is added to the registry. * * This class is not part of public API, and can be changed without notice. */ class KRITAPIGMENT_EXPORT KoColorConversionSystem { public: struct Node; struct Vertex; struct NodeKey; friend uint qHash(const KoColorConversionSystem::NodeKey &key); struct Path; /** * Construct a Color Conversion System, leave to the KoColorSpaceRegistry to * create it. */ KoColorConversionSystem(); ~KoColorConversionSystem(); /** * This function is called by the KoColorSpaceRegistry to add a new color space * to the graph of transformation. */ void insertColorSpace(const KoColorSpaceFactory*); void insertColorProfile(const KoColorProfile*); /** * This function is called by the color space to create a color conversion * between two color space. This function search in the graph of transformations * the best possible path between the two color space. */ KoColorConversionTransformation* createColorConverter(const KoColorSpace * srcColorSpace, const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const; /** * This function creates two transformations, one from the color space and one to the * color space. The destination color space is picked from a list of color space, such * as the conversion between the two color space is of the best quality. * * The typical use case of this function is for KoColorTransformationFactory which * doesn't support all color spaces, so unsupported color space have to find an * acceptable conversion in order to use that KoColorTransformationFactory. * * @param colorSpace the source color space * @param possibilities a list of color space among which we need to find the best * conversion * @param fromCS the conversion from the source color space will be affected to this * variable * @param toCS the revert conversion to the source color space will be affected to this * variable */ - void createColorConverters(const KoColorSpace* colorSpace, QList< QPair >& possibilities, KoColorConversionTransformation*& fromCS, KoColorConversionTransformation*& toCS) const; + void createColorConverters(const KoColorSpace* colorSpace, const QList< QPair >& possibilities, KoColorConversionTransformation*& fromCS, KoColorConversionTransformation*& toCS) const; public: /** * This function return a text that can be compiled using dot to display * the graph of color conversion connection. */ QString toDot() const; /** * This function return a text that can be compiled using dot to display * the graph of color conversion connection, with a red link to show the * path of the best color conversion. */ QString bestPathToDot(const QString& srcKey, const QString& dstKey) const; public: /** * @return true if there is a path between two color spaces */ bool existsPath(const QString& srcModelId, const QString& srcDepthId, const QString& srcProfileName, const QString& dstModelId, const QString& dstDepthId, const QString& dstProfileName) const; /** * @return true if there is a good path between two color spaces */ bool existsGoodPath(const QString& srcModelId, const QString& srcDepthId, const QString& srcProfileName, const QString& dstModelId, const QString& dstDepthId, const QString& dstProfileName) const; private: - QString vertexToDot(Vertex* v, QString options) const; + QString vertexToDot(Vertex* v, const QString &options) const; private: /** * Insert an engine. */ Node* insertEngine(const KoColorSpaceEngine* engine); KoColorConversionTransformation* createTransformationFromPath(const KoColorConversionSystem::Path& path, const KoColorSpace* srcColorSpace, const KoColorSpace* dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const; /** * Query the registry to get the color space associated with this * node. (default profile) */ const KoColorSpace* defaultColorSpaceForNode(const Node* node) const; /** * Create a new node */ Node* createNode(const QString& _modelId, const QString& _depthId, const QString& _profileName); /** * Initialise a node for ICC color spaces */ void connectToEngine(Node* _node, Node* _engine); const Node* nodeFor(const KoColorSpace*) const; /** * @return the node corresponding to that key, or create it if needed */ Node* nodeFor(const NodeKey& key); const Node* nodeFor(const NodeKey& key) const; /** * @return the list of nodes that correspond to a given model and depth. */ QList nodesFor(const QString& _modelId, const QString& _depthId); /** * @return the node associated with that key, and create it if needed */ Node* nodeFor(const QString& colorModelId, const QString& colorDepthId, const QString& _profileName); const Node* nodeFor(const QString& colorModelId, const QString& colorDepthId, const QString& _profileName) const; /** * @return the vertex between two nodes, or null if the vertex doesn't exist */ Vertex* vertexBetween(Node* srcNode, Node* dstNode); /** * create a vertex between two nodes and return it. */ Vertex* createVertex(Node* srcNode, Node* dstNode); /** * looks for the best path between two nodes */ Path findBestPath(const Node* srcNode, const Node* dstNode) const; /** * Delete all the paths of the list given in argument. */ void deletePaths(QList paths) const; /** * Don't call that function, but raher findBestPath * @internal */ inline Path findBestPathImpl2(const Node* srcNode, const Node* dstNode, bool ignoreHdr, bool ignoreColorCorrectness) const; /** * Don't call that function, but raher findBestPath * @internal */ inline Path findBestPathImpl(const Node* srcNode, const Node* dstNode, bool ignoreHdr) const; private: struct Private; Private* const d; }; #endif diff --git a/libs/pigment/KoColorConversionSystem_p.h b/libs/pigment/KoColorConversionSystem_p.h index 7bd8dfd854..d52f7249bd 100644 --- a/libs/pigment/KoColorConversionSystem_p.h +++ b/libs/pigment/KoColorConversionSystem_p.h @@ -1,303 +1,306 @@ /* * 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. */ #ifndef KOCOLORCONVERSIONSYSTEM_P_H #define KOCOLORCONVERSIONSYSTEM_P_H #include "DebugPigment.h" #include "KoColorSpaceRegistry.h" #include "KoColorModelStandardIds.h" #include "KoColorConversionTransformationFactory.h" #include "KoColorSpaceEngine.h" #include struct KoColorConversionSystem::Node { Node() : isHdr(false) , isInitialized(false) , referenceDepth(0) , isGray(false) , crossingCost(1) , colorSpaceFactory(0) , isEngine(false) , engine(0) {} void init(const KoColorSpaceFactory* _colorSpaceFactory) { dbgPigment << "Initialise " << modelId << " " << depthId << " " << profileName; if (isInitialized) { dbgPigment << "Re-initializing node. Old factory" << colorSpaceFactory << "new factory" << _colorSpaceFactory; } isInitialized = true; if (_colorSpaceFactory) { isHdr = _colorSpaceFactory->isHdr(); colorSpaceFactory = _colorSpaceFactory; referenceDepth = _colorSpaceFactory->referenceDepth(); isGray = (_colorSpaceFactory->colorModelId() == GrayAColorModelID || _colorSpaceFactory->colorModelId() == GrayColorModelID); } } void init(const KoColorSpaceEngine* _engine) { Q_ASSERT(!isInitialized); isEngine = true; isInitialized = true; isHdr = true; engine = _engine; } QString id() const { return modelId + " " + depthId + " " + profileName; } QString modelId; QString depthId; QString profileName; bool isHdr; bool isInitialized; int referenceDepth; QList outputVertexes; bool isGray; int crossingCost; const KoColorSpaceFactory* colorSpaceFactory; bool isEngine; const KoColorSpaceEngine* engine; }; +Q_DECLARE_TYPEINFO(KoColorConversionSystem::Node, Q_MOVABLE_TYPE); struct KoColorConversionSystem::Vertex { Vertex(Node* _srcNode, Node* _dstNode) : srcNode(_srcNode) , dstNode(_dstNode) , factoryFromSrc(0) , factoryFromDst(0) { } ~Vertex() { if (factoryFromSrc == factoryFromDst) { delete factoryFromSrc; } else { delete factoryFromSrc; delete factoryFromDst; } } void setFactoryFromSrc(KoColorConversionTransformationFactory* factory) { factoryFromSrc = factory; initParameter(factoryFromSrc); } void setFactoryFromDst(KoColorConversionTransformationFactory* factory) { factoryFromDst = factory; if (!factoryFromSrc) initParameter(factoryFromDst); } void initParameter(KoColorConversionTransformationFactory* transfo) { conserveColorInformation = transfo->conserveColorInformation(); conserveDynamicRange = transfo->conserveDynamicRange(); } KoColorConversionTransformationFactory* factory() { if (factoryFromSrc) return factoryFromSrc; return factoryFromDst; } Node* srcNode; Node* dstNode; bool conserveColorInformation; bool conserveDynamicRange; private: KoColorConversionTransformationFactory* factoryFromSrc; // Factory provided by the destination node KoColorConversionTransformationFactory* factoryFromDst; // Factory provided by the destination node }; struct KoColorConversionSystem::NodeKey { NodeKey(const QString &_modelId, const QString &_depthId, const QString &_profileName) : modelId(_modelId) , depthId(_depthId) , profileName(_profileName) {} bool operator==(const KoColorConversionSystem::NodeKey& rhs) const { return modelId == rhs.modelId && depthId == rhs.depthId && profileName == rhs.profileName; } QString modelId; QString depthId; QString profileName; }; +Q_DECLARE_TYPEINFO(KoColorConversionSystem::NodeKey, Q_MOVABLE_TYPE); struct KoColorConversionSystem::Path { Path() : respectColorCorrectness(true) , referenceDepth(0) , keepDynamicRange(true) , isGood(false) , cost(0) {} Node* startNode() { return (vertexes.first())->srcNode; } - bool operator==(const Path &other) { + bool operator==(const Path &other) const { return other.vertexes == vertexes; } const Node* startNode() const { return (vertexes.first())->srcNode; } Node* endNode() { return (vertexes.last())->dstNode; } const Node* endNode() const { return (vertexes.last())->dstNode; } bool isEmpty() const { return vertexes.isEmpty(); } void appendVertex(Vertex* v) { if (vertexes.empty()) { referenceDepth = v->srcNode->referenceDepth; } vertexes.append(v); if (!v->conserveColorInformation) respectColorCorrectness = false; if (!v->conserveDynamicRange) keepDynamicRange = false; referenceDepth = qMin(referenceDepth, v->dstNode->referenceDepth); cost += v->dstNode->crossingCost; } // Compress path to hide the Engine node and correctly select the factory typedef QPair node2factory; QList< node2factory > compressedPath() const { QList< node2factory > nodes; nodes.push_back(node2factory(vertexes.first()->srcNode , vertexes.first()->factory())); const KoColorConversionTransformationAbstractFactory* previousFactory = 0; Q_FOREACH (Vertex* vertex, vertexes) { // Unless the node is the icc node, add it to the path Node* n = vertex->dstNode; if (n->isEngine) { previousFactory = n->engine; } else { nodes.push_back( node2factory(n, previousFactory ? previousFactory : vertex->factory())); previousFactory = 0; } } return nodes; } int length() const { return vertexes.size(); } bool contains(Node* n) const { Q_FOREACH (Vertex* v, vertexes) { if (v->srcNode == n || v->dstNode == n) { return true; } } return false; } QList vertexes; bool respectColorCorrectness; int referenceDepth; bool keepDynamicRange; bool isGood; int cost; }; +Q_DECLARE_TYPEINFO(KoColorConversionSystem::Path, Q_MOVABLE_TYPE); typedef QHash Node2PathHash; uint qHash(const KoColorConversionSystem::NodeKey &key) { return qHash(key.modelId) + qHash(key.depthId); } struct Q_DECL_HIDDEN KoColorConversionSystem::Private { QHash graph; QList vertexes; Node* alphaNode; }; #define CHECK_ONE_AND_NOT_THE_OTHER(name) \ if(path1. name && !path2. name) \ { \ return true; \ } \ if(!path1. name && path2. name) \ { \ return false; \ } struct PathQualityChecker { PathQualityChecker(int _referenceDepth, bool _ignoreHdr, bool _ignoreColorCorrectness) : referenceDepth(_referenceDepth) , ignoreHdr(_ignoreHdr) , ignoreColorCorrectness(_ignoreColorCorrectness) {} /// @return true if the path maximize all the criterions (except length) inline bool isGoodPath(const KoColorConversionSystem::Path & path) const { return (path.respectColorCorrectness || ignoreColorCorrectness) && (path.referenceDepth >= referenceDepth) && (path.keepDynamicRange || ignoreHdr); } /** * Compare two paths. */ - inline bool lessWorseThan(KoColorConversionSystem::Path &path1, KoColorConversionSystem::Path &path2) const { + inline bool lessWorseThan(const KoColorConversionSystem::Path &path1, const KoColorConversionSystem::Path &path2) const { // There is no point in comparing two paths which doesn't start from the same node or doesn't end at the same node if (!ignoreHdr) { CHECK_ONE_AND_NOT_THE_OTHER(keepDynamicRange) } if (!ignoreColorCorrectness) { CHECK_ONE_AND_NOT_THE_OTHER(respectColorCorrectness) } if (path1.referenceDepth == path2.referenceDepth) { return path1.cost < path2.cost; // if they have the same cost, well anyway you have to choose one, and there is no point in keeping one and not the other } return path1.referenceDepth > path2.referenceDepth; } int referenceDepth; bool ignoreHdr; bool ignoreColorCorrectness; }; #undef CHECK_ONE_AND_NOT_THE_OTHER #endif diff --git a/libs/pigment/KoColorSpace.cpp b/libs/pigment/KoColorSpace.cpp index 4b0dc00a02..238627b509 100644 --- a/libs/pigment/KoColorSpace.cpp +++ b/libs/pigment/KoColorSpace.cpp @@ -1,794 +1,796 @@ /* * Copyright (c) 2005 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; 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 "KoColorSpace.h" #include "KoColorSpace_p.h" #include "KoChannelInfo.h" #include "DebugPigment.h" #include "KoCompositeOp.h" #include "KoColorTransformation.h" #include "KoColorTransformationFactory.h" #include "KoColorTransformationFactoryRegistry.h" #include "KoColorConversionCache.h" #include "KoColorConversionSystem.h" #include "KoColorSpaceRegistry.h" #include "KoColorProfile.h" #include "KoCopyColorConversionTransformation.h" #include "KoFallBackColorTransformation.h" #include "KoUniqueNumberForIdServer.h" #include "KoMixColorsOp.h" #include "KoConvolutionOp.h" #include "KoCompositeOpRegistry.h" #include #include #include #include #include #include KoColorSpace::KoColorSpace() : d(new Private()) { } KoColorSpace::KoColorSpace(const QString &id, const QString &name, KoMixColorsOp* mixColorsOp, KoConvolutionOp* convolutionOp) : d(new Private()) { d->id = id; d->idNumber = KoUniqueNumberForIdServer::instance()->numberForId(d->id); d->name = name; d->mixColorsOp = mixColorsOp; d->convolutionOp = convolutionOp; d->transfoToRGBA16 = 0; d->transfoFromRGBA16 = 0; d->transfoToLABA16 = 0; d->transfoFromLABA16 = 0; d->gamutXYY = QPolygonF(); d->TRCXYY = QPolygonF(); d->colorants = QVector (0); d->lumaCoefficients = QVector (0); d->deletability = NotOwnedByRegistry; } KoColorSpace::~KoColorSpace() { Q_ASSERT(d->deletability != OwnedByRegistryDoNotDelete); qDeleteAll(d->compositeOps); Q_FOREACH (KoChannelInfo * channel, d->channels) { delete channel; } if (d->deletability == NotOwnedByRegistry) { KoColorConversionCache* cache = KoColorSpaceRegistry::instance()->colorConversionCache(); if (cache) { cache->colorSpaceIsDestroyed(this); } } delete d->mixColorsOp; delete d->convolutionOp; delete d->transfoToRGBA16; delete d->transfoFromRGBA16; delete d->transfoToLABA16; delete d->transfoFromLABA16; delete d; } bool KoColorSpace::operator==(const KoColorSpace& rhs) const { const KoColorProfile* p1 = rhs.profile(); const KoColorProfile* p2 = profile(); return d->idNumber == rhs.d->idNumber && ((p1 == p2) || (*p1 == *p2)); } QString KoColorSpace::id() const { return d->id; } QString KoColorSpace::name() const { return d->name; } //Color space info stuff. QPolygonF KoColorSpace::gamutXYY() const { if (d->gamutXYY.empty()) { //now, let's decide on the boundary. This is a bit tricky because icc profiles can be both matrix-shaper and cLUT at once if the maker so pleases. //first make a list of colors. qreal max = 1.0; if ((colorModelId().id()=="CMYKA" || colorModelId().id()=="LABA") && colorDepthId().id()=="F32") { //boundaries for cmyka/laba have trouble getting the max values for Float, and are pretty awkward in general. max = this->channels()[0]->getUIMax(); } int samples = 5;//amount of samples in our color space. QString name = KoColorSpaceRegistry::instance()->colorSpaceFactory("XYZAF32")->defaultProfile(); const KoColorSpace* xyzColorSpace = KoColorSpaceRegistry::instance()->colorSpace("XYZA", "F32", name); quint8 *data = new quint8[pixelSize()]; quint8 data2[16]; // xyza f32 is 4 floats, that is 16 bytes per pixel. //QVector sampleCoordinates(pow(colorChannelCount(),samples)); //sampleCoordinates.fill(0.0); // This is fixed to 5 since the maximum number of channels are 5 for CMYKA QVector channelValuesF(5);//for getting the coordinates. for(int x=0;xnormalisedChannelsValue(data2, channelValuesF); qreal x = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); qreal y = channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->gamutXYY << QPointF(x,y); } else { for(int y=0;ynormalisedChannelsValue(data2, channelValuesF); qreal x = channelValuesF[0] / (channelValuesF[0] + channelValuesF[1] + channelValuesF[2]); qreal y = channelValuesF[1] / (channelValuesF[0] + channelValuesF[1] + channelValuesF[2]); d->gamutXYY<< QPointF(x,y); } } else { channelValuesF[0]=(max/(samples-1))*(x); channelValuesF[1]=(max/(samples-1))*(y); channelValuesF[2]=(max/(samples-1))*(z); channelValuesF[3]=max; if (colorModelId().id()!="XYZA") { //no need for conversion when using xyz. fromNormalisedChannelsValue(data, channelValuesF); convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::adjustmentConversionFlags()); xyzColorSpace->normalisedChannelsValue(data2,channelValuesF); } qreal x = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); qreal y = channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->gamutXYY<< QPointF(x,y); } } } } } delete[] data; //if we ever implement a boundary-checking thing I'd add it here. return d->gamutXYY; } else { return d->gamutXYY; } } QPolygonF KoColorSpace::estimatedTRCXYY() const { if (d->TRCXYY.empty()){ qreal max = 1.0; if ((colorModelId().id()=="CMYKA" || colorModelId().id()=="LABA") && colorDepthId().id()=="F32") { //boundaries for cmyka/laba have trouble getting the max values for Float, and are pretty awkward in general. max = this->channels()[0]->getUIMax(); } QString name = KoColorSpaceRegistry::instance()->colorSpaceFactory("XYZAF16")->defaultProfile(); const KoColorSpace* xyzColorSpace = KoColorSpaceRegistry::instance()->colorSpace("XYZA", "F16", name); quint8 *data = new quint8[pixelSize()]; quint8 data2[8]; // xyza is 8 bytes per pixel. // This is fixed to 5 since the maximum number of channels are 5 for CMYKA QVector channelValuesF(5);//for getting the coordinates. for (quint32 i=0; i0; j--){ channelValuesF.fill(0.0); channelValuesF[i] = ((max/4)*(5-j)); if (colorModelId().id()!="XYZA") { //no need for conversion when using xyz. fromNormalisedChannelsValue(data, channelValuesF); convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::adjustmentConversionFlags()); xyzColorSpace->normalisedChannelsValue(data2,channelValuesF); } if (j==0) { colorantY = channelValuesF[1]; if (d->colorants.size()<2){ d->colorants.resize(3*colorChannelCount()); d->colorants[i] = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->colorants[i+1]= channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->colorants[i+2]= channelValuesF[1]; } } d->TRCXYY << QPointF(channelValuesF[1]/colorantY, ((1.0/4)*(5-j))); } } else { for (int j=0; j<5; j++){ channelValuesF.fill(0.0); channelValuesF[i] = ((max/4)*(j)); fromNormalisedChannelsValue(data, channelValuesF); convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::adjustmentConversionFlags()); xyzColorSpace->normalisedChannelsValue(data2,channelValuesF); if (j==0) { colorantY = channelValuesF[1]; if (d->colorants.size()<2){ d->colorants.resize(3*colorChannelCount()); d->colorants[i] = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->colorants[i+1]= channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->colorants[i+2]= channelValuesF[1]; } } d->TRCXYY << QPointF(channelValuesF[1]/colorantY, ((1.0/4)*(j))); } } } delete[] data; return d->TRCXYY; } else { return d->TRCXYY; } } QVector KoColorSpace::colorants() const { if (d->colorants.size()>1){ return d->colorants; } else if (profile() && profile()->hasColorants()) { d->colorants.resize(3*colorChannelCount()); d->colorants = profile()->getColorantsxyY(); return d->colorants; } else { estimatedTRCXYY(); return d->colorants; } } QVector KoColorSpace::lumaCoefficients() const { if (d->lumaCoefficients.size()>1){ return d->lumaCoefficients; } else { d->lumaCoefficients.resize(3); if (colorModelId().id()!="RGBA") { d->lumaCoefficients.fill(0.33); } else { colorants(); if (d->colorants[2]<0 || d->colorants[5]<0 || d->colorants[8]<0) { d->lumaCoefficients[0]=0.2126; d->lumaCoefficients[1]=0.7152; d->lumaCoefficients[2]=0.0722; } else { d->lumaCoefficients[0]=d->colorants[2]; d->lumaCoefficients[1]=d->colorants[5]; d->lumaCoefficients[2]=d->colorants[8]; } } return d->lumaCoefficients; } } QList KoColorSpace::channels() const { return d->channels; } QBitArray KoColorSpace::channelFlags(bool color, bool alpha) const { QBitArray ba(d->channels.size()); if (!color && !alpha) return ba; for (int i = 0; i < d->channels.size(); ++i) { KoChannelInfo * channel = d->channels.at(i); if ((color && channel->channelType() == KoChannelInfo::COLOR) || (alpha && channel->channelType() == KoChannelInfo::ALPHA)) ba.setBit(i, true); } return ba; } void KoColorSpace::addChannel(KoChannelInfo * ci) { d->channels.push_back(ci); } bool KoColorSpace::hasCompositeOp(const QString& id) const { return d->compositeOps.contains(id); } QList KoColorSpace::compositeOps() const { return d->compositeOps.values(); } KoMixColorsOp* KoColorSpace::mixColorsOp() const { return d->mixColorsOp; } KoConvolutionOp* KoColorSpace::convolutionOp() const { return d->convolutionOp; } const KoCompositeOp * KoColorSpace::compositeOp(const QString & id) const { - if (d->compositeOps.contains(id)) - return d->compositeOps.value(id); + const QHash::ConstIterator it = d->compositeOps.constFind(id); + if (it != d->compositeOps.constEnd()) { + return it.value(); + } else { warnPigment << "Asking for non-existent composite operation " << id << ", returning " << COMPOSITE_OVER; return d->compositeOps.value(COMPOSITE_OVER); } } void KoColorSpace::addCompositeOp(const KoCompositeOp * op) { if (op->colorSpace()->id() == id()) { d->compositeOps.insert(op->id(), const_cast(op)); } } const KoColorConversionTransformation* KoColorSpace::toLabA16Converter() const { if (!d->transfoToLABA16) { d->transfoToLABA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(this, KoColorSpaceRegistry::instance()->lab16(""), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ; } return d->transfoToLABA16; } const KoColorConversionTransformation* KoColorSpace::fromLabA16Converter() const { if (!d->transfoFromLABA16) { d->transfoFromLABA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(KoColorSpaceRegistry::instance()->lab16(""), this, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ; } return d->transfoFromLABA16; } const KoColorConversionTransformation* KoColorSpace::toRgbA16Converter() const { if (!d->transfoToRGBA16) { d->transfoToRGBA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(this, KoColorSpaceRegistry::instance()->rgb16(""), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ; } return d->transfoToRGBA16; } const KoColorConversionTransformation* KoColorSpace::fromRgbA16Converter() const { if (!d->transfoFromRGBA16) { d->transfoFromRGBA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(KoColorSpaceRegistry::instance()->rgb16("") , this, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ; } return d->transfoFromRGBA16; } void KoColorSpace::toLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const { toLabA16Converter()->transform(src, dst, nPixels); } void KoColorSpace::fromLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const { fromLabA16Converter()->transform(src, dst, nPixels); } void KoColorSpace::toRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const { toRgbA16Converter()->transform(src, dst, nPixels); } void KoColorSpace::fromRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const { fromRgbA16Converter()->transform(src, dst, nPixels); } KoColorConversionTransformation* KoColorSpace::createColorConverter(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { if (*this == *dstColorSpace) { return new KoCopyColorConversionTransformation(this); } else { return KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(this, dstColorSpace, renderingIntent, conversionFlags); } } bool KoColorSpace::convertPixelsTo(const quint8 * src, quint8 * dst, const KoColorSpace * dstColorSpace, quint32 numPixels, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { if (*this == *dstColorSpace) { if (src != dst) { memcpy(dst, src, numPixels * sizeof(quint8) * pixelSize()); } } else { KoCachedColorConversionTransformation cct = KoColorSpaceRegistry::instance()->colorConversionCache()->cachedConverter(this, dstColorSpace, renderingIntent, conversionFlags); cct.transformation()->transform(src, dst, numPixels); } return true; } void KoColorSpace::bitBlt(const KoColorSpace* srcSpace, const KoCompositeOp::ParameterInfo& params, const KoCompositeOp* op, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { Q_ASSERT_X(*op->colorSpace() == *this, "KoColorSpace::bitBlt", QString("Composite op is for color space %1 (%2) while this is %3 (%4)").arg(op->colorSpace()->id()).arg(op->colorSpace()->profile()->name()).arg(id()).arg(profile()->name()).toLatin1()); if(params.rows <= 0 || params.cols <= 0) return; if(!(*this == *srcSpace)) { if (preferCompositionInSourceColorSpace() && srcSpace->hasCompositeOp(op->id())) { quint32 conversionDstBufferStride = params.cols * srcSpace->pixelSize(); QVector * conversionDstCache = threadLocalConversionCache(params.rows * conversionDstBufferStride); quint8* conversionDstData = conversionDstCache->data(); for(qint32 row=0; rowcompositeOp(op->id()); KoCompositeOp::ParameterInfo paramInfo(params); paramInfo.dstRowStart = conversionDstData; paramInfo.dstRowStride = conversionDstBufferStride; otherOp->composite(paramInfo); for(qint32 row=0; rowconvertPixelsTo(conversionDstData + row * conversionDstBufferStride, params.dstRowStart + row * params.dstRowStride, this, params.cols, renderingIntent, conversionFlags); } } else { quint32 conversionBufferStride = params.cols * pixelSize(); QVector * conversionCache = threadLocalConversionCache(params.rows * conversionBufferStride); quint8* conversionData = conversionCache->data(); for(qint32 row=0; rowconvertPixelsTo(params.srcRowStart + row * params.srcRowStride, conversionData + row * conversionBufferStride, this, params.cols, renderingIntent, conversionFlags); } KoCompositeOp::ParameterInfo paramInfo(params); paramInfo.srcRowStart = conversionData; paramInfo.srcRowStride = conversionBufferStride; op->composite(paramInfo); } } else { op->composite(params); } } QVector * KoColorSpace::threadLocalConversionCache(quint32 size) const { QVector * ba = 0; if (!d->conversionCache.hasLocalData()) { ba = new QVector(size, '0'); d->conversionCache.setLocalData(ba); } else { ba = d->conversionCache.localData(); if ((quint8)ba->size() < size) ba->resize(size); } return ba; } KoColorTransformation* KoColorSpace::createColorTransformation(const QString & id, const QHash & parameters) const { KoColorTransformationFactory* factory = KoColorTransformationFactoryRegistry::instance()->get(id); if (!factory) return 0; QPair model(colorModelId(), colorDepthId()); QList< QPair > models = factory->supportedModels(); if (models.isEmpty() || models.contains(model)) { return factory->createTransformation(this, parameters); } else { // Find the best solution // TODO use the color conversion cache KoColorConversionTransformation* csToFallBack = 0; KoColorConversionTransformation* fallBackToCs = 0; KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverters(this, models, csToFallBack, fallBackToCs); Q_ASSERT(csToFallBack); Q_ASSERT(fallBackToCs); KoColorTransformation* transfo = factory->createTransformation(fallBackToCs->srcColorSpace(), parameters); return new KoFallBackColorTransformation(csToFallBack, fallBackToCs, transfo); } } void KoColorSpace::increaseLuminosity(quint8 * pixel, qreal step) const{ int channelnumber = channelCount(); QVector channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ihasTRC()){ //only linearise and crunch the luma if there's a TRC profile()->linearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); luma = pow(luma, 1/2.2); luma = qMin(1.0, luma + step); luma = pow(luma, 2.2); channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); } else { qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); luma = qMin(1.0, luma + step); channelValues = fromHSY(&hue, &sat, &luma); } for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ihasTRC()){ //only linearise and crunch the luma if there's a TRC profile()->linearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); luma = pow(luma, 1/2.2); if (luma-step<0.0) { luma=0.0; } else { luma -= step; } luma = pow(luma, 2.2); channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); } else { qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); if (luma-step<0.0) { luma=0.0; } else { luma -= step; } channelValues = fromHSY(&hue, &sat, &luma); } for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); sat += step; sat = qBound(0.0, sat, 1.0); channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); sat -= step; sat = qBound(0.0, sat, 1.0); channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); if (hue+step>1.0){ hue=(hue+step)- 1.0; } else { hue += step; } channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); if (hue-step<0.0){ hue=1.0-(step-hue); } else { hue -= step; } channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal y, u, v = 0.0; toYUV(channelValues, &y, &u, &v); u += step; u = qBound(0.0, u, 1.0); channelValues = fromYUV(&y, &u, &v); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal y, u, v = 0.0; toYUV(channelValues, &y, &u, &v); u -= step; u = qBound(0.0, u, 1.0); channelValues = fromYUV(&y, &u, &v); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal y, u, v = 0.0; toYUV(channelValues, &y, &u, &v); v += step; v = qBound(0.0, v, 1.0); channelValues = fromYUV(&y, &u, &v); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal y, u, v = 0.0; toYUV(channelValues, &y, &u, &v); v -= step; v = qBound(0.0, v, 1.0); channelValues = fromYUV(&y, &u, &v); profile()->delinearizeFloatValue(channelValues); for (int i=0;irgb8(dstProfile); if (data) this->convertPixelsTo(const_cast(data), img.bits(), dstCS, width * height, renderingIntent, conversionFlags); return img; } bool KoColorSpace::preferCompositionInSourceColorSpace() const { return false; } diff --git a/libs/pigment/KoColorSpace.h b/libs/pigment/KoColorSpace.h index 5580847658..f29b8e90a1 100644 --- a/libs/pigment/KoColorSpace.h +++ b/libs/pigment/KoColorSpace.h @@ -1,614 +1,614 @@ /* * 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 "KoColorSpaceConstants.h" #include "KoColorConversionTransformation.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 { 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 colorants() 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. virtual QList channels() const; /** * The total number of channels for a single pixel in this color model */ virtual quint32 channelCount() 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". */ virtual QString id() const; /** * User visible name which contains the name of the color model and of the color depth. * For intance "RGBA (8-bits)" or "CMYKA (16-bits)". */ virtual 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. */ virtual 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 */ 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; //============================== 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 different; * 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(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const = 0; + 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(QVector channelValues, qreal *y, qreal *u, qreal *v) 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 param 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 * */ 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 * * 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 * * @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) { dbg.nospace() << cs->name() << " (" << cs->colorModelId().id() << "," << cs->colorDepthId().id() << " )"; return dbg.space(); } #endif // KOCOLORSPACE_H diff --git a/libs/pigment/KoCompositeColorTransformation.h b/libs/pigment/KoCompositeColorTransformation.h index 5a3a76fc6a..02ccf0b52a 100644 --- a/libs/pigment/KoCompositeColorTransformation.h +++ b/libs/pigment/KoCompositeColorTransformation.h @@ -1,77 +1,77 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KO_COMPOSITE_COLOR_TRANSFORMATION_H #define __KO_COMPOSITE_COLOR_TRANSFORMATION_H #include "KoColorTransformation.h" #include /** * A class for storing a composite color transformation. All the * trasnformations added with appendTransform() are applied * sequentually to the process pixels. * * \p mode defines how the buffers are used while processing. * * When using INPLACE mode all transformations but the first one do * the conversion in place, that is in \p dst buffer. That is \p dst * is written at least N - 1 times, where N is the number of embedded * transformations. * * In BUFFERED mode all the transformations are called with distinct * src and dst buffers, which are created in temporary memory owned by * KoCompositeColorTransformation. Please note that this mode IS NOT * IMPLEMENTED YET! */ class KRITAPIGMENT_EXPORT KoCompositeColorTransformation : public KoColorTransformation { public: enum Mode { INPLACE = 0, /// transform pixels in place (in 'dst' buffer) BUFFERED /// transform using a temporary buffer (not implemented yet) }; public: - KoCompositeColorTransformation(Mode mode); + explicit KoCompositeColorTransformation(Mode mode); ~KoCompositeColorTransformation(); void transform(const quint8 *src, quint8 *dst, qint32 nPixels) const; /** * Append a transform to a composite. If \p transform is null, * nothing happens. */ void appendTransform(KoColorTransformation *transform); /** * Convenience method that checks if the transformations in \p * transforms are not null and adds existent ones only. If there * is only one non-null transform, it is returned directly to * avoid extra virtual calls added by KoCompositeColorTransformation. */ static KoColorTransformation* createOptimizedCompositeTransform(const QVector transforms); private: struct Private; const QScopedPointer m_d; }; #endif /* __KO_COMPOSITE_COLOR_TRANSFORMATION_H */ diff --git a/libs/pigment/colorspaces/KoAlphaColorSpace.h b/libs/pigment/colorspaces/KoAlphaColorSpace.h index 1bb1826d7a..4dc2d2a2a0 100644 --- a/libs/pigment/colorspaces/KoAlphaColorSpace.h +++ b/libs/pigment/colorspaces/KoAlphaColorSpace.h @@ -1,208 +1,208 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2006 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KOALPHACOLORSPACE_H #define KOALPHACOLORSPACE_H #include #include "DebugPigment.h" #include "kritapigment_export.h" #include "KoColorSpaceAbstract.h" #include "KoColorSpaceTraits.h" #include "KoColorModelStandardIds.h" #include "KoSimpleColorSpaceFactory.h" typedef KoColorSpaceTrait AlphaU8Traits; class QBitArray; /** * The alpha mask is a special color strategy that treats all pixels as * alpha value with a color common to the mask. The default color is white. */ class KRITAPIGMENT_EXPORT KoAlphaColorSpace : public KoColorSpaceAbstract { public: KoAlphaColorSpace(); virtual ~KoAlphaColorSpace(); static QString colorSpaceId() { return "ALPHA"; } virtual KoID colorModelId() const { return AlphaColorModelID; } virtual KoID colorDepthId() const { return Integer8BitsColorDepthID; } virtual KoColorSpace* clone() const; virtual bool willDegrade(ColorSpaceIndependence independence) const { Q_UNUSED(independence); return false; } virtual bool profileIsCompatible(const KoColorProfile* /*profile*/) const { return false; } virtual void fromQColor(const QColor& color, quint8 *dst, const KoColorProfile * profile = 0) const; virtual void toQColor(const quint8 *src, QColor *c, const KoColorProfile * profile = 0) const; virtual quint8 difference(const quint8 *src1, const quint8 *src2) const; virtual quint8 differenceA(const quint8 *src1, const quint8 *src2) const; virtual quint32 colorChannelCount() const { return 0; } virtual QString channelValueText(const quint8 *pixel, quint32 channelIndex) const; virtual QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) const; virtual void convolveColors(quint8** colors, qreal* kernelValues, quint8 *dst, qreal factor, qreal offset, qint32 nColors, const QBitArray & channelFlags) const; virtual quint32 colorSpaceType() const { return 0; } virtual bool hasHighDynamicRange() const { return false; } virtual const KoColorProfile* profile() const { return m_profile; } virtual QImage convertToQImage(const quint8 *data, qint32 width, qint32 height, const KoColorProfile * dstProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const; virtual void toLabA16(const quint8* src, quint8* dst, quint32 nPixels) const { quint16* lab = reinterpret_cast(dst); while (nPixels--) { lab[3] = src[0]; src++; lab += 4; } } virtual void fromLabA16(const quint8* src, quint8* dst, quint32 nPixels) const { const quint16* lab = reinterpret_cast(src); while (nPixels--) { dst[0] = lab[3]; dst++; lab += 4; } } virtual void toRgbA16(const quint8* src, quint8* dst, quint32 nPixels) const { quint16* rgb = reinterpret_cast(dst); while (nPixels--) { rgb[3] = src[0]; src++; rgb += 4; } } virtual void fromRgbA16(const quint8* src, quint8* dst, quint32 nPixels) const { const quint16* rgb = reinterpret_cast(src); while (nPixels--) { dst[0] = rgb[3]; dst++; rgb += 4; } } virtual KoColorTransformation* createBrightnessContrastAdjustment(const quint16* transferValues) const { Q_UNUSED(transferValues); warnPigment << i18n("Undefined operation in the alpha color space"); return 0; } virtual KoColorTransformation* createPerChannelAdjustment(const quint16* const*) const { warnPigment << i18n("Undefined operation in the alpha color space"); return 0; } virtual KoColorTransformation *createDarkenAdjustment(qint32 , bool , qreal) const { warnPigment << i18n("Undefined operation in the alpha color space"); return 0; } virtual void invertColor(quint8*, qint32) const { warnPigment << i18n("Undefined operation in the alpha color space"); } virtual void colorToXML(const quint8* , QDomDocument& , QDomElement&) const { warnPigment << i18n("Undefined operation in the alpha color space"); } virtual void colorFromXML(quint8* , const QDomElement&) const { warnPigment << i18n("Undefined operation in the alpha color space"); } - virtual void toHSY(QVector , qreal *, qreal *, qreal *) const { + virtual void toHSY(const QVector &, qreal *, qreal *, qreal *) const { warnPigment << i18n("Undefined operation in the alpha color space"); } virtual QVector fromHSY(qreal *, qreal *, qreal *) const { warnPigment << i18n("Undefined operation in the alpha color space"); QVector channelValues (1); channelValues.fill(0.0); return channelValues; } - virtual void toYUV(QVector , qreal *, qreal *, qreal *) const { + virtual void toYUV(const QVector &, qreal *, qreal *, qreal *) const { warnPigment << i18n("Undefined operation in the alpha color space"); } virtual QVector fromYUV(qreal *, qreal *, qreal *) const { warnPigment << i18n("Undefined operation in the alpha color space"); QVector channelValues (1); channelValues.fill(0.0); return channelValues; } protected: virtual bool preferCompositionInSourceColorSpace() const; private: KoColorProfile* m_profile; QList m_compositeOps; }; class KoAlphaColorSpaceFactory : public KoSimpleColorSpaceFactory { public: KoAlphaColorSpaceFactory() : KoSimpleColorSpaceFactory("ALPHA", i18n("Alpha mask"), false, AlphaColorModelID, Integer8BitsColorDepthID, 8) { } virtual KoColorSpace *createColorSpace(const KoColorProfile *) const { return new KoAlphaColorSpace(); } }; #endif // KO_COLORSPACE_ALPHA_H_ diff --git a/libs/pigment/colorspaces/KoLabColorSpace.cpp b/libs/pigment/colorspaces/KoLabColorSpace.cpp index 61f3c0362b..03a37feb27 100644 --- a/libs/pigment/colorspaces/KoLabColorSpace.cpp +++ b/libs/pigment/colorspaces/KoLabColorSpace.cpp @@ -1,196 +1,196 @@ /* * Copyright (c) 2004-2009 Boudewijn Rempt * Copyright (c) 2006 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KoLabColorSpace.h" #include #include #include #include #include #include #include "KoChannelInfo.h" #include "KoID.h" #include "KoIntegerMaths.h" #include "KoColorConversions.h" #include "../compositeops/KoCompositeOps.h" KoLabColorSpace::KoLabColorSpace() : KoSimpleColorSpace(colorSpaceId(), i18n("L*a*b* (16-bit integer/channel, unmanaged)"), LABAColorModelID, Integer16BitsColorDepthID) { addChannel(new KoChannelInfo(i18n("Lightness"), CHANNEL_L * sizeof(quint16), CHANNEL_L, KoChannelInfo::COLOR, KoChannelInfo::UINT16, sizeof(quint16), QColor(100, 100, 100))); addChannel(new KoChannelInfo(i18n("a*"), CHANNEL_A * sizeof(quint16), CHANNEL_A, KoChannelInfo::COLOR, KoChannelInfo::UINT16, sizeof(quint16), QColor(150, 150, 150))); addChannel(new KoChannelInfo(i18n("b*"), CHANNEL_B * sizeof(quint16), CHANNEL_B, KoChannelInfo::COLOR, KoChannelInfo::UINT16, sizeof(quint16), QColor(200, 200, 200))); addChannel(new KoChannelInfo(i18n("Alpha"), CHANNEL_ALPHA * sizeof(quint16), CHANNEL_ALPHA, KoChannelInfo::ALPHA, KoChannelInfo::UINT16, sizeof(quint16))); // ADD, ALPHA_DARKEN, BURN, DIVIDE, DODGE, ERASE, MULTIPLY, OVER, OVERLAY, SCREEN, SUBTRACT addStandardCompositeOps(this); } KoLabColorSpace::~KoLabColorSpace() { } QString KoLabColorSpace::colorSpaceId() { return QString("LABA"); } KoColorSpace* KoLabColorSpace::clone() const { return new KoLabColorSpace(); } void KoLabColorSpace::fromQColor(const QColor& c, quint8 *dst, const KoColorProfile * /*profile*/) const { // Convert between RGB and CIE-Lab color spaces // Uses ITU-R recommendation BT.709 with D65 as reference white. // algorithm contributed by "Mark A. Ruzon" int R, G, B, A; c.getRgb(&R, &G, &B, &A); double X, Y, Z, fX, fY, fZ; X = 0.412453 * R + 0.357580 * G + 0.180423 * B; Y = 0.212671 * R + 0.715160 * G + 0.072169 * B; Z = 0.019334 * R + 0.119193 * G + 0.950227 * B; X /= (255 * 0.950456); Y /= 255; Z /= (255 * 1.088754); quint8 L, a, b; if (Y > 0.008856) { fY = pow(Y, 1.0 / 3.0); L = static_cast(116.0 * fY - 16.0 + 0.5); } else { fY = 7.787 * Y + 16.0 / 116.0; L = static_cast(903.3 * Y + 0.5); } if (X > 0.008856) fX = pow(X, 1.0 / 3.0); else fX = 7.787 * X + 16.0 / 116.0; if (Z > 0.008856) fZ = pow(Z, 1.0 / 3.0); else fZ = 7.787 * Z + 16.0 / 116.0; a = static_cast(500.0 * (fX - fY) + 0.5); b = static_cast(200.0 * (fY - fZ) + 0.5); dst[CHANNEL_L] = UINT8_TO_UINT16(L); dst[CHANNEL_A] = UINT8_TO_UINT16(a); dst[CHANNEL_B] = UINT8_TO_UINT16(b); dst[CHANNEL_ALPHA] = UINT8_TO_UINT16(A); } void KoLabColorSpace::toQColor(const quint8 * src, QColor *c, const KoColorProfile * /*profile*/) const { // Convert between RGB and CIE-Lab color spaces // Uses ITU-R recommendation BT.709 with D65 as reference white. // algorithm contributed by "Mark A. Ruzon" quint8 L, a, b, A; L = UINT16_TO_UINT8(src[CHANNEL_L]); a = UINT16_TO_UINT8(src[CHANNEL_A]); b = UINT16_TO_UINT8(src[CHANNEL_B]); A = UINT16_TO_UINT8(src[CHANNEL_ALPHA]); double X, Y, Z, fX, fY, fZ; int RR, GG, BB; fY = pow((L + 16.0) / 116.0, 3.0); if (fY < 0.008856) fY = L / 903.3; Y = fY; if (fY > 0.008856) fY = pow(fY, 1.0 / 3.0); else fY = 7.787 * fY + 16.0 / 116.0; fX = a / 500.0 + fY; if (fX > 0.206893) X = pow(fX, 3.0); else X = (fX - 16.0 / 116.0) / 7.787; fZ = fY - b / 200.0; if (fZ > 0.206893) Z = pow(fZ, 3.0); else Z = (fZ - 16.0 / 116.0) / 7.787; X *= 0.950456 * 255; Y *= 255; Z *= 1.088754 * 255; RR = static_cast(3.240479 * X - 1.537150 * Y - 0.498535 * Z + 0.5); GG = static_cast(-0.969256 * X + 1.875992 * Y + 0.041556 * Z + 0.5); BB = static_cast(0.055648 * X - 0.204043 * Y + 1.057311 * Z + 0.5); quint8 R = RR < 0 ? 0 : RR > 255 ? 255 : RR; quint8 G = GG < 0 ? 0 : GG > 255 ? 255 : GG; quint8 B = BB < 0 ? 0 : BB > 255 ? 255 : BB; c->setRgba(qRgba(R, G, B, A)); } -void KoLabColorSpace::toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const +void KoLabColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { LabToLCH(channelValues[0],channelValues[1],channelValues[2], luma, sat, hue); } QVector KoLabColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); LCHToLab(*luma, *sat, *hue, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } -void KoLabColorSpace::toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const +void KoLabColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { *y =channelValues[0]; *v=channelValues[1]; *u=channelValues[2]; } QVector KoLabColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); channelValues[0]=*y; channelValues[1]=*v; channelValues[2]=*u; channelValues[3]=1.0; return channelValues; -} \ No newline at end of file +} diff --git a/libs/pigment/colorspaces/KoLabColorSpace.h b/libs/pigment/colorspaces/KoLabColorSpace.h index 986ce65364..86bc5ef963 100644 --- a/libs/pigment/colorspaces/KoLabColorSpace.h +++ b/libs/pigment/colorspaces/KoLabColorSpace.h @@ -1,88 +1,88 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2006 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KOLABCOLORSPACE_H #define KOLABCOLORSPACE_H #include #include "KoSimpleColorSpace.h" #include "KoSimpleColorSpaceFactory.h" #include "KoColorModelStandardIds.h" struct KoLabU16Traits; /** * Basic and simple implementation of the LAB colorspace */ class KoLabColorSpace : public KoSimpleColorSpace { public: KoLabColorSpace(); virtual ~KoLabColorSpace(); static QString colorSpaceId(); virtual KoColorSpace* clone() const; virtual void fromQColor(const QColor& color, quint8 *dst, const KoColorProfile * profile = 0) const; virtual void toQColor(const quint8 *src, QColor *c, const KoColorProfile * profile = 0) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; private: static const quint32 CHANNEL_L = 0; static const quint32 CHANNEL_A = 1; static const quint32 CHANNEL_B = 2; static const quint32 CHANNEL_ALPHA = 3; static const quint32 MAX_CHANNEL_L = 0xff00; static const quint32 MAX_CHANNEL_AB = 0xffff; static const quint32 CHANNEL_AB_ZERO_OFFSET = 0x8000; }; class KoLabColorSpaceFactory : public KoSimpleColorSpaceFactory { public: KoLabColorSpaceFactory() : KoSimpleColorSpaceFactory("LABA", i18n("L*a*b* (16-bit integer/channel, unmanaged)"), true, LABAColorModelID, Integer16BitsColorDepthID, 16) { } virtual KoColorSpace *createColorSpace(const KoColorProfile *) const { return new KoLabColorSpace(); } }; #endif diff --git a/libs/pigment/colorspaces/KoRgbU16ColorSpace.cpp b/libs/pigment/colorspaces/KoRgbU16ColorSpace.cpp index d6f944a63d..927ae3c04e 100644 --- a/libs/pigment/colorspaces/KoRgbU16ColorSpace.cpp +++ b/libs/pigment/colorspaces/KoRgbU16ColorSpace.cpp @@ -1,98 +1,98 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2006 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KoRgbU16ColorSpace.h" #include #include #include #include #include #include "KoChannelInfo.h" #include "KoID.h" #include "KoIntegerMaths.h" #include "KoColorConversions.h" KoRgbU16ColorSpace::KoRgbU16ColorSpace() : KoSimpleColorSpace(colorSpaceId(), i18n("RGB (16-bit integer/channel, unmanaged)"), RGBAColorModelID, Integer16BitsColorDepthID) { } KoRgbU16ColorSpace::~KoRgbU16ColorSpace() { } QString KoRgbU16ColorSpace::colorSpaceId() { return QString("RGBA16"); } KoColorSpace* KoRgbU16ColorSpace::clone() const { return new KoRgbU16ColorSpace(); } void KoRgbU16ColorSpace::fromQColor(const QColor& c, quint8 *dst, const KoColorProfile * /*profile*/) const { QVector channelValues; channelValues << c.blueF() << c.greenF() << c.redF() << c.alphaF(); fromNormalisedChannelsValue(dst, channelValues); } void KoRgbU16ColorSpace::toQColor(const quint8 * src, QColor *c, const KoColorProfile * /*profile*/) const { QVector channelValues(4); normalisedChannelsValue(src, channelValues); c->setRgbF(channelValues[2], channelValues[1], channelValues[0], channelValues[3]); } -void KoRgbU16ColorSpace::toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const +void KoRgbU16ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { RGBToHSY(channelValues[0],channelValues[1],channelValues[2], hue, sat, luma); } QVector KoRgbU16ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); HSYToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } -void KoRgbU16ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const +void KoRgbU16ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { RGBToYUV(channelValues[0],channelValues[1],channelValues[2], y, u, v); } QVector KoRgbU16ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; -} \ No newline at end of file +} diff --git a/libs/pigment/colorspaces/KoRgbU16ColorSpace.h b/libs/pigment/colorspaces/KoRgbU16ColorSpace.h index d1ddf39d28..6a866dcb52 100644 --- a/libs/pigment/colorspaces/KoRgbU16ColorSpace.h +++ b/libs/pigment/colorspaces/KoRgbU16ColorSpace.h @@ -1,77 +1,77 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2006 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KORGBU16COLORSPACE_H #define KORGBU16COLORSPACE_H #include #include "KoSimpleColorSpace.h" #include "KoSimpleColorSpaceFactory.h" #include "KoColorModelStandardIds.h" struct KoBgrU16Traits; /** * The alpha mask is a special color strategy that treats all pixels as * alpha value with a color common to the mask. The default color is white. */ class KoRgbU16ColorSpace : public KoSimpleColorSpace { public: KoRgbU16ColorSpace(); virtual ~KoRgbU16ColorSpace(); static QString colorSpaceId(); virtual KoColorSpace* clone() const; virtual void fromQColor(const QColor& color, quint8 *dst, const KoColorProfile * profile = 0) const; virtual void toQColor(const quint8 *src, QColor *c, const KoColorProfile * profile = 0) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; }; class KoRgbU16ColorSpaceFactory : public KoSimpleColorSpaceFactory { public: KoRgbU16ColorSpaceFactory() : KoSimpleColorSpaceFactory(KoRgbU16ColorSpace::colorSpaceId(), i18n("RGB (16-bit integer/channel, unmanaged)"), true, RGBAColorModelID, Integer16BitsColorDepthID, 16) { } virtual KoColorSpace *createColorSpace(const KoColorProfile *) const { return new KoRgbU16ColorSpace(); } }; #endif diff --git a/libs/pigment/colorspaces/KoRgbU8ColorSpace.cpp b/libs/pigment/colorspaces/KoRgbU8ColorSpace.cpp index 9619f7005a..9539a18f8d 100644 --- a/libs/pigment/colorspaces/KoRgbU8ColorSpace.cpp +++ b/libs/pigment/colorspaces/KoRgbU8ColorSpace.cpp @@ -1,111 +1,111 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2006 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KoRgbU8ColorSpace.h" #include #include #include #include #include #include "KoChannelInfo.h" #include "KoID.h" #include "KoIntegerMaths.h" #include "compositeops/KoCompositeOps.h" #include "KoColorConversions.h" KoRgbU8ColorSpace::KoRgbU8ColorSpace() : KoSimpleColorSpace(colorSpaceId(), i18n("RGB (8-bit integer/channel, unmanaged)"), RGBAColorModelID, Integer8BitsColorDepthID) { addChannel(new KoChannelInfo(i18n("Blue"), 0, 2, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(0, 0, 255))); addChannel(new KoChannelInfo(i18n("Green"), 1, 1, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(0, 255, 0))); addChannel(new KoChannelInfo(i18n("Red"), 2, 0, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(255, 0, 0))); addChannel(new KoChannelInfo(i18n("Alpha"), 3, 3, KoChannelInfo::ALPHA, KoChannelInfo::UINT8)); // ADD, ALPHA_DARKEN, BURN, DIVIDE, DODGE, ERASE, MULTIPLY, OVER, OVERLAY, SCREEN, SUBTRACT addStandardCompositeOps(this); } KoRgbU8ColorSpace::~KoRgbU8ColorSpace() { } QString KoRgbU8ColorSpace::colorSpaceId() { return QString("RGBA"); } KoColorSpace* KoRgbU8ColorSpace::clone() const { return new KoRgbU8ColorSpace(); } void KoRgbU8ColorSpace::fromQColor(const QColor& c, quint8 *dst, const KoColorProfile * /*profile*/) const { QVector channelValues; channelValues << c.blueF() << c.greenF() << c.redF() << c.alphaF(); fromNormalisedChannelsValue(dst, channelValues); } void KoRgbU8ColorSpace::toQColor(const quint8 * src, QColor *c, const KoColorProfile * /*profile*/) const { QVector channelValues(4); normalisedChannelsValue(src, channelValues); c->setRgbF(channelValues[2], channelValues[1], channelValues[0], channelValues[3]); } -void KoRgbU8ColorSpace::toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const +void KoRgbU8ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { RGBToHSY(channelValues[0],channelValues[1],channelValues[2], hue, sat, luma); } QVector KoRgbU8ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); HSYToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } -void KoRgbU8ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const +void KoRgbU8ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { RGBToYUV(channelValues[0],channelValues[1],channelValues[2], y, u, v); } QVector KoRgbU8ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; -} \ No newline at end of file +} diff --git a/libs/pigment/colorspaces/KoRgbU8ColorSpace.h b/libs/pigment/colorspaces/KoRgbU8ColorSpace.h index 09577815bc..d5157d4adb 100644 --- a/libs/pigment/colorspaces/KoRgbU8ColorSpace.h +++ b/libs/pigment/colorspaces/KoRgbU8ColorSpace.h @@ -1,77 +1,77 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2006 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KORGBU8COLORSPACE_H #define KORGBU8COLORSPACE_H #include #include "KoSimpleColorSpace.h" #include "KoSimpleColorSpaceFactory.h" #include "KoColorModelStandardIds.h" struct KoBgrU8Traits; /** * The alpha mask is a special color strategy that treats all pixels as * alpha value with a color common to the mask. The default color is white. */ class KoRgbU8ColorSpace : public KoSimpleColorSpace { public: KoRgbU8ColorSpace(); virtual ~KoRgbU8ColorSpace(); static QString colorSpaceId(); virtual KoColorSpace* clone() const; virtual void fromQColor(const QColor& color, quint8 *dst, const KoColorProfile * profile = 0) const; virtual void toQColor(const quint8 *src, QColor *c, const KoColorProfile * profile = 0) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; }; class KoRgbU8ColorSpaceFactory : public KoSimpleColorSpaceFactory { public: KoRgbU8ColorSpaceFactory() : KoSimpleColorSpaceFactory("RGBA", i18n("RGB (8-bit integer/channel, unmanaged)"), true, RGBAColorModelID, Integer8BitsColorDepthID, 8) { } virtual KoColorSpace *createColorSpace(const KoColorProfile *) const { return new KoRgbU8ColorSpace(); } }; #endif diff --git a/libs/pigment/colorspaces/KoSimpleColorSpace.h b/libs/pigment/colorspaces/KoSimpleColorSpace.h index 34c92b2d96..08c914df4e 100644 --- a/libs/pigment/colorspaces/KoSimpleColorSpace.h +++ b/libs/pigment/colorspaces/KoSimpleColorSpace.h @@ -1,229 +1,229 @@ /* * Copyright (c) 2004-2009 Boudewijn Rempt * Copyright (c) 2006 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KOSIMPLECOLORSPACE_H #define KOSIMPLECOLORSPACE_H #include #include "DebugPigment.h" #include "KoColorSpaceAbstract.h" #include "KoSimpleColorSpaceFactory.h" #include "KoColorModelStandardIds.h" #include "colorprofiles/KoDummyColorProfile.h" template class KoSimpleColorSpace : public KoColorSpaceAbstract<_CSTraits> { public: KoSimpleColorSpace(const QString& id, const QString& name, const KoID& colorModelId, const KoID& colorDepthId) : KoColorSpaceAbstract<_CSTraits>(id, name) , m_name(name) , m_colorModelId(colorModelId) , m_colorDepthId(colorDepthId) , m_profile(new KoDummyColorProfile) { } virtual ~KoSimpleColorSpace() { delete m_profile; } virtual KoID colorModelId() const { return m_colorModelId; } virtual KoID colorDepthId() const { return m_colorDepthId; } virtual bool willDegrade(ColorSpaceIndependence independence) const { Q_UNUSED(independence); return false; } virtual bool profileIsCompatible(const KoColorProfile* /*profile*/) const { return true; } virtual quint8 difference(const quint8 *src1, const quint8 *src2) const { Q_UNUSED(src1); Q_UNUSED(src2); warnPigment << i18n("Undefined operation in the %1 space", m_name); return 0; } virtual quint8 differenceA(const quint8 *src1, const quint8 *src2) const { Q_UNUSED(src1); Q_UNUSED(src2); warnPigment << i18n("Undefined operation in the %1 space", m_name); return 0; } virtual quint32 colorSpaceType() const { return 0; } virtual bool hasHighDynamicRange() const { return false; } virtual const KoColorProfile* profile() const { return m_profile; } virtual KoColorTransformation* createBrightnessContrastAdjustment(const quint16*) const { warnPigment << i18n("Undefined operation in the %1 color space", m_name); return 0; } virtual KoColorTransformation* createDesaturateAdjustment() const { warnPigment << i18n("Undefined operation in the %1 color space", m_name); return 0; } virtual KoColorTransformation* createPerChannelAdjustment(const quint16* const*) const { warnPigment << i18n("Undefined operation in the %1 color space", m_name); return 0; } virtual KoColorTransformation *createDarkenAdjustment(qint32 , bool , qreal) const { warnPigment << i18n("Undefined operation in the %1 color space", m_name); return 0; } virtual void invertColor(quint8*, qint32) const { warnPigment << i18n("Undefined operation in the %1 color space", m_name); } virtual void colorToXML(const quint8* , QDomDocument& , QDomElement&) const { warnPigment << i18n("Undefined operation in the %1 color space", m_name); } virtual void colorFromXML(quint8* , const QDomElement&) const { warnPigment << i18n("Undefined operation in the %1 color space", m_name); } - virtual void toHSY(QVector , qreal *, qreal *, qreal *) const { + virtual void toHSY(const QVector &, qreal *, qreal *, qreal *) const { warnPigment << i18n("Undefined operation in the %1 color space", m_name); } virtual QVector fromHSY(qreal *, qreal *, qreal *) const { warnPigment << i18n("Undefined operation in the %1 color space", m_name); QVector channelValues (2); channelValues.fill(0.0); return channelValues; } - virtual void toYUV(QVector , qreal *, qreal *, qreal *) const { + virtual void toYUV(const QVector &, qreal *, qreal *, qreal *) const { warnPigment << i18n("Undefined operation in the %1 color space", m_name); } virtual QVector fromYUV(qreal *, qreal *, qreal *) const { warnPigment << i18n("Undefined operation in the %1 color space", m_name); QVector channelValues (2); channelValues.fill(0.0); return channelValues; } virtual void toLabA16(const quint8* src, quint8* dst, quint32 nPixels) const { if (colorDepthId() == Integer16BitsColorDepthID && colorModelId() == LABAColorModelID) { memcpy(dst, src, nPixels * 2); } else { const KoColorSpace* dstCs = KoColorSpaceRegistry::instance()->lab16(); convertPixelsTo(src, dst, dstCs, nPixels, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); } } virtual void fromLabA16(const quint8* src, quint8* dst, quint32 nPixels) const { if (colorDepthId() == Integer16BitsColorDepthID && colorModelId() == LABAColorModelID) { memcpy(dst, src, nPixels * 2); } else { const KoColorSpace* srcCs = KoColorSpaceRegistry::instance()->lab16(); srcCs->convertPixelsTo(src, dst, this, nPixels, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); } } virtual void toRgbA16(const quint8* src, quint8* dst, quint32 nPixels) const { if (colorDepthId() == Integer16BitsColorDepthID && colorModelId() == RGBAColorModelID) { memcpy(dst, src, nPixels * 2); } else { const KoColorSpace* dstCs = KoColorSpaceRegistry::instance()->rgb16(); convertPixelsTo(src, dst, dstCs, nPixels, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); } } virtual void fromRgbA16(const quint8* src, quint8* dst, quint32 nPixels) const { if (colorDepthId() == Integer16BitsColorDepthID && colorModelId() == RGBAColorModelID) { memcpy(dst, src, nPixels * 2); } else { const KoColorSpace* srcCs = KoColorSpaceRegistry::instance()->rgb16(); srcCs->convertPixelsTo(src, dst, this, nPixels, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); } } virtual bool convertPixelsTo(const quint8 *src, quint8 *dst, const KoColorSpace * dstColorSpace, quint32 numPixels, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { Q_UNUSED(renderingIntent); Q_UNUSED(conversionFlags); QColor c; quint32 srcPixelsize = this->pixelSize(); quint32 dstPixelsize = dstColorSpace->pixelSize(); while (numPixels > 0) { this->toQColor(src, &c); dstColorSpace->fromQColor(c, dst); src += srcPixelsize; dst += dstPixelsize; --numPixels; } return true; } virtual QString colorSpaceEngine() const { return "simple"; } private: QString m_name; KoID m_colorModelId; KoID m_colorDepthId; KoColorProfile* m_profile; }; #endif // KOSIMPLECOLORSPACE_H diff --git a/libs/pigment/compositeops/KoOptimizedCompositeOpAlphaDarken128.h b/libs/pigment/compositeops/KoOptimizedCompositeOpAlphaDarken128.h index 7632578081..87e3aacdd7 100644 --- a/libs/pigment/compositeops/KoOptimizedCompositeOpAlphaDarken128.h +++ b/libs/pigment/compositeops/KoOptimizedCompositeOpAlphaDarken128.h @@ -1,221 +1,221 @@ /* * Copyright (c) 2016 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. */ #ifndef KOOPTIMIZEDCOMPOSITEOPALPHADARKEN128_H #define KOOPTIMIZEDCOMPOSITEOPALPHADARKEN128_H #include "KoCompositeOpBase.h" #include "KoCompositeOpRegistry.h" #include "KoStreamedMath.h" template struct AlphaDarkenCompositor128 { struct OptionalParams { OptionalParams(const KoCompositeOp::ParameterInfo& params) : flow(params.flow) , averageOpacity(*params.lastOpacity * params.flow) , premultipliedOpacity(params.opacity * params.flow) { } float flow; float averageOpacity; float premultipliedOpacity; }; struct Pixel { channels_type red; channels_type green; channels_type blue; channels_type alpha; }; /** * This is a vector equivalent of compositeOnePixelScalar(). It is considered - * to process Vc::float_v::Size pixels in a single pass. + * to process Vc::float_v::size() pixels in a single pass. * * o the \p haveMask parameter points whether the real (non-null) mask * pointer is passed to the function. * o the \p src pointer may be aligned to vector boundary or may be * not. In case not, it must be pointed with a special parameter * \p src_aligned. * o the \p dst pointer must always(!) be aligned to the boundary * of a streaming vector. Unaligned writes are really expensive. * o This function is *never* used if HAVE_VC is not present */ template static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const OptionalParams &oparams) { const Pixel *sp = reinterpret_cast(src); Pixel *dp = reinterpret_cast(dst); Vc::float_v src_c1; Vc::float_v src_c2; Vc::float_v src_c3; Vc::float_v src_alpha; const Vc::float_v::IndexType indexes(Vc::IndexesFromZero); Vc::InterleavedMemoryWrapper data(const_cast(sp)); (src_c1, src_c2, src_c3, src_alpha) = data[indexes]; Vc::float_v msk_norm_alpha; if (haveMask) { const Vc::float_v uint8Rec1((float)1.0 / 255.0); Vc::float_v mask_vec = KoStreamedMath<_impl>::fetch_mask_8(mask); msk_norm_alpha = mask_vec * uint8Rec1 * src_alpha; } else { msk_norm_alpha = src_alpha; } Vc::float_v opacity_vec(oparams.premultipliedOpacity); src_alpha = msk_norm_alpha * opacity_vec; const Vc::float_v zeroValue(KoColorSpaceMathsTraits::zeroValue); Vc::float_v dst_c1; Vc::float_v dst_c2; Vc::float_v dst_c3; Vc::float_v dst_alpha; Vc::InterleavedMemoryWrapper dataDest(dp); (dst_c1, dst_c2, dst_c3, dst_alpha) = dataDest[indexes]; Vc::float_m empty_dst_pixels_mask = dst_alpha == zeroValue; if (!empty_dst_pixels_mask.isFull()) { if (empty_dst_pixels_mask.isEmpty()) { dst_c1 = (src_c1 - dst_c1) * src_alpha + dst_c1; dst_c2 = (src_c2 - dst_c2) * src_alpha + dst_c2; dst_c3 = (src_c3 - dst_c3) * src_alpha + dst_c3; } else { dst_c1(empty_dst_pixels_mask) = src_c1; dst_c2(empty_dst_pixels_mask) = src_c2; dst_c3(empty_dst_pixels_mask) = src_c3; Vc::float_m not_empty_dst_pixels_mask = !empty_dst_pixels_mask; dst_c1(not_empty_dst_pixels_mask) = (src_c1 - dst_c1) * src_alpha + dst_c1; dst_c2(not_empty_dst_pixels_mask) = (src_c2 - dst_c2) * src_alpha + dst_c2; dst_c3(not_empty_dst_pixels_mask) = (src_c3 - dst_c3) * src_alpha + dst_c3; } } else { dst_c1 = src_c1; dst_c2 = src_c2; dst_c3 = src_c3; } Vc::float_v fullFlowAlpha(dst_alpha); if (oparams.averageOpacity > opacity) { Vc::float_v average_opacity_vec(oparams.averageOpacity); Vc::float_m fullFlowAlpha_mask = average_opacity_vec > dst_alpha; fullFlowAlpha(fullFlowAlpha_mask) = (average_opacity_vec - src_alpha) * (dst_alpha / average_opacity_vec) + src_alpha; } else { Vc::float_m fullFlowAlpha_mask = opacity_vec > dst_alpha; fullFlowAlpha(fullFlowAlpha_mask) = (opacity_vec - dst_alpha) * msk_norm_alpha + dst_alpha; } if (oparams.flow == 1.0) { dst_alpha = fullFlowAlpha; } else { Vc::float_v zeroFlowAlpha = src_alpha + dst_alpha - src_alpha * dst_alpha; Vc::float_v flow_norm_vec(oparams.flow); dst_alpha = (fullFlowAlpha - zeroFlowAlpha) * flow_norm_vec + zeroFlowAlpha; } dataDest[indexes] = (dst_c1, dst_c2, dst_c3, dst_alpha); } /** * Composes one pixel of the source into the destination */ template static ALWAYS_INLINE void compositeOnePixelScalar(const quint8 *s, quint8 *d, const quint8 *mask, float opacity, const OptionalParams &oparams) { using namespace Arithmetic; const qint32 alpha_pos = 3; const channels_type *src = reinterpret_cast(s); channels_type *dst = reinterpret_cast(d); float dstAlphaNorm = dst[alpha_pos]; const float uint8Rec1 = 1.0 / 255.0; float mskAlphaNorm = haveMask ? float(*mask) * uint8Rec1 * src[alpha_pos] : src[alpha_pos]; opacity = oparams.premultipliedOpacity; float srcAlphaNorm = mskAlphaNorm * opacity; if (dstAlphaNorm != 0) { dst[0] = lerp(dst[0], src[0], srcAlphaNorm); dst[1] = lerp(dst[1], src[1], srcAlphaNorm); dst[2] = lerp(dst[2], src[2], srcAlphaNorm); } else { const pixel_type *s = reinterpret_cast(src); pixel_type *d = reinterpret_cast(dst); *d = *s; } float flow = oparams.flow; float averageOpacity = oparams.averageOpacity; float fullFlowAlpha; if (averageOpacity > opacity) { fullFlowAlpha = averageOpacity > dstAlphaNorm ? lerp(srcAlphaNorm, averageOpacity, dstAlphaNorm / averageOpacity) : dstAlphaNorm; } else { fullFlowAlpha = opacity > dstAlphaNorm ? lerp(dstAlphaNorm, opacity, mskAlphaNorm) : dstAlphaNorm; } if (flow == 1.0) { dst[alpha_pos] = fullFlowAlpha; } else { float zeroFlowAlpha = unionShapeOpacity(srcAlphaNorm, dstAlphaNorm); dst[alpha_pos] = lerp(zeroFlowAlpha, fullFlowAlpha, flow); } } }; /** * An optimized version of a composite op for the use in 16 byte * colorspaces with alpha channel placed at the last byte of * the pixel: C1_C2_C3_A. */ template class KoOptimizedCompositeOpAlphaDarken128 : public KoCompositeOp { public: KoOptimizedCompositeOpAlphaDarken128(const KoColorSpace* cs) : KoCompositeOp(cs, COMPOSITE_ALPHA_DARKEN, i18n("Alpha darken"), KoCompositeOp::categoryMix()) {} using KoCompositeOp::composite; virtual void composite(const KoCompositeOp::ParameterInfo& params) const { if(params.maskRowStart) { KoStreamedMath<_impl>::template genericComposite128 >(params); } else { KoStreamedMath<_impl>::template genericComposite128 >(params); } } }; #endif // KOOPTIMIZEDCOMPOSITEOPALPHADARKEN128_H diff --git a/libs/pigment/compositeops/KoOptimizedCompositeOpAlphaDarken32.h b/libs/pigment/compositeops/KoOptimizedCompositeOpAlphaDarken32.h index 6af0d3df75..e8f7b5a23f 100644 --- a/libs/pigment/compositeops/KoOptimizedCompositeOpAlphaDarken32.h +++ b/libs/pigment/compositeops/KoOptimizedCompositeOpAlphaDarken32.h @@ -1,262 +1,262 @@ /* * Copyright (c) 2006 Cyrille Berger * Copyright (c) 2011 Silvio Heinrich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU 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 KOOPTIMIZEDCOMPOSITEOPALPHADARKEN32_H_ #define KOOPTIMIZEDCOMPOSITEOPALPHADARKEN32_H_ #include "KoCompositeOpBase.h" #include "KoCompositeOpRegistry.h" #include #include "KoStreamedMath.h" template struct AlphaDarkenCompositor32 { struct OptionalParams { OptionalParams(const KoCompositeOp::ParameterInfo& params) : flow(params.flow), averageOpacity(*params.lastOpacity * params.flow), premultipliedOpacity(params.opacity * params.flow) { } float flow; float averageOpacity; float premultipliedOpacity; }; /** * This is a vector equivalent of compositeOnePixelScalar(). It is considered - * to process Vc::float_v::Size pixels in a single pass. + * to process Vc::float_v::size() pixels in a single pass. * * o the \p haveMask parameter points whether the real (non-null) mask * pointer is passed to the function. * o the \p src pointer may be aligned to vector boundary or may be * not. In case not, it must be pointed with a special parameter * \p src_aligned. * o the \p dst pointer must always(!) be aligned to the boundary * of a streaming vector. Unaligned writes are really expensive. * o This function is *never* used if HAVE_VC is not present */ template static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const OptionalParams &oparams) { Vc::float_v src_alpha; Vc::float_v dst_alpha; Vc::float_v opacity_vec(255.0 * oparams.premultipliedOpacity); Vc::float_v average_opacity_vec(255.0 * oparams.averageOpacity); Vc::float_v flow_norm_vec(oparams.flow); Vc::float_v uint8MaxRec2((float)1.0 / (255.0 * 255.0)); Vc::float_v uint8MaxRec1((float)1.0 / 255.0); Vc::float_v uint8Max((float)255.0); Vc::float_v zeroValue(Vc::Zero); Vc::float_v msk_norm_alpha; src_alpha = KoStreamedMath<_impl>::template fetch_alpha_32(src); if (haveMask) { Vc::float_v mask_vec = KoStreamedMath<_impl>::fetch_mask_8(mask); msk_norm_alpha = src_alpha * mask_vec * uint8MaxRec2; } else { msk_norm_alpha = src_alpha * uint8MaxRec1; } dst_alpha = KoStreamedMath<_impl>::template fetch_alpha_32(dst); src_alpha = msk_norm_alpha * opacity_vec; Vc::float_m empty_dst_pixels_mask = dst_alpha == zeroValue; Vc::float_v src_c1; Vc::float_v src_c2; Vc::float_v src_c3; Vc::float_v dst_c1; Vc::float_v dst_c2; Vc::float_v dst_c3; KoStreamedMath<_impl>::template fetch_colors_32(src, src_c1, src_c2, src_c3); bool srcAlphaIsZero = (src_alpha == zeroValue).isFull(); if (srcAlphaIsZero) return; bool dstAlphaIsZero = empty_dst_pixels_mask.isFull(); Vc::float_v dst_blend = src_alpha * uint8MaxRec1; bool srcAlphaIsUnit = (src_alpha == uint8Max).isFull(); if (dstAlphaIsZero) { dst_c1 = src_c1; dst_c2 = src_c2; dst_c3 = src_c3; } else if (srcAlphaIsUnit) { bool dstAlphaIsUnit = (dst_alpha == uint8Max).isFull(); if (dstAlphaIsUnit) { - memcpy(dst, src, 4 * Vc::float_v::Size); + memcpy(dst, src, 4 * Vc::float_v::size()); return; } else { dst_c1 = src_c1; dst_c2 = src_c2; dst_c3 = src_c3; } } else if (empty_dst_pixels_mask.isEmpty()) { KoStreamedMath<_impl>::template fetch_colors_32(dst, dst_c1, dst_c2, dst_c3); dst_c1 = dst_blend * (src_c1 - dst_c1) + dst_c1; dst_c2 = dst_blend * (src_c2 - dst_c2) + dst_c2; dst_c3 = dst_blend * (src_c3 - dst_c3) + dst_c3; } else { KoStreamedMath<_impl>::template fetch_colors_32(dst, dst_c1, dst_c2, dst_c3); dst_c1(empty_dst_pixels_mask) = src_c1; dst_c2(empty_dst_pixels_mask) = src_c2; dst_c3(empty_dst_pixels_mask) = src_c3; Vc::float_m not_empty_dst_pixels_mask = !empty_dst_pixels_mask; dst_c1(not_empty_dst_pixels_mask) = dst_blend * (src_c1 - dst_c1) + dst_c1; dst_c2(not_empty_dst_pixels_mask) = dst_blend * (src_c2 - dst_c2) + dst_c2; dst_c3(not_empty_dst_pixels_mask) = dst_blend * (src_c3 - dst_c3) + dst_c3; } Vc::float_v fullFlowAlpha; if (oparams.averageOpacity > opacity) { Vc::float_m fullFlowAlpha_mask = average_opacity_vec > dst_alpha; if (fullFlowAlpha_mask.isEmpty()) { fullFlowAlpha = dst_alpha; } else { Vc::float_v reverse_blend = dst_alpha / average_opacity_vec; Vc::float_v opt1 = (average_opacity_vec - src_alpha) * reverse_blend + src_alpha; fullFlowAlpha(!fullFlowAlpha_mask) = dst_alpha; fullFlowAlpha(fullFlowAlpha_mask) = opt1; } } else { Vc::float_m fullFlowAlpha_mask = opacity_vec > dst_alpha; if (fullFlowAlpha_mask.isEmpty()) { fullFlowAlpha = dst_alpha; } else { Vc::float_v opt1 = (opacity_vec - dst_alpha) * msk_norm_alpha + dst_alpha; fullFlowAlpha(!fullFlowAlpha_mask) = dst_alpha; fullFlowAlpha(fullFlowAlpha_mask) = opt1; } } if (oparams.flow == 1.0) { dst_alpha = fullFlowAlpha; } else { Vc::float_v zeroFlowAlpha = src_alpha + dst_alpha - dst_blend * dst_alpha; dst_alpha = (fullFlowAlpha - zeroFlowAlpha) * flow_norm_vec + zeroFlowAlpha; } KoStreamedMath<_impl>::write_channels_32(dst, dst_alpha, dst_c1, dst_c2, dst_c3); } /** * Composes one pixel of the source into the destination */ template static ALWAYS_INLINE void compositeOnePixelScalar(const channels_type *src, channels_type *dst, const quint8 *mask, float opacity, const OptionalParams &oparams) { using namespace Arithmetic; const qint32 alpha_pos = 3; const float uint8Rec1 = 1.0 / 255.0; const float uint8Rec2 = 1.0 / (255.0 * 255.0); const float uint8Max = 255.0; quint8 dstAlphaInt = dst[alpha_pos]; float dstAlphaNorm = dstAlphaInt ? dstAlphaInt * uint8Rec1 : 0.0; float srcAlphaNorm; float mskAlphaNorm; opacity = oparams.premultipliedOpacity; if (haveMask) { mskAlphaNorm = float(*mask) * uint8Rec2 * src[alpha_pos]; srcAlphaNorm = mskAlphaNorm * opacity; } else { mskAlphaNorm = src[alpha_pos] * uint8Rec1; srcAlphaNorm = mskAlphaNorm * opacity; } if (dstAlphaInt != 0) { dst[0] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[0], src[0], srcAlphaNorm); dst[1] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[1], src[1], srcAlphaNorm); dst[2] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[2], src[2], srcAlphaNorm); } else { const pixel_type *s = reinterpret_cast(src); pixel_type *d = reinterpret_cast(dst); *d = *s; } float flow = oparams.flow; float averageOpacity = oparams.averageOpacity; float fullFlowAlpha; if (averageOpacity > opacity) { fullFlowAlpha = averageOpacity > dstAlphaNorm ? lerp(srcAlphaNorm, averageOpacity, dstAlphaNorm / averageOpacity) : dstAlphaNorm; } else { fullFlowAlpha = opacity > dstAlphaNorm ? lerp(dstAlphaNorm, opacity, mskAlphaNorm) : dstAlphaNorm; } float dstAlpha; if (flow == 1.0) { dstAlpha = fullFlowAlpha * uint8Max; } else { float zeroFlowAlpha = unionShapeOpacity(srcAlphaNorm, dstAlphaNorm); dstAlpha = lerp(zeroFlowAlpha, fullFlowAlpha, flow) * uint8Max; } dst[alpha_pos] = KoStreamedMath<_impl>::round_float_to_uint(dstAlpha); } }; /** * An optimized version of a composite op for the use in 4 byte * colorspaces with alpha channel placed at the last byte of * the pixel: C1_C2_C3_A. */ template class KoOptimizedCompositeOpAlphaDarken32 : public KoCompositeOp { public: KoOptimizedCompositeOpAlphaDarken32(const KoColorSpace* cs) : KoCompositeOp(cs, COMPOSITE_ALPHA_DARKEN, i18n("Alpha darken"), KoCompositeOp::categoryMix()) {} using KoCompositeOp::composite; virtual void composite(const KoCompositeOp::ParameterInfo& params) const { if(params.maskRowStart) { KoStreamedMath<_impl>::template genericComposite32 >(params); } else { KoStreamedMath<_impl>::template genericComposite32 >(params); } } }; #endif // KOOPTIMIZEDCOMPOSITEOPALPHADARKEN32_H_ diff --git a/libs/pigment/compositeops/KoOptimizedCompositeOpFactoryPerArch.cpp b/libs/pigment/compositeops/KoOptimizedCompositeOpFactoryPerArch.cpp index 8076557f1e..f817b5571e 100644 --- a/libs/pigment/compositeops/KoOptimizedCompositeOpFactoryPerArch.cpp +++ b/libs/pigment/compositeops/KoOptimizedCompositeOpFactoryPerArch.cpp @@ -1,92 +1,90 @@ /* * Copyright (c) 2012 Dmitry Kazakov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #if !defined _MSC_VER #pragma GCC diagnostic ignored "-Wundef" #endif #include "KoOptimizedCompositeOpFactoryPerArch.h" #include "KoOptimizedCompositeOpAlphaDarken32.h" #include "KoOptimizedCompositeOpAlphaDarken128.h" #include "KoOptimizedCompositeOpOver32.h" #include "KoOptimizedCompositeOpOver128.h" #include #include "DebugPigment.h" #include #if defined(__clang__) #pragma GCC diagnostic ignored "-Wlocal-type-template-args" #endif template<> template<> KoOptimizedCompositeOpFactoryPerArch::ReturnType -KoOptimizedCompositeOpFactoryPerArch::create(ParamType param) +KoOptimizedCompositeOpFactoryPerArch::create(ParamType param) { - return new KoOptimizedCompositeOpAlphaDarken32(param); + return new KoOptimizedCompositeOpAlphaDarken32(param); } template<> template<> KoOptimizedCompositeOpFactoryPerArch::ReturnType -KoOptimizedCompositeOpFactoryPerArch::create(ParamType param) +KoOptimizedCompositeOpFactoryPerArch::create(ParamType param) { - return new KoOptimizedCompositeOpOver32(param); + return new KoOptimizedCompositeOpOver32(param); } template<> template<> KoOptimizedCompositeOpFactoryPerArch::ReturnType -KoOptimizedCompositeOpFactoryPerArch::create(ParamType param) +KoOptimizedCompositeOpFactoryPerArch::create(ParamType param) { - return new KoOptimizedCompositeOpAlphaDarken128(param); + return new KoOptimizedCompositeOpAlphaDarken128(param); } template<> template<> KoOptimizedCompositeOpFactoryPerArch::ReturnType -KoOptimizedCompositeOpFactoryPerArch::create(ParamType param) +KoOptimizedCompositeOpFactoryPerArch::create(ParamType param) { - return new KoOptimizedCompositeOpOver128(param); + return new KoOptimizedCompositeOpOver128(param); } #define __stringify(_s) #_s #define stringify(_s) __stringify(_s) inline void printFeatureSupported(const QString &feature, Vc::Implementation impl) { dbgPigment << "\t" << feature << "\t---\t" << (Vc::isImplementationSupported(impl) ? "yes" : "no"); } template<> KoReportCurrentArch::ReturnType -KoReportCurrentArch::create(ParamType) +KoReportCurrentArch::create(ParamType) { - dbgPigment << "Compiled for arch:" << stringify(VC_IMPL); + dbgPigment << "Compiled for arch:" << Vc::CurrentImplementation::current(); dbgPigment << "Features supported:"; printFeatureSupported("SSE2", Vc::SSE2Impl); printFeatureSupported("SSSE3", Vc::SSSE3Impl); printFeatureSupported("SSE4.1", Vc::SSE41Impl); printFeatureSupported("AVX ", Vc::AVXImpl); -#if VC_VERSION_NUMBER >= VC_VERSION_CHECK(0, 8, 0) printFeatureSupported("AVX2 ", Vc::AVX2Impl); -#endif } diff --git a/libs/pigment/compositeops/KoOptimizedCompositeOpOver32.h b/libs/pigment/compositeops/KoOptimizedCompositeOpOver32.h index 2ad712239a..e6f958a2cc 100644 --- a/libs/pigment/compositeops/KoOptimizedCompositeOpOver32.h +++ b/libs/pigment/compositeops/KoOptimizedCompositeOpOver32.h @@ -1,248 +1,248 @@ /* * Copyright (c) 2006 Cyrille Berger * Copyright (c) 2011 Silvio Heinrich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU 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 KOOPTIMIZEDCOMPOSITEOPOVER32_H_ #define KOOPTIMIZEDCOMPOSITEOPOVER32_H_ #include "KoCompositeOpBase.h" #include "KoCompositeOpRegistry.h" #include "KoStreamedMath.h" template struct OverCompositor32 { struct OptionalParams { OptionalParams(const KoCompositeOp::ParameterInfo& params) : channelFlags(params.channelFlags) { } const QBitArray &channelFlags; }; // \see docs in AlphaDarkenCompositor32 template static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const OptionalParams &oparams) { Q_UNUSED(oparams); Vc::float_v src_alpha; Vc::float_v dst_alpha; src_alpha = KoStreamedMath<_impl>::template fetch_alpha_32(src); bool haveOpacity = opacity != 1.0; Vc::float_v opacity_norm_vec(opacity); Vc::float_v uint8Max((float)255.0); Vc::float_v uint8MaxRec1((float)1.0 / 255.0); Vc::float_v zeroValue(Vc::Zero); Vc::float_v oneValue(Vc::One); src_alpha *= opacity_norm_vec; if (haveMask) { Vc::float_v mask_vec = KoStreamedMath<_impl>::fetch_mask_8(mask); src_alpha *= mask_vec * uint8MaxRec1; } // The source cannot change the colors in the destination, // since its fully transparent if ((src_alpha == zeroValue).isFull()) { return; } dst_alpha = KoStreamedMath<_impl>::template fetch_alpha_32(dst); Vc::float_v src_c1; Vc::float_v src_c2; Vc::float_v src_c3; Vc::float_v dst_c1; Vc::float_v dst_c2; Vc::float_v dst_c3; KoStreamedMath<_impl>::template fetch_colors_32(src, src_c1, src_c2, src_c3); Vc::float_v src_blend; Vc::float_v new_alpha; if ((dst_alpha == uint8Max).isFull()) { new_alpha = dst_alpha; src_blend = src_alpha * uint8MaxRec1; } else if ((dst_alpha == zeroValue).isFull()) { new_alpha = src_alpha; src_blend = oneValue; } else { /** * The value of new_alpha can have *some* zero values, * which will result in NaN values while division. But * when converted to integers these NaN values will * be converted to zeroes, which is exactly what we need */ new_alpha = dst_alpha + (uint8Max - dst_alpha) * src_alpha * uint8MaxRec1; src_blend = src_alpha / new_alpha; } if (!(src_blend == oneValue).isFull()) { KoStreamedMath<_impl>::template fetch_colors_32(dst, dst_c1, dst_c2, dst_c3); dst_c1 = src_blend * (src_c1 - dst_c1) + dst_c1; dst_c2 = src_blend * (src_c2 - dst_c2) + dst_c2; dst_c3 = src_blend * (src_c3 - dst_c3) + dst_c3; } else { if (!haveMask && !haveOpacity) { - memcpy(dst, src, 4 * Vc::float_v::Size); + memcpy(dst, src, 4 * Vc::float_v::size()); return; } else { // opacity has changed the alpha of the source, // so we can't just memcpy the bytes dst_c1 = src_c1; dst_c2 = src_c2; dst_c3 = src_c3; } } KoStreamedMath<_impl>::write_channels_32(dst, new_alpha, dst_c1, dst_c2, dst_c3); } template static ALWAYS_INLINE void compositeOnePixelScalar(const channels_type *src, channels_type *dst, const quint8 *mask, float opacity, const OptionalParams &oparams) { using namespace Arithmetic; const qint32 alpha_pos = 3; const float uint8Rec1 = 1.0 / 255.0; const float uint8Max = 255.0; float srcAlpha = src[alpha_pos]; srcAlpha *= opacity; if (haveMask) { srcAlpha *= float(*mask) * uint8Rec1; } if (srcAlpha != 0.0) { float dstAlpha = dst[alpha_pos]; float srcBlendNorm; if (dstAlpha == uint8Max) { srcBlendNorm = srcAlpha * uint8Rec1; } else if (dstAlpha == 0.0) { dstAlpha = srcAlpha; srcBlendNorm = 1.0; if (!allChannelsFlag) { pixel_type *d = reinterpret_cast(dst); *d = 0; // dstAlpha is already null } } else { dstAlpha += (uint8Max - dstAlpha) * srcAlpha * uint8Rec1; srcBlendNorm = srcAlpha / dstAlpha; } if(allChannelsFlag) { if (srcBlendNorm == 1.0) { if (!alphaLocked) { const pixel_type *s = reinterpret_cast(src); pixel_type *d = reinterpret_cast(dst); *d = *s; } else { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; } } else if (srcBlendNorm != 0.0){ dst[0] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[0], src[0], srcBlendNorm); dst[1] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[1], src[1], srcBlendNorm); dst[2] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[2], src[2], srcBlendNorm); } } else { const QBitArray &channelFlags = oparams.channelFlags; if (srcBlendNorm == 1.0) { if(channelFlags.at(0)) dst[0] = src[0]; if(channelFlags.at(1)) dst[1] = src[1]; if(channelFlags.at(2)) dst[2] = src[2]; } else if (srcBlendNorm != 0.0) { if(channelFlags.at(0)) dst[0] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[0], src[0], srcBlendNorm); if(channelFlags.at(1)) dst[1] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[1], src[1], srcBlendNorm); if(channelFlags.at(2)) dst[2] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[2], src[2], srcBlendNorm); } } if (!alphaLocked) { dst[alpha_pos] = KoStreamedMath<_impl>::round_float_to_uint(dstAlpha); } } } }; /** * An optimized version of a composite op for the use in 4 byte * colorspaces with alpha channel placed at the last byte of * the pixel: C1_C2_C3_A. */ template class KoOptimizedCompositeOpOver32 : public KoCompositeOp { public: KoOptimizedCompositeOpOver32(const KoColorSpace* cs) : KoCompositeOp(cs, COMPOSITE_OVER, i18n("Normal"), KoCompositeOp::categoryMix()) {} using KoCompositeOp::composite; virtual void composite(const KoCompositeOp::ParameterInfo& params) const { if(params.maskRowStart) { composite(params); } else { composite(params); } } template inline void composite(const KoCompositeOp::ParameterInfo& params) const { if (params.channelFlags.isEmpty() || params.channelFlags == QBitArray(4, true)) { KoStreamedMath<_impl>::template genericComposite32 >(params); } else { const bool allChannelsFlag = params.channelFlags.at(0) && params.channelFlags.at(1) && params.channelFlags.at(2); const bool alphaLocked = !params.channelFlags.at(3); if (allChannelsFlag && alphaLocked) { KoStreamedMath<_impl>::template genericComposite32_novector >(params); } else if (!allChannelsFlag && !alphaLocked) { KoStreamedMath<_impl>::template genericComposite32_novector >(params); } else /*if (!allChannelsFlag && alphaLocked) */{ KoStreamedMath<_impl>::template genericComposite32_novector >(params); } } } }; #endif // KOOPTIMIZEDCOMPOSITEOPOVER32_H_ diff --git a/libs/pigment/compositeops/KoStreamedMath.h b/libs/pigment/compositeops/KoStreamedMath.h index 2fcb0b29ec..999fe46003 100644 --- a/libs/pigment/compositeops/KoStreamedMath.h +++ b/libs/pigment/compositeops/KoStreamedMath.h @@ -1,473 +1,433 @@ /* * Copyright (c) 2012 Dmitry Kazakov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef __KOSTREAMED_MATH_H #define __KOSTREAMED_MATH_H #if defined _MSC_VER // Lets shut up the "possible loss of data" and "forcing value to bool 'true' or 'false' #pragma warning ( push ) #pragma warning ( disable : 4244 ) #pragma warning ( disable : 4800 ) #endif #include #include #if defined _MSC_VER #pragma warning ( pop ) #endif #include #include #include #define BLOCKDEBUG 0 #if !defined _MSC_VER #pragma GCC diagnostic ignored "-Wcast-align" #endif template struct KoStreamedMath { +using int_v = Vc::SimdArray; +using uint_v = Vc::SimdArray; + /** * Composes src into dst without using vector instructions */ template static void genericComposite_novector(const KoCompositeOp::ParameterInfo& params) { using namespace Arithmetic; const qint32 linearInc = pixelSize; qint32 srcLinearInc = params.srcRowStride ? pixelSize : 0; quint8* dstRowStart = params.dstRowStart; const quint8* maskRowStart = params.maskRowStart; const quint8* srcRowStart = params.srcRowStart; typename Compositor::OptionalParams optionalParams(params); for(quint32 r=params.rows; r>0; --r) { const quint8 *mask = maskRowStart; const quint8 *src = srcRowStart; quint8 *dst = dstRowStart; int blockRest = params.cols; for(int i = 0; i < blockRest; i++) { Compositor::template compositeOnePixelScalar(src, dst, mask, params.opacity, optionalParams); src += srcLinearInc; dst += linearInc; if (useMask) { mask++; } } srcRowStart += params.srcRowStride; dstRowStart += params.dstRowStride; if (useMask) { maskRowStart += params.maskRowStride; } } } template static void genericComposite32_novector(const KoCompositeOp::ParameterInfo& params) { genericComposite_novector(params); } template static void genericComposite128_novector(const KoCompositeOp::ParameterInfo& params) { genericComposite_novector(params); } static inline quint8 round_float_to_uint(float value) { return quint8(value + float(0.5)); } static inline quint8 lerp_mixed_u8_float(quint8 a, quint8 b, float alpha) { return round_float_to_uint(qint16(b - a) * alpha + a); } /** - * Get a vector containing first Vc::float_v::Size values of mask. + * Get a vector containing first Vc::float_v::size() values of mask. * Each source mask element is considered to be a 8-bit integer */ static inline Vc::float_v fetch_mask_8(const quint8 *data) { - Vc::uint_v data_i(data); - return Vc::float_v(Vc::int_v(data_i)); + uint_v data_i(data); + return Vc::float_v(int_v(data_i)); } /** - * Get an alpha values from Vc::float_v::Size pixels 32-bit each + * Get an alpha values from Vc::float_v::size() pixels 32-bit each * (4 channels, 8 bit per channel). The alpha value is considered * to be stored in the most significat byte of the pixel * * \p aligned controls whether the \p data is fetched using aligned * instruction or not. * 1) Fetching aligned data with unaligned instruction * degrades performance. * 2) Fetching unaligned data with aligned instruction * causes #GP (General Protection Exception) */ template static inline Vc::float_v fetch_alpha_32(const quint8 *data) { - Vc::uint_v data_i; + uint_v data_i; if (aligned) { data_i.load((const quint32*)data, Vc::Aligned); } else { data_i.load((const quint32*)data, Vc::Unaligned); } - return Vc::float_v(Vc::int_v(data_i >> 24)); + return Vc::float_v(int_v(data_i >> 24)); } /** - * Get color values from Vc::float_v::Size pixels 32-bit each + * Get color values from Vc::float_v::size() pixels 32-bit each * (4 channels, 8 bit per channel). The color data is considered * to be stored in the 3 least significant bytes of the pixel. * * \p aligned controls whether the \p data is fetched using aligned * instruction or not. * 1) Fetching aligned data with unaligned instruction * degrades performance. * 2) Fetching unaligned data with aligned instruction * causes #GP (General Protection Exception) */ template static inline void fetch_colors_32(const quint8 *data, Vc::float_v &c1, Vc::float_v &c2, Vc::float_v &c3) { - Vc::uint_v data_i; - if (aligned) { - data_i.load((const quint32*)data, Vc::Aligned); - } else { - data_i.load((const quint32*)data, Vc::Unaligned); - } - - const quint32 lowByteMask = 0xFF; - Vc::uint_v mask(lowByteMask); - - c1 = Vc::float_v(Vc::int_v((data_i >> 16) & mask)); - c2 = Vc::float_v(Vc::int_v((data_i >> 8) & mask)); - c3 = Vc::float_v(Vc::int_v( data_i & mask)); -} - -/** - * - */ -template -static inline void fetch_all_32(const quint8 *data, - Vc::float_v &alpha, - Vc::float_v &c1, - Vc::float_v &c2, - Vc::float_v &c3) { - Vc::uint_v data_i; + int_v data_i; if (aligned) { data_i.load((const quint32*)data, Vc::Aligned); } else { data_i.load((const quint32*)data, Vc::Unaligned); } const quint32 lowByteMask = 0xFF; - Vc::uint_v mask(lowByteMask); + uint_v mask(lowByteMask); - alpha = Vc::float_v(Vc::int_v(data_i >> 24)); - c1 = Vc::float_v(Vc::int_v((data_i >> 16) & mask)); - c2 = Vc::float_v(Vc::int_v((data_i >> 8) & mask)); - c3 = Vc::float_v(Vc::int_v( data_i & mask)); -} - -template -static inline void fetch_8_offset(const quint8 *data, - Vc::float_v &value, - const quint32 offset) { - Vc::uint_v data_i; - if (aligned) { - data_i.load((const quint32*)data, Vc::Aligned); - } else { - data_i.load((const quint32*)data, Vc::Unaligned); - } - - const quint32 lowByteMask = 0xFF; - Vc::uint_v mask(lowByteMask); - - value = Vc::float_v(Vc::int_v((data_i >> offset) & mask)); + c1 = Vc::float_v(int_v((data_i >> 16) & mask)); + c2 = Vc::float_v(int_v((data_i >> 8) & mask)); + c3 = Vc::float_v(int_v( data_i & mask)); } /** - * Pack color and alpha values to Vc::float_v::Size pixels 32-bit each + * Pack color and alpha values to Vc::float_v::size() pixels 32-bit each * (4 channels, 8 bit per channel). The color data is considered * to be stored in the 3 least significant bytes of the pixel, alpha - * in the most significant byte * * NOTE: \p data must be aligned pointer! */ static inline void write_channels_32(quint8 *data, Vc::float_v::AsArg alpha, Vc::float_v::AsArg c1, Vc::float_v::AsArg c2, Vc::float_v::AsArg c3) { /** * FIXME: make conversion float->int * use methematical rounding */ const quint32 lowByteMask = 0xFF; - Vc::uint_v mask(lowByteMask); // FIXME: Use single-instruction rounding + conversion // The achieve that we need to implement Vc::iRound() - Vc::uint_v v1 = Vc::uint_v(Vc::int_v(Vc::round(alpha))) << 24; - Vc::uint_v v2 = (Vc::uint_v(Vc::int_v(Vc::round(c1))) & mask) << 16; - Vc::uint_v v3 = (Vc::uint_v(Vc::int_v(Vc::round(c2))) & mask) << 8; + uint_v mask(lowByteMask); + uint_v v1 = uint_v(int_v(Vc::round(alpha))) << 24; + uint_v v2 = (uint_v(int_v(Vc::round(c1))) & mask) << 16; + uint_v v3 = (uint_v(int_v(Vc::round(c2))) & mask) << 8; + uint_v v4 = uint_v(int_v(Vc::round(c3))) & mask; v1 = v1 | v2; - Vc::uint_v v4 = Vc::uint_v(Vc::int_v(Vc::round(c3))) & mask; v3 = v3 | v4; - - *((Vc::uint_v*)data) = v1 | v3; + (v1 | v3).store((quint32*)data, Vc::Aligned); } /** * Composes src pixels into dst pixles. Is optimized for 32-bit-per-pixel * colorspaces. Uses \p Compositor strategy parameter for doing actual * math of the composition */ template static void genericComposite(const KoCompositeOp::ParameterInfo& params) { using namespace Arithmetic; - const int vectorSize = Vc::float_v::Size; + const int vectorSize = Vc::float_v::size(); const qint32 vectorInc = pixelSize * vectorSize; const qint32 linearInc = pixelSize; qint32 srcVectorInc = vectorInc; qint32 srcLinearInc = pixelSize; quint8* dstRowStart = params.dstRowStart; const quint8* maskRowStart = params.maskRowStart; const quint8* srcRowStart = params.srcRowStart; typename Compositor::OptionalParams optionalParams(params); if (!params.srcRowStride) { if (pixelSize == 4) { quint32 *buf = Vc::malloc(vectorSize); - *((Vc::uint_v*)buf) = Vc::uint_v(*((const quint32*)params.srcRowStart)); + *((uint_v*)buf) = uint_v(*((const quint32*)params.srcRowStart)); srcRowStart = reinterpret_cast(buf); srcLinearInc = 0; srcVectorInc = 0; } else { quint8 *buf = Vc::malloc(vectorInc); quint8 *ptr = buf; for (int i = 0; i < vectorSize; i++) { memcpy(ptr, params.srcRowStart, pixelSize); ptr += pixelSize; } srcRowStart = buf; srcLinearInc = 0; srcVectorInc = 0; } } #if BLOCKDEBUG int totalBlockAlign = 0; int totalBlockAlignedVector = 0; int totalBlockUnalignedVector = 0; int totalBlockRest = 0; #endif for(quint32 r=params.rows; r>0; --r) { // Hint: Mask is allowed to be unaligned const quint8 *mask = maskRowStart; const quint8 *src = srcRowStart; quint8 *dst = dstRowStart; const int pixelsAlignmentMask = vectorSize * sizeof(float) - 1; uintptr_t srcPtrValue = reinterpret_cast(src); uintptr_t dstPtrValue = reinterpret_cast(dst); uintptr_t srcAlignment = srcPtrValue & pixelsAlignmentMask; uintptr_t dstAlignment = dstPtrValue & pixelsAlignmentMask; // Uncomment if facing problems with alignment: // Q_ASSERT_X(!(dstAlignment & 3), "Compositioning", // "Pixel data must be aligned on pixels borders!"); int blockAlign = params.cols; int blockAlignedVector = 0; int blockUnalignedVector = 0; int blockRest = 0; int *vectorBlock = srcAlignment == dstAlignment || !srcVectorInc ? &blockAlignedVector : &blockUnalignedVector; if (!dstAlignment) { blockAlign = 0; *vectorBlock = params.cols / vectorSize; blockRest = params.cols % vectorSize; } else if (params.cols > 2 * vectorSize) { blockAlign = (vectorInc - dstAlignment) / pixelSize; const int restCols = params.cols - blockAlign; if (restCols > 0) { *vectorBlock = restCols / vectorSize; blockRest = restCols % vectorSize; } else { blockAlign = params.cols; *vectorBlock = 0; blockRest = 0; } } #if BLOCKDEBUG totalBlockAlign += blockAlign; totalBlockAlignedVector += blockAlignedVector; totalBlockUnalignedVector += blockUnalignedVector; totalBlockRest += blockRest; #endif for(int i = 0; i < blockAlign; i++) { Compositor::template compositeOnePixelScalar(src, dst, mask, params.opacity, optionalParams); src += srcLinearInc; dst += linearInc; if(useMask) { mask++; } } for (int i = 0; i < blockAlignedVector; i++) { Compositor::template compositeVector(src, dst, mask, params.opacity, optionalParams); src += srcVectorInc; dst += vectorInc; if (useMask) { mask += vectorSize; } } for (int i = 0; i < blockUnalignedVector; i++) { Compositor::template compositeVector(src, dst, mask, params.opacity, optionalParams); src += srcVectorInc; dst += vectorInc; if (useMask) { mask += vectorSize; } } for(int i = 0; i < blockRest; i++) { Compositor::template compositeOnePixelScalar(src, dst, mask, params.opacity, optionalParams); src += srcLinearInc; dst += linearInc; if (useMask) { mask++; } } srcRowStart += params.srcRowStride; dstRowStart += params.dstRowStride; if (useMask) { maskRowStart += params.maskRowStride; } } #if BLOCKDEBUG qDebug() << "I" << "rows:" << params.rows << "\tpad(S):" << totalBlockAlign << "\tbav(V):" << totalBlockAlignedVector << "\tbuv(V):" << totalBlockUnalignedVector << "\tres(S)" << totalBlockRest; // << srcAlignment << dstAlignment; #endif if (!params.srcRowStride) { Vc::free(reinterpret_cast(const_cast(srcRowStart))); } } template static void genericComposite32(const KoCompositeOp::ParameterInfo& params) { genericComposite(params); } template static void genericComposite128(const KoCompositeOp::ParameterInfo& params) { genericComposite(params); } }; namespace KoStreamedMathFunctions { template ALWAYS_INLINE void clearPixel(quint8* dst) { qFatal("Not implemented"); } template<> ALWAYS_INLINE void clearPixel<4>(quint8* dst) { quint32 *d = reinterpret_cast(dst); *d = 0; } template<> ALWAYS_INLINE void clearPixel<16>(quint8* dst) { quint64 *d = reinterpret_cast(dst); d[0] = 0; d[1] = 0; } template ALWAYS_INLINE void copyPixel(const quint8 *src, quint8* dst) { qFatal("Not implemented"); } template<> ALWAYS_INLINE void copyPixel<4>(const quint8 *src, quint8* dst) { const quint32 *s = reinterpret_cast(src); quint32 *d = reinterpret_cast(dst); *d = *s; } template<> ALWAYS_INLINE void copyPixel<16>(const quint8 *src, quint8* dst) { const quint64 *s = reinterpret_cast(src); quint64 *d = reinterpret_cast(dst); d[0] = s[0]; d[1] = s[1]; } } #endif /* __KOSTREAMED_MATH_H */ diff --git a/libs/pigment/compositeops/KoVcMultiArchBuildSupport.h b/libs/pigment/compositeops/KoVcMultiArchBuildSupport.h index 09677a680d..bb5153bdb1 100644 --- a/libs/pigment/compositeops/KoVcMultiArchBuildSupport.h +++ b/libs/pigment/compositeops/KoVcMultiArchBuildSupport.h @@ -1,127 +1,132 @@ /* * Copyright (c) 2012 Dmitry Kazakov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef __KOVCMULTIARCHBUILDSUPPORT_H #define __KOVCMULTIARCHBUILDSUPPORT_H #include "config-vc.h" #ifdef HAVE_VC #if defined(__clang__) #pragma GCC diagnostic ignored "-Wlocal-type-template-args" #endif #if defined _MSC_VER // Lets shut up the "possible loss of data" and "forcing value to bool 'true' or 'false' #pragma warning ( push ) #pragma warning ( disable : 4244 ) #pragma warning ( disable : 4800 ) #endif +#include #include #include #if defined _MSC_VER #pragma warning ( pop ) #endif #else /* HAVE_VC */ namespace Vc { - typedef enum {ScalarImpl} Implementation; + enum Implementation /*: std::uint_least32_t*/ { + ScalarImpl, + }; + class CurrentImplementation { + public: + static constexpr Implementation current() + { + return static_cast(ScalarImpl); + } + }; } -#define VC_IMPL ::Vc::ScalarImpl - #ifdef DO_PACKAGERS_BUILD #ifdef __GNUC__ #warning "Packagers build is not available without the presence of Vc library. Disabling." #endif #undef DO_PACKAGERS_BUILD #endif #endif /* HAVE_VC */ #ifdef DO_PACKAGERS_BUILD #include #include #include #include template typename FactoryType::ReturnType createOptimizedClass(typename FactoryType::ParamType param) { static bool isConfigInitialized = false; static bool useVectorization = true; if (!isConfigInitialized) { KConfigGroup cfg = KSharedConfig::openConfig()->group(""); useVectorization = !cfg.readEntry("amdDisableVectorWorkaround", false); isConfigInitialized = true; } if (!useVectorization) { qWarning() << "WARNING: vector instructions disabled by \'amdDisableVectorWorkaround\' option!"; return FactoryType::template create(param); } /** * We use SSE2, SSSE3, SSE4.1, AVX and AVX2. * The rest are integer and string instructions mostly. * * TODO: Add FMA3/4 when it is adopted by Vc */ -#if VC_VERSION_NUMBER >= VC_VERSION_CHECK(0, 8, 0) if (Vc::isImplementationSupported(Vc::AVX2Impl)) { return FactoryType::template create(param); - } else -#endif - if (Vc::isImplementationSupported(Vc::AVXImpl)) { + } else if (Vc::isImplementationSupported(Vc::AVXImpl)) { return FactoryType::template create(param); } else if (Vc::isImplementationSupported(Vc::SSE41Impl)) { return FactoryType::template create(param); } else if (Vc::isImplementationSupported(Vc::SSSE3Impl)) { return FactoryType::template create(param); } else if (Vc::isImplementationSupported(Vc::SSE2Impl)) { return FactoryType::template create(param); } else { return FactoryType::template create(param); } } #else /* DO_PACKAGERS_BUILD */ /** * When doing not a packager's build we have one architecture only, * so the factory methods are simplified */ template typename FactoryType::ReturnType createOptimizedClass(typename FactoryType::ParamType param) { - return FactoryType::template create(param); + return FactoryType::template create(param); } #endif /* DO_PACKAGERS_BUILD */ #endif /* __KOVCMULTIARCHBUILDSUPPORT_H */ diff --git a/libs/pigment/resources/KoHashGenerator.h b/libs/pigment/resources/KoHashGenerator.h index d9db0cc017..383ff9bc15 100644 --- a/libs/pigment/resources/KoHashGenerator.h +++ b/libs/pigment/resources/KoHashGenerator.h @@ -1,32 +1,32 @@ /* * Copyright (c) 2015 Stefano Bonicatti * * 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 KOHASHGENERATOR_H #define KOHASHGENERATOR_H #include #include class KoHashGenerator { public: - virtual QByteArray generateHash(QString filename) = 0; + virtual QByteArray generateHash(const QString &filename) = 0; virtual QByteArray generateHash(const QByteArray &array) = 0; virtual ~KoHashGenerator(){} }; #endif diff --git a/libs/pigment/resources/KoHashGeneratorProvider.cpp b/libs/pigment/resources/KoHashGeneratorProvider.cpp index 455c9be857..b2bcb53d2b 100644 --- a/libs/pigment/resources/KoHashGeneratorProvider.cpp +++ b/libs/pigment/resources/KoHashGeneratorProvider.cpp @@ -1,59 +1,59 @@ /* * Copyright (c) 2015 Stefano Bonicatti * * 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 "KoHashGeneratorProvider.h" #include #include #include "KoMD5Generator.h" KoHashGeneratorProvider *KoHashGeneratorProvider::instance_var = 0; Q_GLOBAL_STATIC(KoHashGeneratorProvider, s_instance); KoHashGeneratorProvider::KoHashGeneratorProvider() { // Initialize default generators hashGenerators.insert("MD5", new KoMD5Generator()); } KoHashGeneratorProvider::~KoHashGeneratorProvider() { qDeleteAll(hashGenerators); } -KoHashGenerator *KoHashGeneratorProvider::getGenerator(QString algorithm) +KoHashGenerator *KoHashGeneratorProvider::getGenerator(const QString &algorithm) { QMutexLocker locker(&mutex); return hashGenerators.value(algorithm); } -void KoHashGeneratorProvider::setGenerator(QString algorithm, KoHashGenerator *generator) +void KoHashGeneratorProvider::setGenerator(const QString &algorithm, KoHashGenerator *generator) { if (hashGenerators.contains(algorithm)) { delete hashGenerators.take(algorithm); hashGenerators[algorithm] = generator; } else hashGenerators.insert(algorithm, generator); } KoHashGeneratorProvider *KoHashGeneratorProvider::instance() { return s_instance; } diff --git a/libs/pigment/resources/KoHashGeneratorProvider.h b/libs/pigment/resources/KoHashGeneratorProvider.h index 6e68ed6043..5be1781341 100644 --- a/libs/pigment/resources/KoHashGeneratorProvider.h +++ b/libs/pigment/resources/KoHashGeneratorProvider.h @@ -1,44 +1,44 @@ /* * Copyright (c) 2015 Stefano Bonicatti * * 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 KOHASHGENERATORPROVIDER_H #define KOHASHGENERATORPROVIDER_H #include #include #include class KoHashGenerator; class KRITAPIGMENT_EXPORT KoHashGeneratorProvider { public: KoHashGeneratorProvider(); ~KoHashGeneratorProvider(); - KoHashGenerator *getGenerator(QString algorithm); - void setGenerator(QString algorithm, KoHashGenerator *generator); + KoHashGenerator *getGenerator(const QString &algorithm); + void setGenerator(const QString &algorithm, KoHashGenerator *generator); static KoHashGeneratorProvider *instance(); private: static KoHashGeneratorProvider *instance_var; QHash hashGenerators; QMutex mutex; }; #endif diff --git a/libs/pigment/resources/KoMD5Generator.cpp b/libs/pigment/resources/KoMD5Generator.cpp index a785960e74..3c65fadde9 100644 --- a/libs/pigment/resources/KoMD5Generator.cpp +++ b/libs/pigment/resources/KoMD5Generator.cpp @@ -1,57 +1,57 @@ /* * Copyright (c) 2015 Stefano Bonicatti * * 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 "KoMD5Generator.h" #include #include #include KoMD5Generator::KoMD5Generator() { } KoMD5Generator::~KoMD5Generator() { } QByteArray KoMD5Generator::generateHash(const QByteArray &array) { if (!array.isEmpty()) { QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(array); return md5.result(); } return array; } -QByteArray KoMD5Generator::generateHash(QString filename) +QByteArray KoMD5Generator::generateHash(const QString &filename) { QByteArray result; QFile f(filename); if (f.exists() && f.open(QIODevice::ReadOnly)) { QByteArray ba = f.readAll(); result = generateHash(ba); } return result; } diff --git a/libs/pigment/resources/KoMD5Generator.h b/libs/pigment/resources/KoMD5Generator.h index 5f98b2f4b7..292be6756f 100644 --- a/libs/pigment/resources/KoMD5Generator.h +++ b/libs/pigment/resources/KoMD5Generator.h @@ -1,35 +1,35 @@ /* * Copyright (c) 2015 Stefano Bonicatti * * 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 KOMD5GENERATOR_H #define KOMD5GENERATOR_H #include "KoHashGenerator.h" #include class KRITAPIGMENT_EXPORT KoMD5Generator : public KoHashGenerator { public: KoMD5Generator(); virtual ~KoMD5Generator(); - virtual QByteArray generateHash(QString filename); + virtual QByteArray generateHash(const QString &filename); virtual QByteArray generateHash(const QByteArray &array); }; #endif diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt index a88c7b7dcf..b91d0c7451 100644 --- a/libs/ui/CMakeLists.txt +++ b/libs/ui/CMakeLists.txt @@ -1,532 +1,533 @@ # Disable -Wswitch because of the extra definitions we here: # kis_input_manager.cpp: In member function ‘virtual bool KisInputManager::eventFilter(QObject*, QEvent*)’: # warning: case value ‘1001’ not in enumerated type ‘QEvent::Type’ [-Wswitch] # warning: case value ‘1002’ not in enumerated type ‘QEvent::Type’ [-Wswitch] if (CMAKE_COMPILER_IS_GNUCXX) add_definitions(${KDE4_ENABLE_EXCEPTIONS} -Wno-switch) endif () include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/qtlockedfile ) include_directories(SYSTEM ${EXIV2_INCLUDE_DIR} ${EIGEN3_INCLUDE_DIR} ${OCIO_INCLUDE_DIR} ${Boost_INCLUDE_DIRS} ) add_subdirectory( tests ) if (APPLE) find_library(FOUNDATION_LIBRARY Foundation) endif () set(kritaui_LIB_SRCS canvas/kis_canvas_widget_base.cpp canvas/kis_canvas2.cpp canvas/kis_canvas_updates_compressor.cpp canvas/kis_canvas_controller.cpp canvas/kis_paintop_transformation_connector.cpp canvas/kis_display_color_converter.cpp canvas/kis_display_filter.cpp canvas/kis_exposure_gamma_correction_interface.cpp canvas/kis_tool_proxy.cpp canvas/kis_canvas_decoration.cc canvas/kis_coordinates_converter.cpp canvas/kis_grid_manager.cpp canvas/kis_grid_decoration.cpp canvas/kis_grid_config.cpp canvas/kis_prescaled_projection.cpp canvas/kis_qpainter_canvas.cpp canvas/kis_projection_backend.cpp canvas/kis_update_info.cpp canvas/kis_image_patch.cpp canvas/kis_image_pyramid.cpp canvas/kis_infinity_manager.cpp canvas/kis_change_guides_command.cpp canvas/kis_guides_decoration.cpp canvas/kis_guides_manager.cpp canvas/kis_guides_config.cpp canvas/kis_snap_config.cpp canvas/kis_snap_line_strategy.cpp dialogs/kis_about_application.cpp dialogs/kis_dlg_adj_layer_props.cc dialogs/kis_dlg_adjustment_layer.cc dialogs/kis_dlg_filter.cpp dialogs/kis_dlg_generator_layer.cpp dialogs/kis_dlg_file_layer.cpp dialogs/kis_dlg_image_properties.cc dialogs/kis_dlg_layer_properties.cc dialogs/kis_dlg_preferences.cc dialogs/slider_and_spin_box_sync.cpp dialogs/kis_dlg_blacklist_cleanup.cpp dialogs/kis_dlg_layer_style.cpp dialogs/kis_dlg_png_import.cpp dialogs/kis_dlg_import_image_sequence.cpp dialogs/kis_delayed_save_dialog.cpp flake/kis_node_dummies_graph.cpp flake/kis_dummies_facade_base.cpp flake/kis_dummies_facade.cpp flake/kis_node_shapes_graph.cpp flake/kis_node_shape.cpp flake/kis_shape_controller.cpp flake/kis_shape_layer.cc flake/kis_shape_layer_canvas.cpp flake/kis_shape_selection.cpp flake/kis_shape_selection_canvas.cpp flake/kis_shape_selection_model.cpp flake/kis_take_all_shapes_command.cpp kis_aspect_ratio_locker.cpp kis_autogradient.cc kis_bookmarked_configurations_editor.cc kis_bookmarked_configurations_model.cc kis_bookmarked_filter_configurations_model.cc kis_canvas_resource_provider.cpp kis_derived_resources.cpp kis_categories_mapper.cpp kis_categorized_list_model.cpp kis_categorized_item_delegate.cpp kis_clipboard.cc kis_config.cc kis_config_notifier.cpp kis_control_frame.cpp kis_composite_ops_model.cc kis_paint_ops_model.cpp kis_cursor.cc kis_cursor_cache.cpp kis_custom_pattern.cc kis_file_layer.cpp kis_safe_document_loader.cpp kis_splash_screen.cpp kis_filter_manager.cc kis_filters_model.cc kis_histogram_view.cc kis_image_manager.cc kis_image_view_converter.cpp kis_import_catcher.cc kis_layer_manager.cc kis_mask_manager.cc kis_mimedata.cpp kis_node_commands_adapter.cpp kis_node_manager.cpp kis_node_juggler_compressed.cpp kis_node_selection_adapter.cpp kis_node_insertion_adapter.cpp kis_node_model.cpp kis_node_filter_proxy_model.cpp kis_model_index_converter_base.cpp kis_model_index_converter.cpp kis_model_index_converter_show_all.cpp kis_painting_assistant.cc kis_painting_assistants_decoration.cpp kis_painting_assistants_manager.cpp kis_paintop_box.cc kis_paintop_option.cpp kis_paintop_options_model.cpp kis_paintop_settings_widget.cpp kis_popup_palette.cpp kis_png_converter.cpp kis_preference_set_registry.cpp kis_resource_server_provider.cpp kis_selection_decoration.cc kis_selection_manager.cc kis_statusbar.cc kis_zoom_manager.cc kis_favorite_resource_manager.cpp kis_workspace_resource.cpp kis_action.cpp kis_action_manager.cpp kis_view_plugin.cpp kis_canvas_controls_manager.cpp kis_tooltip_manager.cpp kis_multinode_property.cpp kis_async_action_feedback.cpp kisexiv2/kis_exif_io.cpp kisexiv2/kis_exiv2.cpp kisexiv2/kis_iptc_io.cpp kisexiv2/kis_xmp_io.cpp kra/kis_kra_utils.cpp kra/kis_kra_load_visitor.cpp kra/kis_kra_loader.cpp kra/kis_kra_save_visitor.cpp kra/kis_kra_saver.cpp kra/kis_kra_savexml_visitor.cpp opengl/kis_opengl.cpp opengl/kis_opengl_canvas2.cpp opengl/kis_opengl_canvas_debugger.cpp opengl/kis_opengl_image_textures.cpp opengl/kis_texture_tile.cpp opengl/kis_texture_tile_update_info.cpp kis_fps_decoration.cpp ora/kis_open_raster_stack_load_visitor.cpp ora/kis_open_raster_stack_save_visitor.cpp ora/ora_load_context.cc ora/ora_save_context.cc recorder/kis_node_query_path_editor.cc recorder/kis_recorded_action_creator.cc recorder/kis_recorded_action_creator_factory.cc recorder/kis_recorded_action_creator_factory_registry.cc recorder/kis_recorded_action_editor_factory.cc recorder/kis_recorded_action_editor_factory_registry.cc recorder/kis_recorded_filter_action_editor.cc recorder/kis_recorded_filter_action_creator.cpp recorder/kis_recorded_paint_action_editor.cc tool/kis_selection_tool_helper.cpp tool/kis_selection_tool_config_widget_helper.cpp tool/kis_rectangle_constraint_widget.cpp tool/kis_shape_tool_helper.cpp tool/kis_tool.cc tool/kis_delegated_tool_policies.cpp tool/kis_tool_freehand.cc tool/kis_speed_smoother.cpp tool/kis_painting_information_builder.cpp + tool/kis_stabilized_events_sampler.cpp tool/kis_tool_freehand_helper.cpp tool/kis_tool_multihand_helper.cpp tool/kis_figure_painting_tool_helper.cpp tool/kis_recording_adapter.cpp tool/kis_tool_paint.cc tool/kis_tool_shape.cc tool/kis_tool_ellipse_base.cpp tool/kis_tool_rectangle_base.cpp tool/kis_tool_polyline_base.cpp tool/kis_tool_utils.cpp tool/kis_resources_snapshot.cpp tool/kis_smoothing_options.cpp tool/strokes/freehand_stroke.cpp tool/strokes/kis_painter_based_stroke_strategy.cpp tool/strokes/kis_filter_stroke_strategy.cpp tool/strokes/kis_color_picker_stroke_strategy.cpp widgets/kis_cmb_composite.cc widgets/kis_cmb_contour.cpp widgets/kis_cmb_gradient.cpp widgets/kis_paintop_list_widget.cpp widgets/kis_cmb_idlist.cc widgets/kis_color_space_selector.cc widgets/kis_advanced_color_space_selector.cc widgets/kis_cie_tongue_widget.cpp widgets/kis_tone_curve_widget.cpp widgets/kis_curve_widget.cpp widgets/kis_custom_image_widget.cc widgets/kis_image_from_clipboard_widget.cpp widgets/kis_double_widget.cc widgets/kis_filter_selector_widget.cc widgets/kis_gradient_chooser.cc widgets/kis_gradient_slider_widget.cc widgets/kis_gradient_slider.cpp widgets/kis_iconwidget.cc widgets/kis_mask_widgets.cpp widgets/kis_meta_data_merge_strategy_chooser_widget.cc widgets/kis_multi_bool_filter_widget.cc widgets/kis_multi_double_filter_widget.cc widgets/kis_multi_integer_filter_widget.cc widgets/kis_multipliers_double_slider_spinbox.cpp widgets/kis_paintop_presets_popup.cpp widgets/kis_tool_options_popup.cpp widgets/kis_paintop_presets_chooser_popup.cpp widgets/kis_pattern_chooser.cc widgets/kis_popup_button.cc widgets/kis_preset_chooser.cpp widgets/kis_progress_widget.cpp widgets/kis_selection_options.cc widgets/kis_scratch_pad.cpp widgets/kis_scratch_pad_event_filter.cpp widgets/kis_preset_selector_strip.cpp widgets/kis_slider_spin_box.cpp widgets/kis_size_group.cpp widgets/kis_size_group_p.cpp widgets/kis_wdg_generator.cpp widgets/kis_workspace_chooser.cpp widgets/squeezedcombobox.cpp widgets/kis_categorized_list_view.cpp widgets/kis_widget_chooser.cpp widgets/kis_tool_button.cpp widgets/kis_floating_message.cpp widgets/kis_lod_availability_widget.cpp widgets/kis_color_label_selector_widget.cpp widgets/kis_color_filter_combo.cpp input/kis_input_manager.cpp input/kis_input_manager_p.cpp input/kis_extended_modifiers_mapper.cpp input/kis_abstract_input_action.cpp input/kis_tool_invocation_action.cpp input/kis_pan_action.cpp input/kis_alternate_invocation_action.cpp input/kis_rotate_canvas_action.cpp input/kis_zoom_action.cpp input/kis_change_frame_action.cpp input/kis_gamma_exposure_action.cpp input/kis_show_palette_action.cpp input/kis_change_primary_setting_action.cpp input/kis_abstract_shortcut.cpp input/kis_single_action_shortcut.cpp input/kis_stroke_shortcut.cpp input/kis_shortcut_matcher.cpp input/kis_select_layer_action.cpp operations/kis_operation.cpp operations/kis_operation_configuration.cpp operations/kis_operation_registry.cpp operations/kis_operation_ui_factory.cpp operations/kis_operation_ui_widget.cpp operations/kis_filter_selection_operation.cpp actions/kis_selection_action_factories.cpp input/kis_touch_shortcut.cpp kis_document_undo_store.cpp kis_transaction_based_command.cpp kis_gui_context_command.cpp kis_gui_context_command_p.cpp input/kis_tablet_debugger.cpp input/kis_input_profile_manager.cpp input/kis_input_profile.cpp input/kis_shortcut_configuration.cpp input/config/kis_input_configuration_page.cpp input/config/kis_edit_profiles_dialog.cpp input/config/kis_input_profile_model.cpp input/config/kis_input_configuration_page_item.cpp input/config/kis_action_shortcuts_model.cpp input/config/kis_input_type_delegate.cpp input/config/kis_input_mode_delegate.cpp input/config/kis_input_button.cpp input/config/kis_input_editor_delegate.cpp input/config/kis_mouse_input_editor.cpp input/config/kis_wheel_input_editor.cpp input/config/kis_key_input_editor.cpp processing/fill_processing_visitor.cpp kis_asl_layer_style_serializer.cpp kis_psd_layer_style_resource.cpp canvas/kis_mirror_axis.cpp kis_abstract_perspective_grid.cpp KisApplication.cpp KisAutoSaveRecoveryDialog.cpp KisDetailsPane.cpp KisDocument.cpp KisNodeDelegate.cpp kis_node_view_visibility_delegate.cpp KisNodeToolTip.cpp KisNodeView.cpp kis_node_view_color_scheme.cpp KisFilterChain.cpp KisFilterChainLink.cpp KisFilterChainLinkList.cpp KisImportExportFilter.cpp KisFilterEdge.cpp KisFilterEntry.cpp KisFilterGraph.cpp KisImportExportManager.cpp KisFilterVertex.cpp KisMainWindow.cpp KisOpenPane.cpp KisPart.cpp KisPrintJob.cpp KisTemplate.cpp KisTemplateCreateDia.cpp KisTemplateGroup.cpp KisTemplates.cpp KisTemplatesPane.cpp KisTemplateTree.cpp KisUndoStackAction.cpp KisView.cpp thememanager.cpp kis_mainwindow_observer.cpp KisViewManager.cpp kis_mirror_manager.cpp qtlockedfile/qtlockedfile.cpp qtsingleapplication/qtlocalpeer.cpp qtsingleapplication/qtsingleapplication.cpp KisResourceBundle.cpp KisResourceBundleManifest.cpp kis_md5_generator.cpp KisApplicationArguments.cpp KisNetworkAccessManager.cpp KisMultiFeedRSSModel.cpp KisRemoteFileFetcher.cpp KisPaletteModel.cpp KisColorsetChooser.cpp KisSaveGroupVisitor.cpp ) if(WIN32) if (NOT Qt5Gui_PRIVATE_INCLUDE_DIRS) message(FATAL_ERROR "Qt5Gui Private header are missing!") endif() set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/kis_tablet_event.cpp input/wintab/kis_tablet_support_win.cpp input/wintab/kis_screen_size_choice_dialog.cpp qtlockedfile/qtlockedfile_win.cpp ) include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS}) endif() set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} kis_animation_frame_cache.cpp kis_animation_cache_populator.cpp canvas/kis_animation_player.cpp kis_animation_exporter.cpp kis_animation_importer.cpp ) if(UNIX) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/kis_tablet_event.cpp input/wintab/kis_tablet_support.cpp qtlockedfile/qtlockedfile_unix.cpp ) if(NOT APPLE) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/wintab/kis_tablet_support_x11.cpp input/wintab/qxcbconnection_xi2.cpp input/wintab/qxcbconnection.cpp input/wintab/kis_xi2_event_filter.cpp ) endif() endif() if(WIN32) #ki18n_wrap_ui( # input/wintab/kis_screen_size_choice_dialog.ui #) endif() ki18n_wrap_ui(kritaui_LIB_SRCS forms/wdgdlgpngimport.ui forms/wdgfullscreensettings.ui forms/wdgautogradient.ui forms/wdggeneralsettings.ui forms/wdgperformancesettings.ui forms/wdggenerators.ui forms/wdgcustompalette.ui forms/wdgbookmarkedconfigurationseditor.ui forms/wdgapplyprofile.ui forms/wdgcustompattern.ui forms/wdglayerproperties.ui forms/wdgcolorsettings.ui forms/wdgtabletsettings.ui forms/wdgcolorspaceselector.ui forms/wdgcolorspaceselectoradvanced.ui forms/wdgdisplaysettings.ui forms/kis_previewwidgetbase.ui forms/kis_matrix_widget.ui forms/wdgselectionoptions.ui forms/wdggeometryoptions.ui forms/wdgnewimage.ui forms/wdgimageproperties.ui forms/wdgmaskfromselection.ui forms/wdgmasksource.ui forms/wdgfilterdialog.ui forms/wdgmetadatamergestrategychooser.ui forms/wdgpaintoppresets.ui forms/wdgpaintopsettings.ui forms/wdgdlggeneratorlayer.ui forms/wdgdlgfilelayer.ui forms/wdgfilterselector.ui forms/wdgfilternodecreation.ui forms/wdgpaintactioneditor.ui forms/wdgmultipliersdoublesliderspinbox.ui forms/wdgnodequerypatheditor.ui forms/wdgpresetselectorstrip.ui forms/wdgdlgblacklistcleanup.ui forms/wdgrectangleconstraints.ui forms/wdgimportimagesequence.ui forms/KisDetailsPaneBase.ui forms/KisOpenPaneBase.ui dialogs/kis_delayed_save_dialog.ui input/config/kis_input_configuration_page.ui input/config/kis_edit_profiles_dialog.ui input/config/kis_input_configuration_page_item.ui input/config/kis_mouse_input_editor.ui input/config/kis_wheel_input_editor.ui input/config/kis_key_input_editor.ui layerstyles/wdgBevelAndEmboss.ui layerstyles/wdgblendingoptions.ui layerstyles/WdgColorOverlay.ui layerstyles/wdgContour.ui layerstyles/wdgdropshadow.ui layerstyles/WdgGradientOverlay.ui layerstyles/wdgInnerGlow.ui layerstyles/wdglayerstyles.ui layerstyles/WdgPatternOverlay.ui layerstyles/WdgSatin.ui layerstyles/WdgStroke.ui layerstyles/wdgstylesselector.ui layerstyles/wdgTexture.ui wdgsplash.ui input/wintab/kis_screen_size_choice_dialog.ui ) QT5_WRAP_CPP(kritaui_HEADERS_MOC KisNodePropertyAction_p.h) add_library(kritaui SHARED ${kritaui_HEADERS_MOC} ${kritaui_LIB_SRCS} ) generate_export_header(kritaui BASE_NAME kritaui) target_link_libraries(kritaui KF5::CoreAddons KF5::Completion KF5::I18n KF5::ItemViews Qt5::Network kritacolor kritaimage kritalibbrush kritawidgets kritawidgetutils ${PNG_LIBRARIES} ${EXIV2_LIBRARIES} ) if (HAVE_KIO) target_link_libraries(kritaui KF5::KIOCore) endif() if (NOT WIN32 AND NOT APPLE) target_link_libraries(kritaui ${X11_X11_LIB} ${X11_Xinput_LIB} ${XCB_LIBRARIES}) endif() if(APPLE) target_link_libraries(kritaui ${FOUNDATION_LIBRARY}) endif () target_link_libraries(kritaui ${OPENEXR_LIBRARIES}) # Add VSync disable workaround if(NOT WIN32 AND NOT APPLE) target_link_libraries(kritaui ${CMAKE_DL_LIBS} Qt5::X11Extras) endif() if(X11_FOUND) target_link_libraries(kritaui Qt5::X11Extras ${X11_LIBRARIES}) endif() target_link_libraries(kritaui LINK_INTERFACE_LIBRARIES kritaimage kritalibbrush kritapigment KF5::Completion KF5::I18n ${GL_INTERFACE_LIBRARIES}) target_include_directories(kritaui PUBLIC $ $ $ $ $ $ ) set_target_properties(kritaui PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritaui ${INSTALL_TARGETS_DEFAULT_ARGS}) if (APPLE) install(FILES osx.stylesheet DESTINATION ${DATA_INSTALL_DIR}/krita) endif () diff --git a/libs/ui/KisApplication.cpp b/libs/ui/KisApplication.cpp index dbc4b7ba98..34e0efcacb 100644 --- a/libs/ui/KisApplication.cpp +++ b/libs/ui/KisApplication.cpp @@ -1,726 +1,737 @@ -/* This file is part of the KDE project + /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2009 Thomas Zander Copyright (C) 2012 Boudewijn Rempt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisApplication.h" #include #ifdef Q_OS_WIN #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoGlobal.h" #include "KoConfig.h" #include #include #include #include "thememanager.h" #include "KisPrintJob.h" #include "KisDocument.h" #include "KisMainWindow.h" #include "KisAutoSaveRecoveryDialog.h" #include "KisPart.h" #include #include "kis_md5_generator.h" #include "kis_splash_screen.h" #include "kis_config.h" #include "flake/kis_shape_selection.h" #include #include #include #include #include #include #include #include "kisexiv2/kis_exiv2.h" #include "KisApplicationArguments.h" #include #include "kis_action_registry.h" #include #include #include #include "kis_image_barrier_locker.h" #include "opengl/kis_opengl.h" #include namespace { const QTime appStartTime(QTime::currentTime()); } class KisApplicationPrivate { public: KisApplicationPrivate() : splashScreen(0) {} - QWidget *splashScreen; + KisSplashScreen *splashScreen; }; class KisApplication::ResetStarting { public: - ResetStarting(QWidget *splash = 0) + ResetStarting(KisSplashScreen *splash = 0) : m_splash(splash) { } ~ResetStarting() { if (m_splash) { KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen"); bool hideSplash = cfg.readEntry("HideSplashAfterStartup", false); if (hideSplash) { m_splash->hide(); } else { - m_splash->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); + m_splash->setWindowFlags(Qt::Tool); QRect r(QPoint(), m_splash->size()); m_splash->move(QApplication::desktop()->availableGeometry().center() - r.center()); m_splash->setWindowTitle(qAppName()); + m_splash->setParent(qApp->activeWindow()); Q_FOREACH (QObject *o, m_splash->children()) { QWidget *w = qobject_cast(o); if (w && w->isHidden()) { w->setVisible(true); } } m_splash->show(); } } } - QWidget *m_splash; + KisSplashScreen *m_splash; }; KisApplication::KisApplication(const QString &key, int &argc, char **argv) : QtSingleApplication(key, argc, argv) , d(new KisApplicationPrivate) + , m_autosaveDialog(0) + , m_mainWindow(0) + , m_batchRun(false) { QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath()); setApplicationDisplayName("Krita"); setApplicationName("krita"); // Note: Qt docs suggest we set this, but if we do, we get resource paths of the form of krita/krita, which is weird. // setOrganizationName("krita"); setOrganizationDomain("krita.org"); QString version = KritaVersionWrapper::versionString(true); setApplicationVersion(version); setWindowIcon(KisIconUtils::loadIcon("calligrakrita")); - QStringList styles = QStringList() << "Fusion" << "Plastique"; - if (!styles.contains(style()->objectName())) { + QStringList styles = QStringList() << "fusion" << "plastique"; + if (!styles.contains(style()->objectName().toLower())) { Q_FOREACH (const QString & style, styles) { if (!setStyle(style)) { qDebug() << "No" << style << "available."; } else { qDebug() << "Set style" << style; break; } } } KisOpenGL::initialize(); qDebug() << "krita has opengl" << KisOpenGL::hasOpenGL(); } #if defined(Q_OS_WIN) && defined(ENV32BIT) typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); LPFN_ISWOW64PROCESS fnIsWow64Process; BOOL isWow64() { BOOL bIsWow64 = FALSE; //IsWow64Process is not available on all supported versions of Windows. //Use GetModuleHandle to get a handle to the DLL that contains the function //and GetProcAddress to get a pointer to the function if available. fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress( GetModuleHandle(TEXT("kernel32")),"IsWow64Process"); if(0 != fnIsWow64Process) { if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64)) { //handle error } } return bIsWow64; } #endif void initializeGlobals(const KisApplicationArguments &args) { int dpiX = args.dpiX(); int dpiY = args.dpiY(); if (dpiX > 0 && dpiY > 0) { KoDpi::setDPI(dpiX, dpiY); } } void addResourceTypes() { // All Krita's resource types KoResourcePaths::addResourceType("kis_pics", "data", "/pics/"); KoResourcePaths::addResourceType("kis_images", "data", "/images/"); KoResourcePaths::addResourceType("icc_profiles", "data", "/profiles/"); KoResourcePaths::addResourceType("metadata_schema", "data", "/metadata/schemas/"); KoResourcePaths::addResourceType("kis_brushes", "data", "/brushes/"); KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/"); KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/"); KoResourcePaths::addResourceType("gmic_definitions", "data", "/gmic/"); KoResourcePaths::addResourceType("kis_resourcebundles", "data", "/bundles/"); KoResourcePaths::addResourceType("kis_defaultpresets", "data", "/defaultpresets/"); KoResourcePaths::addResourceType("kis_paintoppresets", "data", "/paintoppresets/"); KoResourcePaths::addResourceType("kis_workspaces", "data", "/workspaces/"); KoResourcePaths::addResourceType("psd_layer_style_collections", "data", "/asl"); KoResourcePaths::addResourceType("ko_patterns", "data", "/patterns/", true); KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/"); KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/", true); KoResourcePaths::addResourceType("ko_palettes", "data", "/palettes/", true); KoResourcePaths::addResourceType("kis_shortcuts", "data", "/shortcuts/"); KoResourcePaths::addResourceType("kis_actions", "data", "/actions"); KoResourcePaths::addResourceType("icc_profiles", "data", "/color/icc"); KoResourcePaths::addResourceType("ko_effects", "data", "/effects/"); KoResourcePaths::addResourceType("tags", "data", "/tags/"); // // Extra directories to look for create resources. (Does anyone actually use that anymore?) // KoResourcePaths::addResourceDir("ko_gradients", "/usr/share/create/gradients/gimp"); // KoResourcePaths::addResourceDir("ko_gradients", QDir::homePath() + QString("/.create/gradients/gimp")); // KoResourcePaths::addResourceDir("ko_patterns", "/usr/share/create/patterns/gimp"); // KoResourcePaths::addResourceDir("ko_patterns", QDir::homePath() + QString("/.create/patterns/gimp")); // KoResourcePaths::addResourceDir("kis_brushes", "/usr/share/create/brushes/gimp"); // KoResourcePaths::addResourceDir("kis_brushes", QDir::homePath() + QString("/.create/brushes/gimp")); // KoResourcePaths::addResourceDir("ko_palettes", "/usr/share/create/swatches"); // KoResourcePaths::addResourceDir("ko_palettes", QDir::homePath() + QString("/.create/swatches")); // Make directories for all resources we can save, and tags QDir d; d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tags/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/asl/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/bundles/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/gradients/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/paintoppresets/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/palettes/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/patterns/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/taskset/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/workspaces/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/input/"); } void KisApplication::loadResources() { setSplashScreenLoadingText(i18n("Loading Gradients...")); KoResourceServerProvider::instance()->gradientServer(true); // Load base resources setSplashScreenLoadingText(i18n("Loading Patterns...")); KoResourceServerProvider::instance()->patternServer(true); setSplashScreenLoadingText(i18n("Loading Palettes...")); KoResourceServerProvider::instance()->paletteServer(false); setSplashScreenLoadingText(i18n("Loading Brushes...")); KisBrushServer::instance()->brushServer(true); // load paintop presets setSplashScreenLoadingText(i18n("Loading Paint Operations...")); KisResourceServerProvider::instance()->paintOpPresetServer(true); setSplashScreenLoadingText(i18n("Loading Resource Bundles...")); KisResourceServerProvider::instance()->resourceBundleServer(); } void KisApplication::loadPlugins() { KoShapeRegistry* r = KoShapeRegistry::instance(); r->add(new KisShapeSelectionFactory()); KisActionRegistry::instance(); KisFilterRegistry::instance(); KisGeneratorRegistry::instance(); KisPaintOpRegistry::instance(); KoColorSpaceRegistry::instance(); // Load the krita-specific tools setSplashScreenLoadingText(i18n("Loading Plugins for Krita/Tool...")); KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Tool"), QString::fromLatin1("[X-Krita-Version] == 28")); // Load dockers setSplashScreenLoadingText(i18n("Loading Plugins for Krita/Dock...")); KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Dock"), QString::fromLatin1("[X-Krita-Version] == 28")); // XXX_EXIV: make the exiv io backends real plugins setSplashScreenLoadingText(i18n("Loading Plugins Exiv/IO...")); KisExiv2::initialize(); } bool KisApplication::start(const KisApplicationArguments &args) { #if defined(Q_OS_WIN) || defined (Q_OS_MAC) #ifdef ENV32BIT KisConfig cfg; if (isWow64() && !cfg.readEntry("WarnedAbout32Bits", false)) { QMessageBox::information(0, i18nc("@title:window", "Krita: Warning"), i18n("You are running a 32 bits build on a 64 bits Windows.\n" "This is not recommended.\n" "Please download and install the x64 build instead.")); cfg.writeEntry("WarnedAbout32Bits", true); } #endif #endif setSplashScreenLoadingText(i18n("Initializing Globals")); initializeGlobals(args); const bool doTemplate = args.doTemplate(); const bool print = args.print(); const bool exportAs = args.exportAs(); const bool exportAsPdf = args.exportAsPdf(); const QString exportFileName = args.exportFileName(); - const QString profileFileName = args.profileFileName(); - const bool batchRun = (print || exportAs || exportAsPdf); + m_batchRun = (print || exportAs || exportAsPdf || !exportFileName.isEmpty()); // print & exportAsPdf do user interaction ATM const bool needsMainWindow = !exportAs; // only show the mainWindow when no command-line mode option is passed // TODO: fix print & exportAsPdf to work without mainwindow shown const bool showmainWindow = !exportAs; // would be !batchRun; - const bool showSplashScreen = !batchRun && qgetenv("NOSPLASH").isEmpty(); + const bool showSplashScreen = !m_batchRun && qgetenv("NOSPLASH").isEmpty() && qgetenv("XDG_CURRENT_DESKTOP") != "GNOME"; if (showSplashScreen) { d->splashScreen->show(); d->splashScreen->repaint(); processEvents(); } KoHashGeneratorProvider::instance()->setGenerator("MD5", new KisMD5Generator()); // Initialize all Krita directories etc. KoGlobal::initialize(); KConfigGroup group(KSharedConfig::openConfig(), "theme"); Digikam::ThemeManager themeManager; themeManager.setCurrentTheme(group.readEntry("Theme", "Krita dark")); ResetStarting resetStarting(d->splashScreen); // remove the splash when done Q_UNUSED(resetStarting); // Make sure we can save resources and tags setSplashScreenLoadingText(i18n("Adding resource types")); addResourceTypes(); // Load all resources and tags before the plugins do that loadResources(); // Load the plugins loadPlugins(); - KisMainWindow *mainWindow = 0; - if (needsMainWindow) { // show a mainWindow asap, if we want that setSplashScreenLoadingText(i18n("Loading Main Window...")); - mainWindow = KisPart::instance()->createMainWindow(); + m_mainWindow = KisPart::instance()->createMainWindow(); if (showmainWindow) { - QTimer::singleShot(1, mainWindow, SLOT(show())); + QTimer::singleShot(1, m_mainWindow, SLOT(show())); } } short int numberOfOpenDocuments = 0; // number of documents open // Check for autosave files that can be restored, if we're not running a batchrun (test, print, export to pdf) - if (!batchRun) { + if (!m_batchRun) { checkAutosaveFiles(); } setSplashScreenLoadingText(""); // done loading, so clear out label // Get the command line arguments which we have to parse int argsCount = args.filenames().count(); if (argsCount > 0) { - QTextStream profileoutput; - profileoutput.setCodec("UTF-8"); - QFile profileFile(profileFileName); - if (!profileFileName.isEmpty() - && profileFile.open(QFile::WriteOnly | QFile::Truncate)) { - profileoutput.setDevice(&profileFile); - } - // Loop through arguments short int nPrinted = 0; for (int argNumber = 0; argNumber < argsCount; argNumber++) { QString fileName = args.filenames().at(argNumber); // are we just trying to open a template? if (doTemplate) { // called in mix with batch options? ignore and silently skip - if (batchRun) { + if (m_batchRun) { continue; } - if (createNewDocFromTemplate(fileName, mainWindow)) { + if (createNewDocFromTemplate(fileName, m_mainWindow)) { ++numberOfOpenDocuments; } // now try to load } else { if (exportAs) { QString outputMimetype = KisMimeDatabase::mimeTypeForFile(exportFileName); if (outputMimetype == "application/octetstream") { dbgKrita << i18n("Mimetype not found, try using the -mimetype option") << endl; return 1; } KisDocument *doc = KisPart::instance()->createDocument(); + doc->setFileBatchMode(m_batchRun); doc->openUrl(QUrl::fromLocalFile(fileName)); qApp->processEvents(); // For vector layers to be updated KisImageBarrierLocker locker(doc->image()); KisImportExportFilter::ConversionStatus status = KisImportExportFilter::OK; KisImportExportManager manager(doc); manager.setBatchMode(true); QByteArray mime(outputMimetype.toLatin1()); status = manager.exportDocument(exportFileName, mime); if (status != KisImportExportFilter::OK) { dbgKrita << "Could not export " << fileName << "to" << exportFileName << ":" << (int)status; } nPrinted++; QTimer::singleShot(0, this, SLOT(quit())); } - else if (mainWindow) { + else if (m_mainWindow) { KisDocument *doc = KisPart::instance()->createDocument(); - if (mainWindow->openDocumentInternal(QUrl::fromLocalFile(fileName), doc)) { + doc->setFileBatchMode(m_batchRun); + if (m_mainWindow->openDocumentInternal(QUrl::fromLocalFile(fileName), doc)) { if (print) { - mainWindow->slotFilePrint(); + m_mainWindow->slotFilePrint(); nPrinted++; // TODO: trigger closing of app once printing is done } else if (exportAsPdf) { - KisPrintJob *job = mainWindow->exportToPdf(exportFileName); + KisPrintJob *job = m_mainWindow->exportToPdf(exportFileName); if (job) - connect (job, SIGNAL(destroyed(QObject*)), mainWindow, + connect (job, SIGNAL(destroyed(QObject*)), m_mainWindow, SLOT(slotFileQuit()), Qt::QueuedConnection); nPrinted++; } else { // Normal case, success numberOfOpenDocuments++; } } else { // .... if failed // delete doc; done by openDocument } } } } - if (batchRun) { + if (m_batchRun) { return nPrinted > 0; } } // not calling this before since the program will quit there. return true; } KisApplication::~KisApplication() { delete d; } void KisApplication::setSplashScreen(QWidget *splashScreen) { - d->splashScreen = splashScreen; + d->splashScreen = qobject_cast(splashScreen); } void KisApplication::setSplashScreenLoadingText(QString textToLoad) { - static_cast(d->splashScreen)->loadingLabel->setText(textToLoad); - static_cast(d->splashScreen)->repaint(); + d->splashScreen->loadingLabel->setText(textToLoad); + d->splashScreen->repaint(); +} + +void KisApplication::hideSplashScreen() +{ + if (d->splashScreen) { + // hide the splashscreen to see the dialog + d->splashScreen->hide(); + } } bool KisApplication::notify(QObject *receiver, QEvent *event) { try { return QApplication::notify(receiver, event); } catch (std::exception &e) { qWarning("Error %s sending event %i to object %s", e.what(), event->type(), qPrintable(receiver->objectName())); } catch (...) { qWarning("Error sending event %i to object %s", event->type(), qPrintable(receiver->objectName())); } return false; } void KisApplication::remoteArguments(QByteArray message, QObject *socket) { Q_UNUSED(socket); // check if we have any mainwindow KisMainWindow *mw = qobject_cast(qApp->activeWindow()); if (!mw) { mw = KisPart::instance()->mainWindows().first(); } if (!mw) { return; } KisApplicationArguments args = KisApplicationArguments::deserialize(message); const bool doTemplate = args.doTemplate(); const int argsCount = args.filenames().count(); if (argsCount > 0) { // Loop through arguments for (int argNumber = 0; argNumber < argsCount; ++argNumber) { QString filename = args.filenames().at(argNumber); // are we just trying to open a template? if (doTemplate) { createNewDocFromTemplate(filename, mw); } else if (QFile(filename).exists()) { KisDocument *doc = KisPart::instance()->createDocument(); + doc->setFileBatchMode(m_batchRun); mw->openDocumentInternal(QUrl::fromLocalFile(filename), doc); } } } } void KisApplication::fileOpenRequested(const QString &url) { KisMainWindow *mainWindow = KisPart::instance()->mainWindows().first(); if (mainWindow) { KisDocument *doc = KisPart::instance()->createDocument(); + doc->setFileBatchMode(m_batchRun); mainWindow->openDocumentInternal(QUrl::fromLocalFile(url), doc); } } void KisApplication::checkAutosaveFiles() { + if (m_batchRun) return; + // Check for autosave files from a previous run. There can be several, and // we want to offer a restore for every one. Including a nice thumbnail! QStringList filters; filters << QString(".krita-*-*-autosave.kra"); #ifdef Q_OS_WIN QDir dir = QDir::temp(); #else QDir dir = QDir::home(); #endif // all autosave files for our application - autoSaveFiles = dir.entryList(filters, QDir::Files | QDir::Hidden); + m_autosaveFiles = dir.entryList(filters, QDir::Files | QDir::Hidden); // Allow the user to make their selection - if (autoSaveFiles.size() > 0) { + if (m_autosaveFiles.size() > 0) { if (d->splashScreen) { // hide the splashscreen to see the dialog d->splashScreen->hide(); } - dlg = new KisAutoSaveRecoveryDialog(autoSaveFiles, activeWindow()); - connect(dlg, SIGNAL(finished(int)), this, SLOT(onAutoSaveFinished(int))); - dlg->exec(); + m_autosaveDialog = new KisAutoSaveRecoveryDialog(m_autosaveFiles, activeWindow()); + connect(m_autosaveDialog, SIGNAL(finished(int)), this, SLOT(onAutoSaveFinished(int))); + m_autosaveDialog->exec(); } } -void KisApplication::onAutoSaveFinished(int result) { +void KisApplication::onAutoSaveFinished(int result) +{ + if (m_batchRun) return; #ifdef Q_OS_WIN QDir dir = QDir::temp(); #else QDir dir = QDir::home(); #endif if (result == QDialog::Accepted) { - QStringList filesToRecover = dlg->recoverableFiles(); - Q_FOREACH (const QString &autosaveFile, autoSaveFiles) { + QStringList filesToRecover = m_autosaveDialog->recoverableFiles(); + Q_FOREACH (const QString &autosaveFile, m_autosaveFiles) { if (!filesToRecover.contains(autosaveFile)) { QFile::remove(dir.absolutePath() + "/" + autosaveFile); } } - autoSaveFiles = filesToRecover; + m_autosaveFiles = filesToRecover; } else { - autoSaveFiles.clear(); + m_autosaveFiles.clear(); } QList autosaveUrls; - if (autoSaveFiles.size() > 0) { + if (m_autosaveFiles.size() > 0) { - Q_FOREACH (const QString &autoSaveFile, autoSaveFiles) { + Q_FOREACH (const QString &autoSaveFile, m_autosaveFiles) { const QUrl url = QUrl::fromLocalFile(dir.absolutePath() + QLatin1Char('/') + autoSaveFile); autosaveUrls << url; } } - if (mainWindow) { + if (m_mainWindow) { Q_FOREACH (const QUrl &url, autosaveUrls) { KisDocument *doc = KisPart::instance()->createDocument(); - mainWindow->openDocumentInternal(url, doc); + doc->setFileBatchMode(m_batchRun); + m_mainWindow->openDocumentInternal(url, doc); } } } bool KisApplication::createNewDocFromTemplate(const QString &fileName, KisMainWindow *mainWindow) { QString templatePath; const QUrl templateUrl = QUrl::fromLocalFile(fileName); if (QFile::exists(fileName)) { templatePath = templateUrl.toLocalFile(); dbgUI << "using full path..."; } else { QString desktopName(fileName); const QString templatesResourcePath = QStringLiteral("templates/"); QStringList paths = KoResourcePaths::findAllResources("data", templatesResourcePath + "*/" + desktopName); if (paths.isEmpty()) { paths = KoResourcePaths::findAllResources("data", templatesResourcePath + desktopName); } if (paths.isEmpty()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("No template found for: %1", desktopName)); } else if (paths.count() > 1) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Too many templates found for: %1", desktopName)); } else { templatePath = paths.at(0); } } if (!templatePath.isEmpty()) { QUrl templateBase; templateBase.setPath(templatePath); KDesktopFile templateInfo(templatePath); QString templateName = templateInfo.readUrl(); QUrl templateURL; templateURL.setPath(templateBase.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path() + '/' + templateName); KisDocument *doc = KisPart::instance()->createDocument(); + doc->setFileBatchMode(m_batchRun); if (mainWindow->openDocumentInternal(templateURL, doc)) { doc->resetURL(); doc->setEmpty(); doc->setTitleModified(); dbgUI << "Template loaded..."; return true; } else { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Template %1 failed to load.", templateURL.toDisplayString())); } } return false; } void KisApplication::clearConfig() { KIS_ASSERT_RECOVER_RETURN(qApp->thread() == QThread::currentThread()); KSharedConfigPtr config = KSharedConfig::openConfig(); // find user settings file bool createDir = false; QString kritarcPath = KoResourcePaths::locateLocal("config", "kritarc", createDir); QFile configFile(kritarcPath); if (configFile.exists()) { // clear file if (configFile.open(QFile::WriteOnly)) { configFile.close(); } else { QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Failed to clear %1\n\n" "Please make sure no other program is using the file and try again.", kritarcPath), QMessageBox::Ok, QMessageBox::Ok); } } // reload from disk; with the user file settings cleared, // this should load any default configuration files shipping with the program config->reparseConfiguration(); config->sync(); } void KisApplication::askClearConfig() { Qt::KeyboardModifiers mods = QApplication::queryKeyboardModifiers(); bool askClearConfig = (mods & Qt::ControlModifier) && (mods & Qt::ShiftModifier) && (mods & Qt::AltModifier); if (askClearConfig) { bool ok = QMessageBox::question(0, i18nc("@title:window", "Krita"), i18n("Do you want to clear the settings file?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes; if (ok) { clearConfig(); } } } diff --git a/libs/ui/KisApplication.h b/libs/ui/KisApplication.h index 3454b2fb59..f03afe89a9 100644 --- a/libs/ui/KisApplication.h +++ b/libs/ui/KisApplication.h @@ -1,122 +1,123 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_APPLICATION_H #define KIS_APPLICATION_H +#include #include #include "kritaui_export.h" #include class KisMainWindow; class KisApplicationPrivate; class QWidget; class KisApplicationArguments; class KisAutoSaveRecoveryDialog; #include /** * @brief Base class for the %Krita app * * This class handles arguments given on the command line and * shows a generic about dialog for the Krita app. * * In addition it adds the standard directories where Krita * can find its images etc. * * If the last mainwindow becomes closed, KisApplication automatically * calls QApplication::quit. */ class KRITAUI_EXPORT KisApplication : public QtSingleApplication { Q_OBJECT public: /** * Creates an application object, adds some standard directories and * initializes kimgio. */ explicit KisApplication(const QString &key, int &argc, char **argv); /** * Destructor. */ virtual ~KisApplication(); /** * Call this to start the application. * * Parses command line arguments and creates the initial main windowss and docs * from them (or an empty doc if no cmd-line argument is specified ). * * You must call this method directly before calling QApplication::exec. * * It is valid behaviour not to call this method at all. In this case you * have to process your command line parameters by yourself. */ virtual bool start(const KisApplicationArguments &args); /** * Checks if user is holding ctrl+alt+shift keys and asks if the settings file should be cleared. * * Typically called during startup before reading the config. */ void askClearConfig(); /** * Tell KisApplication to show this splashscreen when you call start(); * when start returns, the splashscreen is hidden. Use KSplashScreen - * to have the splash show correctly on Xinerama displays. + * to have the splash show correctly on Xinerama displays. */ void setSplashScreen(QWidget *splash); void setSplashScreenLoadingText(QString); - - + void hideSplashScreen(); /// Overridden to handle exceptions from event handlers. bool notify(QObject *receiver, QEvent *event); public Q_SLOTS: void remoteArguments(QByteArray message, QObject*socket); void fileOpenRequested(const QString & url); void onAutoSaveFinished(int result); private: /// @return the number of autosavefiles opened void checkAutosaveFiles(); - bool createNewDocFromTemplate(const QString &fileName, KisMainWindow *mainWindow); + bool createNewDocFromTemplate(const QString &fileName, KisMainWindow *m_mainWindow); void clearConfig(); void loadResources(); void loadPlugins(); private: KisApplicationPrivate * const d; class ResetStarting; friend class ResetStarting; - KisAutoSaveRecoveryDialog *dlg; - QStringList autoSaveFiles; - KisMainWindow *mainWindow; + KisAutoSaveRecoveryDialog *m_autosaveDialog; + QStringList m_autosaveFiles; + QPointer m_mainWindow; // The first mainwindow we create on startup + bool m_batchRun; }; #endif diff --git a/libs/ui/KisApplicationArguments.cpp b/libs/ui/KisApplicationArguments.cpp index bbfed53dad..6b6f462f16 100644 --- a/libs/ui/KisApplicationArguments.cpp +++ b/libs/ui/KisApplicationArguments.cpp @@ -1,234 +1,222 @@ /* * Copyright (c) 2015 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; 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 "KisApplicationArguments.h" #include #include #include #include #include #include #include #include #include #include struct Q_DECL_HIDDEN KisApplicationArguments::Private { Private() : dpiX(0) , dpiY(0) , doTemplate(false) , print(false) , exportAs(false) , exportAsPdf(false) { } QStringList filenames; int dpiX; int dpiY; bool doTemplate; bool print; bool exportAs; bool exportAsPdf; QString exportFileName; - QString profileFileName; - }; KisApplicationArguments::KisApplicationArguments(const QApplication &app) : d(new Private) { QCommandLineParser parser; parser.addVersionOption(); parser.addHelpOption(); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("print"), i18n("Only print and exit"))); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("template"), i18n("Open a new document with a template"))); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("dpi"), i18n("Override display DPI"), QLatin1String("dpiX,dpiY"))); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("export-pdf"), i18n("Only export to PDF and exit"))); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("export"), i18n("Export to the given filename and exit"))); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("export-filename"), i18n("Filename for export/export-pdf"), QLatin1String("filename"))); parser.addPositionalArgument(QLatin1String("[file(s)]"), i18n("File(s) or URL(s) to open")); parser.process(app); const QDir currentDir = QDir::current(); Q_FOREACH (const QString &filename, parser.positionalArguments()) { d->filenames << currentDir.absoluteFilePath(filename); } QString dpiValues = parser.value("dpi"); if (!dpiValues.isEmpty()) { int sep = dpiValues.indexOf(QRegExp("[x, ]")); bool ok = true; if (sep != -1) { d->dpiY = dpiValues.mid(sep + 1).toInt(&ok); dpiValues.truncate(sep); } if (ok) { d->dpiX = dpiValues.toInt(&ok); if (ok) { if (!d->dpiY) d->dpiY = d->dpiX; } } } d->doTemplate = parser.isSet("template"); d->print = parser.isSet("print"); d->exportAs = parser.isSet("export"); d->exportAsPdf = parser.isSet("export-pdf"); d->exportFileName = parser.value("export-filename"); - d->profileFileName = parser.value("profile-filename"); } KisApplicationArguments::KisApplicationArguments(const KisApplicationArguments &rhs) : d(new Private) { d->filenames = rhs.filenames(); d->dpiX = rhs.dpiX(); d->dpiY = rhs.dpiY(); d->doTemplate = rhs.doTemplate(); d->print = rhs.print(); d->exportAs = rhs.exportAs(); d->exportAsPdf = rhs.exportAsPdf(); d->exportFileName = rhs.exportFileName(); - d->profileFileName = rhs.profileFileName(); } KisApplicationArguments::~KisApplicationArguments() { } void KisApplicationArguments::operator=(const KisApplicationArguments &rhs) { d->filenames = rhs.filenames(); d->dpiX = rhs.dpiX(); d->dpiY = rhs.dpiY(); d->doTemplate = rhs.doTemplate(); d->print = rhs.print(); d->exportAs = rhs.exportAs(); d->exportAsPdf = rhs.exportAsPdf(); d->exportFileName = rhs.exportFileName(); - d->profileFileName = rhs.profileFileName(); } QByteArray KisApplicationArguments::serialize() { QByteArray ba; QBuffer buf(&ba); buf.open(QIODevice::WriteOnly); QDataStream ds(&buf); ds.setVersion(QDataStream::Qt_5_0); ds << d->filenames.count(); Q_FOREACH (const QString &filename, d->filenames) { ds << filename; } ds << d->dpiX; ds << d->dpiY; ds << d->doTemplate; ds << d->print; ds << d->exportAs; ds << d->exportAsPdf; ds << d->exportFileName; - ds << d->profileFileName; buf.close(); return ba; } KisApplicationArguments KisApplicationArguments::deserialize(QByteArray &serialized) { KisApplicationArguments args; QBuffer buf(&serialized); buf.open(QIODevice::ReadOnly); QDataStream ds(&buf); ds.setVersion(QDataStream::Qt_5_0); int count; ds >> count; for(int i = 0; i < count; ++i) { QString s; ds >> s; args.d->filenames << s; } ds >> args.d->dpiX; ds >> args.d->dpiY; ds >> args.d->doTemplate; ds >> args.d->print; ds >> args.d->exportAs; ds >> args.d->exportAsPdf; ds >> args.d->exportFileName; - ds >> args.d->profileFileName; buf.close(); return args; } QStringList KisApplicationArguments::filenames() const { return d->filenames; } int KisApplicationArguments::dpiX() const { return d->dpiX; } int KisApplicationArguments::dpiY() const { return d->dpiY; } bool KisApplicationArguments::doTemplate() const { return d->doTemplate; } bool KisApplicationArguments::print() const { return d->print; } bool KisApplicationArguments::exportAs() const { return d->exportAs; } bool KisApplicationArguments::exportAsPdf() const { return d->exportAsPdf; } QString KisApplicationArguments::exportFileName() const { return d->exportFileName; } -QString KisApplicationArguments::profileFileName() const -{ - return d->profileFileName; -} - KisApplicationArguments::KisApplicationArguments() : d(new Private) { } diff --git a/libs/ui/KisApplicationArguments.h b/libs/ui/KisApplicationArguments.h index 161f41f241..753be67f5b 100644 --- a/libs/ui/KisApplicationArguments.h +++ b/libs/ui/KisApplicationArguments.h @@ -1,61 +1,60 @@ /* * Copyright (c) 2015 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; 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 KISAPPLICATIONARGUMENTS_H #define KISAPPLICATIONARGUMENTS_H #include class QApplication; class QByteArray; class QStringList; #include "kritaui_export.h" class KRITAUI_EXPORT KisApplicationArguments { public: KisApplicationArguments(const QApplication &app); KisApplicationArguments(const KisApplicationArguments &rhs); ~KisApplicationArguments(); void operator=(const KisApplicationArguments& rhs); QByteArray serialize(); static KisApplicationArguments deserialize(QByteArray &serialized); QStringList filenames() const; int dpiX() const; int dpiY() const; bool doTemplate() const; bool print() const; bool exportAs() const; bool exportAsPdf() const; QString exportFileName() const; - QString profileFileName() const; private: KisApplicationArguments(); struct Private; const QScopedPointer d; }; #endif // KISAPPLICATIONARGUMENTS_H diff --git a/libs/ui/KisDocument.cpp b/libs/ui/KisDocument.cpp index 1e29e6b2e8..a567dc6ae3 100644 --- a/libs/ui/KisDocument.cpp +++ b/libs/ui/KisDocument.cpp @@ -1,2456 +1,2451 @@ /* This file is part of the Krita project * * Copyright (C) 2014 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisMainWindow.h" // XXX: remove #include // XXX: remove #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Krita Image #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Local #include "KisViewManager.h" #include "kis_clipboard.h" #include "widgets/kis_custom_image_widget.h" #include "canvas/kis_canvas2.h" #include "flake/kis_shape_controller.h" #include "kra/kis_kra_loader.h" #include "kra/kis_kra_saver.h" #include "kis_statusbar.h" #include "widgets/kis_progress_widget.h" #include "kis_canvas_resource_provider.h" #include "kis_resource_server_provider.h" #include "kis_node_manager.h" #include "KisPart.h" #include "KisApplication.h" #include "KisDocument.h" #include "KisImportExportManager.h" #include "KisPart.h" #include "KisView.h" #include "kis_async_action_feedback.h" #include "kis_grid_config.h" #include "kis_guides_config.h" #include "kis_image_barrier_lock_adapter.h" #include static const char CURRENT_DTD_VERSION[] = "2.0"; // Define the protocol used here for embedded documents' URL // This used to "store" but QUrl didn't like it, // so let's simply make it "tar" ! #define STORE_PROTOCOL "tar" // The internal path is a hack to make QUrl happy and for document children #define INTERNAL_PROTOCOL "intern" #define INTERNAL_PREFIX "intern:/" // Warning, keep it sync in koStore.cc #include using namespace std; /********************************************************** * * KisDocument * **********************************************************/ namespace { class DocumentProgressProxy : public KoProgressProxy { public: KisMainWindow *m_mainWindow; DocumentProgressProxy(KisMainWindow *mainWindow) : m_mainWindow(mainWindow) { } ~DocumentProgressProxy() { // signal that the job is done setValue(-1); } int maximum() const { return 100; } void setValue(int value) { if (m_mainWindow) { m_mainWindow->slotProgress(value); } } void setRange(int /*minimum*/, int /*maximum*/) { } void setFormat(const QString &/*format*/) { } }; } //static QString KisDocument::newObjectName() { static int s_docIFNumber = 0; QString name; name.setNum(s_docIFNumber++); name.prepend("document_"); return name; } class UndoStack : public KUndo2Stack { public: UndoStack(KisDocument *doc) : m_doc(doc) { } void setIndex(int idx) { KisImageWSP image = this->image(); image->requestStrokeCancellation(); if(image->tryBarrierLock()) { KUndo2Stack::setIndex(idx); image->unlock(); } } void notifySetIndexChangedOneCommand() { KisImageWSP image = this->image(); image->unlock(); image->barrierLock(); } void undo() { KisImageWSP image = this->image(); image->requestUndoDuringStroke(); if(image->tryBarrierLock()) { KUndo2Stack::undo(); image->unlock(); } } void redo() { KisImageWSP image = this->image(); if(image->tryBarrierLock()) { KUndo2Stack::redo(); image->unlock(); } } private: KisImageWSP image() { KisImageWSP currentImage = m_doc->image(); Q_ASSERT(currentImage); return currentImage; } private: KisDocument *m_doc; }; class Q_DECL_HIDDEN KisDocument::Private { public: Private(KisDocument *document) : document(document), // XXX: the part should _not_ be modified from the document docInfo(0), progressUpdater(0), progressProxy(0), filterManager(0), specialOutputFlag(0), // default is native format isImporting(false), isExporting(false), password(QString()), modifiedAfterAutosave(false), isAutosaving(false), - shouldCheckAutoSaveFile(true), autoErrorHandlingEnabled(true), backupFile(true), backupPath(QString()), doNotSaveExtDoc(false), storeInternal(false), isLoading(false), undoStack(0), m_saveOk(false), m_waitForSave(false), m_duringSaveAs(false), m_bTemp(false), m_bAutoDetectedMime(false), modified(false), readwrite(true), disregardAutosaveFailure(false), nserver(0), macroNestDepth(0), imageIdleWatcher(2000 /*ms*/), kraLoader(0), suppressProgress(false), fileProgressProxy(0) { if (QLocale().measurementSystem() == QLocale::ImperialSystem) { unit = KoUnit::Inch; } else { unit = KoUnit::Centimeter; } } ~Private() { // Don't delete m_d->shapeController because it's in a QObject hierarchy. delete nserver; } KisDocument *document; KoDocumentInfo *docInfo; KoProgressUpdater *progressUpdater; KoProgressProxy *progressProxy; KoUnit unit; KisImportExportManager *filterManager; // The filter-manager to use when loading/saving [for the options] QByteArray mimeType; // The actual mimetype of the document QByteArray outputMimeType; // The mimetype to use when saving bool confirmNonNativeSave [2] = {true, true}; // used to pop up a dialog when saving for the // first time if the file is in a foreign format // (Save/Save As, Export) int specialOutputFlag; // See KoFileDialog in koMainWindow.cc bool isImporting; bool isExporting; // File --> Import/Export vs File --> Open/Save QString password; // The password used to encrypt an encrypted document QTimer autoSaveTimer; QString lastErrorMessage; // see openFile() int autoSaveDelay; // in seconds, 0 to disable. bool modifiedAfterAutosave; bool isAutosaving; - bool shouldCheckAutoSaveFile; // usually true bool autoErrorHandlingEnabled; // usually true bool backupFile; QString backupPath; bool doNotSaveExtDoc; // makes it possible to save only internally stored child documents bool storeInternal; // Store this doc internally even if url is external bool isLoading; // True while loading (openUrl is async) KUndo2Stack *undoStack; KisGuidesConfig guidesConfig; bool isEmpty; KoPageLayout pageLayout; QUrl m_originalURL; // for saveAs QString m_originalFilePath; // for saveAs bool m_saveOk : 1; bool m_waitForSave : 1; bool m_duringSaveAs : 1; bool m_bTemp: 1; // If @p true, @p m_file is a temporary file that needs to be deleted later. bool m_bAutoDetectedMime : 1; // whether the mimetype in the arguments was detected by the part itself QUrl m_url; // Remote (or local) url - the one displayed to the user. QString m_file; // Local file - the only one the part implementation should deal with. QEventLoop m_eventLoop; QMutex savingMutex; bool modified; bool readwrite; QDateTime firstMod; QDateTime lastMod; bool disregardAutosaveFailure; KisNameServer *nserver; qint32 macroNestDepth; KisImageSP image; KisNodeSP preActivatedNode; KisShapeController* shapeController; KoShapeController* koShapeController; KisIdleWatcher imageIdleWatcher; QScopedPointer imageIdleConnection; KisKraLoader* kraLoader; KisKraSaver* kraSaver; bool suppressProgress; KoProgressProxy* fileProgressProxy; QList assistants; KisGridConfig gridConfig; bool openFile() { document->setFileProgressProxy(); document->setUrl(m_url); bool ok = document->openFile(); document->clearFileProgressProxy(); return ok; } bool openLocalFile() { m_bTemp = false; // set the mimetype only if it was not already set (for example, by the host application) if (mimeType.isEmpty()) { // get the mimetype of the file // using findByUrl() to avoid another string -> url conversion QString mime = KisMimeDatabase::mimeTypeForFile(m_url.toLocalFile()); mimeType = mime.toLocal8Bit(); m_bAutoDetectedMime = true; } const bool ret = openFile(); if (ret) { emit document->completed(); } else { emit document->canceled(QString()); } return ret; } // Set m_file correctly for m_url void prepareSaving() { // Local file if ( m_url.isLocalFile() ) { if ( m_bTemp ) // get rid of a possible temp file first { // (happens if previous url was remote) QFile::remove( m_file ); m_bTemp = false; } m_file = m_url.toLocalFile(); } } void setImageAndInitIdleWatcher(KisImageSP _image) { image = _image; imageIdleWatcher.setTrackedImage(image); if (image) { imageIdleConnection.reset( new KisSignalAutoConnection( &imageIdleWatcher, SIGNAL(startedIdleMode()), image.data(), SLOT(explicitRegenerateLevelOfDetail()))); } } class SafeSavingLocker; }; class KisDocument::Private::SafeSavingLocker { public: SafeSavingLocker(KisDocument::Private *_d) : d(_d), m_locked(false), m_imageLock(d->image, true), m_savingLock(&d->savingMutex) { const int realAutoSaveInterval = KisConfig().autoSaveInterval(); const int emergencyAutoSaveInterval = 10; // sec /** * Initial try to lock both objects. Locking the image guards * us from any image composition threads running in the * background, while savingMutex guards us from entering the * saving code twice by autosave and main threads. * * Since we are trying to lock multiple objects, so we should * do it in a safe manner. */ m_locked = std::try_lock(m_imageLock, m_savingLock) < 0; if (!m_locked) { if (d->isAutosaving) { d->disregardAutosaveFailure = true; if (realAutoSaveInterval) { d->document->setAutoSave(emergencyAutoSaveInterval); } } else { d->image->requestStrokeEnd(); QApplication::processEvents(); // one more try... m_locked = std::try_lock(m_imageLock, m_savingLock) < 0; } } if (m_locked) { d->disregardAutosaveFailure = false; } } ~SafeSavingLocker() { if (m_locked) { m_imageLock.unlock(); m_savingLock.unlock(); const int realAutoSaveInterval = KisConfig().autoSaveInterval(); d->document->setAutoSave(realAutoSaveInterval); } } bool successfullyLocked() const { return m_locked; } private: KisDocument::Private *d; bool m_locked; KisImageBarrierLockAdapter m_imageLock; StdLockableWrapper m_savingLock; }; KisDocument::KisDocument() : d(new Private(this)) { d->undoStack = new UndoStack(this); d->undoStack->setParent(this); d->isEmpty = true; d->filterManager = new KisImportExportManager(this); d->filterManager->setProgresUpdater(d->progressUpdater); connect(&d->autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave())); setAutoSave(defaultAutoSave()); setObjectName(newObjectName()); d->docInfo = new KoDocumentInfo(this); d->pageLayout.width = 0; d->pageLayout.height = 0; d->pageLayout.topMargin = 0; d->pageLayout.bottomMargin = 0; d->pageLayout.leftMargin = 0; d->pageLayout.rightMargin = 0; KConfigGroup cfgGrp( KSharedConfig::openConfig(), "Undo"); d->undoStack->setUndoLimit(cfgGrp.readEntry("UndoLimit", 1000)); d->firstMod = QDateTime::currentDateTime(); d->lastMod = QDateTime::currentDateTime(); connect(d->undoStack, SIGNAL(indexChanged(int)), this, SLOT(slotUndoStackIndexChanged(int))); // preload the krita resources KisResourceServerProvider::instance(); init(); undoStack()->setUndoLimit(KisConfig().undoStackLimit()); setBackupFile(KisConfig().backupFile()); } KisDocument::~KisDocument() { /** * Push a timebomb, which will try to release the memory after * the document has been deleted */ KisPaintDevice::createMemoryReleaseObject()->deleteLater(); d->autoSaveTimer.disconnect(this); d->autoSaveTimer.stop(); delete d->filterManager; // Despite being QObject they needs to be deleted before the image delete d->shapeController; delete d->koShapeController; if (d->image) { d->image->notifyAboutToBeDeleted(); /** * WARNING: We should wait for all the internal image jobs to * finish before entering KisImage's destructor. The problem is, * while execution of KisImage::~KisImage, all the weak shared * pointers pointing to the image enter an inconsistent * state(!). The shared counter is already zero and destruction * has started, but the weak reference doesn't know about it, * because KisShared::~KisShared hasn't been executed yet. So all * the threads running in background and having weak pointers will * enter the KisImage's destructor as well. */ d->image->requestStrokeCancellation(); d->image->waitForDone(); } // clear undo commands that can still point to the image d->undoStack->clear(); KisImageWSP sanityCheckPointer = d->image; // The following line trigger the deletion of the image d->image.clear(); // check if the image has actually been deleted KIS_ASSERT_RECOVER_NOOP(!sanityCheckPointer.isValid()); delete d; } void KisDocument::init() { delete d->nserver; d->nserver = 0; d->nserver = new KisNameServer(1); Q_CHECK_PTR(d->nserver); d->shapeController = new KisShapeController(this, d->nserver); d->koShapeController = new KoShapeController(0, d->shapeController); d->kraSaver = 0; d->kraLoader = 0; } bool KisDocument::reload() { // XXX: reimplement! return false; } bool KisDocument::exportDocument(const QUrl &_url) { bool ret; d->isExporting = true; // // Preserve a lot of state here because we need to restore it in order to // be able to fake a File --> Export. Can't do this in saveFile() because, // for a start, KParts has already set url and m_file and because we need // to restore the modified flag etc. and don't want to put a load on anyone // reimplementing saveFile() (Note: importDocument() and exportDocument() // will remain non-virtual). // QUrl oldURL = url(); QString oldFile = localFilePath(); bool wasModified = isModified(); QByteArray oldMimeType = mimeType(); // save... ret = saveAs(_url); // // This is sooooo hacky :( // Hopefully we will restore enough state. // dbgUI << "Restoring KisDocument state to before export"; // always restore url & m_file because KParts has changed them // (regardless of failure or success) setUrl(oldURL); setLocalFilePath(oldFile); // on successful export we need to restore modified etc. too // on failed export, mimetype/modified hasn't changed anyway if (ret) { setModified(wasModified); d->mimeType = oldMimeType; } d->isExporting = false; return ret; } bool KisDocument::saveFile() { dbgUI << "doc=" << url().url(); // Save it to be able to restore it after a failed save const bool wasModified = isModified(); // The output format is set by koMainWindow, and by openFile QByteArray outputMimeType = d->outputMimeType; if (outputMimeType.isEmpty()) outputMimeType = d->outputMimeType = nativeFormatMimeType(); QApplication::setOverrideCursor(Qt::WaitCursor); if (backupFile()) { Q_ASSERT(url().isLocalFile()); KBackup::backupFile(url().toLocalFile(), d->backupPath); } qApp->processEvents(); bool ret = false; bool suppressErrorDialog = false; KisImportExportFilter::ConversionStatus status = KisImportExportFilter::OK; setFileProgressUpdater(i18n("Saving Document")); if (!isNativeFormat(outputMimeType)) { dbgUI << "Saving to format" << outputMimeType << "in" << localFilePath(); Private::SafeSavingLocker locker(d); if (locker.successfullyLocked()) { status = d->filterManager->exportDocument(localFilePath(), outputMimeType); } else { status = KisImportExportFilter::UsageError; } ret = status == KisImportExportFilter::OK; suppressErrorDialog = (status == KisImportExportFilter::UserCancelled || status == KisImportExportFilter::BadConversionGraph); dbgFile << "Export status was" << status; } else { // Native format => normal save Q_ASSERT(!localFilePath().isEmpty()); ret = saveNativeFormat(localFilePath()); } if (ret) { if (!d->suppressProgress) { QPointer updater = d->progressUpdater->startSubtask(1, "clear undo stack"); updater->setProgress(0); d->undoStack->setClean(); updater->setProgress(100); } else { d->undoStack->setClean(); } removeAutoSaveFiles(); // Restart the autosave timer // (we don't want to autosave again 2 seconds after a real save) setAutoSave(d->autoSaveDelay); } clearFileProgressUpdater(); QApplication::restoreOverrideCursor(); if (!ret) { if (!suppressErrorDialog) { if (errorMessage().isEmpty()) { setErrorMessage(KisImportExportFilter::conversionStatusString(status)); } if (errorMessage().isEmpty()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save\n%1", localFilePath())); } else if (errorMessage() != "USER_CANCELED") { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save %1\nReason: %2", localFilePath(), errorMessage())); } } // couldn't save file so this new URL is invalid // FIXME: we should restore the current document's true URL instead of // setting it to nothing otherwise anything that depends on the URL // being correct will not work (i.e. the document will be called // "Untitled" which may not be true) // // Update: now the URL is restored in KisMainWindow but really, this // should still be fixed in KisDocument/KParts (ditto for file). // We still resetURL() here since we may or may not have been called // by KisMainWindow - Clarence resetURL(); // As we did not save, restore the "was modified" status setModified(wasModified); } if (ret) { d->mimeType = outputMimeType; setConfirmNonNativeSave(isExporting(), false); } return ret; } QByteArray KisDocument::mimeType() const { return d->mimeType; } void KisDocument::setMimeType(const QByteArray & mimeType) { d->mimeType = mimeType; } void KisDocument::setOutputMimeType(const QByteArray & mimeType, int specialOutputFlag) { d->outputMimeType = mimeType; d->specialOutputFlag = specialOutputFlag; } QByteArray KisDocument::outputMimeType() const { return d->outputMimeType; } int KisDocument::specialOutputFlag() const { return d->specialOutputFlag; } bool KisDocument::confirmNonNativeSave(const bool exporting) const { // "exporting ? 1 : 0" is different from "exporting" because a bool is // usually implemented like an "int", not "unsigned : 1" return d->confirmNonNativeSave [ exporting ? 1 : 0 ]; } void KisDocument::setConfirmNonNativeSave(const bool exporting, const bool on) { d->confirmNonNativeSave [ exporting ? 1 : 0] = on; } bool KisDocument::fileBatchMode() const { return d->filterManager->getBatchMode(); } void KisDocument::setFileBatchMode(const bool batchMode) { d->filterManager->setBatchMode(batchMode); } bool KisDocument::isImporting() const { return d->isImporting; } bool KisDocument::isExporting() const { return d->isExporting; } -void KisDocument::setCheckAutoSaveFile(bool b) -{ - d->shouldCheckAutoSaveFile = b; -} - void KisDocument::setAutoErrorHandlingEnabled(bool b) { d->autoErrorHandlingEnabled = b; } bool KisDocument::isAutoErrorHandlingEnabled() const { return d->autoErrorHandlingEnabled; } void KisDocument::slotAutoSave() { if (d->modified && d->modifiedAfterAutosave && !d->isLoading) { // Give a warning when trying to autosave an encrypted file when no password is known (should not happen) if (d->specialOutputFlag == SaveEncrypted && d->password.isNull()) { // That advice should also fix this error from occurring again emit statusBarMessage(i18n("The password of this encrypted document is not known. Autosave aborted! Please save your work manually.")); } else { connect(this, SIGNAL(sigProgress(int)), KisPart::instance()->currentMainwindow(), SLOT(slotProgress(int))); emit statusBarMessage(i18n("Autosaving...")); d->isAutosaving = true; bool ret = saveNativeFormat(autoSaveFile(localFilePath())); setModified(true); if (ret) { d->modifiedAfterAutosave = false; d->autoSaveTimer.stop(); // until the next change } d->isAutosaving = false; emit clearStatusBarMessage(); disconnect(this, SIGNAL(sigProgress(int)), KisPart::instance()->currentMainwindow(), SLOT(slotProgress(int))); if (!ret && !d->disregardAutosaveFailure) { emit statusBarMessage(i18n("Error during autosave! Partition full?")); } } } } void KisDocument::setReadWrite(bool readwrite) { d->readwrite = readwrite; setAutoSave(d->autoSaveDelay); Q_FOREACH (KisMainWindow *mainWindow, KisPart::instance()->mainWindows()) { mainWindow->setReadWrite(readwrite); } } void KisDocument::setAutoSave(int delay) { d->autoSaveDelay = delay; if (isReadWrite() && d->autoSaveDelay > 0) d->autoSaveTimer.start(d->autoSaveDelay * 1000); else d->autoSaveTimer.stop(); } KoDocumentInfo *KisDocument::documentInfo() const { return d->docInfo; } bool KisDocument::isModified() const { return d->modified; } bool KisDocument::saveNativeFormat(const QString & file) { Private::SafeSavingLocker locker(d); if (!locker.successfullyLocked()) return false; d->lastErrorMessage.clear(); //dbgUI <<"Saving to store"; KoStore::Backend backend = KoStore::Auto; if (d->specialOutputFlag == SaveAsDirectoryStore) { backend = KoStore::Directory; dbgUI << "Saving as uncompressed XML, using directory store."; } else if (d->specialOutputFlag == SaveAsFlatXML) { dbgUI << "Saving as a flat XML file."; QFile f(file); if (f.open(QIODevice::WriteOnly | QIODevice::Text)) { bool success = saveToStream(&f); f.close(); return success; } else return false; } dbgUI << "KisDocument::saveNativeFormat nativeFormatMimeType=" << nativeFormatMimeType(); // TODO: use std::auto_ptr or create store on stack [needs API fixing], // to remove all the 'delete store' in all the branches KoStore *store = KoStore::createStore(file, KoStore::Write, d->outputMimeType, backend); if (d->specialOutputFlag == SaveEncrypted && !d->password.isNull()) { store->setPassword(d->password); } if (store->bad()) { d->lastErrorMessage = i18n("Could not create the file for saving"); // more details needed? delete store; return false; } bool result = false; if (!d->isAutosaving) { KisAsyncActionFeedback f(i18n("Saving document..."), 0); result = f.runAction(std::bind(&KisDocument::saveNativeFormatCalligra, this, store)); } else { result = saveNativeFormatCalligra(store); } return result; } bool KisDocument::saveNativeFormatCalligra(KoStore *store) { dbgUI << "Saving root"; if (store->open("root")) { KoStoreDevice dev(store); if (!saveToStream(&dev) || !store->close()) { dbgUI << "saveToStream failed"; delete store; return false; } } else { d->lastErrorMessage = i18n("Not able to write '%1'. Partition full?", QString("maindoc.xml")); delete store; return false; } if (store->open("documentinfo.xml")) { QDomDocument doc = KisDocument::createDomDocument("document-info" /*DTD name*/, "document-info" /*tag name*/, "1.1"); doc = d->docInfo->save(doc); KoStoreDevice dev(store); QByteArray s = doc.toByteArray(); // this is already Utf8! (void)dev.write(s.data(), s.size()); (void)store->close(); } if (!d->isAutosaving) { if (store->open("preview.png")) { // ### TODO: missing error checking (The partition could be full!) savePreview(store); (void)store->close(); } } if (!completeSaving(store)) { delete store; return false; } dbgUI << "Saving done of url:" << url().url(); if (!store->finalize()) { delete store; return false; } // Success delete store; return true; } bool KisDocument::saveToStream(QIODevice *dev) { QDomDocument doc = saveXML(); // 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(); } // Called for embedded documents bool KisDocument::saveToStore(KoStore *_store, const QString & _path) { dbgUI << "Saving document to store" << _path; _store->pushDirectory(); // Use the path as the internal url if (_path.startsWith(STORE_PROTOCOL)) setUrl(QUrl(_path)); else // ugly hack to pass a relative URI setUrl(QUrl(INTERNAL_PREFIX + _path)); // In the current directory we're the king :-) if (_store->open("root")) { KoStoreDevice dev(_store); if (!saveToStream(&dev)) { _store->close(); return false; } if (!_store->close()) return false; } if (!completeSaving(_store)) return false; // Now that we're done leave the directory again _store->popDirectory(); dbgUI << "Saved document to store"; return true; } bool KisDocument::savePreview(KoStore *store) { QPixmap pix = generatePreview(QSize(256, 256)); const QImage preview(pix.toImage().convertToFormat(QImage::Format_ARGB32, Qt::ColorOnly)); KoStoreDevice io(store); if (!io.open(QIODevice::WriteOnly)) return false; if (! preview.save(&io, "PNG")) // ### TODO What is -9 in quality terms? return false; io.close(); return true; } QPixmap KisDocument::generatePreview(const QSize& size) { if (d->image) { QRect bounds = d->image->bounds(); QSize newSize = bounds.size(); newSize.scale(size, Qt::KeepAspectRatio); return QPixmap::fromImage(d->image->convertToQImage(newSize, 0)); } return QPixmap(size); } QString KisDocument::autoSaveFile(const QString & path) const { QString retval; // Using the extension allows to avoid relying on the mime magic when opening const QString extension (".kra"); if (path.isEmpty()) { // Never saved? #ifdef Q_OS_WIN // On Windows, use the temp location (https://bugs.kde.org/show_bug.cgi?id=314921) retval = QString("%1%2.%3-%4-%5-autosave%6").arg(QDir::tempPath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension); #else // On Linux, use a temp file in $HOME then. Mark it with the pid so two instances don't overwrite each other's autosave file retval = QString("%1%2.%3-%4-%5-autosave%6").arg(QDir::homePath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension); #endif } else { QFileInfo fi(path); QString dir = fi.absolutePath(); QString filename = fi.fileName(); retval = QString("%1%2.%3-autosave%4").arg(dir).arg(QDir::separator()).arg(filename).arg(extension); } return retval; } bool KisDocument::importDocument(const QUrl &_url) { bool ret; dbgUI << "url=" << _url.url(); d->isImporting = true; // open... ret = openUrl(_url); // reset url & m_file (kindly? set by KisParts::openUrl()) to simulate a // File --> Import if (ret) { dbgUI << "success, resetting url"; resetURL(); setTitleModified(); } d->isImporting = false; return ret; } bool KisDocument::openUrl(const QUrl &_url, KisDocument::OpenUrlFlags flags) { if (!_url.isLocalFile()) { qDebug() << "not a local file" << _url; return false; } dbgUI << "url=" << _url.url(); d->lastErrorMessage.clear(); // Reimplemented, to add a check for autosave files and to improve error reporting if (!_url.isValid()) { d->lastErrorMessage = i18n("Malformed URL\n%1", _url.url()); // ## used anywhere ? return false; } QUrl url(_url); bool autosaveOpened = false; d->isLoading = true; - if (url.isLocalFile() && d->shouldCheckAutoSaveFile) { + if (url.isLocalFile() && !fileBatchMode()) { QString file = url.toLocalFile(); QString asf = autoSaveFile(file); if (QFile::exists(asf)) { + KisApplication *kisApp = static_cast(qApp); + kisApp->hideSplashScreen(); //dbgUI <<"asf=" << asf; // ## TODO compare timestamps ? int res = QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("An autosaved file exists for this document.\nDo you want to open it instead?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes); switch (res) { case QMessageBox::Yes : url.setPath(asf); autosaveOpened = true; break; case QMessageBox::No : QFile::remove(asf); break; default: // Cancel d->isLoading = false; return false; } } } bool ret = openUrlInternal(url); if (autosaveOpened) { resetURL(); // Force save to act like 'Save As' setReadWrite(true); // enable save button setModified(true); } else { if( !(flags & OPEN_URL_FLAG_DO_NOT_ADD_TO_RECENT_FILES) ) { KisPart::instance()->addRecentURLToAllMainWindows(_url); } if (ret) { // Detect readonly local-files; remote files are assumed to be writable QFileInfo fi(url.toLocalFile()); setReadWrite(fi.isWritable()); } } return ret; } bool KisDocument::openFile() { //dbgUI <<"for" << localFilePath(); if (!QFile::exists(localFilePath())) { QApplication::restoreOverrideCursor(); if (d->autoErrorHandlingEnabled) // Maybe offer to create a new document with that name ? QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("File %1 does not exist.", localFilePath())); d->isLoading = false; return false; } QApplication::setOverrideCursor(Qt::WaitCursor); d->specialOutputFlag = 0; QString filename = localFilePath(); QString typeName = mimeType(); if (typeName.isEmpty()) { typeName = KisMimeDatabase::mimeTypeForFile(filename); } //qDebug() << "mimetypes 4:" << typeName; // Allow to open backup files, don't keep the mimetype application/x-trash. if (typeName == "application/x-trash") { QString path = filename; while (path.length() > 0) { path.chop(1); typeName = KisMimeDatabase::mimeTypeForFile(path); //qDebug() << "\t" << path << typeName; if (!typeName.isEmpty()) { break; } } //qDebug() << "chopped" << filename << "to" << path << "Was trash, is" << typeName; } dbgUI << localFilePath() << "type:" << typeName; QString importedFile = localFilePath(); setFileProgressUpdater(i18n("Opening Document")); if (!isNativeFormat(typeName.toLatin1())) { KisImportExportFilter::ConversionStatus status; importedFile = d->filterManager->importDocument(localFilePath(), typeName, status); if (status != KisImportExportFilter::OK) { QApplication::restoreOverrideCursor(); QString msg = KisImportExportFilter::conversionStatusString(status); if (d->autoErrorHandlingEnabled && !msg.isEmpty()) { QString errorMsg(i18n("Could not open %2.\nReason: %1.\n%3", msg, prettyPathOrUrl(), errorMessage())); QMessageBox::critical(0, i18nc("@title:window", "Krita"), errorMsg); } d->isLoading = false; clearFileProgressUpdater(); return false; } d->isEmpty = false; //qDebug() << "importedFile" << importedFile << "status:" << static_cast(status); } QApplication::restoreOverrideCursor(); bool ok = true; if (!importedFile.isEmpty()) { // Something to load (tmp or native file) ? // The filter, if any, has been applied. It's all native format now. if (!loadNativeFormat(importedFile)) { ok = false; if (d->autoErrorHandlingEnabled) { showLoadingErrorDialog(); } } } if (importedFile != localFilePath()) { // We opened a temporary file (result of an import filter) // Set document URL to empty - we don't want to save in /tmp ! // But only if in readwrite mode (no saving problem otherwise) // -- // But this isn't true at all. If this is the result of an // import, then importedFile=temporary_file.kwd and // file/m_url=foreignformat.ext so m_url is correct! // So don't resetURL() or else the caption won't be set when // foreign files are opened (an annoying bug). // - Clarence // #if 0 if (isReadWrite()) resetURL(); #endif // remove temp file - uncomment this to debug import filters if (!importedFile.isEmpty()) { #ifndef NDEBUG if (!getenv("CALLIGRA_DEBUG_FILTERS")) #endif QFile::remove(importedFile); } } if (ok) { setMimeTypeAfterLoading(typeName); emit sigLoadingFinished(); } if (!d->suppressProgress) { QPointer updater = d->progressUpdater->startSubtask(1, "clear undo stack"); updater->setProgress(0); undoStack()->clear(); updater->setProgress(100); clearFileProgressUpdater(); } else { undoStack()->clear(); } d->isLoading = false; return ok; } KoProgressUpdater *KisDocument::progressUpdater() const { return d->progressUpdater; } void KisDocument::setProgressProxy(KoProgressProxy *progressProxy) { d->progressProxy = progressProxy; } KoProgressProxy* KisDocument::progressProxy() const { if (!d->progressProxy) { KisMainWindow *mainWindow = 0; if (KisPart::instance()->mainwindowCount() > 0) { mainWindow = KisPart::instance()->mainWindows()[0]; } d->progressProxy = new DocumentProgressProxy(mainWindow); } return d->progressProxy; } // shared between openFile and koMainWindow's "create new empty document" code void KisDocument::setMimeTypeAfterLoading(const QString& mimeType) { d->mimeType = mimeType.toLatin1(); d->outputMimeType = d->mimeType; const bool needConfirm = !isNativeFormat(d->mimeType); setConfirmNonNativeSave(false, needConfirm); setConfirmNonNativeSave(true, needConfirm); } // The caller must call store->close() if loadAndParse returns true. bool KisDocument::oldLoadAndParse(KoStore *store, const QString& filename, KoXmlDocument& doc) { //dbgUI <<"Trying to open" << filename; if (!store->open(filename)) { warnUI << "Entry " << filename << " not found!"; d->lastErrorMessage = i18n("Could not find %1", filename); return false; } // Error variables for QDomDocument::setContent QString errorMsg; int errorLine, errorColumn; bool ok = doc.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; d->lastErrorMessage = i18n("Parsing error in %1 at line %2, column %3\nError message: %4" , filename , errorLine, errorColumn , QCoreApplication::translate("QXml", errorMsg.toUtf8(), 0, QCoreApplication::UnicodeUTF8)); return false; } dbgUI << "File" << filename << " loaded and parsed"; return true; } bool KisDocument::loadNativeFormat(const QString & file_) { QString file = file_; QFileInfo fileInfo(file); if (!fileInfo.exists()) { // check duplicated from openUrl, but this is useful for templates d->lastErrorMessage = i18n("The file %1 does not exist.", file); return false; } if (!fileInfo.isFile()) { file += "/content.xml"; QFileInfo fileInfo2(file); if (!fileInfo2.exists() || !fileInfo2.isFile()) { d->lastErrorMessage = i18n("%1 is not a file." , file_); return false; } } QApplication::setOverrideCursor(Qt::WaitCursor); dbgUI << file; QFile in; bool isRawXML = false; if (d->specialOutputFlag != SaveAsDirectoryStore) { // Don't try to open a directory ;) in.setFileName(file); if (!in.open(QIODevice::ReadOnly)) { QApplication::restoreOverrideCursor(); d->lastErrorMessage = i18n("Could not open the file for reading (check read permissions)."); return false; } char buf[6]; buf[5] = 0; int pos = 0; do { if (in.read(buf + pos , 1) < 1) { QApplication::restoreOverrideCursor(); in.close(); d->lastErrorMessage = i18n("Could not read the beginning of the file."); return false; } if (QChar(buf[pos]).isSpace()) continue; pos++; } while (pos < 5); isRawXML = (qstrnicmp(buf, "lastErrorMessage = i18n("parsing error in the main document at line %1, column %2\nError message: %3", errorLine, errorColumn, i18n(errorMsg.toUtf8())); res = false; } QApplication::restoreOverrideCursor(); in.close(); d->isEmpty = false; return res; } else { // It's a calligra store (tar.gz, zip, directory, etc.) in.close(); KoStore::Backend backend = (d->specialOutputFlag == SaveAsDirectoryStore) ? KoStore::Directory : KoStore::Auto; KoStore *store = KoStore::createStore(file, KoStore::Read, "", backend); if (store->bad()) { d->lastErrorMessage = i18n("Not a valid Krita file: %1", file); delete store; QApplication::restoreOverrideCursor(); return false; } // Remember that the file was encrypted if (d->specialOutputFlag == 0 && store->isEncrypted() && !d->isImporting) d->specialOutputFlag = SaveEncrypted; const bool success = loadNativeFormatFromStoreInternal(store); // Retrieve the password after loading the file, only then is it guaranteed to exist if (success && store->isEncrypted() && !d->isImporting) d->password = store->password(); delete store; return success; } } bool KisDocument::loadNativeFormatFromByteArray(QByteArray &data) { bool succes; KoStore::Backend backend = (d->specialOutputFlag == SaveAsDirectoryStore) ? KoStore::Directory : KoStore::Auto; QBuffer buffer(&data); KoStore *store = KoStore::createStore(&buffer, KoStore::Read, "", backend); if (store->bad()) { delete store; return false; } // Remember that the file was encrypted if (d->specialOutputFlag == 0 && store->isEncrypted() && !d->isImporting) d->specialOutputFlag = SaveEncrypted; succes = loadNativeFormatFromStoreInternal(store); // Retrieve the password after loading the file, only then is it guaranteed to exist if (succes && store->isEncrypted() && !d->isImporting) d->password = store->password(); delete store; return succes; } bool KisDocument::loadNativeFormatFromStoreInternal(KoStore *store) { if (store->hasFile("root") || store->hasFile("maindoc.xml")) { // Fallback to "old" file format (maindoc.xml) KoXmlDocument doc = KoXmlDocument(true); bool ok = oldLoadAndParse(store, "root", doc); if (ok) ok = loadXML(doc, store); if (!ok) { QApplication::restoreOverrideCursor(); return false; } } else { errUI << "ERROR: No maindoc.xml" << endl; d->lastErrorMessage = i18n("Invalid document: no file 'maindoc.xml'."); QApplication::restoreOverrideCursor(); return false; } if (store->hasFile("documentinfo.xml")) { KoXmlDocument doc = KoXmlDocument(true); if (oldLoadAndParse(store, "documentinfo.xml", doc)) { d->docInfo->load(doc); } } else { //dbgUI <<"cannot open document info"; delete d->docInfo; d->docInfo = new KoDocumentInfo(this); } bool res = completeLoading(store); QApplication::restoreOverrideCursor(); d->isEmpty = false; return res; } // For embedded documents bool KisDocument::loadFromStore(KoStore *_store, const QString& url) { if (_store->open(url)) { KoXmlDocument doc = KoXmlDocument(true); doc.setContent(_store->device()); if (!loadXML(doc, _store)) { _store->close(); return false; } _store->close(); } else { dbgKrita << "couldn't open " << url; } _store->pushDirectory(); // Store as document URL if (url.startsWith(STORE_PROTOCOL)) { setUrl(QUrl::fromUserInput(url)); } else { setUrl(QUrl(INTERNAL_PREFIX + url)); _store->enterDirectory(url); } bool result = completeLoading(_store); // Restore the "old" path _store->popDirectory(); return result; } bool KisDocument::loadOdf(KoOdfReadStore & odfStore) { Q_UNUSED(odfStore); setErrorMessage(i18n("Krita does not support the OpenDocument file format.")); return false; } bool KisDocument::saveOdf(SavingContext &documentContext) { Q_UNUSED(documentContext); setErrorMessage(i18n("Krita does not support the OpenDocument file format.")); return false; } bool KisDocument::isStoredExtern() const { return !storeInternal() && hasExternURL(); } void KisDocument::setModified() { d->modified = true; } void KisDocument::setModified(bool mod) { if (mod) { updateEditingTime(false); } if (d->isAutosaving) // ignore setModified calls due to autosaving return; if ( !d->readwrite && d->modified ) { errKrita << "Can't set a read-only document to 'modified' !" << endl; return; } //dbgUI<<" url:" << url.path(); //dbgUI<<" mod="<docInfo->aboutInfo("editing-time").toInt() + d->firstMod.secsTo(d->lastMod))); d->firstMod = now; } else if (firstModDelta > 60 || forceStoreElapsed) { d->docInfo->setAboutInfo("editing-time", QString::number(d->docInfo->aboutInfo("editing-time").toInt() + firstModDelta)); d->firstMod = now; } d->lastMod = now; } QString KisDocument::prettyPathOrUrl() const { QString _url(url().toDisplayString()); #ifdef Q_OS_WIN if (url().isLocalFile()) { _url = QDir::toNativeSeparators(_url); } #endif return _url; } // Get caption from document info (title(), in about page) QString KisDocument::caption() const { QString c; if (documentInfo()) { c = documentInfo()->aboutInfo("title"); } const QString _url(url().fileName()); if (!c.isEmpty() && !_url.isEmpty()) { c = QString("%1 - %2").arg(c).arg(_url); } else if (c.isEmpty()) { c = _url; // Fall back to document URL } return c; } void KisDocument::setTitleModified() { emit titleModified(caption(), isModified()); } bool KisDocument::completeLoading(KoStore* store) { if (!d->image) { if (d->kraLoader->errorMessages().isEmpty()) { setErrorMessage(i18n("Unknown error.")); } else { setErrorMessage(d->kraLoader->errorMessages().join(".\n")); } return false; } d->kraLoader->loadBinaryData(store, d->image, url().url(), isStoredExtern()); bool retval = true; if (!d->kraLoader->errorMessages().isEmpty()) { setErrorMessage(d->kraLoader->errorMessages().join(".\n")); retval = false; } if (retval) { vKisNodeSP preselectedNodes = d->kraLoader->selectedNodes(); if (preselectedNodes.size() > 0) { d->preActivatedNode = preselectedNodes.first(); } // before deleting the kraloader, get the list with preloaded assistants and save it d->assistants = d->kraLoader->assistants(); d->shapeController->setImage(d->image); connect(d->image.data(), SIGNAL(sigImageModified()), this, SLOT(setImageModified())); if (d->image) { d->image->initialRefreshGraph(); } setAutoSave(KisConfig().autoSaveInterval()); emit sigLoadingFinished(); } delete d->kraLoader; d->kraLoader = 0; return retval; } bool KisDocument::completeSaving(KoStore* store) { d->kraSaver->saveKeyframes(store, url().url(), isStoredExtern()); d->kraSaver->saveBinaryData(store, d->image, url().url(), isStoredExtern(), d->isAutosaving); bool retval = true; if (!d->kraSaver->errorMessages().isEmpty()) { setErrorMessage(d->kraSaver->errorMessages().join(".\n")); retval = false; } delete d->kraSaver; d->kraSaver = 0; emit sigSavingFinished(); return retval; } QDomDocument KisDocument::createDomDocument(const QString& tagName, const QString& version) const { return createDomDocument("krita", tagName, version); } //static QDomDocument KisDocument::createDomDocument(const QString& appName, const QString& tagName, const QString& version) { QDomImplementation impl; QString url = QString("http://www.calligra.org/DTD/%1-%2.dtd").arg(appName).arg(version); QDomDocumentType dtype = impl.createDocumentType(tagName, QString("-//KDE//DTD %1 %2//EN").arg(appName).arg(version), url); // The namespace URN doesn't need to include the version number. QString namespaceURN = QString("http://www.calligra.org/DTD/%1").arg(appName); QDomDocument doc = impl.createDocument(namespaceURN, tagName, dtype); doc.insertBefore(doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""), doc.documentElement()); return doc; } bool KisDocument::loadXML(const KoXmlDocument& doc, KoStore *store) { Q_UNUSED(store); if (d->image) { d->shapeController->setImage(0); d->image = 0; } KoXmlElement root; KoXmlNode node; KisImageSP image; if (doc.doctype().name() != "DOC") { setErrorMessage(i18n("The format is not supported or the file is corrupted")); return false; } root = doc.documentElement(); int syntaxVersion = root.attribute("syntaxVersion", "3").toInt(); if (syntaxVersion > 2) { setErrorMessage(i18n("The file is too new for this version of Krita (%1).", syntaxVersion)); return false; } if (!root.hasChildNodes()) { setErrorMessage(i18n("The file has no layers.")); return false; } if (d->kraLoader) delete d->kraLoader; d->kraLoader = new KisKraLoader(this, syntaxVersion); // Legacy from the multi-image .kra file period. for (node = root.firstChild(); !node.isNull(); node = node.nextSibling()) { if (node.isElement()) { if (node.nodeName() == "IMAGE") { KoXmlElement elem = node.toElement(); if (!(image = d->kraLoader->loadXML(elem))) { if (d->kraLoader->errorMessages().isEmpty()) { setErrorMessage(i18n("Unknown error.")); } else { setErrorMessage(d->kraLoader->errorMessages().join(".\n")); } return false; } } else { if (d->kraLoader->errorMessages().isEmpty()) { setErrorMessage(i18n("The file does not contain an image.")); } return false; } } } if (d->image) { // Disconnect existing sig/slot connections d->image->disconnect(this); } d->setImageAndInitIdleWatcher(image); return true; } QDomDocument KisDocument::saveXML() { dbgFile << url(); QDomDocument doc = createDomDocument("DOC", CURRENT_DTD_VERSION); QDomElement root = doc.documentElement(); root.setAttribute("editor", "Krita"); root.setAttribute("syntaxVersion", "2"); if (d->kraSaver) delete d->kraSaver; d->kraSaver = new KisKraSaver(this); root.appendChild(d->kraSaver->saveXML(doc, d->image)); if (!d->kraSaver->errorMessages().isEmpty()) { setErrorMessage(d->kraSaver->errorMessages().join(".\n")); } return doc; } bool KisDocument::isNativeFormat(const QByteArray& mimetype) const { if (mimetype == nativeFormatMimeType()) return true; return extraNativeMimeTypes().contains(mimetype); } int KisDocument::supportedSpecialFormats() const { return 0; // we don't support encryption. } void KisDocument::setErrorMessage(const QString& errMsg) { d->lastErrorMessage = errMsg; } QString KisDocument::errorMessage() const { return d->lastErrorMessage; } void KisDocument::showLoadingErrorDialog() { if (errorMessage().isEmpty()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not open\n%1", localFilePath())); } else if (errorMessage() != "USER_CANCELED") { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not open %1\nReason: %2", localFilePath(), errorMessage())); } } bool KisDocument::isLoading() const { return d->isLoading; } void KisDocument::removeAutoSaveFiles() { // Eliminate any auto-save file QString asf = autoSaveFile(localFilePath()); // the one in the current dir if (QFile::exists(asf)) QFile::remove(asf); asf = autoSaveFile(QString()); // and the one in $HOME if (QFile::exists(asf)) QFile::remove(asf); } void KisDocument::setBackupFile(bool _b) { d->backupFile = _b; } bool KisDocument::backupFile()const { return d->backupFile; } void KisDocument::setBackupPath(const QString & _path) { d->backupPath = _path; } QString KisDocument::backupPath()const { return d->backupPath; } bool KisDocument::storeInternal() const { return d->storeInternal; } void KisDocument::setStoreInternal(bool i) { d->storeInternal = i; //dbgUI<<"="<storeInternal<<" doc:"<pageLayout; } void KisDocument::setPageLayout(const KoPageLayout &pageLayout) { d->pageLayout = pageLayout; } KoUnit KisDocument::unit() const { return d->unit; } void KisDocument::setUnit(const KoUnit &unit) { if (d->unit != unit) { d->unit = unit; emit unitChanged(unit); } } KUndo2Stack *KisDocument::undoStack() { return d->undoStack; } void KisDocument::addCommand(KUndo2Command *command) { if (command) d->undoStack->push(command); } void KisDocument::beginMacro(const KUndo2MagicString & text) { d->undoStack->beginMacro(text); } void KisDocument::endMacro() { d->undoStack->endMacro(); } void KisDocument::slotUndoStackIndexChanged(int idx) { // even if the document was already modified, call setModified to re-start autosave timer setModified(idx != d->undoStack->cleanIndex()); } void KisDocument::clearUndoHistory() { d->undoStack->clear(); } KisGridConfig KisDocument::gridConfig() const { return d->gridConfig; } void KisDocument::setGridConfig(const KisGridConfig &config) { d->gridConfig = config; } const KisGuidesConfig& KisDocument::guidesConfig() const { return d->guidesConfig; } void KisDocument::setGuidesConfig(const KisGuidesConfig &data) { if (d->guidesConfig == data) return; d->guidesConfig = data; emit sigGuidesConfigChanged(d->guidesConfig); } bool KisDocument::isEmpty() const { return d->isEmpty; } void KisDocument::setEmpty() { d->isEmpty = true; } // static int KisDocument::defaultAutoSave() { return 300; } void KisDocument::resetURL() { setUrl(QUrl()); setLocalFilePath(QString()); } int KisDocument::pageCount() const { return 1; } KoDocumentInfoDlg *KisDocument::createDocumentInfoDialog(QWidget *parent, KoDocumentInfo *docInfo) const { return new KoDocumentInfoDlg(parent, docInfo); } bool KisDocument::isReadWrite() const { return d->readwrite; } QUrl KisDocument::url() const { return d->m_url; } bool KisDocument::closeUrl(bool promptToSave) { if (promptToSave) { if ( d->document->isReadWrite() && d->document->isModified()) { Q_FOREACH (KisView *view, KisPart::instance()->views()) { if (view && view->document() == this) { if (!view->queryClose()) { return false; } } } } } // Not modified => ok and delete temp file. d->mimeType = QByteArray(); if ( d->m_bTemp ) { QFile::remove( d->m_file ); d->m_bTemp = false; } // It always succeeds for a read-only part, // but the return value exists for reimplementations // (e.g. pressing cancel for a modified read-write part) return true; } bool KisDocument::saveAs(const QUrl &kurl) { if (!kurl.isValid()) { errKrita << "saveAs: Malformed URL " << kurl.url() << endl; return false; } d->m_duringSaveAs = true; d->m_originalURL = d->m_url; d->m_originalFilePath = d->m_file; d->m_url = kurl; // Store where to upload in saveToURL d->prepareSaving(); bool result = save(); // Save local file and upload local file if (!result) { d->m_url = d->m_originalURL; d->m_file = d->m_originalFilePath; d->m_duringSaveAs = false; d->m_originalURL = QUrl(); d->m_originalFilePath.clear(); } return result; } bool KisDocument::save() { d->m_saveOk = false; if ( d->m_file.isEmpty() ) { // document was created empty d->prepareSaving(); } updateEditingTime(true); d->document->setFileProgressProxy(); d->document->setUrl(url()); bool ok = d->document->saveFile(); d->document->clearFileProgressProxy(); if (ok) { return saveToUrl(); } else { emit canceled(QString()); } return false; } bool KisDocument::waitSaveComplete() { return d->m_saveOk; } void KisDocument::setUrl(const QUrl &url) { d->m_url = url; } QString KisDocument::localFilePath() const { return d->m_file; } void KisDocument::setLocalFilePath( const QString &localFilePath ) { d->m_file = localFilePath; } bool KisDocument::saveToUrl() { if ( d->m_url.isLocalFile() ) { d->document->setModified( false ); emit completed(); // if m_url is a local file there won't be a temp file -> nothing to remove Q_ASSERT( !d->m_bTemp ); d->m_saveOk = true; d->m_duringSaveAs = false; d->m_originalURL = QUrl(); d->m_originalFilePath.clear(); return true; // Nothing to do } return false; } bool KisDocument::openUrlInternal(const QUrl &url) { if ( !url.isValid() ) return false; if (d->m_bAutoDetectedMime) { d->mimeType = QByteArray(); d->m_bAutoDetectedMime = false; } QByteArray mimetype = d->mimeType; if ( !closeUrl() ) return false; d->mimeType = mimetype; setUrl(url); d->m_file.clear(); if (d->m_url.isLocalFile()) { d->m_file = d->m_url.toLocalFile(); return d->openLocalFile(); } return false; } KisImageWSP KisDocument::newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace* colorspace) { KoColor backgroundColor(Qt::white, colorspace); /** * FIXME: check whether this is a good value */ double defaultResolution=1.; newImage(name, width, height, colorspace, backgroundColor, "", defaultResolution); return image(); } bool KisDocument::newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace * cs, const KoColor &bgColor, const QString &imageDescription, const double imageResolution) { return newImage(name, width, height, cs, bgColor, false, 1, imageDescription, imageResolution); } bool KisDocument::newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace* cs, const KoColor &bgColor, bool backgroundAsLayer, int numberOfLayers, const QString &description, const double imageResolution) { Q_ASSERT(cs); KisConfig cfg; KisImageSP image; KisPaintLayerSP layer; if (!cs) return false; QApplication::setOverrideCursor(Qt::BusyCursor); image = new KisImage(createUndoStore(), width, height, cs, name); Q_CHECK_PTR(image); connect(image.data(), SIGNAL(sigImageModified()), this, SLOT(setImageModified())); image->setResolution(imageResolution, imageResolution); image->assignImageProfile(cs->profile()); documentInfo()->setAboutInfo("title", name); if (name != i18n("Unnamed") && !name.isEmpty()) { setUrl(QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation) + '/' + name + ".kra")); } documentInfo()->setAboutInfo("abstract", description); layer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, cs); Q_CHECK_PTR(layer); if (backgroundAsLayer) { image->setDefaultProjectionColor(KoColor(cs)); if (bgColor.opacityU8() == OPACITY_OPAQUE_U8) { layer->paintDevice()->setDefaultPixel(bgColor.data()); } else { // Hack: with a semi-transparent background color, the projection isn't composited right if we just set the default pixel KisFillPainter painter; painter.begin(layer->paintDevice()); painter.fillRect(0, 0, width, height, bgColor, bgColor.opacityU8()); } } else { image->setDefaultProjectionColor(bgColor); } layer->setDirty(QRect(0, 0, width, height)); image->addNode(layer.data(), image->rootLayer().data()); setCurrentImage(image); for(int i = 1; i < numberOfLayers; ++i) { KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), OPACITY_OPAQUE_U8, cs); image->addNode(layer, image->root(), i); layer->setDirty(QRect(0, 0, width, height)); } cfg.defImageWidth(width); cfg.defImageHeight(height); cfg.defImageResolution(imageResolution); cfg.defColorModel(image->colorSpace()->colorModelId().id()); cfg.setDefaultColorDepth(image->colorSpace()->colorDepthId().id()); cfg.defColorProfile(image->colorSpace()->profile()->name()); QApplication::restoreOverrideCursor(); return true; } KoShapeBasedDocumentBase *KisDocument::shapeController() const { return d->shapeController; } KoShapeLayer* KisDocument::shapeForNode(KisNodeSP layer) const { return d->shapeController->shapeForNode(layer); } vKisNodeSP KisDocument::activeNodes() const { vKisNodeSP nodes; Q_FOREACH (KisView *v, KisPart::instance()->views()) { if (v->document() == this && v->viewManager()) { KisNodeSP activeNode = v->viewManager()->activeNode(); if (activeNode && !nodes.contains(activeNode)) { if (activeNode->inherits("KisMask")) { activeNode = activeNode->parent(); } nodes.append(activeNode); } } } return nodes; } QList KisDocument::assistants() const { return d->assistants; } void KisDocument::setAssistants(const QList value) { d->assistants = value; } void KisDocument::setPreActivatedNode(KisNodeSP activatedNode) { d->preActivatedNode = activatedNode; } KisNodeSP KisDocument::preActivatedNode() const { return d->preActivatedNode; } void KisDocument::prepareForImport() { /* TODO: remove this function? I kept it because it might be useful for * other kind of preparing, but currently it was checking on d->nserver * being null and then calling init() if it was, but the document is always * initialized in the constructor (and init() does other things too). * Moreover, nserver cannot be nulled by some external call.*/ } void KisDocument::setFileProgressUpdater(const QString &text) { d->suppressProgress = d->filterManager->getBatchMode(); if (!d->suppressProgress) { d->progressUpdater = new KoProgressUpdater(d->progressProxy, KoProgressUpdater::Unthreaded); d->progressUpdater->start(100, text); d->filterManager->setProgresUpdater(d->progressUpdater); connect(this, SIGNAL(sigProgress(int)), KisPart::instance()->currentMainwindow(), SLOT(slotProgress(int))); connect(KisPart::instance()->currentMainwindow(), SIGNAL(sigProgressCanceled()), this, SIGNAL(sigProgressCanceled())); } } void KisDocument::clearFileProgressUpdater() { if (!d->suppressProgress && d->progressUpdater) { disconnect(KisPart::instance()->currentMainwindow(), SIGNAL(sigProgressCanceled()), this, SIGNAL(sigProgressCanceled())); disconnect(this, SIGNAL(sigProgress(int)), KisPart::instance()->currentMainwindow(), SLOT(slotProgress(int))); delete d->progressUpdater; d->filterManager->setProgresUpdater(0); d->progressUpdater = 0; } } void KisDocument::setFileProgressProxy() { if (!d->progressProxy && !d->filterManager->getBatchMode()) { d->fileProgressProxy = progressProxy(); } else { d->fileProgressProxy = 0; } } void KisDocument::clearFileProgressProxy() { if (d->fileProgressProxy) { setProgressProxy(0); delete d->fileProgressProxy; d->fileProgressProxy = 0; } } KisImageWSP KisDocument::image() const { return d->image; } void KisDocument::setCurrentImage(KisImageSP image) { if (!image) return; if (d->image) { // Disconnect existing sig/slot connections d->image->disconnect(this); d->shapeController->setImage(0); } d->setImageAndInitIdleWatcher(image); d->shapeController->setImage(image); setModified(false); connect(d->image, SIGNAL(sigImageModified()), this, SLOT(setImageModified())); d->image->initialRefreshGraph(); setAutoSave(KisConfig().autoSaveInterval()); } void KisDocument::initEmpty() { KisConfig cfg; const KoColorSpace * rgb = KoColorSpaceRegistry::instance()->rgb8(); newImage("", cfg.defImageWidth(), cfg.defImageHeight(), rgb); } void KisDocument::setImageModified() { setModified(true); } KisUndoStore* KisDocument::createUndoStore() { return new KisDocumentUndoStore(this); } bool KisDocument::isAutosaving() const { return d->isAutosaving; } diff --git a/libs/ui/KisDocument.h b/libs/ui/KisDocument.h index b56e321d84..c0ec301a17 100644 --- a/libs/ui/KisDocument.h +++ b/libs/ui/KisDocument.h @@ -1,830 +1,822 @@ /* This file is part of the Krita project * * Copyright (C) 2014 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KISDOCUMENT_H #define KISDOCUMENT_H #include #include #include #include #include #include #include #include #include #include #include "kritaui_export.h" class QString; class KUndo2Command; class KoUnit; class KoColor; class KoColorSpace; class KoShapeBasedDocumentBase; class KoShapeLayer; class KoStore; class KoOdfReadStore; class KoDocumentInfo; class KoProgressUpdater; class KoProgressProxy; class KoDocumentInfoDlg; class KisUndoStore; class KisPaintingAssistant; class KisPart; class KisGridConfig; class KisGuidesConfig; class QDomDocument; class KisPart; #define KIS_MIME_TYPE "application/x-krita" /** * The %Calligra document class * * This class provides some functionality each %Calligra document should have. * * @short The %Calligra document class */ class KRITAUI_EXPORT KisDocument : public QObject, public KoDocumentBase { Q_OBJECT Q_PROPERTY(bool backupFile READ backupFile WRITE setBackupFile) Q_PROPERTY(int pageCount READ pageCount) protected: explicit KisDocument(); public: enum OpenUrlFlags { OPEN_URL_FLAG_NONE = 1 << 0, OPEN_URL_FLAG_DO_NOT_ADD_TO_RECENT_FILES = 1 << 1, }; /** * Destructor. * * The destructor does not delete any attached KisView objects and it does not * delete the attached widget as returned by widget(). */ virtual ~KisDocument(); /** * @brief reload Reloads the document from the original url * @return the result of loading the document */ bool reload(); /** * @brief openUrl Open an URL * @param url The URL to open * @param flags Control specific behavior * @return success status */ bool openUrl(const QUrl &url, OpenUrlFlags flags = OPEN_URL_FLAG_NONE); /** * Opens the document given by @p url, without storing the URL * in the KisDocument. * Call this instead of openUrl() to implement KisMainWindow's * File --> Import feature. * * @note This will call openUrl(). To differentiate this from an ordinary * Open operation (in any reimplementation of openUrl() or openFile()) * call isImporting(). */ bool importDocument(const QUrl &url); /** * Saves the document as @p url without changing the state of the * KisDocument (URL, modified flag etc.). Call this instead of * KisParts::ReadWritePart::saveAs() to implement KisMainWindow's * File --> Export feature. * * @note This will call KisDocument::saveAs(). To differentiate this * from an ordinary Save operation (in any reimplementation of * saveFile()) call isExporting(). */ bool exportDocument(const QUrl &url); /** * @brief Sets whether the document can be edited or is read only. * * This recursively applied to all child documents and * KisView::updateReadWrite is called for every attached * view. */ void setReadWrite(bool readwrite = true); /** * To be preferred when a document exists. It is fast when calling * it multiple times since it caches the result that readNativeFormatMimeType() * delivers. * This comes from the X-KDE-NativeMimeType key in the .desktop file. */ static QByteArray nativeFormatMimeType() { return KIS_MIME_TYPE; } /// Checks whether a given mimetype can be handled natively. bool isNativeFormat(const QByteArray& mimetype) const; /// Returns a list of the mimetypes considered "native", i.e. which can /// be saved by KisDocument without a filter, in *addition* to the main one static QStringList extraNativeMimeTypes() { return QStringList() << KIS_MIME_TYPE; } /// Enum values used by specialOutputFlag - note that it's a bitfield for supportedSpecialFormats enum { /*SaveAsCalligra1dot1 = 1,*/ // old and removed SaveAsDirectoryStore = 2, SaveAsFlatXML = 4, SaveEncrypted = 8 // bitfield! next value is 16 }; /** * Return the set of SupportedSpecialFormats that the application wants to * offer in the "Save" file dialog. */ virtual int supportedSpecialFormats() const; /** * Returns the actual mimetype of the document */ QByteArray mimeType() const; /** * @brief Sets the mime type for the document. * * When choosing "save as" this is also the mime type * selected by default. */ void setMimeType(const QByteArray & mimeType); /** * @brief Set the format in which the document should be saved. * * This is called on loading, and in "save as", so you shouldn't * have to call it. * * @param mimeType the mime type (format) to use. * @param specialOutputFlag is for "save as older version" etc. */ void setOutputMimeType(const QByteArray & mimeType, int specialOutputFlag = 0); QByteArray outputMimeType() const; int specialOutputFlag() const; /** * Returns true if this document was the result of opening a foreign * file format and if the user hasn't yet saved the document (in any * format). * * Used by KisMainWindow to warn the user when s/he lazily presses * CTRL+S to save in the same foreign format, putting all his/her * formatting at risk (normally an export confirmation only comes up * with Save As). * * @param exporting specifies whether this is the setting for a * File --> Export or File --> Save/Save As operation. */ bool confirmNonNativeSave(const bool exporting) const; void setConfirmNonNativeSave(const bool exporting, const bool on); /** * @return true if file operations should inhibit the option dialog */ bool fileBatchMode() const; /** * @param batchMode if true, do not show the option dialog for file operations. */ void setFileBatchMode(const bool batchMode); /** * Sets the error message to be shown to the user (use i18n()!) * when loading or saving fails. * If you asked the user about something and they chose "Cancel", * set the message to the magic string "USER_CANCELED", to skip the error dialog. */ void setErrorMessage(const QString& errMsg); /** * Return the last error message. Usually KisDocument takes care of * showing it; this method is mostly provided for non-interactive use. */ QString errorMessage() const; /** * Show the last error message in a message box. * The dialog box will mention a loading problem. * openUrl/openFile takes care of doing it, but not loadNativeFormat itself, * so this is often called after loadNativeFormat returned false. */ void showLoadingErrorDialog(); /** * @brief Generates a preview picture of the document * @note The preview is used in the File Dialog and also to create the Thumbnail */ QPixmap generatePreview(const QSize& size); /** * Tells the document that its title has been modified, either because * the modified status changes (this is done by setModified() ) or * because the URL or the document-info's title changed. */ void setTitleModified(); /** * @return true if the document is empty. */ virtual bool isEmpty() const; /** * @brief Sets the document to empty. * * Used after loading a template * (which is not empty, but not the user's input). * * @see isEmpty() */ void setEmpty(); /** * @brief Loads a document from a store. * * You should never have to reimplement. * * @param store The store to load from * @param url An internal url, like tar:/1/2 */ bool loadFromStore(KoStore *store, const QString& url); /// Unused virtual bool loadOdf(KoOdfReadStore & odfStore); /// Unused virtual bool saveOdf(SavingContext &documentContext); /** * @brief Saves a sub-document to a store. * * You should not have to reimplement this. */ virtual bool saveToStore(KoStore *store, const QString& path); /** * Reimplement this method to load the contents of your Calligra document, * from the XML document. This is for the pre-Oasis file format (maindoc.xml). */ virtual bool loadXML(const KoXmlDocument & doc, KoStore *store); /** * Reimplement this to save the contents of the %Calligra document into * a QDomDocument. The framework takes care of saving it to the store. */ QDomDocument saveXML(); /** * Return a correctly created QDomDocument for this KisDocument, * including processing instruction, complete DOCTYPE tag (with systemId and publicId), and root element. * @param tagName the name of the tag for the root element * @param version the DTD version (usually the application's version). */ QDomDocument createDomDocument(const QString& tagName, const QString& version) const; /** * Return a correctly created QDomDocument for an old (1.3-style) %Calligra document, * including processing instruction, complete DOCTYPE tag (with systemId and publicId), and root element. * This static method can be used e.g. by filters. * @param appName the app's instance name, e.g. words, kspread, kpresenter etc. * @param tagName the name of the tag for the root element, e.g. DOC for words/kpresenter. * @param version the DTD version (usually the application's version). */ static QDomDocument createDomDocument(const QString& appName, const QString& tagName, const QString& version); /** * The first thing to do in loadOasis is get hold of the office:body tag, then its child. * If the child isn't the expected one, the error message can indicate what it is instead. * This method returns a translated name for the type of document, * e.g. i18n("Word Processing") for office:text. */ static QString tagNameToDocumentType(const QString& localName); /** * Loads a document in the native format from a given URL. * Reimplement if your native format isn't XML. * * @param file the file to load - usually KReadOnlyPart::m_file or the result of a filter */ bool loadNativeFormat(const QString & file); /** * Saves the document in native format, to a given file * You should never have to reimplement. * Made public for writing templates. */ bool saveNativeFormat(const QString & file); /** * Saves the document in the native format to the given store. */ bool saveNativeFormatCalligra(KoStore *store); /** * Activate/deactivate/configure the autosave feature. * @param delay in seconds, 0 to disable */ void setAutoSave(int delay); - /** - * Set whether the next openUrl call should check for an auto-saved file - * and offer to open it. This is usually true, but can be turned off - * (e.g. for the preview module). This only checks for names auto-saved - * files, unnamed auto-saved files are only checked on KisApplication startup. - */ - void setCheckAutoSaveFile(bool b); - /** * Set whether the next openUrl call should show error message boxes in case * of errors. This is usually the case, but e.g. not when generating thumbnail * previews. */ void setAutoErrorHandlingEnabled(bool b); /** * Checks whether error message boxes should be shown. */ bool isAutoErrorHandlingEnabled() const; /** * Retrieve the default value for autosave in seconds. * Called by the applications to use the correct default in their config */ static int defaultAutoSave(); /** * @return the information concerning this document. * @see KoDocumentInfo */ KoDocumentInfo *documentInfo() const; /** * @return the object to report progress to. * * This is only not zero if loading or saving is in progress. * * One can add more KoUpdaters to it to make the progress reporting more * accurate. If no active progress reporter is present, 0 is returned. **/ KoProgressUpdater *progressUpdater() const; /** * Set a custom progress proxy to use to report loading * progress to. */ void setProgressProxy(KoProgressProxy *progressProxy); KoProgressProxy* progressProxy() const; /** * Return true if url() is a real filename, false if url() is * an internal url in the store, like "tar:/..." */ virtual bool isStoredExtern() const; /** * @return the page layout associated with this document (margins, pageSize, etc). * Override this if you want to provide different sized pages. * * @see KoPageLayout */ KoPageLayout pageLayout(int pageNumber = 0) const; void setPageLayout(const KoPageLayout &pageLayout); /** * Performs a cleanup of unneeded backup files */ void removeAutoSaveFiles(); void setBackupFile(bool _b); bool backupFile()const; /** * Returns true if this document or any of its internal child documents are modified. */ bool isModified() const; /** * Returns true during loading (openUrl can be asynchronous) */ bool isLoading() const; /** * Sets the backup path of the document */ void setBackupPath(const QString & _path); /** * @return path to the backup document */ QString backupPath()const; /** * @return caption of the document * * Caption is of the form "[title] - [url]", * built out of the document info (title) and pretty-printed * document URL. * If the title is not present, only the URL it returned. */ QString caption() const; /** * Sets the document URL to empty URL * KParts doesn't allow this, but %Calligra apps have e.g. templates * After using loadNativeFormat on a template, one wants * to set the url to QUrl() */ void resetURL(); /** * Set when you want an external embedded document to be stored internally */ void setStoreInternal(bool i); /** * @return true when external embedded documents are stored internally */ bool storeInternal() const; bool hasExternURL() const; /** * @internal (public for KisMainWindow) */ void setMimeTypeAfterLoading(const QString& mimeType); /** * @return returns the number of pages in the document. */ virtual int pageCount() const; /** * Returns the unit used to display all measures/distances. */ KoUnit unit() const; /** * Sets the unit used to display all measures/distances. */ void setUnit(const KoUnit &unit); /** * Save the unit to the settings writer * * @param settingsWriter */ bool loadNativeFormatFromByteArray(QByteArray &data); KisGridConfig gridConfig() const; void setGridConfig(const KisGridConfig &config); /// returns the guides data for this document. const KisGuidesConfig& guidesConfig() const; void setGuidesConfig(const KisGuidesConfig &data); void clearUndoHistory(); /** * Sets the modified flag on the document. This means that it has * to be saved or not before deleting it. */ void setModified(bool _mod); void updateEditingTime(bool forceStoreElapsed); /** * Initialize an empty document using default values */ void initEmpty(); /** * Returns the global undo stack */ KUndo2Stack *undoStack(); public Q_SLOTS: /** * Adds a command to the undo stack and executes it by calling the redo() function. * @param command command to add to the undo stack */ void addCommand(KUndo2Command *command); /** * Begins recording of a macro command. At the end endMacro needs to be called. * @param text command description */ void beginMacro(const KUndo2MagicString &text); /** * Ends the recording of a macro command. */ void endMacro(); Q_SIGNALS: /** * This signal is emitted when the unit is changed by setUnit(). * It is common to connect views to it, in order to change the displayed units * (e.g. in the rulers) */ void unitChanged(const KoUnit &unit); /** * Progress info while loading or saving. The value is in percents (i.e. a number between 0 and 100) * Your KisDocument-derived class should emit the signal now and then during load/save. * KisMainWindow will take care of displaying a progress bar automatically. */ void sigProgress(int value); /** * Progress cancel button pressed * This is emitted by KisDocument */ void sigProgressCanceled(); /** * Emitted e.g. at the beginning of a save operation * This is emitted by KisDocument and used by KisView to display a statusbar message */ void statusBarMessage(const QString& text); /** * Emitted e.g. at the end of a save operation * This is emitted by KisDocument and used by KisView to clear the statusbar message */ void clearStatusBarMessage(); /** * Emitted when the document is modified */ void modified(bool); void titleModified(const QString &caption, bool isModified); void sigLoadingFinished(); void sigSavingFinished(); void sigGuidesConfigChanged(const KisGuidesConfig &config); private: friend class KisPart; friend class SafeSavingLocker; /** * Generate a name for the document. */ QString newObjectName(); QString autoSaveFile(const QString & path) const; /** * Loads a document * * Applies a filter if necessary, and calls loadNativeFormat in any case * You should not have to reimplement, except for very special cases. * * NOTE: this method also creates a new KisView instance! * * This method is called from the KReadOnlyPart::openUrl method. */ bool openFile(); /** * Saves a document * * Applies a filter if necessary, and calls saveNativeFormat in any case * You should not have to reimplement, except for very special cases. */ bool saveFile(); /** * Overload this function if you have to load additional files * from a store. This function is called after loadXML() * and after loadChildren() have been called. */ bool completeLoading(KoStore *store); /** * If you want to write additional files to a store, * then you must do it here. * In the implementation, you should prepend the document * url (using url().url()) before the filename, so that everything is kept relative * to this document. For instance it will produce urls such as * tar:/1/pictures/picture0.png, if the doc url is tar:/1 * But do this ONLY if the document is not stored extern (see isStoredExtern() ). * If it is, then the pictures should be saved to tar:/pictures. */ bool completeSaving(KoStore *store); /** @internal */ void setModified(); /** * Returns whether or not the current openUrl() or openFile() call is * actually an import operation (like File --> Import). * This is for informational purposes only. */ bool isImporting() const; /** * Returns whether or not the current saveFile() call is actually an export * operation (like File --> Export). * If this function returns true during saveFile() and you are changing * some sort of state, you _must_ restore it before the end of saveFile(); * otherwise, File --> Export will not work properly. */ bool isExporting() const; /** * Legacy method from KoDocumentBase. Don't use it anywhere * outside KisDocument! */ bool isAutosaving() const; public: QString localFilePath() const; void setLocalFilePath( const QString &localFilePath ); KoDocumentInfoDlg* createDocumentInfoDialog(QWidget *parent, KoDocumentInfo *docInfo) const; bool isReadWrite() const; QUrl url() const; void setUrl(const QUrl &url); bool closeUrl(bool promptToSave = true); bool saveAs( const QUrl &url ); public Q_SLOTS: bool save(); bool waitSaveComplete(); Q_SIGNALS: void completed(); void canceled(const QString &); private Q_SLOTS: void setImageModified(); void slotAutoSave(); /// Called by the undo stack when undo or redo is called void slotUndoStackIndexChanged(int idx); protected: bool oldLoadAndParse(KoStore *store, const QString& filename, KoXmlDocument& doc); public: /** * Create a new image that has this document as a parent and * replace the current image with this image. */ bool newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace * cs, const KoColor &bgColor, const QString &imageDescription, const double imageResolution); /** * Create a new image that has this document as a parent and * replace the current image with this image. */ bool newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace * cs, const KoColor &bgColor, bool backgroundAsLayer, int numberOfLayers, const QString &imageDescription, const double imageResolution); /** * Create a new image that has this document as a parent and * replace the current image with this image. */ KisImageWSP newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace * colorspace); KisImageWSP image() const; /** * Makes an otherwise empty document ready for import/export */ void prepareForImport(); /** * Adds progressproxy for file operations */ void setFileProgressProxy(); /** * Clears progressproxy for file operations */ void clearFileProgressProxy(); /** * Adds progressupdater for file operations */ void setFileProgressUpdater(const QString &text); /** * Clears progressupdater for file operations */ void clearFileProgressUpdater(); /** * Set the current image to the specified image and turn undo on. */ void setCurrentImage(KisImageSP image); KisUndoStore* createUndoStore(); /** * The shape controller matches internal krita image layers with * the flake shape hierarchy. */ KoShapeBasedDocumentBase * shapeController() const; KoShapeLayer* shapeForNode(KisNodeSP layer) const; /** * @return a list of all layers that are active in all current views */ vKisNodeSP activeNodes() const; /** * set the list of nodes that were marked as currently active */ void setPreActivatedNode(KisNodeSP activatedNode); /** * @return the node that was set as active during loading */ KisNodeSP preActivatedNode() const; QList assistants() const; void setAssistants(const QList value); private: void init(); bool saveToStream(QIODevice *dev); bool loadNativeFormatFromStoreInternal(KoStore *store); bool savePreview(KoStore *store); QString prettyPathOrUrl() const; bool saveToUrl(); bool openUrlInternal(const QUrl &url); class Private; Private *const d; }; Q_DECLARE_METATYPE(KisDocument*) #endif diff --git a/libs/ui/KisImportExportManager.cpp b/libs/ui/KisImportExportManager.cpp index 894928f0ee..564844919b 100644 --- a/libs/ui/KisImportExportManager.cpp +++ b/libs/ui/KisImportExportManager.cpp @@ -1,295 +1,294 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis 2000, 2001 Werner Trobin Copyright (C) 2004 Nicolas Goutte Copyright (C) 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 "KisImportExportManager.h" #include #include #include #include #include #include #include #include #include #include #include #include "KoProgressUpdater.h" #include "KoJsonTrader.h" #include #include #include "KisImportExportFilter.h" #include "KisDocument.h" #include #include // static cache for import and export mimetypes QStringList KisImportExportManager::m_importMimeTypes; QStringList KisImportExportManager::m_exportMimeTypes; class Q_DECL_HIDDEN KisImportExportManager::Private { public: bool batch; QByteArray importMimeType; QWeakPointer progressUpdater; Private(KoProgressUpdater *progressUpdater_ = 0) : progressUpdater(progressUpdater_) { } }; KisImportExportManager::KisImportExportManager(KisDocument* document) : m_document(document) , m_graph("") , d(new Private(0)) { d->batch = false; } KisImportExportManager::KisImportExportManager(const QString& location) : m_document(0) , m_importFileName(location) , m_graph("") , d(new Private) { d->batch = false; } KisImportExportManager::KisImportExportManager(const QByteArray& mimeType) : m_document(0) , m_graph("") , d(new Private) { d->batch = false; d->importMimeType = mimeType; } KisImportExportManager::~KisImportExportManager() { delete d; } QString KisImportExportManager::importDocument(const QString& location, const QString& documentMimeType, KisImportExportFilter::ConversionStatus& status) { // Find the mime type for the file to be imported. QString typeName = documentMimeType; if (typeName.isEmpty()) { typeName = KisMimeDatabase::mimeTypeForFile(location); } m_graph.setSourceMimeType(typeName.toLatin1()); // .latin1() is okay here (Werner) if (!m_graph.isValid()) { errFile << "Couldn't create a valid graph for this source mimetype: " << typeName; status = KisImportExportFilter::BadConversionGraph; return QString(); } KisFilterChainSP chain(0); // Are we owned by a KisDocument? if (m_document) { QByteArray mimeType = m_document->nativeFormatMimeType(); QStringList extraMimes = m_document->extraNativeMimeTypes(); int i = 0; int n = extraMimes.count(); chain = m_graph.chain(this, mimeType); while (i < n) { QByteArray extraMime = extraMimes[i].toUtf8(); // TODO check if its the same target mime then continue KisFilterChainSP newChain(0); newChain = m_graph.chain(this, extraMime); if (!chain || (newChain && newChain->weight() < chain->weight())) chain = newChain; ++i; } } else if (!d->importMimeType.isEmpty()) { chain = m_graph.chain(this, d->importMimeType); } else { errFile << "You aren't supposed to use import() from a filter!" << endl; status = KisImportExportFilter::UsageError; return QString(); } if (!chain) { errFile << "Couldn't create a valid filter chain!" << endl; importErrorHelper(typeName); status = KisImportExportFilter::BadConversionGraph; return QString(); } // Okay, let's invoke the filters one after the other m_direction = Import; // vital information! m_importFileName = location; m_exportFileName.clear(); status = chain->invokeChain(); m_importFileName.clear(); // Reset the import URL if (status == KisImportExportFilter::OK) return chain->chainOutput(); return QString(); } KisImportExportFilter::ConversionStatus KisImportExportManager::exportDocument(const QString& location, QByteArray& mimeType) { bool userCancelled = false; // The import url should already be set correctly (null if we have a KisDocument // file manager and to the correct URL if we have an embedded manager) m_direction = Export; // vital information! m_exportFileName = location; KisFilterChainSP chain; if (m_document) { // We have to pick the right native mimetype as source. QStringList nativeMimeTypes; nativeMimeTypes.append(m_document->nativeFormatMimeType()); nativeMimeTypes += m_document->extraNativeMimeTypes(); QStringList::ConstIterator it = nativeMimeTypes.constBegin(); const QStringList::ConstIterator end = nativeMimeTypes.constEnd(); for (; !chain && it != end; ++it) { m_graph.setSourceMimeType((*it).toLatin1()); if (m_graph.isValid()) chain = m_graph.chain(this, mimeType); } } else { QString t = KisMimeDatabase::mimeTypeForFile(m_importFileName); m_graph.setSourceMimeType(t.toLatin1()); } if (!m_graph.isValid()) { errFile << "Couldn't create a valid graph for this source mimetype."; QApplication::restoreOverrideCursor(); if (!d->batch && !userCancelled) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not export file: the export filter is missing.")); } return KisImportExportFilter::BadConversionGraph; } if (!chain) { // already set when coming from the m_document case chain = m_graph.chain(this, mimeType); } if (!chain) { errFile << "Couldn't create a valid filter chain to " << mimeType << " !" << endl; if (!d->batch) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not export file: the export filter is missing.")); } QApplication::restoreOverrideCursor(); return KisImportExportFilter::BadConversionGraph; } return chain->invokeChain(); } // The static method to figure out to which parts of the // graph this mimetype has a connection to. -QStringList KisImportExportManager::mimeFilter(const QByteArray &mimetype, Direction direction, const QStringList &extraNativeMimeTypes) +QStringList KisImportExportManager::mimeFilter(Direction direction) { - Q_UNUSED(mimetype); - Q_UNUSED(extraNativeMimeTypes); // Find the right mimetype by the extension QSet mimeTypes; +// mimeTypes << KisDocument::nativeFormatMimeType() << "application/x-krita-paintoppreset" << "image/openraster"; if (direction == KisImportExportManager::Import) { if (m_importMimeTypes.isEmpty()) { KoJsonTrader trader; QListlist = trader.query("Krita/FileFilter", ""); Q_FOREACH(QPluginLoader *loader, list) { QJsonObject json = loader->metaData().value("MetaData").toObject(); Q_FOREACH(const QString &mimetype, json.value("X-KDE-Import").toString().split(",")) { mimeTypes << mimetype; } } qDeleteAll(list); m_importMimeTypes = mimeTypes.toList(); } return m_importMimeTypes; } else if (direction == KisImportExportManager::Export) { if (m_exportMimeTypes.isEmpty()) { KoJsonTrader trader; QListlist = trader.query("Krita/FileFilter", ""); Q_FOREACH(QPluginLoader *loader, list) { QJsonObject json = loader->metaData().value("MetaData").toObject(); Q_FOREACH(const QString &mimetype, json.value("X-KDE-Export").toString().split(",")) { mimeTypes << mimetype; } } qDeleteAll(list); m_exportMimeTypes = mimeTypes.toList(); } qDebug() << m_exportMimeTypes; return m_exportMimeTypes; } return QStringList(); } void KisImportExportManager::importErrorHelper(const QString& mimeType, const bool suppressDialog) { // ###### FIXME: use KLibLoader::lastErrorMessage() here if (!suppressDialog) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not import file of type\n%1. The import filter is missing.", mimeType)); } } void KisImportExportManager::setBatchMode(const bool batch) { d->batch = batch; } bool KisImportExportManager::getBatchMode(void) const { return d->batch; } void KisImportExportManager::setProgresUpdater(KoProgressUpdater *updater) { d->progressUpdater = updater; } KoProgressUpdater* KisImportExportManager::progressUpdater() const { if (d->progressUpdater.isNull()) { // somebody, probably its parent, deleted our progress updater for us return 0; } return d->progressUpdater.data(); } #include diff --git a/libs/ui/KisImportExportManager.h b/libs/ui/KisImportExportManager.h index a28ccb16d5..a449bf8628 100644 --- a/libs/ui/KisImportExportManager.h +++ b/libs/ui/KisImportExportManager.h @@ -1,181 +1,180 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis 2000, 2001 Werner Trobin Copyright (C) 2004 Nicolas Goutte This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_IMPORT_EXPORT_MANAGER_H #define KIS_IMPORT_EXPORT_MANAGER_H #include #include #include #include #include "KisFilterGraph.h" #include "kritaui_export.h" class KisFilterChain; class KisDocument; class KoProgressUpdater; /** * @brief The class managing all the filters. * * This class manages all filters for a %Calligra application. Normally * you will not have to use it, since KisMainWindow takes care of loading * and saving documents. * * @ref KisFilter * * @author Kalle Dalheimer * @author Torben Weis * @author Werner Trobin */ class KRITAUI_EXPORT KisImportExportManager : public QObject { Q_OBJECT public: /** * This enum is used to distinguish the import/export cases */ enum Direction { Import = 1, Export = 2 }; /** * Create a filter manager for a document */ explicit KisImportExportManager(KisDocument *document); /** * Create a filter manager for the Shape Collection docker. * @param mimeType the mimetype to import to. */ explicit KisImportExportManager(const QByteArray& mimeType); /** * Create a filter manager for a filter which wants to embed something. * The path it passes is the file to convert. You cannot use * the @ref importDocument() method -- use @ref exportDocument() to convert * the file to the destination mimetype you prefer. * * @param path The location you want to export */ explicit KisImportExportManager(const QString& location); virtual ~KisImportExportManager(); /** * Imports the specified document and returns the resultant filename * (most likely some file in /tmp). * @p path can be either a URL or a filename. * @p documentMimeType gives importDocument a hint about what type * the document may be. It can be left empty. * @p status signals the success/error of the conversion. * If the QString which is returned isEmpty() and the status is OK, * then we imported the file directly into the document. */ QString importDocument(const QString& location, const QString& documentMimeType, KisImportExportFilter::ConversionStatus& status); /** * @brief Exports the given file/document to the specified URL/mimetype. * * If @p mimeType is empty, then the closest matching Calligra part is searched * and when the method returns @p mimeType contains this mimetype. * Oh, well, export is a C++ keyword ;) */ KisImportExportFilter::ConversionStatus exportDocument(const QString& location, QByteArray& mimeType); ///@name Static API //@{ /** * Suitable for passing to KoFileDialog::setMimeTypeFilters. The default mime * gets set by the "users" of this method, as we do not have enough * information here. * Optionally, @p extraNativeMimeTypes are added after the native mimetype. */ - static QStringList mimeFilter(const QByteArray& mimetype, Direction direction, - const QStringList& extraNativeMimeTypes = QStringList()); + static QStringList mimeFilter(Direction direction); /** * Set the filter manager is batch mode (no dialog shown) * instead of the interactive mode (dialog shown) */ void setBatchMode(const bool batch); /** * Get if the filter manager is batch mode (true) * or in interactive mode (true) */ bool getBatchMode(void) const; void setProgresUpdater(KoProgressUpdater *updater); /** * Return the KoProgressUpdater or 0 if there is none. **/ KoProgressUpdater *progressUpdater() const; private: // === API for KisFilterChains === (internal) // The friend methods are private in KisFilterChain and // just forward calls to the methods here. Should be // pretty safe. friend QString KisFilterChain::filterManagerImportFile() const; QString importFile() const { return m_importFileName; } friend QString KisFilterChain::filterManagerExportFile() const; QString exportFile() const { return m_exportFileName; } friend KisDocument *KisFilterChain::filterManagerKisDocument() const; KisDocument *document() const { return m_document; } friend int KisFilterChain::filterManagerDirection() const; int direction() const { return static_cast(m_direction); } // Private API KisImportExportManager(const KisImportExportManager& rhs); KisImportExportManager &operator=(const KisImportExportManager& rhs); // Convert file path string or URL string into QUrl QUrl locationToUrl(QString location) const; void importErrorHelper(const QString& mimeType, const bool suppressDialog = false); KisDocument *m_document; QString m_importFileName; QString m_exportFileName; CalligraFilter::Graph m_graph; Direction m_direction; /// A static cache for the availability checks of filters static QStringList m_importMimeTypes; static QStringList m_exportMimeTypes; class Private; Private * const d; }; #endif // __KO_FILTER_MANAGER_H__ diff --git a/libs/ui/KisMainWindow.cpp b/libs/ui/KisMainWindow.cpp index 0108e8b932..6281c17e33 100644 --- a/libs/ui/KisMainWindow.cpp +++ b/libs/ui/KisMainWindow.cpp @@ -1,2497 +1,2500 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2000-2006 David Faure Copyright (C) 2007, 2009 Thomas zander Copyright (C) 2010 Benjamin Port This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisMainWindow.h" #include // qt includes #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 #ifdef HAVE_KIO #include #endif #include #include #include #include #include #include #include #include #include #include #include "KoDockFactoryBase.h" #include "KoDockWidgetTitleBar.h" #include "KoDocumentInfoDlg.h" #include "KoDocumentInfo.h" #include "KoFileDialog.h" #include #include #include #include #include #include "KoToolDocker.h" #include #include #include #include #include #include #include "dialogs/kis_about_application.h" #include "dialogs/kis_delayed_save_dialog.h" #include "dialogs/kis_dlg_preferences.h" #include "kis_action.h" #include "kis_action_manager.h" #include "KisApplication.h" #include "kis_canvas2.h" #include "kis_canvas_controller.h" #include "kis_canvas_resource_provider.h" #include "kis_clipboard.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_config_notifier.h" #include "kis_custom_image_widget.h" #include #include "KisDocument.h" #include "KisDocument.h" #include "kis_group_layer.h" #include "kis_icon_utils.h" #include "kis_image_from_clipboard_widget.h" #include "kis_image.h" #include #include "KisImportExportManager.h" #include "kis_mainwindow_observer.h" #include "kis_node.h" #include "KisOpenPane.h" #include "kis_paintop_box.h" #include "KisPart.h" #include "KisPrintJob.h" #include "kis_resource_server_provider.h" #include "kis_signal_compressor_with_param.h" #include "KisView.h" #include "KisViewManager.h" #include "thememanager.h" #include "kis_animation_importer.h" #include "dialogs/kis_dlg_import_image_sequence.h" #include "kis_animation_exporter.h" #include class ToolDockerFactory : public KoDockFactoryBase { public: ToolDockerFactory() : KoDockFactoryBase() { } QString id() const { return "sharedtooldocker"; } QDockWidget* createDockWidget() { KoToolDocker* dockWidget = new KoToolDocker(); dockWidget->setTabEnabled(false); return dockWidget; } DockPosition defaultDockPosition() const { return DockRight; } }; class Q_DECL_HIDDEN KisMainWindow::Private { public: Private(KisMainWindow *parent) : q(parent) , viewManager(0) , firstTime(true) , windowSizeDirty(false) , readOnly(false) , isImporting(false) , isExporting(false) , noCleanup(false) , showDocumentInfo(0) , saveAction(0) , saveActionAs(0) // , printAction(0) // , printActionPreview(0) , exportPdf(0) , closeAll(0) // , reloadFile(0) , importFile(0) , exportFile(0) , undo(0) , redo(0) , newWindow(0) , close(0) , mdiCascade(0) , mdiTile(0) , mdiNextWindow(0) , mdiPreviousWindow(0) , toggleDockers(0) , toggleDockerTitleBars(0) , dockWidgetMenu(new KActionMenu(i18nc("@action:inmenu", "&Dockers"), parent)) , windowMenu(new KActionMenu(i18nc("@action:inmenu", "&Window"), parent)) , documentMenu(new KActionMenu(i18nc("@action:inmenu", "New &View"), parent)) , helpMenu(0) , recentFiles(0) , toolOptionsDocker(0) , deferredClosingEvent(0) , themeManager(0) , mdiArea(new QMdiArea(parent)) , activeSubWindow(0) , windowMapper(new QSignalMapper(parent)) , documentMapper(new QSignalMapper(parent)) , lastExportSpecialOutputFlag(0) , geometryInitialized(false) { } ~Private() { qDeleteAll(toolbarList); } KisMainWindow *q; KisViewManager *viewManager; QPointer activeView; QPointer progress; QPointer progressCancel; QMutex progressMutex; QList toolbarList; bool firstTime; bool windowSizeDirty; bool readOnly; bool isImporting; bool isExporting; bool noCleanup; KisAction *showDocumentInfo; KisAction *saveAction; KisAction *saveActionAs; // KisAction *printAction; // KisAction *printActionPreview; KisAction *exportPdf; KisAction *importAnimation; KisAction *exportAnimation; KisAction *closeAll; // KisAction *reloadFile; KisAction *importFile; KisAction *exportFile; KisAction *undo; KisAction *redo; KisAction *newWindow; KisAction *close; KisAction *mdiCascade; KisAction *mdiTile; KisAction *mdiNextWindow; KisAction *mdiPreviousWindow; KisAction *toggleDockers; KisAction *toggleDockerTitleBars; KisAction *expandingSpacers[2]; KActionMenu *dockWidgetMenu; KActionMenu *windowMenu; KActionMenu *documentMenu; KHelpMenu *helpMenu; KRecentFilesAction *recentFiles; QUrl lastExportUrl; QMap dockWidgetsMap; QMap dockWidgetVisibilityMap; QByteArray dockerStateBeforeHiding; KoToolDocker *toolOptionsDocker; QCloseEvent *deferredClosingEvent; Digikam::ThemeManager *themeManager; QMdiArea *mdiArea; QMdiSubWindow *activeSubWindow; QSignalMapper *windowMapper; QSignalMapper *documentMapper; QByteArray lastExportedFormat; int lastExportSpecialOutputFlag; QScopedPointer > tabSwitchCompressor; bool geometryInitialized; QMutex savingEntryMutex; KisActionManager * actionManager() { return viewManager->actionManager(); } QTabBar* findTabBarHACK() { QObjectList objects = mdiArea->children(); Q_FOREACH (QObject *object, objects) { QTabBar *bar = qobject_cast(object); if (bar) { return bar; } } return 0; } }; KisMainWindow::KisMainWindow() : KXmlGuiWindow() , d(new Private(this)) { KisConfig cfg; d->viewManager = new KisViewManager(this, actionCollection()); KConfigGroup group( KSharedConfig::openConfig(), "theme"); d->themeManager = new Digikam::ThemeManager(group.readEntry("Theme", "Krita dark"), this); setAcceptDrops(true); setStandardToolBarMenuEnabled(true); setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North); setDockNestingEnabled(true); qApp->setStartDragDistance(25); // 25 px is a distance that works well for Tablet and Mouse events #ifdef Q_OS_MAC setUnifiedTitleAndToolBarOnMac(true); #endif connect(this, SIGNAL(restoringDone()), this, SLOT(forceDockTabFonts())); connect(this, SIGNAL(documentSaved()), d->viewManager, SLOT(slotDocumentSaved())); connect(this, SIGNAL(themeChanged()), d->viewManager, SLOT(updateIcons())); connect(KisPart::instance(), SIGNAL(documentClosed(QString)), SLOT(updateWindowMenu())); connect(KisPart::instance(), SIGNAL(documentOpened(QString)), SLOT(updateWindowMenu())); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), this, SLOT(configChanged())); actionCollection()->addAssociatedWidget(this); KoToolBoxFactory toolBoxFactory; QDockWidget *toolbox = createDockWidget(&toolBoxFactory); toolbox->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetClosable); if (cfg.toolOptionsInDocker()) { ToolDockerFactory toolDockerFactory; d->toolOptionsDocker = qobject_cast(createDockWidget(&toolDockerFactory)); d->toolOptionsDocker->toggleViewAction()->setEnabled(true); } QMap dockwidgetActions; dockwidgetActions[toolbox->toggleViewAction()->text()] = toolbox->toggleViewAction(); Q_FOREACH (const QString & docker, KoDockRegistry::instance()->keys()) { KoDockFactoryBase *factory = KoDockRegistry::instance()->value(docker); QDockWidget *dw = createDockWidget(factory); dockwidgetActions[dw->toggleViewAction()->text()] = dw->toggleViewAction(); } if (d->toolOptionsDocker) { dockwidgetActions[d->toolOptionsDocker->toggleViewAction()->text()] = d->toolOptionsDocker->toggleViewAction(); } Q_FOREACH (QString title, dockwidgetActions.keys()) { d->dockWidgetMenu->addAction(dockwidgetActions[title]); } Q_FOREACH (QDockWidget *wdg, dockWidgets()) { if ((wdg->features() & QDockWidget::DockWidgetClosable) == 0) { wdg->setVisible(true); } } Q_FOREACH (KoCanvasObserverBase* observer, canvasObservers()) { observer->setObservedCanvas(0); KisMainwindowObserver* mainwindowObserver = dynamic_cast(observer); if (mainwindowObserver) { mainwindowObserver->setMainWindow(d->viewManager); } } d->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); d->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); d->mdiArea->setTabPosition(QTabWidget::North); d->mdiArea->setTabsClosable(true); setCentralWidget(d->mdiArea); connect(d->mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(subWindowActivated())); connect(d->windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); connect(d->documentMapper, SIGNAL(mapped(QObject*)), this, SLOT(newView(QObject*))); createActions(); setAutoSaveSettings("krita", false); KoPluginLoader::instance()->load("Krita/ViewPlugin", "Type == 'Service' and ([X-Krita-Version] == 28)", KoPluginLoader::PluginsConfig(), viewManager(), false); subWindowActivated(); updateWindowMenu(); if (isHelpMenuEnabled() && !d->helpMenu) { // workaround for KHelpMenu (or rather KAboutData::applicationData()) internally // not using the Q*Application metadata ATM, which results e.g. in the bugreport wizard // not having the app version preset // fixed hopefully in KF5 5.22.0, patch pending QGuiApplication *app = qApp; KAboutData aboutData(app->applicationName(), app->applicationDisplayName(), app->applicationVersion()); aboutData.setOrganizationDomain(app->organizationDomain().toUtf8()); d->helpMenu = new KHelpMenu(this, aboutData, false); // workaround-less version: // d->helpMenu = new KHelpMenu(this, QString()/*unused*/, false); // The difference between using KActionCollection->addAction() is that // these actions do not get tied to the MainWindow. What does this all do? KActionCollection *actions = d->viewManager->actionCollection(); QAction *helpContentsAction = d->helpMenu->action(KHelpMenu::menuHelpContents); QAction *whatsThisAction = d->helpMenu->action(KHelpMenu::menuWhatsThis); QAction *reportBugAction = d->helpMenu->action(KHelpMenu::menuReportBug); QAction *switchLanguageAction = d->helpMenu->action(KHelpMenu::menuSwitchLanguage); QAction *aboutAppAction = d->helpMenu->action(KHelpMenu::menuAboutApp); QAction *aboutKdeAction = d->helpMenu->action(KHelpMenu::menuAboutKDE); if (helpContentsAction) { actions->addAction(helpContentsAction->objectName(), helpContentsAction); } if (whatsThisAction) { actions->addAction(whatsThisAction->objectName(), whatsThisAction); } if (reportBugAction) { actions->addAction(reportBugAction->objectName(), reportBugAction); } if (switchLanguageAction) { actions->addAction(switchLanguageAction->objectName(), switchLanguageAction); } if (aboutAppAction) { actions->addAction(aboutAppAction->objectName(), aboutAppAction); } if (aboutKdeAction) { actions->addAction(aboutKdeAction->objectName(), aboutKdeAction); } connect(d->helpMenu, SIGNAL(showAboutApplication()), SLOT(showAboutApplication())); } // KDE' libs 4''s help contents action is broken outside kde, for some reason... We can handle it just as easily ourselves QAction *helpAction = actionCollection()->action("help_contents"); helpAction->disconnect(); connect(helpAction, SIGNAL(triggered()), this, SLOT(showManual())); #if 0 //check for colliding shortcuts QSet existingShortcuts; Q_FOREACH (QAction* action, actionCollection()->actions()) { if(action->shortcut() == QKeySequence(0)) { continue; } dbgKrita << "shortcut " << action->text() << " " << action->shortcut(); Q_ASSERT(!existingShortcuts.contains(action->shortcut())); existingShortcuts.insert(action->shortcut()); } #endif configChanged(); // If we have customized the toolbars, load that first setLocalXMLFile(KoResourcePaths::locateLocal("data", "krita.xmlgui")); setXMLFile(":/kxmlgui5/krita.xmlgui"); guiFactory()->addClient(this); // Create and plug toolbar list for Settings menu QList toolbarList; Q_FOREACH (QWidget* it, guiFactory()->containers("ToolBar")) { KToolBar * toolBar = ::qobject_cast(it); if (toolBar) { if (toolBar->objectName() == "BrushesAndStuff") { toolBar->setEnabled(false); } KToggleAction* act = new KToggleAction(i18n("Show %1 Toolbar", toolBar->windowTitle()), this); actionCollection()->addAction(toolBar->objectName().toUtf8(), act); act->setCheckedState(KGuiItem(i18n("Hide %1 Toolbar", toolBar->windowTitle()))); connect(act, SIGNAL(toggled(bool)), this, SLOT(slotToolbarToggled(bool))); act->setChecked(!toolBar->isHidden()); toolbarList.append(act); } else warnUI << "Toolbar list contains a " << it->metaObject()->className() << " which is not a toolbar!"; } plugActionList("toolbarlist", toolbarList); setToolbarList(toolbarList); applyToolBarLayout(); d->viewManager->updateGUI(); d->viewManager->updateIcons(); QTimer::singleShot(1000, this, SLOT(checkSanity())); { using namespace std::placeholders; // For _1 placeholder std::function callback( std::bind(&KisMainWindow::switchTab, this, _1)); d->tabSwitchCompressor.reset( new KisSignalCompressorWithParam(500, callback, KisSignalCompressor::FIRST_INACTIVE)); } } void KisMainWindow::setNoCleanup(bool noCleanup) { d->noCleanup = noCleanup; } KisMainWindow::~KisMainWindow() { // Q_FOREACH (QAction *ac, actionCollection()->actions()) { // QAction *action = qobject_cast(ac); // if (action) { // dbgKrita << "", "").replace("", "") // << "iconText=" << action->iconText().replace("&", "&") // << "shortcut=" << action->shortcut(QAction::ActiveShortcut).toString() // << "defaultShortcut=" << action->shortcut(QAction::DefaultShortcut).toString() // << "isCheckable=" << QString((action->isChecked() ? "true" : "false")) // << "statusTip=" << action->statusTip() // << "/>" ; // } // else { // dbgKrita << "Got a QAction:" << ac->objectName(); // } // } // The doc and view might still exist (this is the case when closing the window) KisPart::instance()->removeMainWindow(this); if (d->noCleanup) return; delete d->viewManager; delete d; } void KisMainWindow::addView(KisView *view) { if (d->activeView == view) return; if (d->activeView) { d->activeView->disconnect(this); } showView(view); updateCaption(); emit restoringDone(); if (d->activeView) { connect(d->activeView, SIGNAL(titleModified(QString,bool)), SLOT(slotDocumentTitleModified(QString,bool))); } } void KisMainWindow::showView(KisView *imageView) { if (imageView && activeView() != imageView) { // XXX: find a better way to initialize this! imageView->setViewManager(d->viewManager); imageView->canvasBase()->setFavoriteResourceManager(d->viewManager->paintOpBox()->favoriteResourcesManager()); imageView->slotLoadingFinished(); QMdiSubWindow *subwin = d->mdiArea->addSubWindow(imageView); subwin->setAttribute(Qt::WA_DeleteOnClose, true); connect(subwin, SIGNAL(destroyed()), SLOT(updateWindowMenu())); KisConfig cfg; subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); subwin->setWindowIcon(qApp->windowIcon()); - setActiveView(imageView); - /** * Hack alert! * * Here we explicitly request KoToolManager to emit all the tool * activation signals, to reinitialize the tool options docker. * * That is needed due to a design flaw we have in the * initialization procedure. The tool in the KoToolManager is * initialized in KisView::setViewManager() calls, which * happens early enough. During this call the tool manager * requests KoCanvasControllerWidget to emit the signal to * update the widgets in the tool docker. *But* at that moment * of time the view is not yet connected to the main window, * because it happens in KisViewManager::setCurrentView a bit * later. This fact makes the widgets updating signals be lost * and never reach the tool docker. * * So here we just explicitly call the tool activation stub. */ KoToolManager::instance()->initializeCurrentToolForCanvas(); if (d->mdiArea->subWindowList().size() == 1) { imageView->showMaximized(); } else { imageView->show(); } + // No, no, no: do not try to call this _before_ the show() has + // been called on the view; only when that has happened is the + // opengl context active, and very bad things happen if we tell + // the dockers to update themselves with a view if the opengl + // context is not active. + setActiveView(imageView); + updateWindowMenu(); updateCaption(); } } void KisMainWindow::slotPreferences() { if (KisDlgPreferences::editPreferences()) { KisConfigNotifier::instance()->notifyConfigChanged(); // XXX: should this be changed for the views in other windows as well? Q_FOREACH (QPointer koview, KisPart::instance()->views()) { KisViewManager *view = qobject_cast(koview); if (view) { // Update the settings for all nodes -- they don't query // KisConfig directly because they need the settings during // compositing, and they don't connect to the config notifier // because nodes are not QObjects (because only one base class // can be a QObject). KisNode* node = dynamic_cast(view->image()->rootLayer().data()); node->updateSettings(); } } d->viewManager->showHideScrollbars(); } } void KisMainWindow::slotThemeChanged() { // save theme changes instantly KConfigGroup group( KSharedConfig::openConfig(), "theme"); group.writeEntry("Theme", d->themeManager->currentThemeName()); // reload action icons! Q_FOREACH (QAction *action, actionCollection()->actions()) { KisIconUtils::updateIcon(action); } emit themeChanged(); } void KisMainWindow::updateReloadFileAction(KisDocument *doc) { Q_UNUSED(doc); // d->reloadFile->setEnabled(doc && !doc->url().isEmpty()); } void KisMainWindow::setReadWrite(bool readwrite) { d->saveAction->setEnabled(readwrite); d->importFile->setEnabled(readwrite); d->readOnly = !readwrite; updateCaption(); } void KisMainWindow::addRecentURL(const QUrl &url) { dbgUI << "KisMainWindow::addRecentURL url=" << url.toDisplayString(); // Add entry to recent documents list // (call coming from KisDocument because it must work with cmd line, template dlg, file/open, etc.) if (!url.isEmpty()) { bool ok = true; if (url.isLocalFile()) { QString path = url.adjusted(QUrl::StripTrailingSlash).toLocalFile(); const QStringList tmpDirs = KoResourcePaths::resourceDirs("tmp"); for (QStringList::ConstIterator it = tmpDirs.begin() ; ok && it != tmpDirs.end() ; ++it) if (path.contains(*it)) ok = false; // it's in the tmp resource #ifdef HAVE_KIO if (ok) { - KRecentDocument::add(path); + KRecentDocument::add(QUrl::fromLocalFile(path)); } #endif } #ifdef HAVE_KIO else { - KRecentDocument::add(url.url(QUrl::StripTrailingSlash), true); + KRecentDocument::add(url.adjusted(QUrl::StripTrailingSlash)); } #endif if (ok) { d->recentFiles->addUrl(url); } saveRecentFiles(); } } void KisMainWindow::saveRecentFiles() { // Save list of recent files KSharedConfigPtr config = KSharedConfig::openConfig(); d->recentFiles->saveEntries(config->group("RecentFiles")); config->sync(); // Tell all windows to reload their list, after saving // Doesn't work multi-process, but it's a start Q_FOREACH (KMainWindow* window, KMainWindow::memberList()) static_cast(window)->reloadRecentFileList(); } void KisMainWindow::reloadRecentFileList() { d->recentFiles->loadEntries( KSharedConfig::openConfig()->group("RecentFiles")); } void KisMainWindow::updateCaption() { if (!d->mdiArea->activeSubWindow()) { updateCaption(QString(), false); } else { QString caption( d->activeView->document()->caption() ); if (d->readOnly) { caption += ' ' + i18n("(write protected)"); } d->activeView->setWindowTitle(caption); updateCaption(caption, d->activeView->document()->isModified()); if (!d->activeView->document()->url().fileName().isEmpty()) d->saveAction->setToolTip(i18n("Save as %1", d->activeView->document()->url().fileName())); else d->saveAction->setToolTip(i18n("Save")); } } void KisMainWindow::updateCaption(const QString & caption, bool mod) { dbgUI << "KisMainWindow::updateCaption(" << caption << "," << mod << ")"; #ifdef KRITA_ALPHA setCaption(QString("ALPHA %1: %2").arg(KRITA_ALPHA).arg(caption), mod); return; #endif #ifdef KRITA_BETA setCaption(QString("BETA %1: %2").arg(KRITA_BETA).arg(caption), mod); return; #endif #ifdef KRITA_RC setCaption(QString("RELEASE CANDIDATE %1: %2").arg(KRITA_RC).arg(caption), mod); return; #endif setCaption(caption, mod); } KisView *KisMainWindow::activeView() const { if (d->activeView) { return d->activeView; } return 0; } bool KisMainWindow::openDocument(const QUrl &url) { if (!QFile(url.toLocalFile()).exists()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("The file %1 does not exist.", url.url())); d->recentFiles->removeUrl(url); //remove the file from the recent-opened-file-list saveRecentFiles(); return false; } return openDocumentInternal(url); } bool KisMainWindow::openDocumentInternal(const QUrl &url, KisDocument *newdoc) { if (!url.isLocalFile()) { qDebug() << "KisMainWindow::openDocumentInternal. Not a local file:" << url; return false; } if (!newdoc) { newdoc = KisPart::instance()->createDocument(); } d->firstTime = true; connect(newdoc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); connect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); connect(newdoc, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &))); bool openRet = (!isImporting()) ? newdoc->openUrl(url) : newdoc->importDocument(url); if (!openRet) { delete newdoc; return false; } KisPart::instance()->addDocument(newdoc); updateReloadFileAction(newdoc); if (!QFileInfo(url.toLocalFile()).isWritable()) { setReadWrite(false); } return true; } void KisMainWindow::addViewAndNotifyLoadingCompleted(KisDocument *document) { KisView *view = KisPart::instance()->createView(document, resourceManager(), actionCollection(), this); addView(view); emit guiLoadingFinished(); } QStringList KisMainWindow::showOpenFileDialog() { KoFileDialog dialog(this, KoFileDialog::ImportFiles, "OpenDocument"); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); - dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KIS_MIME_TYPE, - KisImportExportManager::Import, - KisDocument::extraNativeMimeTypes())); - dialog.setHideNameFilterDetailsOption(); + dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import)); dialog.setCaption(isImporting() ? i18n("Import Images") : i18n("Open Images")); return dialog.filenames(); } // Separate from openDocument to handle async loading (remote URLs) void KisMainWindow::slotLoadCompleted() { KisDocument *newdoc = qobject_cast(sender()); addViewAndNotifyLoadingCompleted(newdoc); disconnect(newdoc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); disconnect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); disconnect(newdoc, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &))); emit loadCompleted(); } void KisMainWindow::slotLoadCanceled(const QString & errMsg) { dbgUI << "KisMainWindow::slotLoadCanceled"; if (!errMsg.isEmpty()) // empty when canceled by user QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg); // ... can't delete the document, it's the one who emitted the signal... KisDocument* doc = qobject_cast(sender()); Q_ASSERT(doc); disconnect(doc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); disconnect(doc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); disconnect(doc, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &))); } void KisMainWindow::slotSaveCanceled(const QString &errMsg) { dbgUI << "KisMainWindow::slotSaveCanceled"; if (!errMsg.isEmpty()) // empty when canceled by user QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg); slotSaveCompleted(); } void KisMainWindow::slotSaveCompleted() { dbgUI << "KisMainWindow::slotSaveCompleted"; KisDocument* doc = qobject_cast(sender()); Q_ASSERT(doc); disconnect(doc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); disconnect(doc, SIGNAL(completed()), this, SLOT(slotSaveCompleted())); disconnect(doc, SIGNAL(canceled(const QString &)), this, SLOT(slotSaveCanceled(const QString &))); if (d->deferredClosingEvent) { KXmlGuiWindow::closeEvent(d->deferredClosingEvent); } } bool KisMainWindow::saveDocument(KisDocument *document, bool saveas, bool silent, int specialOutputFlag) { if (!document) { return true; } /** * Make sure that we cannot enter this method twice! * * The lower level functions may call processEvents() so * double-entry is quite possible to achive. Here we try to lock * the mutex, and if it is failed, just cancel saving. */ StdLockableWrapper wrapper(&d->savingEntryMutex); std::unique_lock> l(wrapper, std::try_to_lock); if (!l.owns_lock()) return false; KisDelayedSaveDialog dlg(document->image(), this); dlg.blockIfImageIsBusy(); if (dlg.result() != QDialog::Accepted) { return false; } bool reset_url; if (document->url().isEmpty()) { reset_url = true; saveas = true; } else { reset_url = false; } connect(document, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); connect(document, SIGNAL(completed()), this, SLOT(slotSaveCompleted())); connect(document, SIGNAL(canceled(const QString &)), this, SLOT(slotSaveCanceled(const QString &))); QUrl oldURL = document->url(); QString oldFile = document->localFilePath(); QByteArray _native_format = document->nativeFormatMimeType(); QByteArray oldOutputFormat = document->outputMimeType(); int oldSpecialOutputFlag = document->specialOutputFlag(); QUrl suggestedURL = document->url(); QStringList mimeFilter; if (specialOutputFlag) { mimeFilter = KisMimeDatabase::suffixesForMimeType(_native_format); } else { - mimeFilter = KisImportExportManager::mimeFilter(_native_format, - KisImportExportManager::Export, - document->extraNativeMimeTypes()); + mimeFilter = KisImportExportManager::mimeFilter(KisImportExportManager::Export); } if (!mimeFilter.contains(oldOutputFormat) && !isExporting()) { dbgUI << "KisMainWindow::saveDocument no export filter for" << oldOutputFormat; // --- don't setOutputMimeType in case the user cancels the Save As // dialog and then tries to just plain Save --- // suggest a different filename extension (yes, we fortunately don't all live in a world of magic :)) QString suggestedFilename = suggestedURL.fileName(); if (!suggestedFilename.isEmpty()) { // ".kra" looks strange for a name int c = suggestedFilename.lastIndexOf('.'); const QString ext = KisMimeDatabase::suffixesForMimeType(_native_format).first(); if (!ext.isEmpty()) { if (c < 0) - suggestedFilename += ext; + suggestedFilename = suggestedFilename + "." + ext; else - suggestedFilename = suggestedFilename.left(c) + ext; + suggestedFilename = suggestedFilename.left(c) + "." + ext; } else { // current filename extension wrong anyway if (c > 0) { // this assumes that a . signifies an extension, not just a . suggestedFilename = suggestedFilename.left(c); } } suggestedURL = suggestedURL.adjusted(QUrl::RemoveFilename); suggestedURL.setPath(suggestedURL.path() + suggestedFilename); } // force the user to choose outputMimeType saveas = true; } bool ret = false; if (document->url().isEmpty() || saveas) { // if you're just File/Save As'ing to change filter options you // don't want to be reminded about overwriting files etc. bool justChangingFilterOptions = false; KoFileDialog dialog(this, KoFileDialog::SaveFile, "SaveDocument"); dialog.setCaption(i18n("untitled")); if (isExporting() && !d->lastExportUrl.isEmpty()) { dialog.setDefaultDir(d->lastExportUrl.toLocalFile(), true); } else { dialog.setDefaultDir(suggestedURL.toLocalFile(), true); } // Default to all supported file types if user is exporting, otherwise use Krita default - dialog.setMimeTypeFilters(mimeFilter, isExporting() ? "" : KIS_MIME_TYPE); + dialog.setMimeTypeFilters(mimeFilter); QUrl newURL = QUrl::fromUserInput(dialog.filename()); if (newURL.isLocalFile()) { QString fn = newURL.toLocalFile(); if (QFileInfo(fn).completeSuffix().isEmpty()) { fn.append(KisMimeDatabase::suffixesForMimeType(_native_format).first()); newURL = QUrl::fromLocalFile(fn); } } if (document->documentInfo()->aboutInfo("title") == i18n("Unnamed")) { QString fn = newURL.toLocalFile(); QFileInfo info(fn); document->documentInfo()->setAboutInfo("title", info.baseName()); } QByteArray outputFormat = _native_format; if (!specialOutputFlag) { QString outputFormatString = KisMimeDatabase::mimeTypeForFile(newURL.toLocalFile()); outputFormat = outputFormatString.toLatin1(); } if (!isExporting()) justChangingFilterOptions = (newURL == document->url()) && (outputFormat == document->mimeType()) && (specialOutputFlag == oldSpecialOutputFlag); else justChangingFilterOptions = (newURL == d->lastExportUrl) && (outputFormat == d->lastExportedFormat) && (specialOutputFlag == d->lastExportSpecialOutputFlag); bool bOk = true; if (newURL.isEmpty()) { bOk = false; } // adjust URL before doing checks on whether the file exists. if (specialOutputFlag) { QString fileName = newURL.fileName(); if ( specialOutputFlag== KisDocument::SaveAsDirectoryStore) { //dbgKrita << "save to directory: " << newURL.url(); } } if (bOk) { bool wantToSave = true; // don't change this line unless you know what you're doing :) if (!justChangingFilterOptions || document->confirmNonNativeSave(isExporting())) { if (!document->isNativeFormat(outputFormat)) wantToSave = true; } if (wantToSave) { // // Note: // If the user is stupid enough to Export to the current URL, // we do _not_ change this operation into a Save As. Reasons // follow: // // 1. A check like "isExporting() && oldURL == newURL" // doesn't _always_ work on case-insensitive filesystems // and inconsistent behaviour is bad. // 2. It is probably not a good idea to change document->mimeType // and friends because the next time the user File/Save's, // (not Save As) they won't be expecting that they are // using their File/Export settings // // As a bad side-effect of this, the modified flag will not // be updated and it is possible that what is currently on // their screen is not what is stored on disk (through loss // of formatting). But if you are dumb enough to change // mimetype but not the filename, then arguably, _you_ are // the "bug" :) // // - Clarence // document->setOutputMimeType(outputFormat, specialOutputFlag); if (!isExporting()) { // Save As ret = document->saveAs(newURL); if (ret) { dbgUI << "Successful Save As!"; addRecentURL(newURL); setReadWrite(true); } else { dbgUI << "Failed Save As!"; document->setUrl(oldURL); document->setLocalFilePath(oldFile); document->setOutputMimeType(oldOutputFormat, oldSpecialOutputFlag); } } else { // Export ret = document->exportDocument(newURL); if (ret) { // a few file dialog convenience things d->lastExportUrl = newURL; d->lastExportedFormat = outputFormat; d->lastExportSpecialOutputFlag = specialOutputFlag; } // always restore output format document->setOutputMimeType(oldOutputFormat, oldSpecialOutputFlag); } if (silent) // don't let the document change the window caption document->setTitleModified(); } // if (wantToSave) { else ret = false; } // if (bOk) { else ret = false; } else { // saving bool needConfirm = document->confirmNonNativeSave(false) && !document->isNativeFormat(oldOutputFormat); if (!needConfirm || (needConfirm && exportConfirmation(oldOutputFormat /* not so old :) */)) ) { // be sure document has the correct outputMimeType! if (isExporting() || document->isModified()) { ret = document->save(); } if (!ret) { dbgUI << "Failed Save!"; document->setUrl(oldURL); document->setLocalFilePath(oldFile); } } else ret = false; } if (!ret && reset_url) document->resetURL(); //clean the suggested filename as the save dialog was rejected updateReloadFileAction(document); updateCaption(); return ret; } bool KisMainWindow::exportConfirmation(const QByteArray &/*outputFormat*/) { return true; } void KisMainWindow::undo() { if (activeView()) { activeView()->undoAction()->trigger(); d->undo->setText(activeView()->undoAction()->text()); } } void KisMainWindow::redo() { if (activeView()) { activeView()->redoAction()->trigger(); d->redo->setText(activeView()->redoAction()->text()); } } void KisMainWindow::showEvent(QShowEvent *e) { KXmlGuiWindow::showEvent(e); if (!d->geometryInitialized) { /** * We should move the window only *after* it has been shown on * screen, otherwise it will become owned by a wrong screen, which * will make positioning of all the child widgets wrong. * (see bug https://bugs.kde.org/show_bug.cgi?id=362025) * * This is actually a bug/feature of Qt 5.5.x and it has been * fixed in Qt 5.6.0. So we can avoid this delay on newer versions * of Qt. */ QTimer::singleShot(1, this, SLOT(initializeGeometry())); d->geometryInitialized = true; } } void KisMainWindow::closeEvent(QCloseEvent *e) { d->mdiArea->closeAllSubWindows(); KConfigGroup cfg( KSharedConfig::openConfig(), "MainWindow"); cfg.writeEntry("ko_geometry", saveGeometry().toBase64()); cfg.writeEntry("ko_windowstate", saveState().toBase64()); { KConfigGroup group( KSharedConfig::openConfig(), "theme"); group.writeEntry("Theme", d->themeManager->currentThemeName()); } if(d->activeView && d->activeView->document() && d->activeView->document()->isLoading()) { e->setAccepted(false); return; } QList childrenList = d->mdiArea->subWindowList(); if (childrenList.isEmpty()) { d->deferredClosingEvent = e; if (!d->dockerStateBeforeHiding.isEmpty()) { restoreState(d->dockerStateBeforeHiding); } statusBar()->setVisible(true); menuBar()->setVisible(true); saveWindowSettings(); if (d->noCleanup) return; Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) { KisView *view = dynamic_cast(subwin); if (view) { KisPart::instance()->removeView(view); } } if (!d->dockWidgetVisibilityMap.isEmpty()) { // re-enable dockers for persistency Q_FOREACH (QDockWidget* dockWidget, d->dockWidgetsMap) dockWidget->setVisible(d->dockWidgetVisibilityMap.value(dockWidget)); } } else { e->setAccepted(false); } } void KisMainWindow::saveWindowSettings() { KSharedConfigPtr config = KSharedConfig::openConfig(); if (d->windowSizeDirty ) { dbgUI << "KisMainWindow::saveWindowSettings"; KConfigGroup group = config->group("MainWindow"); KWindowConfig::saveWindowSize(windowHandle(), group); config->sync(); d->windowSizeDirty = false; } if (!d->activeView || d->activeView->document()) { // Save toolbar position into the config file of the app, under the doc's component name KConfigGroup group = KSharedConfig::openConfig()->group("krita"); saveMainWindowSettings(group); // Save collapsable state of dock widgets for (QMap::const_iterator i = d->dockWidgetsMap.constBegin(); i != d->dockWidgetsMap.constEnd(); ++i) { if (i.value()->widget()) { KConfigGroup dockGroup = group.group(QString("DockWidget ") + i.key()); dockGroup.writeEntry("Collapsed", i.value()->widget()->isHidden()); dockGroup.writeEntry("Locked", i.value()->property("Locked").toBool()); dockGroup.writeEntry("DockArea", (int) dockWidgetArea(i.value())); dockGroup.writeEntry("xPosition", (int) i.value()->widget()->x()); dockGroup.writeEntry("yPosition", (int) i.value()->widget()->y()); dockGroup.writeEntry("width", (int) i.value()->widget()->width()); dockGroup.writeEntry("height", (int) i.value()->widget()->height()); } } } KSharedConfig::openConfig()->sync(); resetAutoSaveSettings(); // Don't let KMainWindow override the good stuff we wrote down } void KisMainWindow::resizeEvent(QResizeEvent * e) { d->windowSizeDirty = true; KXmlGuiWindow::resizeEvent(e); } void KisMainWindow::setActiveView(KisView* view) { d->activeView = view; updateCaption(); actionCollection()->action("edit_undo")->setText(activeView()->undoAction()->text()); actionCollection()->action("edit_redo")->setText(activeView()->redoAction()->text()); d->viewManager->setCurrentView(view); } void KisMainWindow::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasUrls() || event->mimeData()->hasFormat("application/x-krita-node") || event->mimeData()->hasFormat("application/x-qt-image")) { event->accept(); } } void KisMainWindow::dropEvent(QDropEvent *event) { if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) { Q_FOREACH (const QUrl &url, event->mimeData()->urls()) { openDocument(url); } } } void KisMainWindow::dragMoveEvent(QDragMoveEvent * event) { QTabBar *tabBar = d->findTabBarHACK(); if (!tabBar && d->mdiArea->viewMode() == QMdiArea::TabbedView) { qWarning() << "WARNING!!! Cannot find QTabBar in the main window! Looks like Qt has changed behavior. Drag & Drop between multiple tabs might not work properly (tabs will not switch automatically)!"; } if (tabBar && tabBar->isVisible()) { QPoint pos = tabBar->mapFromGlobal(mapToGlobal(event->pos())); if (tabBar->rect().contains(pos)) { const int tabIndex = tabBar->tabAt(pos); if (tabIndex >= 0 && tabBar->currentIndex() != tabIndex) { d->tabSwitchCompressor->start(tabIndex); } } else if (d->tabSwitchCompressor->isActive()) { d->tabSwitchCompressor->stop(); } } } void KisMainWindow::dragLeaveEvent(QDragLeaveEvent * /*event*/) { if (d->tabSwitchCompressor->isActive()) { d->tabSwitchCompressor->stop(); } } void KisMainWindow::switchTab(int index) { QTabBar *tabBar = d->findTabBarHACK(); if (!tabBar) return; tabBar->setCurrentIndex(index); } void KisMainWindow::slotFileNew() { KisOpenPane *startupWidget = 0; - const QStringList mimeFilter = KisImportExportManager::mimeFilter(KIS_MIME_TYPE, - KisImportExportManager::Import, - KisDocument::extraNativeMimeTypes()); + const QStringList mimeFilter = KisImportExportManager::mimeFilter(KisImportExportManager::Import); startupWidget = new KisOpenPane(this, mimeFilter, QStringLiteral("templates/")); startupWidget->setWindowModality(Qt::WindowModal); KisConfig cfg; int w = cfg.defImageWidth(); int h = cfg.defImageHeight(); const double resolution = cfg.defImageResolution(); const QString colorModel = cfg.defColorModel(); const QString colorDepth = cfg.defaultColorDepth(); const QString colorProfile = cfg.defColorProfile(); CustomDocumentWidgetItem item; item.widget = new KisCustomImageWidget(startupWidget, w, h, resolution, colorModel, colorDepth, colorProfile, i18n("Unnamed")); item.icon = "application-x-krita"; startupWidget->addCustomDocumentWidget(item.widget, item.title, item.icon); QSize sz = KisClipboard::instance()->clipSize(); if (sz.isValid() && sz.width() != 0 && sz.height() != 0) { w = sz.width(); h = sz.height(); } item.widget = new KisImageFromClipboard(startupWidget, w, h, resolution, colorModel, colorDepth, colorProfile, i18n("Unnamed")); item.title = i18n("Create from Clipboard"); item.icon = "klipper"; startupWidget->addCustomDocumentWidget(item.widget, item.title, item.icon); // calls deleteLater connect(startupWidget, SIGNAL(documentSelected(KisDocument*)), KisPart::instance(), SLOT(startCustomDocument(KisDocument*))); // calls deleteLater connect(startupWidget, SIGNAL(openTemplate(const QUrl&)), KisPart::instance(), SLOT(openTemplate(const QUrl&))); startupWidget->exec(); // Cancel calls deleteLater... } void KisMainWindow::slotFileOpen() { QStringList urls = showOpenFileDialog(); if (urls.isEmpty()) return; Q_FOREACH (const QString& url, urls) { if (!url.isEmpty()) { bool res = openDocument(QUrl::fromLocalFile(url)); if (!res) { warnKrita << "Loading" << url << "failed"; } } } } void KisMainWindow::slotFileOpenRecent(const QUrl &url) { (void) openDocument(QUrl(url)); } void KisMainWindow::slotFileSave() { if (saveDocument(d->activeView->document())) emit documentSaved(); } void KisMainWindow::slotFileSaveAs() { if (saveDocument(d->activeView->document(), true)) emit documentSaved(); } KoCanvasResourceManager *KisMainWindow::resourceManager() const { return d->viewManager->resourceProvider()->resourceManager(); } int KisMainWindow::viewCount() const { return d->mdiArea->subWindowList().size(); } bool KisMainWindow::restoreWorkspace(const QByteArray &state) { QByteArray oldState = saveState(); const bool showTitlebars = KisConfig().showDockerTitleBars(); // needed because otherwise the layout isn't correctly restored in some situations Q_FOREACH (QDockWidget *dock, dockWidgets()) { dock->hide(); dock->titleBarWidget()->setVisible(showTitlebars); } bool success = KXmlGuiWindow::restoreState(state); if (!success) { KXmlGuiWindow::restoreState(oldState); Q_FOREACH (QDockWidget *dock, dockWidgets()) { if (dock->titleBarWidget()) { dock->titleBarWidget()->setVisible(showTitlebars || dock->isFloating()); } } return false; } Q_FOREACH (QDockWidget *dock, dockWidgets()) { if (dock->titleBarWidget()) { const bool isCollapsed = (dock->widget() && dock->widget()->isHidden()) || !dock->widget(); dock->titleBarWidget()->setVisible(showTitlebars || (dock->isFloating() && isCollapsed)); } } return success; } KisViewManager *KisMainWindow::viewManager() const { return d->viewManager; } void KisMainWindow::slotDocumentInfo() { if (!d->activeView->document()) return; KoDocumentInfo *docInfo = d->activeView->document()->documentInfo(); if (!docInfo) return; KoDocumentInfoDlg *dlg = d->activeView->document()->createDocumentInfoDialog(this, docInfo); if (dlg->exec()) { if (dlg->isDocumentSaved()) { d->activeView->document()->setModified(false); } else { d->activeView->document()->setModified(true); } d->activeView->document()->setTitleModified(); } delete dlg; } bool KisMainWindow::slotFileCloseAll() { Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) { if (subwin) { if(!subwin->close()) return false; } } updateCaption(); return true; } void KisMainWindow::slotFileQuit() { if(!slotFileCloseAll()) return; close(); Q_FOREACH (QPointer mainWin, KisPart::instance()->mainWindows()) { if (mainWin != this) { if(!mainWin->slotFileCloseAll()) return; mainWin->close(); } } } void KisMainWindow::slotFilePrint() { if (!activeView()) return; KisPrintJob *printJob = activeView()->createPrintJob(); if (printJob == 0) return; applyDefaultSettings(printJob->printer()); QPrintDialog *printDialog = activeView()->createPrintDialog( printJob, this ); if (printDialog && printDialog->exec() == QDialog::Accepted) { printJob->printer().setPageMargins(0.0, 0.0, 0.0, 0.0, QPrinter::Point); printJob->printer().setPaperSize(QSizeF(activeView()->image()->width() / (72.0 * activeView()->image()->xRes()), activeView()->image()->height()/ (72.0 * activeView()->image()->yRes())), QPrinter::Inch); printJob->startPrinting(KisPrintJob::DeleteWhenDone); } else { delete printJob; } delete printDialog; } void KisMainWindow::slotFilePrintPreview() { if (!activeView()) return; KisPrintJob *printJob = activeView()->createPrintJob(); if (printJob == 0) return; /* Sets the startPrinting() slot to be blocking. The Qt print-preview dialog requires the printing to be completely blocking and only return when the full document has been printed. By default the KisPrintingDialog is non-blocking and multithreading, setting blocking to true will allow it to be used in the preview dialog */ printJob->setProperty("blocking", true); QPrintPreviewDialog *preview = new QPrintPreviewDialog(&printJob->printer(), this); printJob->setParent(preview); // will take care of deleting the job connect(preview, SIGNAL(paintRequested(QPrinter*)), printJob, SLOT(startPrinting())); preview->exec(); delete preview; } KisPrintJob* KisMainWindow::exportToPdf(const QString &pdfFileName) { if (!activeView()) return 0; KoPageLayout pageLayout; pageLayout = activeView()->pageLayout(); return exportToPdf(pageLayout, pdfFileName); } KisPrintJob* KisMainWindow::exportToPdf(KoPageLayout pageLayout, QString pdfFileName) { if (!activeView()) return 0; if (!activeView()->document()) return 0; if (pdfFileName.isEmpty()) { KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); QString defaultDir = group.readEntry("SavePdfDialog"); if (defaultDir.isEmpty()) defaultDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); QUrl startUrl = QUrl::fromLocalFile(defaultDir); KisDocument* pDoc = d->activeView->document(); /** if document has a file name, take file name and replace extension with .pdf */ if (pDoc && pDoc->url().isValid()) { startUrl = pDoc->url(); QString fileName = startUrl.fileName(); fileName = fileName.replace( QRegExp( "\\.\\w{2,5}$", Qt::CaseInsensitive ), ".pdf" ); startUrl = startUrl.adjusted(QUrl::RemoveFilename); startUrl.setPath(startUrl.path() + fileName ); } QPointer layoutDlg(new KoPageLayoutDialog(this, pageLayout)); layoutDlg->setWindowModality(Qt::WindowModal); if (layoutDlg->exec() != QDialog::Accepted || !layoutDlg) { delete layoutDlg; return 0; } pageLayout = layoutDlg->pageLayout(); delete layoutDlg; KoFileDialog dialog(this, KoFileDialog::SaveFile, "SaveDocument"); dialog.setCaption(i18n("Export as PDF")); dialog.setDefaultDir(startUrl.toLocalFile()); dialog.setMimeTypeFilters(QStringList() << "application/pdf"); QUrl url = QUrl::fromUserInput(dialog.filename()); pdfFileName = url.toLocalFile(); if (pdfFileName.isEmpty()) return 0; } KisPrintJob *printJob = activeView()->createPrintJob(); if (printJob == 0) return 0; if (isHidden()) { printJob->setProperty("noprogressdialog", true); } applyDefaultSettings(printJob->printer()); // TODO for remote files we have to first save locally and then upload. printJob->printer().setOutputFileName(pdfFileName); printJob->printer().setDocName(pdfFileName); printJob->printer().setColorMode(QPrinter::Color); if (pageLayout.format == KoPageFormat::CustomSize) { printJob->printer().setPaperSize(QSizeF(pageLayout.width, pageLayout.height), QPrinter::Millimeter); } else { printJob->printer().setPaperSize(KoPageFormat::printerPageSize(pageLayout.format)); } printJob->printer().setPageMargins(pageLayout.leftMargin, pageLayout.topMargin, pageLayout.rightMargin, pageLayout.bottomMargin, QPrinter::Millimeter); switch (pageLayout.orientation) { case KoPageFormat::Portrait: printJob->printer().setOrientation(QPrinter::Portrait); break; case KoPageFormat::Landscape: printJob->printer().setOrientation(QPrinter::Landscape); break; } //before printing check if the printer can handle printing if (!printJob->canPrint()) { QMessageBox::critical(this, i18nc("@title:window", "Krita"), i18n("Cannot export to the specified file")); } printJob->startPrinting(KisPrintJob::DeleteWhenDone); return printJob; } void KisMainWindow::importAnimation() { if (!activeView()) return; KisDocument *document = activeView()->document(); if (!document) return; KisDlgImportImageSequence dlg(this, document); if (dlg.exec() == QDialog::Accepted) { QStringList files = dlg.files(); int firstFrame = dlg.firstFrame(); int step = dlg.step(); document->setFileProgressProxy(); document->setFileProgressUpdater(i18n("Import frames")); KisAnimationImporter importer(document); KisImportExportFilter::ConversionStatus status = importer.import(files, firstFrame, step); document->clearFileProgressUpdater(); document->clearFileProgressProxy(); if (status != KisImportExportFilter::OK && status != KisImportExportFilter::InternalError) { QString msg = KisImportExportFilter::conversionStatusString(status); if (!msg.isEmpty()) QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not finish import animation:\n%1", msg)); } activeView()->canvasBase()->refetchDataFromImage(); } } void KisMainWindow::exportAnimation() { if (!activeView()) return; KisDocument *document = activeView()->document(); if (!document) return; document->setFileProgressProxy(); document->setFileProgressUpdater(i18n("Export frames")); KisAnimationExporterUI exporter(this); KisImportExportFilter::ConversionStatus status = exporter.exportSequence(document); document->clearFileProgressUpdater(); document->clearFileProgressProxy(); if (status != KisImportExportFilter::OK && status != KisImportExportFilter::InternalError) { QString msg = KisImportExportFilter::conversionStatusString(status); if (!msg.isEmpty()) QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not finish export animation:\n%1", msg)); } activeView()->canvasBase()->refetchDataFromImage(); } void KisMainWindow::slotConfigureToolbars() { KConfigGroup group = KSharedConfig::openConfig()->group("krita"); saveMainWindowSettings(group); KEditToolBar edit(factory(), this); connect(&edit, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig())); (void) edit.exec(); applyToolBarLayout(); } void KisMainWindow::slotNewToolbarConfig() { applyMainWindowSettings(KSharedConfig::openConfig()->group("krita")); KXMLGUIFactory *factory = guiFactory(); Q_UNUSED(factory); // Check if there's an active view if (!d->activeView) return; plugActionList("toolbarlist", d->toolbarList); applyToolBarLayout(); } void KisMainWindow::slotToolbarToggled(bool toggle) { //dbgUI <<"KisMainWindow::slotToolbarToggled" << sender()->name() <<" toggle=" << true; // The action (sender) and the toolbar have the same name KToolBar * bar = toolBar(sender()->objectName()); if (bar) { if (toggle) { bar->show(); } else { bar->hide(); } if (d->activeView && d->activeView->document()) { KConfigGroup group = KSharedConfig::openConfig()->group("krita"); saveMainWindowSettings(group); } } else warnUI << "slotToolbarToggled : Toolbar " << sender()->objectName() << " not found!"; } void KisMainWindow::viewFullscreen(bool fullScreen) { KisConfig cfg; +#ifdef Q_OS_WIN + cfg.setFullscreenMode(false); +#else cfg.setFullscreenMode(fullScreen); if (fullScreen) { setWindowState(windowState() | Qt::WindowFullScreen); // set } else { setWindowState(windowState() & ~Qt::WindowFullScreen); // reset } +#endif } void KisMainWindow::slotProgress(int value) { qApp->processEvents(); if (!d->progressMutex.tryLock()) return; dbgUI << "KisMainWindow::slotProgress" << value; if (value <= -1 || value >= 100) { if (d->progress) { statusBar()->removeWidget(d->progress); delete d->progress; d->progress = 0; disconnect(d->progressCancel, SIGNAL(clicked()), this, SLOT(slotProgressCanceled())); statusBar()->removeWidget(d->progressCancel); delete d->progressCancel; d->progressCancel = 0; } d->firstTime = true; d->progressMutex.unlock(); return; } if (d->firstTime || !d->progress) { // The statusbar might not even be created yet. // So check for that first, and create it if necessary QStatusBar *bar = findChild(); if (!bar) { statusBar()->show(); QApplication::sendPostedEvents(this, QEvent::ChildAdded); } if (d->progress) { statusBar()->removeWidget(d->progress); delete d->progress; d->progress = 0; disconnect(d->progressCancel, SIGNAL(clicked()), this, SLOT(slotProgressCanceled())); statusBar()->removeWidget(d->progressCancel); delete d->progressCancel; d->progress = 0; } d->progressCancel = new QToolButton(statusBar()); d->progressCancel->setMaximumHeight(statusBar()->fontMetrics().height()); d->progressCancel->setIcon(KisIconUtils::loadIcon("process-stop")); statusBar()->addPermanentWidget(d->progressCancel); d->progress = new QProgressBar(statusBar()); d->progress->setMaximumHeight(statusBar()->fontMetrics().height()); d->progress->setRange(0, 100); statusBar()->addPermanentWidget(d->progress); connect(d->progressCancel, SIGNAL(clicked()), this, SLOT(slotProgressCanceled())); d->progress->show(); d->progressCancel->show(); d->firstTime = false; } if (!d->progress.isNull()) { d->progress->setValue(value); } qApp->processEvents(); d->progressMutex.unlock(); } void KisMainWindow::slotProgressCanceled() { emit sigProgressCanceled(); } void KisMainWindow::setMaxRecentItems(uint _number) { d->recentFiles->setMaxItems(_number); } void KisMainWindow::slotReloadFile() { KisDocument* document = d->activeView->document(); if (!document || document->url().isEmpty()) return; if (document->isModified()) { bool ok = QMessageBox::question(this, i18nc("@title:window", "Krita"), i18n("You will lose all changes made since your last save\n" "Do you want to continue?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes; if (!ok) return; } QUrl url = document->url(); saveWindowSettings(); if (!document->reload()) { QMessageBox::critical(this, i18nc("@title:window", "Krita"), i18n("Error: Could not reload this document")); } return; } void KisMainWindow::slotImportFile() { dbgUI << "slotImportFile()"; d->isImporting = true; slotFileOpen(); d->isImporting = false; } void KisMainWindow::slotExportFile() { dbgUI << "slotExportFile()"; d->isExporting = true; slotFileSaveAs(); d->isExporting = false; } bool KisMainWindow::isImporting() const { return d->isImporting; } bool KisMainWindow::isExporting() const { return d->isExporting; } QDockWidget* KisMainWindow::createDockWidget(KoDockFactoryBase* factory) { QDockWidget* dockWidget = 0; if (!d->dockWidgetsMap.contains(factory->id())) { dockWidget = factory->createDockWidget(); // It is quite possible that a dock factory cannot create the dock; don't // do anything in that case. if (!dockWidget) { warnKrita << "Could not create docker for" << factory->id(); return 0; } KoDockWidgetTitleBar *titleBar = dynamic_cast(dockWidget->titleBarWidget()); // Check if the dock widget is supposed to be collapsable if (!dockWidget->titleBarWidget()) { titleBar = new KoDockWidgetTitleBar(dockWidget); dockWidget->setTitleBarWidget(titleBar); titleBar->setCollapsable(factory->isCollapsable()); } titleBar->setFont(KoDockRegistry::dockFont()); dockWidget->setObjectName(factory->id()); dockWidget->setParent(this); if (dockWidget->widget() && dockWidget->widget()->layout()) dockWidget->widget()->layout()->setContentsMargins(1, 1, 1, 1); Qt::DockWidgetArea side = Qt::RightDockWidgetArea; bool visible = true; switch (factory->defaultDockPosition()) { case KoDockFactoryBase::DockTornOff: dockWidget->setFloating(true); // position nicely? break; case KoDockFactoryBase::DockTop: side = Qt::TopDockWidgetArea; break; case KoDockFactoryBase::DockLeft: side = Qt::LeftDockWidgetArea; break; case KoDockFactoryBase::DockBottom: side = Qt::BottomDockWidgetArea; break; case KoDockFactoryBase::DockRight: side = Qt::RightDockWidgetArea; break; case KoDockFactoryBase::DockMinimized: default: side = Qt::RightDockWidgetArea; visible = false; } KConfigGroup group = KSharedConfig::openConfig()->group("krita").group("DockWidget " + factory->id()); side = static_cast(group.readEntry("DockArea", static_cast(side))); if (side == Qt::NoDockWidgetArea) side = Qt::RightDockWidgetArea; addDockWidget(side, dockWidget); if (!visible) { dockWidget->hide(); } bool collapsed = factory->defaultCollapsed(); bool locked = false; group = KSharedConfig::openConfig()->group("krita").group("DockWidget " + factory->id()); collapsed = group.readEntry("Collapsed", collapsed); locked = group.readEntry("Locked", locked); //dbgKrita << "docker" << factory->id() << dockWidget << "collapsed" << collapsed << "locked" << locked << "titlebar" << titleBar; if (titleBar && collapsed) titleBar->setCollapsed(true); if (titleBar && locked) titleBar->setLocked(true); d->dockWidgetsMap.insert(factory->id(), dockWidget); } else { dockWidget = d->dockWidgetsMap[factory->id()]; } #ifdef Q_OS_MAC dockWidget->setAttribute(Qt::WA_MacSmallSize, true); #endif dockWidget->setFont(KoDockRegistry::dockFont()); connect(dockWidget, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(forceDockTabFonts())); return dockWidget; } void KisMainWindow::forceDockTabFonts() { Q_FOREACH (QObject *child, children()) { if (child->inherits("QTabBar")) { ((QTabBar *)child)->setFont(KoDockRegistry::dockFont()); } } } QList KisMainWindow::dockWidgets() const { return d->dockWidgetsMap.values(); } QDockWidget* KisMainWindow::dockWidget(const QString &id) { if (!d->dockWidgetsMap.contains(id)) return 0; return d->dockWidgetsMap[id]; } QList KisMainWindow::canvasObservers() const { QList observers; Q_FOREACH (QDockWidget *docker, dockWidgets()) { KoCanvasObserverBase *observer = dynamic_cast(docker); if (observer) { observers << observer; } else { warnKrita << docker << "is not a canvas observer"; } } return observers; } void KisMainWindow::toggleDockersVisibility(bool visible) { if (!visible) { d->dockerStateBeforeHiding = saveState(); Q_FOREACH (QObject* widget, children()) { if (widget->inherits("QDockWidget")) { QDockWidget* dw = static_cast(widget); if (dw->isVisible()) { dw->hide(); } } } } else { restoreState(d->dockerStateBeforeHiding); } } void KisMainWindow::setToolbarList(QList toolbarList) { qDeleteAll(d->toolbarList); d->toolbarList = toolbarList; } void KisMainWindow::slotDocumentTitleModified(const QString &caption, bool mod) { updateCaption(caption, mod); updateReloadFileAction(d->activeView ? d->activeView->document() : 0); } void KisMainWindow::subWindowActivated() { bool enabled = (activeKisView() != 0); d->mdiCascade->setEnabled(enabled); d->mdiNextWindow->setEnabled(enabled); d->mdiPreviousWindow->setEnabled(enabled); d->mdiTile->setEnabled(enabled); d->close->setEnabled(enabled); d->closeAll->setEnabled(enabled); setActiveSubWindow(d->mdiArea->activeSubWindow()); Q_FOREACH (QToolBar *tb, toolBars()) { if (tb->objectName() == "BrushesAndStuff") { tb->setEnabled(enabled); } } updateCaption(); d->actionManager()->updateGUI(); } void KisMainWindow::updateWindowMenu() { QMenu *menu = d->windowMenu->menu(); menu->clear(); menu->addAction(d->newWindow); menu->addAction(d->documentMenu); QMenu *docMenu = d->documentMenu->menu(); docMenu->clear(); Q_FOREACH (QPointer doc, KisPart::instance()->documents()) { if (doc) { QString title = doc->url().toDisplayString(); if (title.isEmpty()) title = doc->image()->objectName(); QAction *action = docMenu->addAction(title); action->setIcon(qApp->windowIcon()); connect(action, SIGNAL(triggered()), d->documentMapper, SLOT(map())); d->documentMapper->setMapping(action, doc); } } menu->addSeparator(); menu->addAction(d->close); menu->addAction(d->closeAll); if (d->mdiArea->viewMode() == QMdiArea::SubWindowView) { menu->addSeparator(); menu->addAction(d->mdiTile); menu->addAction(d->mdiCascade); } menu->addSeparator(); menu->addAction(d->mdiNextWindow); menu->addAction(d->mdiPreviousWindow); menu->addSeparator(); QList windows = d->mdiArea->subWindowList(); for (int i = 0; i < windows.size(); ++i) { QPointerchild = qobject_cast(windows.at(i)->widget()); if (child) { QString text; if (i < 9) { text = i18n("&%1 %2", i + 1, child->document()->url().toDisplayString()); } else { text = i18n("%1 %2", i + 1, child->document()->url().toDisplayString()); } QAction *action = menu->addAction(text); action->setIcon(qApp->windowIcon()); action->setCheckable(true); action->setChecked(child == activeKisView()); connect(action, SIGNAL(triggered()), d->windowMapper, SLOT(map())); d->windowMapper->setMapping(action, windows.at(i)); } } updateCaption(); } void KisMainWindow::setActiveSubWindow(QWidget *window) { if (!window) return; QMdiSubWindow *subwin = qobject_cast(window); //dbgKrita << "setActiveSubWindow();" << subwin << d->activeSubWindow; if (subwin && subwin != d->activeSubWindow) { KisView *view = qobject_cast(subwin->widget()); //dbgKrita << "\t" << view << activeView(); if (view && view != activeView()) { setActiveView(view); } d->activeSubWindow = subwin; } updateWindowMenu(); d->actionManager()->updateGUI(); } void KisMainWindow::configChanged() { KisConfig cfg; QMdiArea::ViewMode viewMode = (QMdiArea::ViewMode)cfg.readEntry("mdi_viewmode", (int)QMdiArea::TabbedView); d->mdiArea->setViewMode(viewMode); Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) { subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); } KConfigGroup group( KSharedConfig::openConfig(), "theme"); d->themeManager->setCurrentTheme(group.readEntry("Theme", "Krita dark")); d->actionManager()->updateGUI(); QBrush brush(cfg.getMDIBackgroundColor()); d->mdiArea->setBackground(brush); QString backgroundImage = cfg.getMDIBackgroundImage(); if (backgroundImage != "") { QImage image(backgroundImage); QBrush brush(image); d->mdiArea->setBackground(brush); } d->mdiArea->update(); } void KisMainWindow::newView(QObject *document) { KisDocument *doc = qobject_cast(document); addViewAndNotifyLoadingCompleted(doc); d->actionManager()->updateGUI(); } void KisMainWindow::newWindow() { KisPart::instance()->createMainWindow()->show(); } void KisMainWindow::closeCurrentWindow() { d->mdiArea->currentSubWindow()->close(); d->actionManager()->updateGUI(); } void KisMainWindow::checkSanity() { // print error if the lcms engine is not available if (!KoColorSpaceEngineRegistry::instance()->contains("icc")) { // need to wait 1 event since exiting here would not work. m_errorMessage = i18n("The Calligra LittleCMS color management plugin is not installed. Krita will quit now."); m_dieOnError = true; QTimer::singleShot(0, this, SLOT(showErrorAndDie())); return; } KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); if (rserver->resources().isEmpty()) { m_errorMessage = i18n("Krita cannot find any brush presets! Krita will quit now."); m_dieOnError = true; QTimer::singleShot(0, this, SLOT(showErrorAndDie())); return; } } void KisMainWindow::showErrorAndDie() { QMessageBox::critical(0, i18nc("@title:window", "Installation error"), m_errorMessage); if (m_dieOnError) { exit(10); } } void KisMainWindow::showAboutApplication() { KisAboutApplication dlg(this); dlg.exec(); } QPointerKisMainWindow::activeKisView() { if (!d->mdiArea) return 0; QMdiSubWindow *activeSubWindow = d->mdiArea->activeSubWindow(); //dbgKrita << "activeKisView" << activeSubWindow; if (!activeSubWindow) return 0; return qobject_cast(activeSubWindow->widget()); } void KisMainWindow::newOptionWidgets(const QList > &optionWidgetList) { Q_FOREACH (QWidget *w, optionWidgetList) { #ifdef Q_OS_MAC w->setAttribute(Qt::WA_MacSmallSize, true); #endif w->setFont(KoDockRegistry::dockFont()); } if (d->toolOptionsDocker) { d->toolOptionsDocker->setOptionWidgets(optionWidgetList); } else { d->viewManager->paintOpBox()->newOptionWidgets(optionWidgetList); } } void KisMainWindow::applyDefaultSettings(QPrinter &printer) { if (!d->activeView) return; QString title = d->activeView->document()->documentInfo()->aboutInfo("title"); if (title.isEmpty()) { title = d->activeView->document()->url().fileName(); // strip off the native extension (I don't want foobar.kwd.ps when printing into a file) QString extension = KisMimeDatabase::suffixesForMimeType(d->activeView->document()->outputMimeType()).first(); if (title.endsWith(extension)) { title.chop(extension.length()); } } if (title.isEmpty()) { // #139905 title = i18n("%1 unsaved document (%2)", qApp->applicationDisplayName(), QLocale().toString(QDate::currentDate(), QLocale::ShortFormat)); } printer.setDocName(title); } void KisMainWindow::createActions() { KisActionManager *actionManager = d->actionManager(); actionManager->createStandardAction(KStandardAction::New, this, SLOT(slotFileNew())); actionManager->createStandardAction(KStandardAction::Open, this, SLOT(slotFileOpen())); actionManager->createStandardAction(KStandardAction::Quit, this, SLOT(slotFileQuit())); actionManager->createStandardAction(KStandardAction::ConfigureToolbars, this, SLOT(slotConfigureToolbars())); +#ifndef Q_OS_WIN actionManager->createStandardAction(KStandardAction::FullScreen, this, SLOT(viewFullscreen(bool))); - +#endif d->recentFiles = KStandardAction::openRecent(this, SLOT(slotFileOpenRecent(QUrl)), actionCollection()); connect(d->recentFiles, SIGNAL(recentListCleared()), this, SLOT(saveRecentFiles())); KSharedConfigPtr configPtr = KSharedConfig::openConfig(); d->recentFiles->loadEntries(configPtr->group("RecentFiles")); d->saveAction = actionManager->createStandardAction(KStandardAction::Save, this, SLOT(slotFileSave())); d->saveAction->setActivationFlags(KisAction::ACTIVE_IMAGE); d->saveActionAs = actionManager->createStandardAction(KStandardAction::SaveAs, this, SLOT(slotFileSaveAs())); d->saveActionAs->setActivationFlags(KisAction::ACTIVE_IMAGE); // d->printAction = actionManager->createStandardAction(KStandardAction::Print, this, SLOT(slotFilePrint())); // d->printAction->setActivationFlags(KisAction::ACTIVE_IMAGE); // d->printActionPreview = actionManager->createStandardAction(KStandardAction::PrintPreview, this, SLOT(slotFilePrintPreview())); // d->printActionPreview->setActivationFlags(KisAction::ACTIVE_IMAGE); d->undo = actionManager->createStandardAction(KStandardAction::Undo, this, SLOT(undo())); d->undo ->setActivationFlags(KisAction::ACTIVE_IMAGE); d->redo = actionManager->createStandardAction(KStandardAction::Redo, this, SLOT(redo())); d->redo->setActivationFlags(KisAction::ACTIVE_IMAGE); d->exportPdf = actionManager->createAction("file_export_pdf"); connect(d->exportPdf, SIGNAL(triggered()), this, SLOT(exportToPdf())); d->importAnimation = actionManager->createAction("file_import_animation"); connect(d->importAnimation, SIGNAL(triggered()), this, SLOT(importAnimation())); d->exportAnimation = actionManager->createAction("file_export_animation"); connect(d->exportAnimation, SIGNAL(triggered()), this, SLOT(exportAnimation())); d->closeAll = actionManager->createAction("file_close_all"); connect(d->closeAll, SIGNAL(triggered()), this, SLOT(slotFileCloseAll())); // d->reloadFile = actionManager->createAction("file_reload_file"); // d->reloadFile->setActivationFlags(KisAction::CURRENT_IMAGE_MODIFIED); // connect(d->reloadFile, SIGNAL(triggered(bool)), this, SLOT(slotReloadFile())); d->importFile = actionManager->createAction("file_import_file"); connect(d->importFile, SIGNAL(triggered(bool)), this, SLOT(slotImportFile())); d->exportFile = actionManager->createAction("file_export_file"); connect(d->exportFile, SIGNAL(triggered(bool)), this, SLOT(slotExportFile())); /* The following entry opens the document information dialog. Since the action is named so it intends to show data this entry should not have a trailing ellipses (...). */ d->showDocumentInfo = actionManager->createAction("file_documentinfo"); connect(d->showDocumentInfo, SIGNAL(triggered(bool)), this, SLOT(slotDocumentInfo())); d->themeManager->setThemeMenuAction(new KActionMenu(i18nc("@action:inmenu", "&Themes"), this)); d->themeManager->registerThemeActions(actionCollection()); connect(d->themeManager, SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged())); d->toggleDockers = actionManager->createAction("view_toggledockers"); d->toggleDockers->setChecked(true); connect(d->toggleDockers, SIGNAL(toggled(bool)), SLOT(toggleDockersVisibility(bool))); d->toggleDockerTitleBars = actionManager->createAction("view_toggledockertitlebars"); { KisConfig cfg; d->toggleDockerTitleBars->setChecked(cfg.showDockerTitleBars()); } connect(d->toggleDockerTitleBars, SIGNAL(toggled(bool)), SLOT(showDockerTitleBars(bool))); actionCollection()->addAction("settings_dockers_menu", d->dockWidgetMenu); actionCollection()->addAction("window", d->windowMenu); d->mdiCascade = actionManager->createAction("windows_cascade"); connect(d->mdiCascade, SIGNAL(triggered()), d->mdiArea, SLOT(cascadeSubWindows())); d->mdiTile = actionManager->createAction("windows_tile"); connect(d->mdiTile, SIGNAL(triggered()), d->mdiArea, SLOT(tileSubWindows())); d->mdiNextWindow = actionManager->createAction("windows_next"); connect(d->mdiNextWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activateNextSubWindow())); d->mdiPreviousWindow = actionManager->createAction("windows_previous"); connect(d->mdiPreviousWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activatePreviousSubWindow())); d->newWindow = actionManager->createAction("view_newwindow"); connect(d->newWindow, SIGNAL(triggered(bool)), this, SLOT(newWindow())); d->close = actionManager->createAction("file_close"); connect(d->close, SIGNAL(triggered()), SLOT(closeCurrentWindow())); actionManager->createStandardAction(KStandardAction::Preferences, this, SLOT(slotPreferences())); for (int i = 0; i < 2; i++) { d->expandingSpacers[i] = new KisAction(i18n("Expanding Spacer")); d->expandingSpacers[i]->setDefaultWidget(new QWidget(this)); d->expandingSpacers[i]->defaultWidget()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); actionManager->addAction(QString("expanding_spacer_%1").arg(i), d->expandingSpacers[i]); } } void KisMainWindow::applyToolBarLayout() { const bool isPlastiqueStyle = style()->objectName() == "plastique"; Q_FOREACH (KToolBar *toolBar, toolBars()) { toolBar->layout()->setSpacing(4); if (isPlastiqueStyle) { toolBar->setContentsMargins(0, 0, 0, 2); } } } void KisMainWindow::initializeGeometry() { // if the user didn's specify the geometry on the command line (does anyone do that still?), // we first figure out some good default size and restore the x,y position. See bug 285804Z. KConfigGroup cfg( KSharedConfig::openConfig(), "MainWindow"); QByteArray geom = QByteArray::fromBase64(cfg.readEntry("ko_geometry", QByteArray())); if (!restoreGeometry(geom)) { const int scnum = QApplication::desktop()->screenNumber(parentWidget()); QRect desk = QApplication::desktop()->availableGeometry(scnum); // if the desktop is virtual then use virtual screen size if (QApplication::desktop()->isVirtualDesktop()) { desk = QApplication::desktop()->availableGeometry(QApplication::desktop()->screen(scnum)); } quint32 x = desk.x(); quint32 y = desk.y(); quint32 w = 0; quint32 h = 0; // Default size -- maximize on small screens, something useful on big screens const int deskWidth = desk.width(); if (deskWidth > 1024) { // a nice width, and slightly less than total available // height to componensate for the window decs w = (deskWidth / 3) * 2; h = (desk.height() / 3) * 2; } else { w = desk.width(); h = desk.height(); } x += (desk.width() - w) / 2; y += (desk.height() - h) / 2; move(x,y); setGeometry(geometry().x(), geometry().y(), w, h); } restoreWorkspace(QByteArray::fromBase64(cfg.readEntry("ko_windowstate", QByteArray()))); } void KisMainWindow::showManual() { QDesktopServices::openUrl(QUrl("https://docs.krita.org")); } void KisMainWindow::showDockerTitleBars(bool show) { Q_FOREACH (QDockWidget *dock, dockWidgets()) { if (dock->titleBarWidget()) { const bool isCollapsed = (dock->widget() && dock->widget()->isHidden()) || !dock->widget(); dock->titleBarWidget()->setVisible(show || (dock->isFloating() && isCollapsed)); } } KisConfig cfg; cfg.setShowDockerTitleBars(show); } void KisMainWindow::moveEvent(QMoveEvent *e) { if (qApp->desktop()->screenNumber(this) != qApp->desktop()->screenNumber(e->oldPos())) { KisConfigNotifier::instance()->notifyConfigChanged(); } } #include diff --git a/libs/ui/KisNodeDelegate.cpp b/libs/ui/KisNodeDelegate.cpp index 343a97d06f..1fde161ecc 100644 --- a/libs/ui/KisNodeDelegate.cpp +++ b/libs/ui/KisNodeDelegate.cpp @@ -1,872 +1,872 @@ /* Copyright (c) 2006 Gábor Lehel Copyright (c) 2008 Cyrille Berger Copyright (c) 2011 José Luis Vergara 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_config.h" #include "KisNodeDelegate.h" #include "kis_node_model.h" #include "KisNodeToolTip.h" #include "KisNodeView.h" #include "KisPart.h" #include "input/kis_input_manager.h" #include #include #include #include #include #include #include #include #include #include #include #include "kis_node_view_color_scheme.h" #include "kis_icon_utils.h" #include "kis_layer_properties_icons.h" #include "krita_utils.h" typedef KisBaseNode::Property* OptionalProperty; #include class KisNodeDelegate::Private { public: Private() : view(0), edit(0) { } KisNodeView *view; QPointer edit; KisNodeToolTip tip; QList rightmostProperties(const KisBaseNode::PropertyList &props) const; int numProperties(const QModelIndex &index) const; OptionalProperty findProperty(KisBaseNode::PropertyList &props, const OptionalProperty &refProp) const; OptionalProperty findVisibilityProperty(KisBaseNode::PropertyList &props) const; void toggleProperty(KisBaseNode::PropertyList &props, OptionalProperty prop, bool controlPressed, const QModelIndex &index); }; void KisNodeDelegate::slotOnCloseEditor() { KisPart::currentInputManager()->slotFocusOnEnter(true); } KisNodeDelegate::KisNodeDelegate(KisNodeView *view, QObject *parent) : QAbstractItemDelegate(parent) , d(new Private) { d->view = view; QApplication::instance()->installEventFilter(this); connect(this, SIGNAL(closeEditor(QWidget*)), this, SLOT(slotOnCloseEditor())); } KisNodeDelegate::~KisNodeDelegate() { delete d; } QSize KisNodeDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(index); KisNodeViewColorScheme scm; return QSize(option.rect.width(), scm.rowHeight()); } void KisNodeDelegate::paint(QPainter *p, const QStyleOptionViewItem &o, const QModelIndex &index) const { p->save(); { QStyleOptionViewItemV4 option = getOptions(o, index); QStyle *style = option.widget ? option.widget->style() : QApplication::style(); style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, p, option.widget); bool shouldGrayOut = index.data(KisNodeModel::ShouldGrayOutRole).toBool(); if (shouldGrayOut) { option.state &= ~QStyle::State_Enabled; } p->setFont(option.font); drawColorLabel(p, option, index); drawFrame(p, option, index); drawThumbnail(p, option, index); drawText(p, option, index); drawIcons(p, option, index); drawVisibilityIconHijack(p, option, index); drawDecoration(p, option, index); drawExpandButton(p, option, index); drawBranch(p, option, index); drawProgressBar(p, option, index); } p->restore(); } void KisNodeDelegate::drawBranch(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(index); KisNodeViewColorScheme scm; const QPoint base = scm.relThumbnailRect().translated(option.rect.topLeft()).topLeft() - QPoint( scm.indentation(), 0); // there is no indention if we are starting negative, so don't draw a branch if (base.x() < 0) { return; } QPen oldPen = p->pen(); const qreal oldOpacity = p->opacity(); // remember previous opacity p->setOpacity(1.0); QColor color = scm.gridColor(option, d->view); QColor bgColor = option.state & QStyle::State_Selected ? qApp->palette().color(QPalette::Base) : qApp->palette().color(QPalette::Text); color = KritaUtils::blendColors(color, bgColor, 0.9); // TODO: if we are a mask type, use dotted lines for the branch style // p->setPen(QPen(p->pen().color(), 2, Qt::DashLine, Qt::RoundCap, Qt::RoundJoin)); p->setPen(QPen(color, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); QPoint p2 = base + QPoint(scm.iconSize() - scm.decorationMargin()*2, scm.iconSize()*0.45); QPoint p3 = base + QPoint(scm.iconSize() - scm.decorationMargin()*2, scm.iconSize()); QPoint p4 = base + QPoint(scm.iconSize()*1.4, scm.iconSize()); p->drawLine(p2, p3); p->drawLine(p3, p4); // draw parent lines (keep drawing until x position is less than 0 QPoint p5 = p2 - QPoint(scm.indentation(), 0); QPoint p6 = p3 - QPoint(scm.indentation(), 0); QPoint parentBase1 = p5; QPoint parentBase2 = p6; // indent lines needs to be very subtle to avoid making the docker busy looking color = KritaUtils::blendColors(color, bgColor, 0.9); // makes it a little lighter than L lines p->setPen(QPen(color, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); while (parentBase1.x() > scm.visibilityColumnWidth()) { p->drawLine(parentBase1, parentBase2); parentBase1 = parentBase1 - QPoint(scm.indentation(), 0); parentBase2 = parentBase2 - QPoint(scm.indentation(), 0); } p->setPen(oldPen); p->setOpacity(oldOpacity); } void KisNodeDelegate::drawColorLabel(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { KisNodeViewColorScheme scm; const int label = index.data(KisNodeModel::ColorLabelIndexRole).toInt(); QColor color = scm.colorLabel(label); if (color.alpha() <= 0) return; QColor bgColor = qApp->palette().color(QPalette::Base); - color = KritaUtils::blendColors(color, bgColor, 0.2); + color = KritaUtils::blendColors(color, bgColor, 0.3); const QRect rect = option.state & QStyle::State_Selected ? iconsRect(option, index) : option.rect.adjusted(-scm.indentation(), 0, 0, 0); p->fillRect(rect, color); } void KisNodeDelegate::drawFrame(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { KisNodeViewColorScheme scm; QPen oldPen = p->pen(); p->setPen(scm.gridColor(option, d->view)); const QPoint base = option.rect.topLeft(); QPoint p2 = base + QPoint(-scm.indentation() - 1, 0); QPoint p3 = base + QPoint(2 * scm.decorationMargin() + scm.decorationSize(), 0); QPoint p4 = base + QPoint(-1, 0); QPoint p5(iconsRect(option, index).left() - 1, base.y()); QPoint p6(option.rect.right(), base.y()); QPoint v(0, option.rect.height()); // draw a line that goes the length of the entire frame. one for the // top, and one for the bottom QPoint pTopLeft(0, option.rect.topLeft().y()); QPoint pTopRight(option.rect.bottomRight().x(),option.rect.topLeft().y() ); p->drawLine(pTopLeft, pTopRight); QPoint pBottomLeft(0, option.rect.topLeft().y() + scm.rowHeight()); QPoint pBottomRight(option.rect.bottomRight().x(),option.rect.topLeft().y() + scm.rowHeight() ); p->drawLine(pBottomLeft, pBottomRight); const bool paintForParent = index.parent().isValid() && !index.row(); if (paintForParent) { QPoint p1(-2 * scm.indentation() - 1, 0); p1 += base; p->drawLine(p1, p2); } QPoint k0(0, base.y()); QPoint k1(1 * scm.border() + 2 * scm.visibilityMargin() + scm.visibilitySize(), base.y()); p->drawLine(k0, k1); p->drawLine(k0 + v, k1 + v); p->drawLine(k0, k0 + v); p->drawLine(k1, k1 + v); p->drawLine(p2, p6); p->drawLine(p2 + v, p6 + v); p->drawLine(p2, p2 + v); p->drawLine(p3, p3 + v); p->drawLine(p4, p4 + v); p->drawLine(p5, p5 + v); p->drawLine(p6, p6 + v); //// For debugging purposes only //p->setPen(Qt::blue); //KritaUtils::renderExactRect(p, iconsRect(option, index)); //KritaUtils::renderExactRect(p, textRect(option, index)); //KritaUtils::renderExactRect(p, scm.relThumbnailRect().translated(option.rect.topLeft())); p->setPen(oldPen); } void KisNodeDelegate::drawThumbnail(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { KisNodeViewColorScheme scm; const int thumbSize = scm.thumbnailSize(); const qreal oldOpacity = p->opacity(); // remember previous opacity QImage img = index.data(int(KisNodeModel::BeginThumbnailRole) + thumbSize).value(); if (!(option.state & QStyle::State_Enabled)) { p->setOpacity(0.35); } QRect fitRect = scm.relThumbnailRect().translated(option.rect.topLeft()); QPoint offset; offset.setX((fitRect.width() - img.width()) / 2); offset.setY((fitRect.height() - img.height()) / 2); offset += fitRect.topLeft(); KisConfig cfg; // paint in a checkerboard pattern behind the layer contents to represent transparent const int step = scm.thumbnailSize() / 6; QImage checkers(2 * step, 2 * step, QImage::Format_ARGB32); QPainter gc(&checkers); gc.fillRect(QRect(0, 0, step, step), cfg.checkersColor1()); gc.fillRect(QRect(step, 0, step, step), cfg.checkersColor2()); gc.fillRect(QRect(step, step, step, step), cfg.checkersColor1()); gc.fillRect(QRect(0, step, step, step), cfg.checkersColor2()); QBrush brush(checkers); p->setBrushOrigin(offset); p->fillRect(img.rect().translated(offset), brush); p->drawImage(offset, img); p->setOpacity(oldOpacity); // restore old opacity QRect borderRect = kisGrowRect(img.rect(), 1).translated(offset); KritaUtils::renderExactRect(p, borderRect, scm.gridColor(option, d->view)); } QRect KisNodeDelegate::iconsRect(const QStyleOptionViewItem &option, const QModelIndex &index) const { KisNodeViewColorScheme scm; int propCount = d->numProperties(index); const int iconsWidth = propCount * (scm.iconSize() + 2 * scm.iconMargin()) + (propCount - 1) * scm.border(); const int x = option.rect.x() + option.rect.width() - (iconsWidth + scm.border()); const int y = option.rect.y() + scm.border(); return QRect(x, y, iconsWidth, scm.rowHeight() - scm.border()); } QRect KisNodeDelegate::textRect(const QStyleOptionViewItem &option, const QModelIndex &index) const { KisNodeViewColorScheme scm; static QFont f; static int minbearing = 1337 + 666; //can be 0 or negative, 2003 is less likely if (minbearing == 2003 || f != option.font) { f = option.font; //getting your bearings can be expensive, so we cache them minbearing = option.fontMetrics.minLeftBearing() + option.fontMetrics.minRightBearing(); } const int decorationOffset = 2 * scm.border() + 2 * scm.decorationMargin() + scm.decorationSize(); const int width = iconsRect(option, index).left() - option.rect.x() - scm.border() + minbearing - decorationOffset; return QRect(option.rect.x() - minbearing + decorationOffset, option.rect.y() + scm.border(), width, scm.rowHeight() - scm.border()); } void KisNodeDelegate::drawText(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { KisNodeViewColorScheme scm; const QRect rc = textRect(option, index) .adjusted(scm.textMargin(), 0, -scm.textMargin(), 0); QPen oldPen = p->pen(); const qreal oldOpacity = p->opacity(); // remember previous opacity p->setPen(option.palette.color(QPalette::Active,QPalette::Text )); if (!(option.state & QStyle::State_Enabled)) { p->setOpacity(0.55); } const QString text = index.data(Qt::DisplayRole).toString(); const QString elided = elidedText(p->fontMetrics(), rc.width(), Qt::ElideRight, text); p->drawText(rc, Qt::AlignLeft | Qt::AlignVCenter, elided); p->setPen(oldPen); // restore pen settings p->setOpacity(oldOpacity); } QList KisNodeDelegate::Private::rightmostProperties(const KisBaseNode::PropertyList &props) const { QList list; QList prependList; list << OptionalProperty(0); list << OptionalProperty(0); list << OptionalProperty(0); KisBaseNode::PropertyList::const_iterator it = props.constBegin(); KisBaseNode::PropertyList::const_iterator end = props.constEnd(); for (; it != end; ++it) { if (!it->isMutable) continue; if (it->id == KisLayerPropertiesIcons::visible.id()) { // noop... } else if (it->id == KisLayerPropertiesIcons::locked.id()) { list[0] = OptionalProperty(&(*it)); } else if (it->id == KisLayerPropertiesIcons::inheritAlpha.id()) { list[1] = OptionalProperty(&(*it)); } else if (it->id == KisLayerPropertiesIcons::alphaLocked.id()) { list[2] = OptionalProperty(&(*it)); } else { prependList.prepend(OptionalProperty(&(*it))); } } { QMutableListIterator i(prependList); i.toBack(); while (i.hasPrevious()) { OptionalProperty val = i.previous(); int emptyIndex = list.lastIndexOf(0); if (emptyIndex < 0) break; list[emptyIndex] = val; i.remove(); } } return prependList + list; } int KisNodeDelegate::Private::numProperties(const QModelIndex &index) const { KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value(); QList realProps = rightmostProperties(props); return realProps.size(); } OptionalProperty KisNodeDelegate::Private::findProperty(KisBaseNode::PropertyList &props, const OptionalProperty &refProp) const { KisBaseNode::PropertyList::iterator it = props.begin(); KisBaseNode::PropertyList::iterator end = props.end(); for (; it != end; ++it) { if (it->id == refProp->id) { return &(*it); } } return 0; } OptionalProperty KisNodeDelegate::Private::findVisibilityProperty(KisBaseNode::PropertyList &props) const { KisBaseNode::PropertyList::iterator it = props.begin(); KisBaseNode::PropertyList::iterator end = props.end(); for (; it != end; ++it) { if (it->id == KisLayerPropertiesIcons::visible.id()) { return &(*it); } } return 0; } void KisNodeDelegate::drawIcons(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { KisNodeViewColorScheme scm; const QRect r = iconsRect(option, index); QTransform oldTransform = p->transform(); QPen oldPen = p->pen(); p->setTransform(QTransform::fromTranslate(r.x(), r.y())); p->setPen(scm.gridColor(option, d->view)); int x = 0; const int y = (scm.rowHeight() - scm.border() - scm.iconSize()) / 2; KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value(); QList realProps = d->rightmostProperties(props); Q_FOREACH (OptionalProperty prop, realProps) { x += scm.iconMargin(); if (prop) { QIcon icon = prop->state.toBool() ? prop->onIcon : prop->offIcon; bool fullColor = prop->state.toBool() && option.state & QStyle::State_Enabled; const qreal oldOpacity = p->opacity(); // remember previous opacity if (fullColor) { p->setOpacity(1.0); } else { p->setOpacity(0.35); } p->drawPixmap(x, y, icon.pixmap(scm.iconSize(), QIcon::Normal)); p->setOpacity(oldOpacity); // restore old opacity } x += scm.iconSize() + scm.iconMargin(); p->drawLine(x, 0, x, scm.rowHeight() - scm.border()); x += scm.border(); } p->setTransform(oldTransform); p->setPen(oldPen); } QRect KisNodeDelegate::visibilityClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(index); KisNodeViewColorScheme scm; return QRect(scm.border(), scm.border() + option.rect.top(), 2 * scm.visibilityMargin() + scm.visibilitySize(), scm.rowHeight() - scm.border()); } QRect KisNodeDelegate::decorationClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option); Q_UNUSED(index); KisNodeViewColorScheme scm; QRect realVisualRect = d->view->originalVisualRect(index); return QRect(realVisualRect.left(), scm.border() + realVisualRect.top(), 2 * scm.decorationMargin() + scm.decorationSize(), scm.rowHeight() - scm.border()); } void KisNodeDelegate::drawVisibilityIconHijack(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { /** * Small hack Alert: * * Here wepaint over the area that sits basically outside our layer's * row. Anyway, just update it later... */ KisNodeViewColorScheme scm; KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value(); OptionalProperty prop = d->findVisibilityProperty(props); if (!prop) return; const int x = scm.border() + scm.visibilityMargin(); const int y = option.rect.top() + (scm.rowHeight() - scm.border() - scm.visibilitySize()) / 2; QIcon icon = prop->state.toBool() ? prop->onIcon : prop->offIcon; p->setOpacity(1.0); p->drawPixmap(x, y, icon.pixmap(scm.visibilitySize(), QIcon::Normal)); //// For debugging purposes only // p->save(); // p->setPen(Qt::blue); // KritaUtils::renderExactRect(p, visibilityClickRect(option, index)); // p->restore(); } void KisNodeDelegate::drawDecoration(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { KisNodeViewColorScheme scm; QIcon icon = index.data(Qt::DecorationRole).value(); if (!icon.isNull()) { QPixmap pixmap = icon.pixmap(scm.decorationSize(), (option.state & QStyle::State_Enabled) ? QIcon::Normal : QIcon::Disabled); const QRect rc = scm.relDecorationRect().translated(option.rect.topLeft()); const qreal oldOpacity = p->opacity(); // remember previous opacity if (!(option.state & QStyle::State_Enabled)) { p->setOpacity(0.35); } p->drawPixmap(rc.topLeft(), pixmap); p->setOpacity(oldOpacity); // restore old opacity } } void KisNodeDelegate::drawExpandButton(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(index); KisNodeViewColorScheme scm; QRect rc = scm.relExpandButtonRect().translated(option.rect.topLeft()); rc = kisGrowRect(rc, 0); if (!(option.state & QStyle::State_Children)) { return; } QString iconName = option.state & QStyle::State_Open ? "arrow-down" : "arrow-right"; QIcon icon = KisIconUtils::loadIcon(iconName); QPixmap pixmap = icon.pixmap(rc.width(), (option.state & QStyle::State_Enabled) ? QIcon::Normal : QIcon::Disabled); p->drawPixmap(rc.topLeft(), pixmap); } void KisNodeDelegate::Private::toggleProperty(KisBaseNode::PropertyList &props, OptionalProperty clickedProperty, bool controlPressed, const QModelIndex &index) { QAbstractItemModel *model = view->model(); // Using Ctrl+click to enter stasis if (controlPressed && clickedProperty->canHaveStasis) { // STEP 0: Prepare to Enter or Leave control key stasis quint16 numberOfLeaves = model->rowCount(index.parent()); QModelIndex eachItem; // STEP 1: Go. if (clickedProperty->isInStasis == false) { // Enter /* Make every leaf of this node go State = False, saving the old property value to stateInStasis */ for (quint16 i = 0; i < numberOfLeaves; ++i) { // Foreach leaf in the node (index.parent()) eachItem = model->index(i, 1, index.parent()); // The entire property list has to be altered because model->setData cannot set individual properties KisBaseNode::PropertyList eachPropertyList = eachItem.data(KisNodeModel::PropertiesRole).value(); OptionalProperty prop = findProperty(eachPropertyList, clickedProperty); if (!prop) continue; prop->stateInStasis = prop->state.toBool(); prop->state = eachItem == index; prop->isInStasis = true; model->setData(eachItem, QVariant::fromValue(eachPropertyList), KisNodeModel::PropertiesRole); } for (quint16 i = 0; i < numberOfLeaves; ++i) { // Foreach leaf in the node (index.parent()) eachItem = model->index(i, 1, index.parent()); KisBaseNode::PropertyList eachPropertyList = eachItem.data(KisNodeModel::PropertiesRole).value(); OptionalProperty prop = findProperty(eachPropertyList, clickedProperty); if (!prop) continue; } } else { // Leave /* Make every leaf of this node go State = stateInStasis */ for (quint16 i = 0; i < numberOfLeaves; ++i) { eachItem = model->index(i, 1, index.parent()); // The entire property list has to be altered because model->setData cannot set individual properties KisBaseNode::PropertyList eachPropertyList = eachItem.data(KisNodeModel::PropertiesRole).value(); OptionalProperty prop = findProperty(eachPropertyList, clickedProperty); if (!prop) continue; prop->state = prop->stateInStasis; prop->isInStasis = false; model->setData(eachItem, QVariant::fromValue(eachPropertyList), KisNodeModel::PropertiesRole); } } } else { clickedProperty->state = !clickedProperty->state.toBool(); model->setData(index, QVariant::fromValue(props), KisNodeModel::PropertiesRole); } } bool KisNodeDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) { KisNodeViewColorScheme scm; if ((event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick) && (index.flags() & Qt::ItemIsEnabled)) { QMouseEvent *mouseEvent = static_cast(event); /** * Small hack Alert: * * Here we handle clicking even when it happened outside * the rectangle of the current index. The point is, we * use some virtual scroling offset to move the tree to the * right of the visibility icon. So the icon itself is placed * in an empty area that doesn't belong to any index. But we still * handle it. */ const QRect iconsRect = this->iconsRect(option, index); const bool iconsClicked = iconsRect.isValid() && iconsRect.contains(mouseEvent->pos()); const QRect visibilityRect = visibilityClickRect(option, index); const bool visibilityClicked = visibilityRect.isValid() && visibilityRect.contains(mouseEvent->pos()); const QRect decorationRect = decorationClickRect(option, index); const bool decorationClicked = decorationRect.isValid() && decorationRect.contains(mouseEvent->pos()); const bool leftButton = mouseEvent->buttons() & Qt::LeftButton; if (leftButton && iconsClicked) { KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value(); QList realProps = d->rightmostProperties(props); const int numProps = realProps.size(); const int iconWidth = scm.iconSize() + 2 * scm.iconMargin() + scm.border(); const int xPos = mouseEvent->pos().x() - iconsRect.left(); const int clickedIcon = xPos / iconWidth; const int distToBorder = qMin(xPos % iconWidth, iconWidth - xPos % iconWidth); if (iconsClicked && clickedIcon >= 0 && clickedIcon < numProps && distToBorder > scm.iconMargin()) { OptionalProperty clickedProperty = realProps[clickedIcon]; if (!clickedProperty) return false; d->toggleProperty(props, clickedProperty, mouseEvent->modifiers() == Qt::ControlModifier, index); return true; } } else if (leftButton && visibilityClicked) { KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value(); OptionalProperty clickedProperty = d->findVisibilityProperty(props); if (!clickedProperty) return false; d->toggleProperty(props, clickedProperty, mouseEvent->modifiers() == Qt::ControlModifier, index); return true; } else if (leftButton && decorationClicked) { bool isExpandable = model->hasChildren(index); if (isExpandable) { bool isExpanded = d->view->isExpanded(index); d->view->setExpanded(index, !isExpanded); return true; } } if (mouseEvent->button() == Qt::LeftButton && mouseEvent->modifiers() == Qt::AltModifier) { d->view->setCurrentIndex(index); model->setData(index, true, KisNodeModel::AlternateActiveRole); return true; } } else if (event->type() == QEvent::ToolTip) { if (!KisConfig().hidePopups()) { QHelpEvent *helpEvent = static_cast(event); d->tip.showTip(d->view, helpEvent->pos(), option, index); } return true; } else if (event->type() == QEvent::Leave) { d->tip.hide(); } return false; } QWidget *KisNodeDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem&, const QModelIndex&) const { KisPart::currentInputManager()->slotFocusOnEnter(false); d->edit = new QLineEdit(parent); d->edit->installEventFilter(const_cast(this)); //hack? return d->edit; } void KisNodeDelegate::setEditorData(QWidget *widget, const QModelIndex &index) const { QLineEdit *edit = qobject_cast(widget); Q_ASSERT(edit); edit->setText(index.data(Qt::DisplayRole).toString()); } void KisNodeDelegate::setModelData(QWidget *widget, QAbstractItemModel *model, const QModelIndex &index) const { QLineEdit *edit = qobject_cast(widget); Q_ASSERT(edit); model->setData(index, edit->text(), Qt::DisplayRole); } void KisNodeDelegate::updateEditorGeometry(QWidget *widget, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(index); widget->setGeometry(option.rect); } // PROTECTED bool KisNodeDelegate::eventFilter(QObject *object, QEvent *event) { switch (event->type()) { case QEvent::MouseButtonPress: { if (d->edit) { QMouseEvent *me = static_cast(event); if (!QRect(d->edit->mapToGlobal(QPoint()), d->edit->size()).contains(me->globalPos())) { emit commitData(d->edit); emit closeEditor(d->edit); } } } break; case QEvent::KeyPress: { QLineEdit *edit = qobject_cast(object); if (edit && edit == d->edit) { QKeyEvent *ke = static_cast(event); switch (ke->key()) { case Qt::Key_Escape: emit closeEditor(edit); return true; case Qt::Key_Tab: emit commitData(edit); emit closeEditor(edit,EditNextItem); return true; case Qt::Key_Backtab: emit commitData(edit); emit closeEditor(edit, EditPreviousItem); return true; case Qt::Key_Return: case Qt::Key_Enter: emit commitData(edit); emit closeEditor(edit); return true; default: break; } } } break; case QEvent::FocusOut : { QLineEdit *edit = qobject_cast(object); if (edit && edit == d->edit) { emit commitData(edit); emit closeEditor(edit); } } default: break; } return QAbstractItemDelegate::eventFilter(object, event); } // PRIVATE QStyleOptionViewItemV4 KisNodeDelegate::getOptions(const QStyleOptionViewItem &o, const QModelIndex &index) { QStyleOptionViewItemV4 option = o; QVariant v = index.data(Qt::FontRole); if (v.isValid()) { option.font = v.value(); option.fontMetrics = QFontMetrics(option.font); } v = index.data(Qt::TextAlignmentRole); if (v.isValid()) option.displayAlignment = QFlag(v.toInt()); v = index.data(Qt::TextColorRole); if (v.isValid()) option.palette.setColor(QPalette::Text, v.value()); v = index.data(Qt::BackgroundColorRole); if (v.isValid()) option.palette.setColor(QPalette::Window, v.value()); return option; } QRect KisNodeDelegate::progressBarRect(const QStyleOptionViewItem &option, const QModelIndex &index) const { return iconsRect(option, index); } void KisNodeDelegate::drawProgressBar(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { QVariant value = index.data(KisNodeModel::ProgressRole); if (!value.isNull() && (value.toInt() >= 0 && value.toInt() <= 100)) { const QRect r = progressBarRect(option, index); p->save(); { p->setClipRect(r); QStyle* style = QApplication::style(); QStyleOptionProgressBarV2 opt; opt.minimum = 0; opt.maximum = 100; opt.progress = value.toInt(); opt.textVisible = true; opt.textAlignment = Qt::AlignHCenter; opt.text = i18n("%1 %", opt.progress); opt.rect = r; opt.orientation = Qt::Horizontal; opt.state = option.state; style->drawControl(QStyle::CE_ProgressBar, &opt, p, 0); } p->restore(); } } diff --git a/libs/ui/KisNodeView.cpp b/libs/ui/KisNodeView.cpp index 98102d9bc2..b15fca7e12 100644 --- a/libs/ui/KisNodeView.cpp +++ b/libs/ui/KisNodeView.cpp @@ -1,564 +1,564 @@ /* 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. */ #include "KisNodeView.h" #include "KisNodePropertyAction_p.h" #include "KisNodeDelegate.h" #include "kis_node_view_visibility_delegate.h" #include "kis_node_model.h" #include "kis_signals_blocker.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_node_view_color_scheme.h" #ifdef HAVE_X11 #define DRAG_WHILE_DRAG_WORKAROUND #endif #ifdef DRAG_WHILE_DRAG_WORKAROUND #define DRAG_WHILE_DRAG_WORKAROUND_START() d->isDragging = true #define DRAG_WHILE_DRAG_WORKAROUND_STOP() d->isDragging = false #else #define DRAG_WHILE_DRAG_WORKAROUND_START() #define DRAG_WHILE_DRAG_WORKAROUND_STOP() #endif class Q_DECL_HIDDEN KisNodeView::Private { public: Private(KisNodeView* _q) : delegate(_q, _q) - , mode(DetailedMode) + , mode(DetailedMode) #ifdef DRAG_WHILE_DRAG_WORKAROUND , isDragging(false) #endif { KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup group = config->group("NodeView"); mode = (DisplayMode) group.readEntry("NodeViewMode", (int)MinimalMode); } KisNodeDelegate delegate; DisplayMode mode; QPersistentModelIndex hovered; QPoint lastPos; #ifdef DRAG_WHILE_DRAG_WORKAROUND bool isDragging; #endif }; KisNodeView::KisNodeView(QWidget *parent) : QTreeView(parent) , m_draggingFlag(false) , d(new Private(this)) { setItemDelegateForColumn(0, &d->delegate); setMouseTracking(true); setSelectionBehavior(SelectRows); setDefaultDropAction(Qt::MoveAction); setVerticalScrollMode(QAbstractItemView::ScrollPerItem); setSelectionMode(QAbstractItemView::ExtendedSelection); header()->hide(); setDragEnabled(true); setDragDropMode(QAbstractItemView::DragDrop); setAcceptDrops(true); setDropIndicatorShown(true); } KisNodeView::~KisNodeView() { delete d; } void KisNodeView::setDisplayMode(DisplayMode mode) { if (d->mode != mode) { d->mode = mode; KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup group = config->group("NodeView"); group.writeEntry("NodeViewMode", (int)mode); scheduleDelayedItemsLayout(); } } KisNodeView::DisplayMode KisNodeView::displayMode() const { return d->mode; } void KisNodeView::addPropertyActions(QMenu *menu, const QModelIndex &index) { KisBaseNode::PropertyList list = index.data(KisNodeModel::PropertiesRole).value(); for (int i = 0, n = list.count(); i < n; ++i) { if (list.at(i).isMutable) { PropertyAction *a = new PropertyAction(i, list.at(i), index, menu); connect(a, SIGNAL(toggled(bool, const QPersistentModelIndex&, int)), this, SLOT(slotActionToggled(bool, const QPersistentModelIndex&, int))); menu->addAction(a); } } } void KisNodeView::updateNode(const QModelIndex &index) { dataChanged(index, index); } QItemSelectionModel::SelectionFlags KisNodeView::selectionCommand(const QModelIndex &index, const QEvent *event) const { /** * Qt has a bug: when we Ctrl+click on an item, the item's * selections gets toggled on mouse *press*, whereas usually it is * done on mouse *release*. Therefore the user cannot do a * Ctrl+D&D with the default configuration. This code fixes the * problem by manually returning QItemSelectionModel::NoUpdate * flag when the user clicks on an item and returning * QItemSelectionModel::Toggle on release. */ if (event && (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease) && index.isValid()) { const QMouseEvent *mevent = static_cast(event); if (mevent->button() == Qt::RightButton && selectionModel()->selectedIndexes().contains(index)) { // Allow calling context menu for multiple layers return QItemSelectionModel::NoUpdate; } if (event->type() == QEvent::MouseButtonPress && (mevent->modifiers() & Qt::ControlModifier)) { return QItemSelectionModel::NoUpdate; } if (event->type() == QEvent::MouseButtonRelease && (mevent->modifiers() & Qt::ControlModifier)) { return QItemSelectionModel::Toggle; } } /** * Qt 5.6 has a bug: it reads global modifiers, not the ones * passed from event. So if you paste an item using Ctrl+V it'll * select multiple layers for you */ Qt::KeyboardModifiers globalModifiers = QApplication::keyboardModifiers(); if (!event && globalModifiers != Qt::NoModifier) { return QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows; } return QAbstractItemView::selectionCommand(index, event); } QRect KisNodeView::visualRect(const QModelIndex &index) const { QRect rc = QTreeView::visualRect(index); rc.setLeft(0); return rc; } QRect KisNodeView::originalVisualRect(const QModelIndex &index) const { return QTreeView::visualRect(index); } QModelIndex KisNodeView::indexAt(const QPoint &point) const { KisNodeViewColorScheme scm; QModelIndex index = QTreeView::indexAt(point); if (!index.isValid() && point.x() < scm.visibilityColumnWidth()) { index = QTreeView::indexAt(point + QPoint(scm.visibilityColumnWidth(), 0)); } return index; } bool KisNodeView::viewportEvent(QEvent *e) { if (model()) { switch(e->type()) { case QEvent::MouseButtonPress: { DRAG_WHILE_DRAG_WORKAROUND_STOP(); const QPoint pos = static_cast(e)->pos(); d->lastPos = pos; if (!indexAt(pos).isValid()) { return QTreeView::viewportEvent(e); } QModelIndex index = model()->buddy(indexAt(pos)); if (d->delegate.editorEvent(e, model(), optionForIndex(index), index)) { return true; } } break; case QEvent::Leave: { QEvent e(QEvent::Leave); d->delegate.editorEvent(&e, model(), optionForIndex(d->hovered), d->hovered); d->hovered = QModelIndex(); } break; case QEvent::MouseMove: { #ifdef DRAG_WHILE_DRAG_WORKAROUND if (d->isDragging) { return false; } #endif const QPoint pos = static_cast(e)->pos(); QModelIndex hovered = indexAt(pos); if (hovered != d->hovered) { if (d->hovered.isValid()) { QEvent e(QEvent::Leave); d->delegate.editorEvent(&e, model(), optionForIndex(d->hovered), d->hovered); } if (hovered.isValid()) { QEvent e(QEvent::Enter); d->delegate.editorEvent(&e, model(), optionForIndex(hovered), hovered); } d->hovered = hovered; } /* This is a workaround for a bug in QTreeView that immediately begins a dragging action when the mouse lands on the decoration/icon of a different index and moves 1 pixel or more */ Qt::MouseButtons buttons = static_cast(e)->buttons(); if ((Qt::LeftButton | Qt::MidButton) & buttons) { if ((pos - d->lastPos).manhattanLength() > qApp->startDragDistance()) { return QTreeView::viewportEvent(e); } return true; } } break; case QEvent::ToolTip: { const QPoint pos = static_cast(e)->pos(); if (!indexAt(pos).isValid()) { return QTreeView::viewportEvent(e); } QModelIndex index = model()->buddy(indexAt(pos)); return d->delegate.editorEvent(e, model(), optionForIndex(index), index); } break; case QEvent::Resize: { scheduleDelayedItemsLayout(); break; } default: break; } } return QTreeView::viewportEvent(e); } void KisNodeView::contextMenuEvent(QContextMenuEvent *e) { QTreeView::contextMenuEvent(e); QModelIndex i = indexAt(e->pos()); if (model()) i = model()->buddy(i); showContextMenu(e->globalPos(), i); } void KisNodeView::showContextMenu(const QPoint &globalPos, const QModelIndex &index) { emit contextMenuRequested(globalPos, index); } void KisNodeView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { QTreeView::currentChanged(current, previous); if (current != previous) { Q_ASSERT(!current.isValid() || current.model() == model()); model()->setData(current, true, KisNodeModel::ActiveRole); } } void KisNodeView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &/*roles*/) { QTreeView::dataChanged(topLeft, bottomRight); for (int x = topLeft.row(); x <= bottomRight.row(); ++x) { for (int y = topLeft.column(); y <= bottomRight.column(); ++y) { QModelIndex index = topLeft.sibling(x, y); if (index.data(KisNodeModel::ActiveRole).toBool()) { if (currentIndex() != index) { setCurrentIndex(index); } return; } } } } void KisNodeView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { QTreeView::selectionChanged(selected, deselected); emit selectionChanged(selectedIndexes()); } void KisNodeView::slotActionToggled(bool on, const QPersistentModelIndex &index, int num) { KisBaseNode::PropertyList list = index.data(KisNodeModel::PropertiesRole).value(); list[num].state = on; const_cast(index.model())->setData(index, QVariant::fromValue(list), KisNodeModel::PropertiesRole); } QStyleOptionViewItem KisNodeView::optionForIndex(const QModelIndex &index) const { QStyleOptionViewItem option = viewOptions(); option.rect = visualRect(index); if (index == currentIndex()) option.state |= QStyle::State_HasFocus; return option; } void KisNodeView::startDrag(Qt::DropActions supportedActions) { DRAG_WHILE_DRAG_WORKAROUND_START(); if (displayMode() == KisNodeView::ThumbnailMode) { const QModelIndexList indexes = selectionModel()->selectedIndexes(); if (!indexes.isEmpty()) { QMimeData *data = model()->mimeData(indexes); if (!data) { return; } QDrag *drag = new QDrag(this); drag->setPixmap(createDragPixmap()); drag->setMimeData(data); //m_dragSource = this; drag->exec(supportedActions); } } else { QTreeView::startDrag(supportedActions); } } QPixmap KisNodeView::createDragPixmap() const { const QModelIndexList selectedIndexes = selectionModel()->selectedIndexes(); Q_ASSERT(!selectedIndexes.isEmpty()); const int itemCount = selectedIndexes.count(); // If more than one item is dragged, align the items inside a // rectangular grid. The maximum grid size is limited to 4 x 4 items. int xCount = 2; int size = 96; if (itemCount > 9) { xCount = 4; size = KisIconUtils::SizeLarge; } else if (itemCount > 4) { xCount = 3; size = KisIconUtils::SizeHuge; } else if (itemCount < xCount) { xCount = itemCount; } int yCount = itemCount / xCount; if (itemCount % xCount != 0) { ++yCount; } if (yCount > xCount) { yCount = xCount; } // Draw the selected items into the grid cells QPixmap dragPixmap(xCount * size + xCount - 1, yCount * size + yCount - 1); dragPixmap.fill(Qt::transparent); QPainter painter(&dragPixmap); int x = 0; int y = 0; Q_FOREACH (const QModelIndex &selectedIndex, selectedIndexes) { const QImage img = selectedIndex.data(int(KisNodeModel::BeginThumbnailRole) + size).value(); - painter.drawPixmap(x, y, QPixmap().fromImage(img.scaled(QSize(size, size), Qt::KeepAspectRatio))); + painter.drawPixmap(x, y, QPixmap().fromImage(img.scaled(QSize(size, size), Qt::KeepAspectRatio, Qt::SmoothTransformation))); x += size + 1; if (x >= dragPixmap.width()) { x = 0; y += size + 1; } if (y >= dragPixmap.height()) { break; } } return dragPixmap; } void KisNodeView::resizeEvent(QResizeEvent * event) { KisNodeViewColorScheme scm; header()->setStretchLastSection(false); header()->setOffset(-scm.visibilityColumnWidth()); header()->resizeSection(0, event->size().width() - scm.visibilityColumnWidth()); setIndentation(scm.indentation()); QTreeView::resizeEvent(event); } void KisNodeView::paintEvent(QPaintEvent *event) { event->accept(); QTreeView::paintEvent(event); // Paint the line where the slide should go if (isDragging() && (displayMode() == KisNodeView::ThumbnailMode)) { QSize size(visualRect(model()->index(0, 0, QModelIndex())).width(), visualRect(model()->index(0, 0, QModelIndex())).height()); int numberRow = cursorPageIndex(); int scrollBarValue = verticalScrollBar()->value(); QPoint point1(0, numberRow * size.height() - scrollBarValue); QPoint point2(size.width(), numberRow * size.height() - scrollBarValue); QLineF line(point1, point2); QPainter painter(this->viewport()); QPen pen = QPen(palette().brush(QPalette::Highlight), 8); pen.setCapStyle(Qt::RoundCap); painter.setPen(pen); painter.setOpacity(0.8); painter.drawLine(line); } } void KisNodeView::drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const { Q_UNUSED(painter); Q_UNUSED(rect); Q_UNUSED(index); /** * Noop... Everything is going to be painted by KisNodeDelegate. * So this override basically disables painting of Qt's branch-lines. */ } void KisNodeView::dropEvent(QDropEvent *ev) { if (displayMode() == KisNodeView::ThumbnailMode) { setDraggingFlag(false); ev->accept(); clearSelection(); if (!model()) { return; } int newIndex = cursorPageIndex(); model()->dropMimeData(ev->mimeData(), ev->dropAction(), newIndex, -1, QModelIndex()); return; } QTreeView::dropEvent(ev); DRAG_WHILE_DRAG_WORKAROUND_STOP(); } int KisNodeView::cursorPageIndex() const { QSize size(visualRect(model()->index(0, 0, QModelIndex())).width(), visualRect(model()->index(0, 0, QModelIndex())).height()); int scrollBarValue = verticalScrollBar()->value(); QPoint cursorPosition = QWidget::mapFromGlobal(QCursor::pos()); int numberRow = (cursorPosition.y() + scrollBarValue) / size.height(); //If cursor is at the half button of the page then the move action is performed after the slide, otherwise it is //performed before the page if (abs((cursorPosition.y() + scrollBarValue) - size.height()*numberRow) > (size.height()/2)) { numberRow++; } if (numberRow > model()->rowCount(QModelIndex())) { numberRow = model()->rowCount(QModelIndex()); } return numberRow; } void KisNodeView::dragEnterEvent(QDragEnterEvent *ev) { DRAG_WHILE_DRAG_WORKAROUND_START(); QTreeView::dragEnterEvent(ev); } void KisNodeView::dragMoveEvent(QDragMoveEvent *ev) { DRAG_WHILE_DRAG_WORKAROUND_START(); if (displayMode() == KisNodeView::ThumbnailMode) { ev->accept(); if (!model()) { return; } QTreeView::dragMoveEvent(ev); setDraggingFlag(); viewport()->update(); return; } QTreeView::dragMoveEvent(ev); } void KisNodeView::dragLeaveEvent(QDragLeaveEvent *e) { if (displayMode() == KisNodeView::ThumbnailMode) { setDraggingFlag(false); } else { QTreeView::dragLeaveEvent(e); } DRAG_WHILE_DRAG_WORKAROUND_STOP(); } bool KisNodeView::isDragging() const { return m_draggingFlag; } void KisNodeView::setDraggingFlag(bool flag) { m_draggingFlag = flag; } diff --git a/libs/ui/KisOpenPane.cpp b/libs/ui/KisOpenPane.cpp index c369e3af26..02fa451d35 100644 --- a/libs/ui/KisOpenPane.cpp +++ b/libs/ui/KisOpenPane.cpp @@ -1,378 +1,375 @@ /* This file is part of the KDE project Copyright (C) 2005 Peter Simonsson 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 "KisOpenPane.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisTemplateTree.h" #include "KisTemplateGroup.h" #include "KisTemplate.h" #include "KisDetailsPane.h" #include "KisTemplatesPane.h" #include "ui_KisOpenPaneBase.h" #include #include #include class KoSectionListItem : public QTreeWidgetItem { public: KoSectionListItem(QTreeWidget* treeWidget, const QString& name, int sortWeight, int widgetIndex = -1) : QTreeWidgetItem(treeWidget, QStringList() << name), m_sortWeight(sortWeight), m_widgetIndex(widgetIndex) { Qt::ItemFlags newFlags = Qt::NoItemFlags; if(m_widgetIndex >= 0) newFlags |= Qt::ItemIsEnabled | Qt::ItemIsSelectable; setFlags(newFlags); } virtual bool operator<(const QTreeWidgetItem & other) const { const KoSectionListItem* item = dynamic_cast(&other); if (!item) return 0; return ((item->sortWeight() - sortWeight()) < 0); } int sortWeight() const { return m_sortWeight; } int widgetIndex() const { return m_widgetIndex; } private: int m_sortWeight; int m_widgetIndex; }; class KoSectionListDelegate : public QStyledItemDelegate { public: KoSectionListDelegate(QObject* parent = 0) : QStyledItemDelegate(parent) { } virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { QStyledItemDelegate::paint(painter, option, index); if(!(option.state & (int)(QStyle::State_Active && QStyle::State_Enabled))) { int ypos = option.rect.y() + ((option.rect.height() - 2) / 2); QRect lineRect(option.rect.left(), ypos, option.rect.width(), 2); QLinearGradient gradient(option.rect.topLeft(), option.rect.bottomRight()); gradient.setColorAt(option.direction == Qt::LeftToRight ? 0 : 1, option.palette.color(QPalette::Text)); gradient.setColorAt(option.direction == Qt::LeftToRight ? 1 : 0, Qt::transparent); painter->fillRect(lineRect, gradient); } } }; class KisOpenPanePrivate : public Ui_KisOpenPaneBase { public: KisOpenPanePrivate() : Ui_KisOpenPaneBase() { m_templatesSeparator = 0; } int m_freeCustomWidgetIndex; KoSectionListItem* m_templatesSeparator; }; KisOpenPane::KisOpenPane(QWidget *parent, const QStringList& mimeFilter, const QString& templatesResourcePath) : QDialog(parent) , d(new KisOpenPanePrivate) { d->setupUi(this); m_mimeFilter = mimeFilter; KoSectionListDelegate* delegate = new KoSectionListDelegate(d->m_sectionList); d->m_sectionList->setItemDelegate(delegate); connect(d->m_sectionList, SIGNAL(itemSelectionChanged()), this, SLOT(updateSelectedWidget())); connect(d->m_sectionList, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(itemClicked(QTreeWidgetItem*))); connect(d->m_sectionList, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(itemClicked(QTreeWidgetItem*))); connect(d->cancelButton, SIGNAL(clicked()), this, SLOT(close())); connect(d->cancelButton, SIGNAL(clicked()), this, SLOT(deleteLater())); initTemplates(templatesResourcePath); d->m_freeCustomWidgetIndex = 4; if (!d->m_sectionList->selectedItems().isEmpty()) { KoSectionListItem* selectedItem = static_cast(d->m_sectionList->selectedItems().first()); if (selectedItem) { d->m_widgetStack->widget(selectedItem->widgetIndex())->setFocus(); } } QList sizes; // Set the sizes of the details pane splitters KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog"); sizes = cfgGrp.readEntry("DetailsPaneSplitterSizes", sizes); if (!sizes.isEmpty()) emit splitterResized(0, sizes); connect(this, SIGNAL(splitterResized(KisDetailsPane*, const QList&)), this, SLOT(saveSplitterSizes(KisDetailsPane*, const QList&))); setAcceptDrops(true); } KisOpenPane::~KisOpenPane() { if (!d->m_sectionList->selectedItems().isEmpty()) { KoSectionListItem* item = dynamic_cast(d->m_sectionList->selectedItems().first()); if (item) { if (!qobject_cast(d->m_widgetStack->widget(item->widgetIndex()))) { KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog"); cfgGrp.writeEntry("LastReturnType", item->text(0)); } } } delete d; } void KisOpenPane::openFileDialog() { KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocument"); dialog.setCaption(i18n("Open Existing Document")); dialog.setDefaultDir(qApp->applicationName().contains("krita") || qApp->applicationName().contains("karbon") ? QDesktopServices::storageLocation(QDesktopServices::PicturesLocation) : QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation)); dialog.setMimeTypeFilters(m_mimeFilter); - dialog.setHideNameFilterDetailsOption(); Q_FOREACH (const QString &filename, dialog.filenames()) { emit openExistingFile(QUrl::fromUserInput(filename)); } } void KisOpenPane::initTemplates(const QString& templatesResourcePath) { - qDebug() << "initTemplates();" << templatesResourcePath; - QTreeWidgetItem* selectItem = 0; QTreeWidgetItem* firstItem = 0; const int templateOffset = 1000; if (!templatesResourcePath.isEmpty()) { KisTemplateTree templateTree(templatesResourcePath, true); Q_FOREACH (KisTemplateGroup *group, templateTree.groups()) { if (group->isHidden()) { continue; } if (!d->m_templatesSeparator) { d->m_templatesSeparator = new KoSectionListItem(d->m_sectionList, "", 999); } KisTemplatesPane* pane = new KisTemplatesPane(this, group->name(), group, templateTree.defaultTemplate()); connect(pane, SIGNAL(openUrl(const QUrl&)), this, SIGNAL(openTemplate(const QUrl&))); connect(pane, SIGNAL(alwaysUseChanged(KisTemplatesPane*, const QString&)), this, SIGNAL(alwaysUseChanged(KisTemplatesPane*, const QString&))); connect(this, SIGNAL(alwaysUseChanged(KisTemplatesPane*, const QString&)), pane, SLOT(changeAlwaysUseTemplate(KisTemplatesPane*, const QString&))); connect(pane, SIGNAL(splitterResized(KisDetailsPane*, const QList&)), this, SIGNAL(splitterResized(KisDetailsPane*, const QList&))); connect(this, SIGNAL(splitterResized(KisDetailsPane*, const QList&)), pane, SLOT(resizeSplitter(KisDetailsPane*, const QList&))); QTreeWidgetItem* item = addPane(group->name(), group->templates().first()->loadPicture(), pane, group->sortingWeight() + templateOffset); if (!firstItem) { firstItem = item; } if (group == templateTree.defaultGroup()) { firstItem = item; } if (pane->isSelected()) { selectItem = item; } } } else { firstItem = d->m_sectionList->topLevelItem(0); } KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog"); if (selectItem && (cfgGrp.readEntry("LastReturnType") == "Template")) { d->m_sectionList->setCurrentItem(selectItem, 0, QItemSelectionModel::ClearAndSelect); } else if (d->m_sectionList->selectedItems().isEmpty() && firstItem) { d->m_sectionList->setCurrentItem(firstItem, 0, QItemSelectionModel::ClearAndSelect); } } void KisOpenPane::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasUrls()) { event->accept(); } } void KisOpenPane::dropEvent(QDropEvent *event) { if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) { // XXX: when the MVC refactoring is done, this can open a bunch of // urls, but since the part/document combination is still 1:1 // that won't work for now. emit openExistingFile(event->mimeData()->urls().first()); } } void KisOpenPane::addCustomDocumentWidget(QWidget *widget, const QString& title, const QString& icon) { Q_ASSERT(widget); QString realtitle = title; if (realtitle.isEmpty()) realtitle = i18n("Custom Document"); QTreeWidgetItem* item = addPane(realtitle, icon, widget, d->m_freeCustomWidgetIndex); ++d->m_freeCustomWidgetIndex; KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog"); QString lastActiveItem = cfgGrp.readEntry("LastReturnType"); bool showCustomItemByDefault = cfgGrp.readEntry("ShowCustomDocumentWidgetByDefault", false); if (lastActiveItem == realtitle || (lastActiveItem.isEmpty() && showCustomItemByDefault)) { d->m_sectionList->setCurrentItem(item, 0, QItemSelectionModel::ClearAndSelect); KoSectionListItem* selectedItem = static_cast(item); d->m_widgetStack->widget(selectedItem->widgetIndex())->setFocus(); } } QTreeWidgetItem* KisOpenPane::addPane(const QString &title, const QString &iconName, QWidget *widget, int sortWeight) { if (!widget) { return 0; } int id = d->m_widgetStack->addWidget(widget); KoSectionListItem* listItem = new KoSectionListItem(d->m_sectionList, title, sortWeight, id); listItem->setIcon(0, KisIconUtils::loadIcon(iconName)); return listItem; } QTreeWidgetItem* KisOpenPane::addPane(const QString& title, const QPixmap& icon, QWidget* widget, int sortWeight) { if (!widget) { return 0; } int id = d->m_widgetStack->addWidget(widget); KoSectionListItem* listItem = new KoSectionListItem(d->m_sectionList, title, sortWeight, id); if (!icon.isNull()) { QImage image = icon.toImage(); if ((image.width() > 48) || (image.height() > 48)) { image = image.scaled(48, 48, Qt::KeepAspectRatio, Qt::SmoothTransformation); } image = image.convertToFormat(QImage::Format_ARGB32); image = image.copy((image.width() - 48) / 2, (image.height() - 48) / 2, 48, 48); listItem->setIcon(0, QIcon(QPixmap::fromImage(image))); } return listItem; } void KisOpenPane::updateSelectedWidget() { if(!d->m_sectionList->selectedItems().isEmpty()) { KoSectionListItem* section = dynamic_cast(d->m_sectionList->selectedItems().first()); if (!section) return; d->m_widgetStack->setCurrentIndex(section->widgetIndex()); } } void KisOpenPane::saveSplitterSizes(KisDetailsPane* sender, const QList& sizes) { Q_UNUSED(sender); KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog"); cfgGrp.writeEntry("DetailsPaneSplitterSizes", sizes); } void KisOpenPane::itemClicked(QTreeWidgetItem* item) { KoSectionListItem* selectedItem = static_cast(item); if (selectedItem && selectedItem->widgetIndex() >= 0) { d->m_widgetStack->widget(selectedItem->widgetIndex())->setFocus(); } } diff --git a/libs/ui/KisTemplateTree.cpp b/libs/ui/KisTemplateTree.cpp index ae653ee0d9..eca9d5a6ac 100644 --- a/libs/ui/KisTemplateTree.cpp +++ b/libs/ui/KisTemplateTree.cpp @@ -1,290 +1,288 @@ /* This file is part of the KDE project Copyright (C) 2000 Werner Trobin 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 "KisTemplateTree.h" #include #include #include #include #include #include #include #include #include #include #include #include KisTemplateTree::KisTemplateTree(const QString &templatesResourcePath, bool readTree) : m_templatesResourcePath(templatesResourcePath) , m_defaultGroup(0) , m_defaultTemplate(0) { if (readTree) readTemplateTree(); } KisTemplateTree::~KisTemplateTree() { qDeleteAll(m_groups); } void KisTemplateTree::readTemplateTree() { - readGroups(); readTemplates(); } void KisTemplateTree::writeTemplateTree() { QString localDir = KoResourcePaths::saveLocation("data", m_templatesResourcePath); Q_FOREACH (KisTemplateGroup *group, m_groups) { //dbgUI <<"---------------------------------"; //dbgUI <<"group:" << group->name(); bool touched = false; QList templates = group->templates(); QList::iterator it = templates.begin(); for (; it != templates.end() && !touched && !group->touched(); ++it) touched = (*it)->touched(); if (group->touched() || touched) { //dbgUI <<"touched"; if (!group->isHidden()) { //dbgUI <<"not hidden"; QDir path; path.mkpath(localDir + group->name()); // create the local group dir } else { //dbgUI <<"hidden"; if (group->dirs().count() == 1 && group->dirs().contains(localDir)) { //dbgUI <<"local only"; QFile f(group->dirs().first()); f.remove(); //dbgUI <<"removing:" << group->dirs().first(); } else { //dbgUI <<"global"; QDir path; path.mkpath(localDir + group->name()); } } } Q_FOREACH (KisTemplate *t, templates) { if (t->touched()) { //dbgUI <<"++template:" << t->name(); writeTemplate(t, group, localDir); } if (t->isHidden() && t->touched()) { //dbgUI <<"+++ delete local template ##############"; writeTemplate(t, group, localDir); QFile::remove(t->file()); QFile::remove(t->picture()); } } } } void KisTemplateTree::add(KisTemplateGroup *g) { KisTemplateGroup *group = find(g->name()); if (group == 0) m_groups.append(g); else { group->addDir(g->dirs().first()); // "...there can be only one..." (Queen) delete g; g = 0; } } KisTemplateGroup *KisTemplateTree::find(const QString &name) const { QList::const_iterator it = m_groups.begin(); KisTemplateGroup* ret = 0; while (it != m_groups.end()) { if ((*it)->name() == name) { ret = *it; break; } ++it; } return ret; } void KisTemplateTree::readGroups() { - QStringList dirs = KoResourcePaths::findDirs("data", m_templatesResourcePath); Q_FOREACH (const QString & dirName, dirs) { QDir dir(dirName); // avoid the annoying warning if (!dir.exists()) continue; QStringList templateDirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); Q_FOREACH (const QString & templateDirName, templateDirs) { QDir templateDir(dirName + "/" + templateDirName); QString name = templateDirName; QString defaultTab; int sortingWeight = 1000; if (templateDir.exists(".directory")) { KDesktopFile config(templateDir.absoluteFilePath(".directory")); KConfigGroup dg = config.desktopGroup(); name = dg.readEntry("Name"); defaultTab = dg.readEntry("X-KDE-DefaultTab"); sortingWeight = dg.readEntry("X-KDE-SortingWeight", 1000); } KisTemplateGroup *g = new KisTemplateGroup(name, templateDir.absolutePath() + QDir::separator(), sortingWeight); add(g); if (defaultTab == "true") m_defaultGroup = g; } } } void KisTemplateTree::readTemplates() { QString dontShow = "imperial"; if ( QLocale().measurementSystem() == QLocale::ImperialSystem) { dontShow = "metric"; } Q_FOREACH (KisTemplateGroup* group, m_groups) { QStringList dirs = group->dirs(); for (QStringList::ConstIterator it = dirs.constBegin(); it != dirs.constEnd(); ++it) { QDir d(*it); if (!d.exists()) continue; QStringList files = d.entryList(QDir::Files | QDir::Readable, QDir::Name); for (int i = 0; i < files.count(); ++i) { QString filePath = *it + files[i]; //dbgUI <<"filePath:" << filePath; QString icon; QString text; QString description; QString hidden_str; QString fileName; bool hidden = false; bool defaultTemplate = false; QString templatePath; QString measureSystem; // If a desktop file, then read the name from it. // Otherwise (or if no name in it?) use file name if (KDesktopFile::isDesktopFile(filePath)) { KConfig _config(filePath, KConfig::SimpleConfig); KConfigGroup config(&_config, "Desktop Entry"); if (config.readEntry("Type") == "Link") { text = config.readEntry("Name"); fileName = filePath; description = config.readEntry("Comment"); //dbgUI <<"name:" << text; icon = config.readEntry("Icon"); if (icon[0] != '/' && // allow absolute paths for icons QFile::exists(*it + icon)) // allow icons from icontheme icon = *it + icon; //dbgUI <<"icon2:" << icon; hidden = config.readEntry("X-KDE-Hidden", false); defaultTemplate = config.readEntry("X-KDE-DefaultTemplate", false); measureSystem = config.readEntry("X-KDE-MeasureSystem").toLower(); // Don't add a template that is for the wrong measure system if (measureSystem == dontShow) continue; //dbgUI <<"hidden:" << hidden_str; templatePath = config.readPathEntry("URL", QString()); //dbgUI <<"Link to :" << templatePath; if (templatePath[0] != '/') { if (templatePath.left(6) == "file:/") // I doubt this will happen templatePath = templatePath.right(templatePath.length() - 6); //else // dbgUI <<"dirname=" << *it; templatePath = *it + templatePath; //dbgUI <<"templatePath:" << templatePath; } } else continue; // Invalid } // The else if and the else branch are here for compat. with the old system else if (files[i].right(4) != ".png") // Ignore everything that is not a PNG file continue; else { // Found a PNG file - the template must be here in the same dir. icon = filePath; QFileInfo fi(filePath); text = fi.baseName(); templatePath = filePath; // Note that we store the .png file as the template ! // That's the way it's always been done. Then the app replaces the extension... } KisTemplate *t = new KisTemplate(text, description, templatePath, icon, fileName, measureSystem, hidden); group->add(t, false, false); // false -> we aren't a "user", false -> don't // "touch" the group to avoid useless // creation of dirs in .kde/blah/... if (defaultTemplate) m_defaultTemplate = t; } } } } void KisTemplateTree::writeTemplate(KisTemplate *t, KisTemplateGroup *group, const QString &localDir) { QString fileName; if (t->isHidden()) { fileName = t->fileName(); // try to remove the file if (QFile::remove(fileName) || !QFile::exists(fileName)) { QFile::remove(t->name()); QFile::remove(t->picture()); return; } } // be sure that the template's file name is unique so we don't overwrite an other QString const path = localDir + group->name() + '/'; QString const name = KisTemplates::trimmed(t->name()); fileName = path + name + ".desktop"; if (t->isHidden() && QFile::exists(fileName)) return; QString fill; while (QFile(fileName).exists()) { fill += '_'; fileName = path + fill + name + ".desktop"; } KConfig _config(fileName, KConfig::SimpleConfig); KConfigGroup config(&_config, "Desktop Entry"); config.writeEntry("Type", "Link"); config.writePathEntry("URL", t->file()); config.writeEntry("Name", t->name()); config.writeEntry("Icon", t->picture()); config.writeEntry("X-KDE-Hidden", t->isHidden()); } diff --git a/libs/ui/KisView.cpp b/libs/ui/KisView.cpp index 2531f7a2e1..b6069764ce 100644 --- a/libs/ui/KisView.cpp +++ b/libs/ui/KisView.cpp @@ -1,882 +1,883 @@ /* * Copyright (C) 2014 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisView.h" #include "KisView_p.h" #include #include #include #include "KoDocumentInfo.h" #include "KoPageLayout.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 "kis_canvas2.h" #include "kis_canvas_controller.h" #include "kis_canvas_resource_provider.h" #include "kis_config.h" #include "KisDocument.h" #include "kis_image_manager.h" #include "KisMainWindow.h" #include "kis_mimedata.h" #include "kis_mirror_axis.h" #include "kis_node_commands_adapter.h" #include "kis_node_manager.h" #include "KisPart.h" #include "KisPrintJob.h" #include "kis_shape_controller.h" #include "kis_tool_freehand.h" #include "KisUndoStackAction.h" #include "KisViewManager.h" #include "kis_zoom_manager.h" #include "kis_composite_progress_proxy.h" #include "kis_statusbar.h" #include "kis_painting_assistants_decoration.h" #include "kis_progress_widget.h" #include "kis_signal_compressor.h" #include "kis_filter_manager.h" #include "krita_utils.h" #include "input/kis_input_manager.h" #include "KisRemoteFileFetcher.h" //static QString KisView::newObjectName() { static int s_viewIFNumber = 0; QString name; name.setNum(s_viewIFNumber++); name.prepend("view_"); return name; } bool KisView::s_firstView = true; class Q_DECL_HIDDEN KisView::Private { public: - Private(KisView *_q, KisDocument *document, + Private(KisView *_q, + KisDocument *document, KoCanvasResourceManager *resourceManager, KActionCollection *actionCollection) : actionCollection(actionCollection) , viewConverter() , canvasController(_q, actionCollection) , canvas(&viewConverter, resourceManager, _q, document->shapeController()) , zoomManager(_q, &this->viewConverter, &this->canvasController) - , paintingAssistantsDecoration(_q) + , paintingAssistantsDecoration(new KisPaintingAssistantsDecoration(_q)) , floatingMessageCompressor(100, KisSignalCompressor::POSTPONE) { } KisUndoStackAction *undo = 0; KisUndoStackAction *redo = 0; bool inOperation; //in the middle of an operation (no screen refreshing)? QPointer document; // our KisDocument QWidget *tempActiveWidget = 0; /** * Signals the document has been deleted. Can't use document==0 since this * only happens in ~QObject, and views get deleted by ~KisDocument. * XXX: either provide a better justification to do things this way, or * rework the mechanism. */ bool documentDeleted = false; KActionCollection* actionCollection; KisCoordinatesConverter viewConverter; KisCanvasController canvasController; KisCanvas2 canvas; KisZoomManager zoomManager; KisViewManager *viewManager = 0; KisNodeSP currentNode; - KisPaintingAssistantsDecoration paintingAssistantsDecoration; + KisPaintingAssistantsDecorationSP paintingAssistantsDecoration; bool isCurrent = false; bool showFloatingMessage = false; QPointer savedFloatingMessage; KisSignalCompressor floatingMessageCompressor; // Hmm sorry for polluting the private class with such a big inner class. // At the beginning it was a little struct :) class StatusBarItem { public: StatusBarItem(QWidget * widget, int stretch, bool permanent) : m_widget(widget), m_stretch(stretch), m_permanent(permanent), m_connected(false), m_hidden(false) {} bool operator==(const StatusBarItem& rhs) { return m_widget == rhs.m_widget; } bool operator!=(const StatusBarItem& rhs) { return m_widget != rhs.m_widget; } QWidget * widget() const { return m_widget; } void ensureItemShown(QStatusBar * sb) { Q_ASSERT(m_widget); if (!m_connected) { if (m_permanent) sb->addPermanentWidget(m_widget, m_stretch); else sb->addWidget(m_widget, m_stretch); if(!m_hidden) m_widget->show(); m_connected = true; } } void ensureItemHidden(QStatusBar * sb) { if (m_connected) { m_hidden = m_widget->isHidden(); sb->removeWidget(m_widget); m_widget->hide(); m_connected = false; } } private: QWidget * m_widget = 0; int m_stretch; bool m_permanent; bool m_connected = false; bool m_hidden = false; }; }; KisView::KisView(KisDocument *document, KoCanvasResourceManager *resourceManager, KActionCollection *actionCollection, QWidget *parent) : QWidget(parent) , d(new Private(this, document, resourceManager, actionCollection)) { Q_ASSERT(document); connect(document, SIGNAL(titleModified(QString,bool)), this, SIGNAL(titleModified(QString,bool))); setObjectName(newObjectName()); d->document = document; setFocusPolicy(Qt::StrongFocus); d->undo = new KisUndoStackAction(d->document->undoStack(), KisUndoStackAction::UNDO); d->redo = new KisUndoStackAction(d->document->undoStack(), KisUndoStackAction::RED0); QStatusBar * sb = statusBar(); if (sb) { // No statusbar in e.g. konqueror connect(d->document, SIGNAL(statusBarMessage(const QString&)), this, SLOT(slotActionStatusText(const QString&))); connect(d->document, SIGNAL(clearStatusBarMessage()), this, SLOT(slotClearStatusText())); } d->canvas.setup(); KisConfig cfg; d->canvasController.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); d->canvasController.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); d->canvasController.setDrawShadow(false); d->canvasController.setCanvasMode(KoCanvasController::Infinite); d->canvasController.setVastScrolling(cfg.vastScrolling()); d->canvasController.setCanvas(&d->canvas); d->zoomManager.setup(d->actionCollection); connect(&d->canvasController, SIGNAL(documentSizeChanged()), &d->zoomManager, SLOT(slotScrollAreaSizeChanged())); setAcceptDrops(true); connect(d->document, SIGNAL(sigLoadingFinished()), this, SLOT(slotLoadingFinished())); connect(d->document, SIGNAL(sigSavingFinished()), this, SLOT(slotSavingFinished())); - d->canvas.addDecoration(&d->paintingAssistantsDecoration); - d->paintingAssistantsDecoration.setVisible(true); + d->canvas.addDecoration(d->paintingAssistantsDecoration); + d->paintingAssistantsDecoration->setVisible(true); d->showFloatingMessage = cfg.showCanvasMessages(); } KisView::~KisView() { if (d->viewManager) { KoProgressProxy *proxy = d->viewManager->statusBar()->progress()->progressProxy(); KIS_ASSERT_RECOVER_NOOP(proxy); image()->compositeProgressProxy()->removeProxy(proxy); if (d->viewManager->filterManager()->isStrokeRunning()) { d->viewManager->filterManager()->cancel(); } } KisPart::instance()->removeView(this); KoToolManager::instance()->removeCanvasController(&d->canvasController); delete d; } void KisView::notifyCurrentStateChanged(bool isCurrent) { d->isCurrent = isCurrent; if (!d->isCurrent && d->savedFloatingMessage) { d->savedFloatingMessage->removeMessage(); } KisInputManager *inputManager = globalInputManager(); if (d->isCurrent) { inputManager->attachPriorityEventFilter(&d->canvasController); } else { inputManager->detachPriorityEventFilter(&d->canvasController); } } void KisView::setShowFloatingMessage(bool show) { d->showFloatingMessage = show; } void KisView::showFloatingMessageImpl(const QString &message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment) { if (!d->viewManager) return; if(d->isCurrent && d->showFloatingMessage && d->viewManager->qtMainWindow()) { if (d->savedFloatingMessage) { d->savedFloatingMessage->tryOverrideMessage(message, icon, timeout, priority, alignment); } else { d->savedFloatingMessage = new KisFloatingMessage(message, this->canvasBase()->canvasWidget(), false, timeout, priority, alignment); d->savedFloatingMessage->setShowOverParent(true); d->savedFloatingMessage->setIcon(icon); connect(&d->floatingMessageCompressor, SIGNAL(timeout()), d->savedFloatingMessage, SLOT(showMessage())); d->floatingMessageCompressor.start(); } } } void KisView::setViewManager(KisViewManager *view) { d->viewManager = view; KoToolManager::instance()->addController(&d->canvasController); KoToolManager::instance()->registerToolActions(d->actionCollection, &d->canvasController); dynamic_cast(d->document->shapeController())->setInitialShapeForCanvas(&d->canvas); if (resourceProvider()) { resourceProvider()->slotImageSizeChanged(); } if (d->viewManager && d->viewManager->nodeManager()) { d->viewManager->nodeManager()->nodesUpdated(); } connect(image(), SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)), this, SLOT(slotImageSizeChanged(const QPointF&, const QPointF&))); connect(image(), SIGNAL(sigResolutionChanged(double,double)), this, SLOT(slotImageResolutionChanged())); // executed in a context of an image thread connect(image(), SIGNAL(sigNodeAddedAsync(KisNodeSP)), SLOT(slotImageNodeAdded(KisNodeSP)), Qt::DirectConnection); // executed in a context of the gui thread connect(this, SIGNAL(sigContinueAddNode(KisNodeSP)), SLOT(slotContinueAddNode(KisNodeSP)), Qt::AutoConnection); // executed in a context of an image thread connect(image(), SIGNAL(sigRemoveNodeAsync(KisNodeSP)), SLOT(slotImageNodeRemoved(KisNodeSP)), Qt::DirectConnection); // executed in a context of the gui thread connect(this, SIGNAL(sigContinueRemoveNode(KisNodeSP)), SLOT(slotContinueRemoveNode(KisNodeSP)), Qt::AutoConnection); /* * WARNING: Currently we access the global progress bar in two ways: * connecting to composite progress proxy (strokes) and creating * progress updaters. The latter way should be deprecated in favour * of displaying the status of the global strokes queue */ image()->compositeProgressProxy()->addProxy(d->viewManager->statusBar()->progress()->progressProxy()); connect(d->viewManager->statusBar()->progress(), SIGNAL(sigCancellationRequested()), image(), SLOT(requestStrokeCancellation())); d->viewManager->updateGUI(); KoToolManager::instance()->switchToolRequested("KritaShape/KisToolBrush"); } KisViewManager* KisView::viewManager() const { return d->viewManager; } void KisView::slotImageNodeAdded(KisNodeSP node) { emit sigContinueAddNode(node); } void KisView::slotContinueAddNode(KisNodeSP newActiveNode) { /** * When deleting the last layer, root node got selected. We should * fix it when the first layer is added back. * * Here we basically reimplement what Qt's view/model do. But * since they are not connected, we should do it manually. */ if (!d->isCurrent && (!d->currentNode || !d->currentNode->parent())) { d->currentNode = newActiveNode; } } void KisView::slotImageNodeRemoved(KisNodeSP node) { emit sigContinueRemoveNode(KritaUtils::nearestNodeAfterRemoval(node)); } void KisView::slotContinueRemoveNode(KisNodeSP newActiveNode) { if (!d->isCurrent) { d->currentNode = newActiveNode; } } QAction *KisView::undoAction() const { return d->undo; } QAction *KisView::redoAction() const { return d->redo; } KoZoomController *KisView::zoomController() const { return d->zoomManager.zoomController(); } KisZoomManager *KisView::zoomManager() const { return &d->zoomManager; } KisCanvasController *KisView::canvasController() const { return &d->canvasController; } KisCanvasResourceProvider *KisView::resourceProvider() const { if (d->viewManager) { return d->viewManager->resourceProvider(); } return 0; } KisInputManager* KisView::globalInputManager() const { return d->viewManager ? d->viewManager->inputManager() : 0; } KisCanvas2 *KisView::canvasBase() const { return &d->canvas; } KisImageWSP KisView::image() const { if (d->document) { return d->document->image(); } return 0; } KisCoordinatesConverter *KisView::viewConverter() const { return &d->viewConverter; } void KisView::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasImage() || event->mimeData()->hasUrls() || event->mimeData()->hasFormat("application/x-krita-node")) { event->accept(); // activate view if it should accept the drop this->setFocus(); } else { event->ignore(); } } void KisView::dropEvent(QDropEvent *event) { KisImageWSP kisimage = image(); Q_ASSERT(kisimage); QPoint cursorPos = canvasBase()->coordinatesConverter()->widgetToImage(event->pos()).toPoint(); QRect imageBounds = kisimage->bounds(); QPoint pasteCenter; bool forceRecenter; if (event->keyboardModifiers() & Qt::ShiftModifier && imageBounds.contains(cursorPos)) { pasteCenter = cursorPos; forceRecenter = true; } else { pasteCenter = imageBounds.center(); forceRecenter = false; } if (event->mimeData()->hasFormat("application/x-krita-node") || event->mimeData()->hasImage()) { KisShapeController *kritaShapeController = dynamic_cast(d->document->shapeController()); QList nodes = KisMimeData::loadNodes(event->mimeData(), imageBounds, pasteCenter, forceRecenter, kisimage, kritaShapeController); Q_FOREACH (KisNodeSP node, nodes) { if (node) { KisNodeCommandsAdapter adapter(viewManager()); if (!viewManager()->nodeManager()->activeLayer()) { adapter.addNode(node, kisimage->rootLayer() , 0); } else { adapter.addNode(node, viewManager()->nodeManager()->activeLayer()->parent(), viewManager()->nodeManager()->activeLayer()); } } } } else if (event->mimeData()->hasUrls()) { QList urls = event->mimeData()->urls(); if (urls.length() > 0) { QMenu popup; popup.setObjectName("drop_popup"); QAction *insertAsNewLayer = new QAction(i18n("Insert as New Layer"), &popup); QAction *insertManyLayers = new QAction(i18n("Insert Many Layers"), &popup); QAction *openInNewDocument = new QAction(i18n("Open in New Document"), &popup); QAction *openManyDocuments = new QAction(i18n("Open Many Documents"), &popup); QAction *cancel = new QAction(i18n("Cancel"), &popup); popup.addAction(insertAsNewLayer); popup.addAction(openInNewDocument); popup.addAction(insertManyLayers); popup.addAction(openManyDocuments); insertAsNewLayer->setEnabled(image() && urls.count() == 1); openInNewDocument->setEnabled(urls.count() == 1); insertManyLayers->setEnabled(image() && urls.count() > 1); openManyDocuments->setEnabled(urls.count() > 1); popup.addSeparator(); popup.addAction(cancel); QAction *action = popup.exec(QCursor::pos()); if (action != 0 && action != cancel) { QTemporaryFile *tmp = 0; Q_FOREACH (QUrl url, urls) { if (!url.isLocalFile()) { // download the file and substitute the url KisRemoteFileFetcher fetcher; tmp = new QTemporaryFile(); tmp->setAutoRemove(true); if (!fetcher.fetchFile(url, tmp)) { qDebug() << "Fetching" << url << "failed"; continue; } url = url.fromLocalFile(tmp->fileName()); } if (url.isLocalFile()) { if (action == insertAsNewLayer || action == insertManyLayers) { d->viewManager->imageManager()->importImage(url); activateWindow(); } else { Q_ASSERT(action == openInNewDocument || action == openManyDocuments); if (mainWindow()) { mainWindow()->openDocument(url); } } } delete tmp; tmp = 0; } } } } } KisDocument *KisView::document() const { return d->document; } void KisView::setDocument(KisDocument *document) { d->document->disconnect(this); d->document = document; QStatusBar *sb = statusBar(); if (sb) { // No statusbar in e.g. konqueror connect(d->document, SIGNAL(statusBarMessage(const QString&)), this, SLOT(slotActionStatusText(const QString&))); connect(d->document, SIGNAL(clearStatusBarMessage()), this, SLOT(slotClearStatusText())); } } void KisView::setDocumentDeleted() { d->documentDeleted = true; } KoPageLayout KisView::pageLayout() const { return document()->pageLayout(); } QPrintDialog *KisView::createPrintDialog(KisPrintJob *printJob, QWidget *parent) { Q_UNUSED(parent); QPrintDialog *printDialog = new QPrintDialog(&printJob->printer(), this); printDialog->setMinMax(printJob->printer().fromPage(), printJob->printer().toPage()); printDialog->setEnabledOptions(printJob->printDialogOptions()); return printDialog; } KisMainWindow * KisView::mainWindow() const { return dynamic_cast(window()); } QStatusBar * KisView::statusBar() const { KisMainWindow *mw = mainWindow(); return mw ? mw->statusBar() : 0; } void KisView::slotActionStatusText(const QString &text) { QStatusBar *sb = statusBar(); if (sb) sb->showMessage(text); } void KisView::slotClearStatusText() { QStatusBar *sb = statusBar(); if (sb) sb->clearMessage(); } QList KisView::createChangeUnitActions(bool addPixelUnit) { UnitActionGroup* unitActions = new UnitActionGroup(d->document, addPixelUnit, this); return unitActions->actions(); } void KisView::closeEvent(QCloseEvent *event) { // Check whether we're the last view int viewCount = KisPart::instance()->viewCount(document()); if (viewCount > 1) { // there are others still, so don't bother the user event->accept(); return; } if (queryClose()) { d->viewManager->removeStatusBarItem(zoomManager()->zoomActionWidget()); event->accept(); return; } event->ignore(); } bool KisView::queryClose() { if (!document()) return true; if (document()->isModified()) { QString name; if (document()->documentInfo()) { name = document()->documentInfo()->aboutInfo("title"); } if (name.isEmpty()) name = document()->url().fileName(); if (name.isEmpty()) name = i18n("Untitled"); int res = QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("

The document '%1' has been modified.

Do you want to save it?

", name), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes); switch (res) { case QMessageBox::Yes : { bool isNative = (document()->outputMimeType() == document()->nativeFormatMimeType()); if (!viewManager()->mainWindow()->saveDocument(document(), !isNative)) return false; break; } case QMessageBox::No : document()->removeAutoSaveFiles(); document()->setModified(false); // Now when queryClose() is called by closeEvent it won't do anything. break; default : // case QMessageBox::Cancel : return false; } } return true; } void KisView::resetImageSizeAndScroll(bool changeCentering, const QPointF &oldImageStillPoint, const QPointF &newImageStillPoint) { const KisCoordinatesConverter *converter = d->canvas.coordinatesConverter(); QPointF oldPreferredCenter = d->canvasController.preferredCenter(); /** * Calculating the still point in old coordinates depending on the * parameters given */ QPointF oldStillPoint; if (changeCentering) { oldStillPoint = converter->imageToWidget(oldImageStillPoint) + converter->documentOffset(); } else { QSize oldDocumentSize = d->canvasController.documentSize(); oldStillPoint = QPointF(0.5 * oldDocumentSize.width(), 0.5 * oldDocumentSize.height()); } /** * Updating the document size */ QSizeF size(image()->width() / image()->xRes(), image()->height() / image()->yRes()); KoZoomController *zc = d->zoomManager.zoomController(); zc->setZoom(KoZoomMode::ZOOM_CONSTANT, zc->zoomAction()->effectiveZoom()); zc->setPageSize(size); zc->setDocumentSize(size, true); /** * Calculating the still point in new coordinates depending on the * parameters given */ QPointF newStillPoint; if (changeCentering) { newStillPoint = converter->imageToWidget(newImageStillPoint) + converter->documentOffset(); } else { QSize newDocumentSize = d->canvasController.documentSize(); newStillPoint = QPointF(0.5 * newDocumentSize.width(), 0.5 * newDocumentSize.height()); } d->canvasController.setPreferredCenter(oldPreferredCenter - oldStillPoint + newStillPoint); } void KisView::setCurrentNode(KisNodeSP node) { d->currentNode = node; } KisNodeSP KisView::currentNode() const { return d->currentNode; } KisLayerSP KisView::currentLayer() const { KisNodeSP node; KisMaskSP mask = currentMask(); if (mask) { node = mask->parent(); } else { node = d->currentNode; } return dynamic_cast(node.data()); } KisMaskSP KisView::currentMask() const { return dynamic_cast(d->currentNode.data()); } KisSelectionSP KisView::selection() { KisLayerSP layer = currentLayer(); if (layer) return layer->selection(); // falls through to the global // selection, or 0 in the end if (image()) { return image()->globalSelection(); } return 0; } void KisView::slotLoadingFinished() { if (!document()) return; /** * Cold-start of image size/resolution signals */ slotImageResolutionChanged(); if (image()->locked()) { // If this is the first view on the image, the image will have been locked // so unlock it. image()->blockSignals(false); image()->unlock(); } canvasBase()->initializeImage(); /** * Dirty hack alert */ d->zoomManager.zoomController()->setAspectMode(true); if (viewConverter()) { viewConverter()->setZoomMode(KoZoomMode::ZOOM_PAGE); } connect(image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), this, SIGNAL(sigColorSpaceChanged(const KoColorSpace*))); connect(image(), SIGNAL(sigProfileChanged(const KoColorProfile*)), this, SIGNAL(sigProfileChanged(const KoColorProfile*))); connect(image(), SIGNAL(sigSizeChanged(QPointF,QPointF)), this, SIGNAL(sigSizeChanged(QPointF,QPointF))); KisNodeSP activeNode = document()->preActivatedNode(); document()->setPreActivatedNode(0); // to make sure that we don't keep a reference to a layer the user can later delete. if (!activeNode) { activeNode = image()->rootLayer()->lastChild(); } while (activeNode && !activeNode->inherits("KisLayer")) { activeNode = activeNode->prevSibling(); } setCurrentNode(activeNode); zoomManager()->updateImageBoundsSnapping(); } void KisView::slotSavingFinished() { if (d->viewManager && d->viewManager->mainWindow()) { d->viewManager->mainWindow()->updateCaption(); } } KisPrintJob * KisView::createPrintJob() { return new KisPrintJob(image()); } void KisView::slotImageResolutionChanged() { resetImageSizeAndScroll(false); zoomManager()->updateImageBoundsSnapping(); zoomManager()->updateGUI(); // update KoUnit value for the document if (resourceProvider()) { resourceProvider()->resourceManager()-> setResource(KoCanvasResourceManager::Unit, d->canvas.unit()); } } void KisView::slotImageSizeChanged(const QPointF &oldStillPoint, const QPointF &newStillPoint) { resetImageSizeAndScroll(true, oldStillPoint, newStillPoint); zoomManager()->updateImageBoundsSnapping(); zoomManager()->updateGUI(); } diff --git a/libs/ui/KisViewManager.cpp b/libs/ui/KisViewManager.cpp index c42ef3754e..6a968412c6 100644 --- a/libs/ui/KisViewManager.cpp +++ b/libs/ui/KisViewManager.cpp @@ -1,1236 +1,1241 @@ /* * This file is part of KimageShop^WKrayon^WKrita * * Copyright (c) 1999 Matthias Elter * 1999 Michael Koch * 1999 Carsten Pfeiffer * 2002 Patrick Julien * 2003-2011 Boudewijn Rempt * 2004 Clarence Dang * 2011 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 #include "KisViewManager.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 "input/kis_input_manager.h" #include "canvas/kis_canvas2.h" #include "canvas/kis_canvas_controller.h" #include "canvas/kis_grid_manager.h" #include "dialogs/kis_dlg_blacklist_cleanup.h" #include "input/kis_input_profile_manager.h" #include "kis_action_manager.h" #include "kis_action.h" #include "kis_canvas_controls_manager.h" #include "kis_canvas_resource_provider.h" #include "kis_composite_progress_proxy.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_control_frame.h" #include "kis_coordinates_converter.h" #include "KisDocument.h" #include "kis_favorite_resource_manager.h" #include "kis_filter_manager.h" #include "kis_group_layer.h" #include #include "kis_image_manager.h" #include #include "kis_mainwindow_observer.h" #include "kis_mask_manager.h" #include "kis_mimedata.h" #include "kis_mirror_manager.h" #include "kis_node_commands_adapter.h" #include "kis_node.h" #include "kis_node_manager.h" #include "kis_painting_assistants_manager.h" #include #include "kis_paintop_box.h" #include #include "KisPart.h" #include "KisPrintJob.h" #include "kis_progress_widget.h" #include "kis_resource_server_provider.h" #include "kis_selection.h" #include "kis_selection_manager.h" #include "kis_shape_controller.h" #include "kis_shape_layer.h" #include #include "kis_statusbar.h" #include #include #include "kis_tooltip_manager.h" #include #include "KisView.h" #include "kis_zoom_manager.h" #include "kra/kis_kra_loader.h" #include "widgets/kis_floating_message.h" #include "kis_signal_auto_connection.h" #include "kis_icon_utils.h" #include "kis_guides_manager.h" #include "kis_derived_resources.h" class BlockingUserInputEventFilter : public QObject { bool eventFilter(QObject *watched, QEvent *event) { Q_UNUSED(watched); if(dynamic_cast(event) || dynamic_cast(event) || dynamic_cast(event)) { return true; } else { return false; } } }; class KisViewManager::KisViewManagerPrivate { public: KisViewManagerPrivate(KisViewManager *_q, QWidget *_q_parent) : filterManager(_q) , createTemplate(0) , saveIncremental(0) , saveIncrementalBackup(0) , openResourcesDirectory(0) , rotateCanvasRight(0) , rotateCanvasLeft(0) , resetCanvasRotation(0) , wrapAroundAction(0) , levelOfDetailAction(0) , showRulersAction(0) , rulersTrackMouseAction(0) , zoomTo100pct(0) , zoomIn(0) , zoomOut(0) , selectionManager(_q) , statusBar(_q) , controlFrame(_q, _q_parent) , nodeManager(_q) , imageManager(_q) , gridManager(_q) , canvasControlsManager(_q) , paintingAssistantsManager(_q) , actionManager(_q) , mainWindow(0) , showFloatingMessage(true) , currentImageView(0) , canvasResourceProvider(_q) , canvasResourceManager() , guiUpdateCompressor(30, KisSignalCompressor::POSTPONE, _q) , mirrorManager(_q) , inputManager(_q) , actionAuthor(0) { canvasResourceManager.addDerivedResourceConverter(toQShared(new KisCompositeOpResourceConverter)); canvasResourceManager.addDerivedResourceConverter(toQShared(new KisEffectiveCompositeOpResourceConverter)); canvasResourceManager.addDerivedResourceConverter(toQShared(new KisOpacityResourceConverter)); canvasResourceManager.addDerivedResourceConverter(toQShared(new KisLodAvailabilityResourceConverter)); canvasResourceManager.addDerivedResourceConverter(toQShared(new KisEraserModeResourceConverter)); } public: KisFilterManager filterManager; KisAction *createTemplate; KisAction *createCopy; KisAction *saveIncremental; KisAction *saveIncrementalBackup; KisAction *openResourcesDirectory; KisAction *rotateCanvasRight; KisAction *rotateCanvasLeft; KisAction *resetCanvasRotation; KisAction *wrapAroundAction; KisAction *levelOfDetailAction; KisAction *showRulersAction; KisAction *rulersTrackMouseAction; KisAction *zoomTo100pct; KisAction *zoomIn; KisAction *zoomOut; KisSelectionManager selectionManager; KisGuidesManager guidesManager; KisStatusBar statusBar; KisControlFrame controlFrame; KisNodeManager nodeManager; KisImageManager imageManager; KisGridManager gridManager; KisCanvasControlsManager canvasControlsManager; KisPaintingAssistantsManager paintingAssistantsManager; BlockingUserInputEventFilter blockingEventFilter; KisActionManager actionManager; QMainWindow* mainWindow; QPointer savedFloatingMessage; bool showFloatingMessage; QPointer currentImageView; KisCanvasResourceProvider canvasResourceProvider; KoCanvasResourceManager canvasResourceManager; KisSignalCompressor guiUpdateCompressor; KActionCollection *actionCollection; KisMirrorManager mirrorManager; KisInputManager inputManager; KisSignalAutoConnectionsStore viewConnections; KSelectAction *actionAuthor; // Select action for author profile. QByteArray canvasState; }; KisViewManager::KisViewManager(QWidget *parent, KActionCollection *_actionCollection) : d(new KisViewManagerPrivate(this, parent)) { d->actionCollection = _actionCollection; d->mainWindow = dynamic_cast(parent); d->canvasResourceProvider.setResourceManager(&d->canvasResourceManager); connect(&d->guiUpdateCompressor, SIGNAL(timeout()), this, SLOT(guiUpdateTimeout())); createActions(); setupManagers(); // These initialization functions must wait until KisViewManager ctor is complete. d->statusBar.setup(); d->controlFrame.setup(parent); //Check to draw scrollbars after "Canvas only mode" toggle is created. this->showHideScrollbars(); QScopedPointer dummy(new KoDummyCanvasController(actionCollection())); KoToolManager::instance()->registerToolActions(actionCollection(), dummy.data()); - QTimer::singleShot(0, this, SLOT(makeStatusBarVisible())); + QTimer::singleShot(0, this, SLOT(initializeStatusBarVisibility())); connect(KoToolManager::instance(), SIGNAL(inputDeviceChanged(KoInputDevice)), d->controlFrame.paintopBox(), SLOT(slotInputDeviceChanged(KoInputDevice))); connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*,int)), d->controlFrame.paintopBox(), SLOT(slotToolChanged(KoCanvasController*,int))); connect(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), resourceProvider(), SLOT(slotNodeActivated(KisNodeSP))); connect(resourceProvider()->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), d->controlFrame.paintopBox(), SLOT(slotCanvasResourceChanged(int,QVariant))); connect(KisPart::instance(), SIGNAL(sigViewAdded(KisView*)), SLOT(slotViewAdded(KisView*))); connect(KisPart::instance(), SIGNAL(sigViewRemoved(KisView*)), SLOT(slotViewRemoved(KisView*))); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotUpdateAuthorProfileActions())); KisInputProfileManager::instance()->loadProfiles(); KisConfig cfg; d->showFloatingMessage = cfg.showCanvasMessages(); } KisViewManager::~KisViewManager() { KisConfig cfg; if (resourceProvider() && resourceProvider()->currentPreset()) { cfg.writeEntry("LastPreset", resourceProvider()->currentPreset()->name()); } cfg.writeEntry("baseLength", KoResourceItemChooserSync::instance()->baseLength()); delete d; } KActionCollection *KisViewManager::actionCollection() const { return d->actionCollection; } void KisViewManager::slotViewAdded(KisView *view) { d->inputManager.addTrackedCanvas(view->canvasBase()); if (viewCount() == 0) { d->statusBar.showAllStatusBarItems(); } } void KisViewManager::slotViewRemoved(KisView *view) { d->inputManager.removeTrackedCanvas(view->canvasBase()); if (viewCount() == 0) { d->statusBar.hideAllStatusBarItems(); } } void KisViewManager::setCurrentView(KisView *view) { bool first = true; if (d->currentImageView) { d->currentImageView->notifyCurrentStateChanged(false); d->currentImageView->canvasBase()->setCursor(QCursor(Qt::ArrowCursor)); first = false; KisDocument* doc = d->currentImageView->document(); if (doc) { doc->disconnect(this); } d->currentImageView->canvasController()->proxyObject->disconnect(&d->statusBar); d->viewConnections.clear(); } // Restore the last used brush preset if (first) { KisConfig cfg; KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); QString lastPreset = cfg.readEntry("LastPreset", QString("Basic_tip_default")); KisPaintOpPresetSP preset = rserver->resourceByName(lastPreset); if (!preset) { preset = rserver->resourceByName("Basic_tip_default"); } if (!preset) { preset = rserver->resources().first(); } if (preset) { paintOpBox()->restoreResource(preset.data()); } } QPointerimageView = qobject_cast(view); if (imageView) { // Wait for the async image to have loaded KisDocument* doc = view->document(); // connect(canvasController()->proxyObject, SIGNAL(documentMousePositionChanged(QPointF)), d->statusBar, SLOT(documentMousePositionChanged(QPointF))); d->currentImageView = imageView; KisCanvasController *canvasController = dynamic_cast(d->currentImageView->canvasController()); d->viewConnections.addUniqueConnection(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), doc->image(), SLOT(requestStrokeEndActiveNode())); d->viewConnections.addUniqueConnection(d->rotateCanvasRight, SIGNAL(triggered()), canvasController, SLOT(rotateCanvasRight15())); d->viewConnections.addUniqueConnection(d->rotateCanvasLeft, SIGNAL(triggered()),canvasController, SLOT(rotateCanvasLeft15())); d->viewConnections.addUniqueConnection(d->resetCanvasRotation, SIGNAL(triggered()),canvasController, SLOT(resetCanvasRotation())); d->viewConnections.addUniqueConnection(d->wrapAroundAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleWrapAroundMode(bool))); d->wrapAroundAction->setChecked(canvasController->wrapAroundMode()); d->viewConnections.addUniqueConnection(d->levelOfDetailAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleLevelOfDetailMode(bool))); d->levelOfDetailAction->setChecked(canvasController->levelOfDetailMode()); d->viewConnections.addUniqueConnection(d->currentImageView->canvasController(), SIGNAL(toolOptionWidgetsChanged(QList >)), mainWindow(), SLOT(newOptionWidgets(QList >))); d->viewConnections.addUniqueConnection(d->currentImageView->image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), d->controlFrame.paintopBox(), SLOT(slotColorSpaceChanged(const KoColorSpace*))); d->viewConnections.addUniqueConnection(d->showRulersAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setShowRulers(bool))); d->viewConnections.addUniqueConnection(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setRulersTrackMouse(bool))); d->viewConnections.addUniqueConnection(d->zoomTo100pct, SIGNAL(triggered()), imageView->zoomManager(), SLOT(zoomTo100())); d->viewConnections.addUniqueConnection(d->zoomIn, SIGNAL(triggered()), imageView->zoomController()->zoomAction(), SLOT(zoomIn())); d->viewConnections.addUniqueConnection(d->zoomOut, SIGNAL(triggered()), imageView->zoomController()->zoomAction(), SLOT(zoomOut())); imageView->zoomManager()->setShowRulers(d->showRulersAction->isChecked()); imageView->zoomManager()->setRulersTrackMouse(d->rulersTrackMouseAction->isChecked()); showHideScrollbars(); } d->filterManager.setView(imageView); d->selectionManager.setView(imageView); d->guidesManager.setView(imageView); d->nodeManager.setView(imageView); d->imageManager.setView(imageView); d->canvasControlsManager.setView(imageView); d->actionManager.setView(imageView); d->gridManager.setView(imageView); d->statusBar.setView(imageView); d->paintingAssistantsManager.setView(imageView); d->mirrorManager.setView(imageView); if (d->currentImageView) { d->currentImageView->notifyCurrentStateChanged(true); d->currentImageView->canvasController()->activate(); d->currentImageView->canvasController()->setFocus(); } d->actionManager.updateGUI(); d->viewConnections.addUniqueConnection( image(), SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)), resourceProvider(), SLOT(slotImageSizeChanged())); d->viewConnections.addUniqueConnection( image(), SIGNAL(sigResolutionChanged(double,double)), resourceProvider(), SLOT(slotOnScreenResolutionChanged())); d->viewConnections.addUniqueConnection( image(), SIGNAL(sigNodeChanged(KisNodeSP)), this, SLOT(updateGUI())); d->viewConnections.addUniqueConnection( view->zoomManager()->zoomController(), SIGNAL(zoomChanged(KoZoomMode::Mode,qreal)), resourceProvider(), SLOT(slotOnScreenResolutionChanged())); resourceProvider()->slotImageSizeChanged(); resourceProvider()->slotOnScreenResolutionChanged(); } KoZoomController *KisViewManager::zoomController() const { if (d->currentImageView) { return d->currentImageView->zoomController(); } return 0; } KisImageWSP KisViewManager::image() const { if (document()) { return document()->image(); } return 0; } KisCanvasResourceProvider * KisViewManager::resourceProvider() { return &d->canvasResourceProvider; } KisCanvas2 * KisViewManager::canvasBase() const { if (d && d->currentImageView) { return d->currentImageView->canvasBase(); } return 0; } QWidget* KisViewManager::canvas() const { if (d && d->currentImageView && d->currentImageView->canvasBase()->canvasWidget()) { return d->currentImageView->canvasBase()->canvasWidget(); } return 0; } KisStatusBar * KisViewManager::statusBar() const { return &d->statusBar; } void KisViewManager::addStatusBarItem(QWidget *widget, int stretch, bool permanent) { d->statusBar.addStatusBarItem(widget, stretch, permanent); } void KisViewManager::removeStatusBarItem(QWidget *widget) { d->statusBar.removeStatusBarItem(widget); } KisPaintopBox* KisViewManager::paintOpBox() const { return d->controlFrame.paintopBox(); } KoProgressUpdater* KisViewManager::createProgressUpdater(KoProgressUpdater::Mode mode) { return new KisProgressUpdater(d->statusBar.progress(), document()->progressProxy(), mode); } KisSelectionManager * KisViewManager::selectionManager() { return &d->selectionManager; } KisNodeSP KisViewManager::activeNode() { return d->nodeManager.activeNode(); } KisLayerSP KisViewManager::activeLayer() { return d->nodeManager.activeLayer(); } KisPaintDeviceSP KisViewManager::activeDevice() { return d->nodeManager.activePaintDevice(); } KisZoomManager * KisViewManager::zoomManager() { if (d->currentImageView) { return d->currentImageView->zoomManager(); } return 0; } KisFilterManager * KisViewManager::filterManager() { return &d->filterManager; } KisImageManager * KisViewManager::imageManager() { return &d->imageManager; } KisInputManager* KisViewManager::inputManager() const { return &d->inputManager; } KisSelectionSP KisViewManager::selection() { if (d->currentImageView) { return d->currentImageView->selection(); } return 0; } bool KisViewManager::selectionEditable() { KisLayerSP layer = activeLayer(); if (layer) { KoProperties properties; QList masks = layer->childNodes(QStringList("KisSelectionMask"), properties); if (masks.size() == 1) { return masks[0]->isEditable(); } } // global selection is always editable return true; } KisUndoAdapter * KisViewManager::undoAdapter() { if (!document()) return 0; KisImageWSP image = document()->image(); Q_ASSERT(image); return image->undoAdapter(); } void KisViewManager::createActions() { + KisConfig cfg; + d->saveIncremental = actionManager()->createAction("save_incremental_version"); connect(d->saveIncremental, SIGNAL(triggered()), this, SLOT(slotSaveIncremental())); d->saveIncrementalBackup = actionManager()->createAction("save_incremental_backup"); connect(d->saveIncrementalBackup, SIGNAL(triggered()), this, SLOT(slotSaveIncrementalBackup())); connect(mainWindow(), SIGNAL(documentSaved()), this, SLOT(slotDocumentSaved())); d->saveIncremental->setEnabled(false); d->saveIncrementalBackup->setEnabled(false); KisAction *tabletDebugger = actionManager()->createAction("tablet_debugger"); connect(tabletDebugger, SIGNAL(triggered()), this, SLOT(toggleTabletLogger())); d->createTemplate = actionManager()->createAction("create_template"); connect(d->createTemplate, SIGNAL(triggered()), this, SLOT(slotCreateTemplate())); d->createCopy = actionManager()->createAction("create_copy"); connect(d->createCopy, SIGNAL(triggered()), this, SLOT(slotCreateCopy())); d->openResourcesDirectory = actionManager()->createAction("open_resources_directory"); connect(d->openResourcesDirectory, SIGNAL(triggered()), SLOT(openResourcesDirectory())); d->rotateCanvasRight = actionManager()->createAction("rotate_canvas_right"); d->rotateCanvasLeft = actionManager()->createAction("rotate_canvas_left"); d->resetCanvasRotation = actionManager()->createAction("reset_canvas_rotation"); d->wrapAroundAction = actionManager()->createAction("wrap_around_mode"); d->levelOfDetailAction = actionManager()->createAction("level_of_detail_mode"); KisAction *tAction = actionManager()->createAction("showStatusBar"); - tAction->setChecked(true); + tAction->setChecked(cfg.showStatusBar()); connect(tAction, SIGNAL(toggled(bool)), this, SLOT(showStatusBar(bool))); tAction = actionManager()->createAction("view_show_canvas_only"); tAction->setChecked(false); connect(tAction, SIGNAL(toggled(bool)), this, SLOT(switchCanvasOnly(bool))); //Workaround, by default has the same shortcut as mirrorCanvas KisAction *a = dynamic_cast(actionCollection()->action("format_italic")); if (a) { a->setDefaultShortcut(QKeySequence()); } a = actionManager()->createAction("edit_blacklist_cleanup"); connect(a, SIGNAL(triggered()), this, SLOT(slotBlacklistCleanup())); - KisConfig cfg; d->showRulersAction = actionManager()->createAction("view_ruler"); d->showRulersAction->setChecked(cfg.showRulers()); connect(d->showRulersAction, SIGNAL(toggled(bool)), SLOT(slotSaveShowRulersState(bool))); d->rulersTrackMouseAction = actionManager()->createAction("rulers_track_mouse"); d->rulersTrackMouseAction->setChecked(cfg.rulersTrackMouse()); connect(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), SLOT(slotSaveRulersTrackMouseState(bool))); d->zoomTo100pct = actionManager()->createAction("zoom_to_100pct"); d->zoomIn = actionManager()->createStandardAction(KStandardAction::ZoomIn, 0, ""); d->zoomOut = actionManager()->createStandardAction(KStandardAction::ZoomOut, 0, ""); d->actionAuthor = new KSelectAction(KisIconUtils::loadIcon("im-user"), i18n("Active Author Profile"), this); connect(d->actionAuthor, SIGNAL(triggered(const QString &)), this, SLOT(changeAuthorProfile(const QString &))); actionCollection()->addAction("settings_active_author", d->actionAuthor); slotUpdateAuthorProfileActions(); } void KisViewManager::setupManagers() { // Create the managers for filters, selections, layers etc. // XXX: When the currentlayer changes, call updateGUI on all // managers d->filterManager.setup(actionCollection(), actionManager()); d->selectionManager.setup(actionManager()); d->guidesManager.setup(actionManager()); d->nodeManager.setup(actionCollection(), actionManager()); d->imageManager.setup(actionManager()); d->gridManager.setup(actionManager()); d->paintingAssistantsManager.setup(actionManager()); d->canvasControlsManager.setup(actionManager()); d->mirrorManager.setup(actionCollection()); } void KisViewManager::updateGUI() { d->guiUpdateCompressor.start(); } void KisViewManager::slotBlacklistCleanup() { KisDlgBlacklistCleanup dialog; dialog.exec(); } KisNodeManager * KisViewManager::nodeManager() const { return &d->nodeManager; } KisActionManager* KisViewManager::actionManager() const { return &d->actionManager; } KisGridManager * KisViewManager::gridManager() const { return &d->gridManager; } KisGuidesManager * KisViewManager::guidesManager() const { return &d->guidesManager; } KisPaintingAssistantsManager* KisViewManager::paintingAssistantsManager() const { return &d->paintingAssistantsManager; } KisDocument *KisViewManager::document() const { if (d->currentImageView && d->currentImageView->document()) { return d->currentImageView->document(); } return 0; } int KisViewManager::viewCount() const { KisMainWindow *mw = qobject_cast(d->mainWindow); if (mw) { return mw->viewCount(); } return 0; } void KisViewManager::slotCreateTemplate() { if (!document()) return; KisTemplateCreateDia::createTemplate( QStringLiteral("templates/"), ".kra", document(), mainWindow()); } void KisViewManager::slotCreateCopy() { if (!document()) return; KisDocument *doc = KisPart::instance()->createDocument(); QString name = document()->documentInfo()->aboutInfo("name"); if (name.isEmpty()) { name = document()->url().toLocalFile(); } name = i18n("%1 (Copy)", name); doc->documentInfo()->setAboutInfo("title", name); KisImageWSP image = document()->image(); KisImageSP newImage = new KisImage(doc->createUndoStore(), image->width(), image->height(), image->colorSpace(), name); newImage->setRootLayer(dynamic_cast(image->rootLayer()->clone().data())); doc->setCurrentImage(newImage); KisPart::instance()->addDocument(doc); KisMainWindow *mw = qobject_cast(d->mainWindow); mw->addViewAndNotifyLoadingCompleted(doc); } QMainWindow* KisViewManager::qtMainWindow() const { if (d->mainWindow) return d->mainWindow; //Fallback for when we have not yet set the main window. QMainWindow* w = qobject_cast(qApp->activeWindow()); if(w) return w; return mainWindow(); } void KisViewManager::setQtMainWindow(QMainWindow* newMainWindow) { d->mainWindow = newMainWindow; } void KisViewManager::slotDocumentSaved() { d->saveIncremental->setEnabled(true); d->saveIncrementalBackup->setEnabled(true); } void KisViewManager::slotSaveIncremental() { if (!document()) return; bool foundVersion; bool fileAlreadyExists; bool isBackup; QString version = "000"; QString newVersion; QString letter; QString fileName = document()->localFilePath(); // Find current version filenames // v v Regexp to find incremental versions in the filename, taking our backup scheme into account as well // Considering our incremental version and backup scheme, format is filename_001~001.ext QRegExp regex("_\\d{1,4}[.]|_\\d{1,4}[a-z][.]|_\\d{1,4}[~]|_\\d{1,4}[a-z][~]"); regex.indexIn(fileName); // Perform the search QStringList matches = regex.capturedTexts(); foundVersion = matches.at(0).isEmpty() ? false : true; // Ensure compatibility with Save Incremental Backup // If this regex is not kept separate, the entire algorithm needs modification; // It's simpler to just add this. QRegExp regexAux("_\\d{1,4}[~]|_\\d{1,4}[a-z][~]"); regexAux.indexIn(fileName); // Perform the search QStringList matchesAux = regexAux.capturedTexts(); isBackup = matchesAux.at(0).isEmpty() ? false : true; // If the filename has a version, prepare it for incrementation if (foundVersion) { version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches if (version.contains(QRegExp("[a-z]"))) { version.chop(1); // Trim "." letter = version.right(1); // Save letter version.chop(1); // Trim letter } else { version.chop(1); // Trim "." } version.remove(0, 1); // Trim "_" } else { // ...else, simply add a version to it so the next loop works QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension regex2.indexIn(fileName); QStringList matches2 = regex2.capturedTexts(); QString extensionPlusVersion = matches2.at(0); extensionPlusVersion.prepend(version); extensionPlusVersion.prepend("_"); fileName.replace(regex2, extensionPlusVersion); } // Prepare the base for new version filename int intVersion = version.toInt(0); ++intVersion; QString baseNewVersion = QString::number(intVersion); while (baseNewVersion.length() < version.length()) { baseNewVersion.prepend("0"); } // Check if the file exists under the new name and search until options are exhausted (test appending a to z) do { newVersion = baseNewVersion; newVersion.prepend("_"); if (!letter.isNull()) newVersion.append(letter); if (isBackup) { newVersion.append("~"); } else { newVersion.append("."); } fileName.replace(regex, newVersion); fileAlreadyExists = QFile(fileName).exists(); if (fileAlreadyExists) { if (!letter.isNull()) { char letterCh = letter.at(0).toLatin1(); ++letterCh; letter = QString(QChar(letterCh)); } else { letter = 'a'; } } } while (fileAlreadyExists && letter != "{"); // x, y, z, {... if (letter == "{") { QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental version"), i18n("Alternative names exhausted, try manually saving with a higher number")); return; } document()->setFileBatchMode(true); document()->saveAs(QUrl::fromUserInput(fileName)); document()->setFileBatchMode(false); if (mainWindow()) { mainWindow()->updateCaption(); } } void KisViewManager::slotSaveIncrementalBackup() { if (!document()) return; bool workingOnBackup; bool fileAlreadyExists; QString version = "000"; QString newVersion; QString letter; QString fileName = document()->localFilePath(); // First, discover if working on a backup file, or a normal file QRegExp regex("~\\d{1,4}[.]|~\\d{1,4}[a-z][.]"); regex.indexIn(fileName); // Perform the search QStringList matches = regex.capturedTexts(); workingOnBackup = matches.at(0).isEmpty() ? false : true; if (workingOnBackup) { // Try to save incremental version (of backup), use letter for alt versions version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches if (version.contains(QRegExp("[a-z]"))) { version.chop(1); // Trim "." letter = version.right(1); // Save letter version.chop(1); // Trim letter } else { version.chop(1); // Trim "." } version.remove(0, 1); // Trim "~" // Prepare the base for new version filename int intVersion = version.toInt(0); ++intVersion; QString baseNewVersion = QString::number(intVersion); QString backupFileName = document()->localFilePath(); while (baseNewVersion.length() < version.length()) { baseNewVersion.prepend("0"); } // Check if the file exists under the new name and search until options are exhausted (test appending a to z) do { newVersion = baseNewVersion; newVersion.prepend("~"); if (!letter.isNull()) newVersion.append(letter); newVersion.append("."); backupFileName.replace(regex, newVersion); fileAlreadyExists = QFile(backupFileName).exists(); if (fileAlreadyExists) { if (!letter.isNull()) { char letterCh = letter.at(0).toLatin1(); ++letterCh; letter = QString(QChar(letterCh)); } else { letter = 'a'; } } } while (fileAlreadyExists && letter != "{"); // x, y, z, {... if (letter == "{") { QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental backup"), i18n("Alternative names exhausted, try manually saving with a higher number")); return; } QFile::copy(fileName, backupFileName); document()->saveAs(QUrl::fromUserInput(fileName)); if (mainWindow()) mainWindow()->updateCaption(); } else { // if NOT working on a backup... // Navigate directory searching for latest backup version, ignore letters const quint8 HARDCODED_DIGIT_COUNT = 3; QString baseNewVersion = "000"; QString backupFileName = document()->localFilePath(); QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension regex2.indexIn(backupFileName); QStringList matches2 = regex2.capturedTexts(); QString extensionPlusVersion = matches2.at(0); extensionPlusVersion.prepend(baseNewVersion); extensionPlusVersion.prepend("~"); backupFileName.replace(regex2, extensionPlusVersion); // Save version with 1 number higher than the highest version found ignoring letters do { newVersion = baseNewVersion; newVersion.prepend("~"); newVersion.append("."); backupFileName.replace(regex, newVersion); fileAlreadyExists = QFile(backupFileName).exists(); if (fileAlreadyExists) { // Prepare the base for new version filename, increment by 1 int intVersion = baseNewVersion.toInt(0); ++intVersion; baseNewVersion = QString::number(intVersion); while (baseNewVersion.length() < HARDCODED_DIGIT_COUNT) { baseNewVersion.prepend("0"); } } } while (fileAlreadyExists); // Save both as backup and on current file for interapplication workflow document()->setFileBatchMode(true); QFile::copy(fileName, backupFileName); document()->saveAs(QUrl::fromUserInput(fileName)); document()->setFileBatchMode(false); if (mainWindow()) mainWindow()->updateCaption(); } } void KisViewManager::disableControls() { // prevents possible crashes, if somebody changes the paintop during dragging by using the mousewheel // this is for Bug 250944 // the solution blocks all wheel, mouse and key event, while dragging with the freehand tool // see KisToolFreehand::initPaint() and endPaint() d->controlFrame.paintopBox()->installEventFilter(&d->blockingEventFilter); Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) { child->installEventFilter(&d->blockingEventFilter); } } void KisViewManager::enableControls() { d->controlFrame.paintopBox()->removeEventFilter(&d->blockingEventFilter); Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) { child->removeEventFilter(&d->blockingEventFilter); } } void KisViewManager::showStatusBar(bool toggled) { - if (d->currentImageView && d->currentImageView->statusBar()) { - d->currentImageView->statusBar()->setVisible(toggled); + KisMainWindow *mw = mainWindow(); + if(mw && mw->statusBar()) { + mw->statusBar()->setVisible(toggled); + KisConfig cfg; + cfg.setShowStatusBar(toggled); } } void KisViewManager::switchCanvasOnly(bool toggled) { KisConfig cfg; KisMainWindow* main = mainWindow(); if(!main) { dbgUI << "Unable to switch to canvas-only mode, main window not found"; return; } if (toggled) { d->canvasState = qtMainWindow()->saveState(); } if (cfg.hideStatusbarFullscreen()) { if (main->statusBar()) { if (!toggled) { if (main->statusBar()->dynamicPropertyNames().contains("wasvisible")) { if (main->statusBar()->property("wasvisible").toBool()) { main->statusBar()->setVisible(true); } } } else { main->statusBar()->setProperty("wasvisible", main->statusBar()->isVisible()); main->statusBar()->setVisible(false); } } } if (cfg.hideDockersFullscreen()) { KisAction* action = qobject_cast(main->actionCollection()->action("view_toggledockers")); action->setCheckable(true); if (action && action->isChecked() == toggled) { action->setChecked(!toggled); } } - +#ifndef Q_OS_WIN if (cfg.hideTitlebarFullscreen() && !cfg.fullscreenMode()) { if(toggled) { main->setWindowState( main->windowState() | Qt::WindowFullScreen); } else { main->setWindowState( main->windowState() & ~Qt::WindowFullScreen); } } - +#endif if (cfg.hideMenuFullscreen()) { if (!toggled) { if (main->menuBar()->dynamicPropertyNames().contains("wasvisible")) { if (main->menuBar()->property("wasvisible").toBool()) { main->menuBar()->setVisible(true); } } } else { main->menuBar()->setProperty("wasvisible", main->menuBar()->isVisible()); main->menuBar()->setVisible(false); } } if (cfg.hideToolbarFullscreen()) { QList toolBars = main->findChildren(); Q_FOREACH (QToolBar* toolbar, toolBars) { if (!toggled) { if (toolbar->dynamicPropertyNames().contains("wasvisible")) { if (toolbar->property("wasvisible").toBool()) { toolbar->setVisible(true); } } } else { toolbar->setProperty("wasvisible", toolbar->isVisible()); toolbar->setVisible(false); } } } showHideScrollbars(); if (toggled) { // show a fading heads-up display about the shortcut to go back showFloatingMessage(i18n("Going into Canvas-Only mode.\nPress %1 to go back.", actionCollection()->action("view_show_canvas_only")->shortcut().toString()), QIcon()); } else { main->restoreState(d->canvasState); } } void KisViewManager::toggleTabletLogger() { d->inputManager.toggleTabletLogger(); } void KisViewManager::openResourcesDirectory() { QString dir = KoResourcePaths::locateLocal("data", ""); QDesktopServices::openUrl(QUrl::fromLocalFile(dir)); } void KisViewManager::updateIcons() { if (mainWindow()) { QList dockers = mainWindow()->dockWidgets(); Q_FOREACH (QDockWidget* dock, dockers) { dbgKrita << "name " << dock->objectName(); KoDockWidgetTitleBar* titlebar = dynamic_cast(dock->titleBarWidget()); if (titlebar) { titlebar->updateIcons(); } QObjectList objects; objects.append(dock); while (!objects.isEmpty()) { QObject* object = objects.takeFirst(); objects.append(object->children()); KisIconUtils::updateIconCommon(object); } } } } -void KisViewManager::makeStatusBarVisible() +void KisViewManager::initializeStatusBarVisibility() { - d->mainWindow->statusBar()->setVisible(true); + KisConfig cfg; + d->mainWindow->statusBar()->setVisible(cfg.showStatusBar()); } void KisViewManager::guiUpdateTimeout() { d->nodeManager.updateGUI(); d->selectionManager.updateGUI(); d->filterManager.updateGUI(); if (zoomManager()) { zoomManager()->updateGUI(); } d->gridManager.updateGUI(); d->actionManager.updateGUI(); } void KisViewManager::showFloatingMessage(const QString &message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment) { if (!d->currentImageView) return; d->currentImageView->showFloatingMessageImpl(message, icon, timeout, priority, alignment); emit floatingMessageRequested(message, icon.name()); } KisMainWindow *KisViewManager::mainWindow() const { return qobject_cast(d->mainWindow); } void KisViewManager::showHideScrollbars() { if (!d->currentImageView) return; if (!d->currentImageView->canvasController()) return; KisConfig cfg; bool toggled = actionCollection()->action("view_show_canvas_only")->isChecked(); if ( (toggled && cfg.hideScrollbarsFullscreen()) || (!toggled && cfg.hideScrollbars()) ) { d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } else { d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); } } void KisViewManager::slotSaveShowRulersState(bool value) { KisConfig cfg; cfg.setShowRulers(value); } void KisViewManager::slotSaveRulersTrackMouseState(bool value) { KisConfig cfg; cfg.setRulersTrackMouse(value); } void KisViewManager::setShowFloatingMessage(bool show) { d->showFloatingMessage = show; } void KisViewManager::changeAuthorProfile(const QString &profileName) { KConfigGroup appAuthorGroup(KoGlobal::calligraConfig(), "Author"); if (profileName.isEmpty()) { appAuthorGroup.writeEntry("active-profile", ""); } else if (profileName == i18nc("choice for author profile", "Anonymous")) { appAuthorGroup.writeEntry("active-profile", "anonymous"); } else { appAuthorGroup.writeEntry("active-profile", profileName); } appAuthorGroup.sync(); Q_FOREACH (KisDocument *doc, KisPart::instance()->documents()) { doc->documentInfo()->updateParameters(); } } void KisViewManager::slotUpdateAuthorProfileActions() { Q_ASSERT(d->actionAuthor); if (!d->actionAuthor) { return; } d->actionAuthor->clear(); d->actionAuthor->addAction(i18n("Default Author Profile")); d->actionAuthor->addAction(i18nc("choice for author profile", "Anonymous")); KConfigGroup authorGroup(KoGlobal::calligraConfig(), "Author"); QStringList profiles = authorGroup.readEntry("profile-names", QStringList()); Q_FOREACH (const QString &profile , profiles) { d->actionAuthor->addAction(profile); } KConfigGroup appAuthorGroup(KoGlobal::calligraConfig(), "Author"); QString profileName = appAuthorGroup.readEntry("active-profile", ""); if (profileName == "anonymous") { d->actionAuthor->setCurrentItem(1); } else if (profiles.contains(profileName)) { d->actionAuthor->setCurrentAction(profileName); } else { d->actionAuthor->setCurrentItem(0); } } diff --git a/libs/ui/KisViewManager.h b/libs/ui/KisViewManager.h index 0a35575035..add5dc8df9 100644 --- a/libs/ui/KisViewManager.h +++ b/libs/ui/KisViewManager.h @@ -1,246 +1,246 @@ /* * 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_GUI_CLIENT_H #define KIS_GUI_CLIENT_H #include #include #include #include #include #include #include #include #include "kis_floating_message.h" class QPoint; class KisView; class KisCanvas2; class KisCanvasResourceProvider; class KisDocument; class KisFilterManager; class KisGridManager; class KisGuidesManager; class KisImageManager; class KisNodeManager; class KisPaintingAssistantsManager; class KisPaintopBox; class KisSelectionManager; class KisStatusBar; class KisUndoAdapter; class KisZoomManager; class KisPaintopBox; class KisActionManager; class KisInputManager; /** * Krita view class * * Following the broad model-view-controller idea this class shows you one view on the document. * There can be multiple views of the same document each in with independent settings for viewMode and zoom etc. */ class KRITAUI_EXPORT KisViewManager : public QObject { Q_OBJECT public: /** * Construct a new view on the krita document. * @param document the document we show. * @param parent a parent widget we show ourselves in. */ KisViewManager(QWidget *parent, KActionCollection *actionCollection); virtual ~KisViewManager(); /** * Retrieves the entire action collection. */ virtual KActionCollection* actionCollection() const; public: // Krita specific interfaces void setCurrentView(KisView *view); /// Return the image this view is displaying KisImageWSP image() const; KoZoomController *zoomController() const; /// The resource provider contains all per-view settings, such as /// current color, current paint op etc. KisCanvasResourceProvider * resourceProvider(); /// Return the canvasbase class KisCanvas2 * canvasBase() const; /// Return the actual widget that is displaying the current image QWidget* canvas() const; /// Return the wrapper class around the statusbar KisStatusBar * statusBar() const; /** * This adds a widget to the statusbar for this view. * If you use this method instead of using statusBar() directly, * KisView will take care of removing the items when the view GUI is deactivated * and readding them when it is reactivated. * The parameters are the same as QStatusBar::addWidget(). */ void addStatusBarItem(QWidget * widget, int stretch = 0, bool permanent = false); /** * Remove a widget from the statusbar for this view. */ void removeStatusBarItem(QWidget * widget); KisPaintopBox* paintOpBox() const; /// create a new progress updater KoProgressUpdater *createProgressUpdater(KoProgressUpdater::Mode mode = KoProgressUpdater::Threaded); /// The selection manager handles everything action related to /// selections. KisSelectionManager *selectionManager(); /// The node manager handles everything about nodes KisNodeManager *nodeManager() const; KisActionManager *actionManager() const; /** * Convenience method to get at the active node, which may be * a layer or a mask or a selection */ KisNodeSP activeNode(); /// Convenience method to get at the active layer KisLayerSP activeLayer(); /// Convenience method to get at the active paint device KisPaintDeviceSP activeDevice(); /// The filtermanager handles everything action-related to filters KisFilterManager *filterManager(); /// The image manager handles everything action-related to the /// current image KisImageManager *imageManager(); /// Filters events and sends them to canvas actions KisInputManager *inputManager() const; /// Convenience method to get at the active selection (the /// selection of the current layer, or, if that does not exist, /// the global selection. KisSelectionSP selection(); /// Checks if the current global or local selection is editable bool selectionEditable(); /// The undo adapter is used to add commands to the undo stack KisUndoAdapter *undoAdapter(); KisDocument *document() const; int viewCount() const; public: KisGridManager * gridManager() const; KisGuidesManager * guidesManager() const; KisPaintingAssistantsManager* paintingAssistantsManager() const; /// disable and enable toolbar controls. used for disabling them during painting. void enableControls(); void disableControls(); /// shows a floating message in the top right corner of the canvas void showFloatingMessage(const QString &message, const QIcon& icon, int timeout = 4500, KisFloatingMessage::Priority priority = KisFloatingMessage::Medium, int alignment = Qt::AlignCenter | Qt::TextWordWrap); /// @return the KoMaindow this view is in, or 0 KisMainWindow *mainWindow() const; /// The QMainWindow associated with this view. This is most likely going to be shell(), but /// when running as Gemini or Sketch, this will be set to the applications' own QMainWindow. /// This can be checked by qobject_casting to KisMainWindow to check the difference. QMainWindow* qtMainWindow() const; /// The mainWindow function will return the shell() value, unless this function is called /// with a non-null value. To make it return shell() again, simply pass null to this function. void setQtMainWindow(QMainWindow* newMainWindow); public Q_SLOTS: void switchCanvasOnly(bool toggled); void setShowFloatingMessage(bool show); void showHideScrollbars(); /// Visit all managers to update gui elements, e.g. enable / disable actions. /// This is heavy-duty call, so it uses a compressor. void updateGUI(); /// Update the style of all the icons void updateIcons(); void slotViewAdded(KisView *view); void slotViewRemoved(KisView *view); Q_SIGNALS: void floatingMessageRequested(const QString &message, const QString &iconName); private Q_SLOTS: void slotBlacklistCleanup(); void slotCreateTemplate(); void slotCreateCopy(); void slotDocumentSaved(); void slotSaveIncremental(); void slotSaveIncrementalBackup(); void showStatusBar(bool toggled); void toggleTabletLogger(); void openResourcesDirectory(); - void makeStatusBarVisible(); + void initializeStatusBarVisibility(); void guiUpdateTimeout(); void changeAuthorProfile(const QString &profileName); void slotUpdateAuthorProfileActions(); void slotSaveShowRulersState(bool value); void slotSaveRulersTrackMouseState(bool value); private: void createActions(); void setupManagers(); /// The zoommanager handles everything action-related to zooming KisZoomManager * zoomManager(); private: class KisViewManagerPrivate; KisViewManagerPrivate * const d; }; #endif diff --git a/libs/ui/canvas/kis_abstract_canvas_widget.h b/libs/ui/canvas/kis_abstract_canvas_widget.h index 996360b795..1c163a9f11 100644 --- a/libs/ui/canvas/kis_abstract_canvas_widget.h +++ b/libs/ui/canvas/kis_abstract_canvas_widget.h @@ -1,91 +1,91 @@ /* * Copyright (C) 2007 Boudewijn Rempt , (C) * Copyright (C) 2015 Michael Abrahams * * This program is free software; you can redistribute 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_ABSTRACT_CANVAS_WIDGET_ #define _KIS_ABSTRACT_CANVAS_WIDGET_ class QWidget; class QRect; class QPainter; class QRect; class KoToolProxy; -class KisCanvasDecoration; +#include + class KisDisplayFilter; class KisDisplayColorConverter; class QBitArray; #include "kis_types.h" #include "kis_ui_types.h" - class KisAbstractCanvasWidget { public: KisAbstractCanvasWidget() {} virtual ~KisAbstractCanvasWidget() {} virtual QWidget * widget() = 0; virtual KoToolProxy * toolProxy() const = 0; /// Draw the specified decorations on the view. virtual void drawDecorations(QPainter & gc, const QRect &updateWidgetRect) const = 0; - virtual void addDecoration(KisCanvasDecoration* deco) = 0; + virtual void addDecoration(KisCanvasDecorationSP deco) = 0; - virtual KisCanvasDecoration* decoration(const QString& id) const = 0; + virtual KisCanvasDecorationSP decoration(const QString& id) const = 0; - virtual void setDecorations(const QList> &) = 0; + virtual void setDecorations(const QList &) = 0; - virtual QList> decorations() const = 0; + virtual QList decorations() const = 0; /// set the specified display filter on the canvas virtual void setDisplayFilter(KisDisplayFilter *displayFilter) = 0; virtual void setWrapAroundViewingMode(bool value) = 0; // Called from KisCanvas2::channelSelectionChanged virtual void channelSelectionChanged(const QBitArray &channelFlags) = 0; // Called from KisCanvas2::slotSetDisplayProfile virtual void setDisplayProfile(KisDisplayColorConverter *colorConverter) = 0; // Called from KisCanvas2::finishResizingImage virtual void finishResizingImage(qint32 w, qint32 h) = 0; // Called from KisCanvas2::startUpdateProjection virtual KisUpdateInfoSP startUpdateCanvasProjection(const QRect & rc, const QBitArray &channelFlags) = 0; // Called from KisCanvas2::updateCanvasProjection virtual QRect updateCanvasProjection(KisUpdateInfoSP info) = 0; /** * Returns true if the asynchromous engine of the canvas * (e.g. openGL pipeline) is busy with processing of the previous * update events. This will make KisCanvas2 to postpone and * compress update events. */ virtual bool isBusy() const = 0; }; #endif // _KIS_ABSTRACT_CANVAS_WIDGET_ diff --git a/libs/ui/canvas/kis_animation_player.cpp b/libs/ui/canvas/kis_animation_player.cpp index 62a774bc4b..4fba8b7601 100644 --- a/libs/ui/canvas/kis_animation_player.cpp +++ b/libs/ui/canvas/kis_animation_player.cpp @@ -1,371 +1,372 @@ /* * Copyright (c) 2015 Jouni Pentikäinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_animation_player.h" #include #include #include //#define PLAYER_DEBUG_FRAMERATE #include "kis_global.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_image.h" #include "kis_canvas2.h" #include "kis_animation_frame_cache.h" #include "kis_signal_auto_connection.h" #include "kis_image_animation_interface.h" #include "kis_time_range.h" #include "kis_signal_compressor.h" #include #include #include using namespace boost::accumulators; typedef accumulator_set > FpsAccumulator; struct KisAnimationPlayer::Private { public: Private(KisAnimationPlayer *_q) : q(_q), realFpsAccumulator(tag::rolling_window::window_size = 24), droppedFpsAccumulator(tag::rolling_window::window_size = 24), droppedFramesPortion(tag::rolling_window::window_size = 24), dropFramesMode(true), nextFrameExpectedTime(0), expectedInterval(0), expectedFrame(0), lastTimerInterval(0), lastPaintedFrame(0), playbackStatisticsCompressor(1000, KisSignalCompressor::FIRST_INACTIVE) {} KisAnimationPlayer *q; bool useFastFrameUpload; bool playing; QTimer *timer; int firstFrame; int lastFrame; int fps; qreal playbackSpeed; KisCanvas2 *canvas; KisSignalAutoConnectionsStore cancelStrokeConnections; QElapsedTimer realFpsTimer; FpsAccumulator realFpsAccumulator; FpsAccumulator droppedFpsAccumulator; FpsAccumulator droppedFramesPortion; bool dropFramesMode; QElapsedTimer playbackTime; int nextFrameExpectedTime; int expectedInterval; int expectedFrame; int lastTimerInterval; int lastPaintedFrame; KisSignalCompressor playbackStatisticsCompressor; void stopImpl(bool doUpdates); int incFrame(int frame, int inc) { frame += inc; if (frame > lastFrame) { frame = firstFrame + frame - lastFrame - 1; } return frame; } }; KisAnimationPlayer::KisAnimationPlayer(KisCanvas2 *canvas) : QObject(canvas) , m_d(new Private(this)) { m_d->useFastFrameUpload = false; m_d->playing = false; m_d->fps = 15; m_d->canvas = canvas; m_d->playbackSpeed = 1.0; m_d->timer = new QTimer(this); connect(m_d->timer, SIGNAL(timeout()), this, SLOT(slotUpdate())); m_d->timer->setSingleShot(true); connect(KisConfigNotifier::instance(), SIGNAL(dropFramesModeChanged()), SLOT(slotUpdateDropFramesMode())); slotUpdateDropFramesMode(); connect(&m_d->playbackStatisticsCompressor, SIGNAL(timeout()), this, SIGNAL(sigPlaybackStatisticsUpdated())); } KisAnimationPlayer::~KisAnimationPlayer() {} void KisAnimationPlayer::slotUpdateDropFramesMode() { KisConfig cfg; m_d->dropFramesMode = cfg.animationDropFrames(); } void KisAnimationPlayer::connectCancelSignals() { m_d->cancelStrokeConnections.addConnection( m_d->canvas->image().data(), SIGNAL(sigUndoDuringStrokeRequested()), this, SLOT(slotCancelPlayback())); m_d->cancelStrokeConnections.addConnection( m_d->canvas->image().data(), SIGNAL(sigStrokeCancellationRequested()), this, SLOT(slotCancelPlayback())); m_d->cancelStrokeConnections.addConnection( m_d->canvas->image().data(), SIGNAL(sigStrokeEndRequested()), this, SLOT(slotCancelPlaybackSafe())); m_d->cancelStrokeConnections.addConnection( m_d->canvas->image()->animationInterface(), SIGNAL(sigFramerateChanged()), this, SLOT(slotUpdatePlaybackTimer())); m_d->cancelStrokeConnections.addConnection( m_d->canvas->image()->animationInterface(), SIGNAL(sigFullClipRangeChanged()), this, SLOT(slotUpdatePlaybackTimer())); m_d->cancelStrokeConnections.addConnection( m_d->canvas->image()->animationInterface(), SIGNAL(sigPlaybackRangeChanged()), this, SLOT(slotUpdatePlaybackTimer())); } void KisAnimationPlayer::disconnectCancelSignals() { m_d->cancelStrokeConnections.clear(); } void KisAnimationPlayer::slotUpdatePlaybackTimer() { m_d->timer->stop(); const KisImageAnimationInterface *animation = m_d->canvas->image()->animationInterface(); const KisTimeRange &range = animation->playbackRange(); if (!range.isValid()) return; m_d->fps = animation->framerate(); m_d->firstFrame = range.start(); m_d->lastFrame = range.end(); m_d->expectedFrame = qBound(m_d->firstFrame, m_d->expectedFrame, m_d->lastFrame); m_d->expectedInterval = qreal(1000) / m_d->fps / m_d->playbackSpeed; m_d->lastTimerInterval = m_d->expectedInterval; m_d->timer->start(m_d->expectedInterval); if (m_d->playbackTime.isValid()) { m_d->playbackTime.restart(); } else { m_d->playbackTime.start(); } m_d->nextFrameExpectedTime = m_d->playbackTime.elapsed() + m_d->expectedInterval; } void KisAnimationPlayer::play() { m_d->playing = true; slotUpdatePlaybackTimer(); m_d->expectedFrame = m_d->firstFrame; m_d->lastPaintedFrame = m_d->firstFrame; connectCancelSignals(); } void KisAnimationPlayer::Private::stopImpl(bool doUpdates) { q->disconnectCancelSignals(); timer->stop(); playing = false; if (doUpdates) { canvas->refetchDataFromImage(); } emit q->sigPlaybackStopped(); } void KisAnimationPlayer::stop() { m_d->stopImpl(true); } void KisAnimationPlayer::forcedStopOnExit() { m_d->stopImpl(false); } bool KisAnimationPlayer::isPlaying() { return m_d->playing; } int KisAnimationPlayer::currentTime() { return m_d->lastPaintedFrame; } void KisAnimationPlayer::displayFrame(int time) { uploadFrame(time); } void KisAnimationPlayer::slotUpdate() { uploadFrame(-1); } void KisAnimationPlayer::uploadFrame(int frame) { if (frame < 0) { const int currentTime = m_d->playbackTime.elapsed(); const int framesDiff = currentTime - m_d->nextFrameExpectedTime; const qreal framesDiffNorm = qreal(framesDiff) / m_d->expectedInterval; // qDebug() << ppVar(framesDiff) // << ppVar(m_d->expectedFrame) // << ppVar(framesDiffNorm) // << ppVar(m_d->lastTimerInterval); if (m_d->dropFramesMode) { const int numDroppedFrames = qMax(0, qRound(framesDiffNorm)); frame = m_d->incFrame(m_d->expectedFrame, numDroppedFrames); } else { frame = m_d->expectedFrame; } m_d->nextFrameExpectedTime = currentTime + m_d->expectedInterval; m_d->lastTimerInterval = qMax(0.0, m_d->lastTimerInterval - 0.5 * framesDiff); m_d->expectedFrame = m_d->incFrame(frame, 1); m_d->timer->start(m_d->lastTimerInterval); m_d->playbackStatisticsCompressor.start(); } if (m_d->canvas->frameCache() && m_d->canvas->frameCache()->uploadFrame(frame)) { m_d->canvas->updateCanvas(); m_d->useFastFrameUpload = true; emit sigFrameChanged(); } else { + m_d->useFastFrameUpload = false; + // no OpenGL cache or the frame just not cached yet m_d->canvas->image()->animationInterface()->switchCurrentTimeAsync(frame); - m_d->useFastFrameUpload = false; emit sigFrameChanged(); } if (!m_d->realFpsTimer.isValid()) { m_d->realFpsTimer.start(); } else { const int elapsed = m_d->realFpsTimer.restart(); m_d->realFpsAccumulator(elapsed); int numFrames = frame - m_d->lastPaintedFrame; if (numFrames < 0) { numFrames += m_d->lastFrame - m_d->firstFrame + 1; } m_d->droppedFramesPortion(qreal(int(numFrames != 1))); if (numFrames > 0) { m_d->droppedFpsAccumulator(qreal(elapsed) / numFrames); } #ifdef PLAYER_DEBUG_FRAMERATE qDebug() << " RFPS:" << 1000.0 / rolling_mean(m_d->realFpsAccumulator) << "DFPS:" << 1000.0 / rolling_mean(m_d->droppedFpsAccumulator) << ppVar(numFrames); #endif /* PLAYER_DEBUG_FRAMERATE */ } m_d->lastPaintedFrame = frame; } qreal KisAnimationPlayer::effectiveFps() const { return 1000.0 / rolling_mean(m_d->droppedFpsAccumulator); } qreal KisAnimationPlayer::realFps() const { return 1000.0 / rolling_mean(m_d->realFpsAccumulator); } qreal KisAnimationPlayer::framesDroppedPortion() const { return rolling_mean(m_d->droppedFramesPortion); } void KisAnimationPlayer::slotCancelPlayback() { stop(); } void KisAnimationPlayer::slotCancelPlaybackSafe() { /** * If there is no openGL support, then we have no (!) cache at * all. Therefore we should regenerate frame on every time switch, * which, yeah, can be very slow. What is more important, when * regenerating a frame animation interface will emit a * sigStrokeEndRequested() signal and we should ignore it. That is * not an ideal solution, because the user will be able to paint * on random frames while playing, but it lets users with faulty * GPUs see at least some preview of their animation. */ if (m_d->useFastFrameUpload) { stop(); } } qreal KisAnimationPlayer::playbackSpeed() { return m_d->playbackSpeed; } void KisAnimationPlayer::slotUpdatePlaybackSpeed(double value) { m_d->playbackSpeed = value; if (m_d->playing) { slotUpdatePlaybackTimer(); } } diff --git a/libs/ui/canvas/kis_canvas2.cpp b/libs/ui/canvas/kis_canvas2.cpp index c71ad0ebbb..f38d1e5dd4 100644 --- a/libs/ui/canvas/kis_canvas2.cpp +++ b/libs/ui/canvas/kis_canvas2.cpp @@ -1,891 +1,896 @@ /* This file is part of the KDE project * * Copyright (C) 2006, 2010 Boudewijn Rempt * Copyright (C) Lukáš Tvrdý , (C) 2010 * 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_canvas2.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_tool_proxy.h" #include "kis_coordinates_converter.h" #include "kis_prescaled_projection.h" #include "kis_image.h" #include "kis_image_barrier_locker.h" #include "KisDocument.h" #include "flake/kis_shape_layer.h" #include "kis_canvas_resource_provider.h" #include "KisViewManager.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_abstract_canvas_widget.h" #include "kis_qpainter_canvas.h" #include "kis_group_layer.h" #include "flake/kis_shape_controller.h" #include "kis_node_manager.h" #include "kis_selection.h" #include "kis_selection_component.h" #include "flake/kis_shape_selection.h" #include "kis_image_config.h" #include "kis_infinity_manager.h" #include "kis_signal_compressor.h" #include "kis_display_color_converter.h" #include "kis_exposure_gamma_correction_interface.h" #include "KisView.h" #include "kis_canvas_controller.h" #include "kis_grid_config.h" #include "kis_animation_player.h" #include "kis_animation_frame_cache.h" #include "opengl/kis_opengl_canvas2.h" #include "opengl/kis_opengl.h" #include "kis_fps_decoration.h" #include #include #include "input/kis_input_manager.h" #include "kis_painting_assistants_decoration.h" #include "kis_canvas_updates_compressor.h" class Q_DECL_HIDDEN KisCanvas2::KisCanvas2Private { public: KisCanvas2Private(KoCanvasBase *parent, KisCoordinatesConverter* coordConverter, QPointer view, KoCanvasResourceManager* resourceManager) : coordinatesConverter(coordConverter) , view(view) , shapeManager(parent) , toolProxy(parent) , displayColorConverter(resourceManager, view) { } KisCoordinatesConverter *coordinatesConverter; QPointerview; KisAbstractCanvasWidget *canvasWidget = 0; KoShapeManager shapeManager; bool currentCanvasIsOpenGL; int openGLFilterMode; KisToolProxy toolProxy; KisPrescaledProjectionSP prescaledProjection; bool vastScrolling; KisSignalCompressor updateSignalCompressor; QRect savedUpdateRect; QBitArray channelFlags; KisPopupPalette *popupPalette = 0; KisDisplayColorConverter displayColorConverter; KisCanvasUpdatesCompressor projectionUpdatesCompressor; KisAnimationPlayer *animationPlayer; KisAnimationFrameCacheSP frameCache; bool lodAllowedInCanvas; - bool bootsrapLodBlocked; + bool bootstrapLodBlocked; bool effectiveLodAllowedInCanvas() { - return lodAllowedInCanvas && !bootsrapLodBlocked; + return lodAllowedInCanvas && !bootstrapLodBlocked; } }; KisCanvas2::KisCanvas2(KisCoordinatesConverter *coordConverter, KoCanvasResourceManager *resourceManager, KisView *view, KoShapeBasedDocumentBase *sc) : KoCanvasBase(sc, resourceManager) , m_d(new KisCanvas2Private(this, coordConverter, view, resourceManager)) { /** * While loading LoD should be blocked. Only when GUI has finished * loading and zoom level settled down, LoD is given a green * light. */ - m_d->bootsrapLodBlocked = true; + m_d->bootstrapLodBlocked = true; connect(view->mainWindow(), SIGNAL(guiLoadingFinished()), SLOT(bootstrapFinished())); m_d->updateSignalCompressor.setDelay(10); m_d->updateSignalCompressor.setMode(KisSignalCompressor::FIRST_ACTIVE); } void KisCanvas2::setup() { // a bit of duplication from slotConfigChanged() KisConfig cfg; m_d->vastScrolling = cfg.vastScrolling(); m_d->lodAllowedInCanvas = cfg.levelOfDetailEnabled(); createCanvas(cfg.useOpenGL()); setLodAllowedInCanvas(m_d->lodAllowedInCanvas); m_d->animationPlayer = new KisAnimationPlayer(this); connect(m_d->view->canvasController()->proxyObject, SIGNAL(moveDocumentOffset(QPoint)), SLOT(documentOffsetMoved(QPoint))); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); /** * We switch the shape manager every time vector layer or * shape selection is activated. Flake does not expect this * and connects all the signals of the global shape manager * to the clients in the constructor. To workaround this we * forward the signals of local shape managers stored in the * vector layers to the signals of global shape manager. So the * sequence of signal deliveries is the following: * * shapeLayer.m_d.canvas.m_shapeManager.selection() -> * shapeLayer -> * shapeController -> * globalShapeManager.selection() */ KisShapeController *kritaShapeController = static_cast(shapeController()->documentBase()); connect(kritaShapeController, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged())); connect(kritaShapeController, SIGNAL(selectionContentChanged()), globalShapeManager(), SIGNAL(selectionContentChanged())); connect(kritaShapeController, SIGNAL(currentLayerChanged(const KoShapeLayer*)), globalShapeManager()->selection(), SIGNAL(currentLayerChanged(const KoShapeLayer*))); connect(&m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDoCanvasUpdate())); } KisCanvas2::~KisCanvas2() { if (m_d->animationPlayer->isPlaying()) { m_d->animationPlayer->forcedStopOnExit(); } delete m_d; } void KisCanvas2::setCanvasWidget(QWidget * widget) { KisAbstractCanvasWidget *tmp = dynamic_cast(widget); Q_ASSERT_X(tmp, "setCanvasWidget", "Cannot cast the widget to a KisAbstractCanvasWidget"); if (m_d->popupPalette) { m_d->popupPalette->setParent(widget); } if(m_d->canvasWidget != 0) { tmp->setDecorations(m_d->canvasWidget->decorations()); // Redundant check for the constructor case, see below if(viewManager() != 0) viewManager()->inputManager()->removeTrackedCanvas(this); } m_d->canvasWidget = tmp; // Either tmp was null or we are being called by KisCanvas2 constructor that is called by KisView // constructor, so the view manager still doesn't exists. if(m_d->canvasWidget != 0 && viewManager() != 0) viewManager()->inputManager()->addTrackedCanvas(this); if (!m_d->canvasWidget->decoration(INFINITY_DECORATION_ID)) { KisInfinityManager *manager = new KisInfinityManager(m_d->view, this); manager->setVisible(true); m_d->canvasWidget->addDecoration(manager); } widget->setAutoFillBackground(false); widget->setAttribute(Qt::WA_OpaquePaintEvent); widget->setMouseTracking(true); widget->setAcceptDrops(true); KoCanvasControllerWidget *controller = dynamic_cast(canvasController()); if (controller) { Q_ASSERT(controller->canvas() == this); controller->changeCanvasWidget(widget); } } bool KisCanvas2::canvasIsOpenGL() const { return m_d->currentCanvasIsOpenGL; } KisOpenGL::FilterMode KisCanvas2::openGLFilterMode() const { return KisOpenGL::FilterMode(m_d->openGLFilterMode); } void KisCanvas2::gridSize(QPointF *offset, QSizeF *spacing) const { QTransform transform = coordinatesConverter()->imageToDocumentTransform(); const QPoint intSpacing = m_d->view->document()->gridConfig().spacing(); const QPoint intOffset = m_d->view->document()->gridConfig().offset(); QPointF size = transform.map(QPointF(intSpacing)); spacing->rwidth() = size.x(); spacing->rheight() = size.y(); *offset = transform.map(QPointF(intOffset)); } bool KisCanvas2::snapToGrid() const { return m_d->view->document()->gridConfig().snapToGrid(); } qreal KisCanvas2::rotationAngle() const { return m_d->coordinatesConverter->rotationAngle(); } bool KisCanvas2::xAxisMirrored() const { return m_d->coordinatesConverter->xAxisMirrored(); } bool KisCanvas2::yAxisMirrored() const { return m_d->coordinatesConverter->yAxisMirrored(); } void KisCanvas2::channelSelectionChanged() { KisImageWSP image = this->image(); m_d->channelFlags = image->rootLayer()->channelFlags(); image->barrierLock(); m_d->canvasWidget->channelSelectionChanged(m_d->channelFlags); startUpdateInPatches(image->bounds()); image->unlock(); } void KisCanvas2::addCommand(KUndo2Command *command) { // This method exists to support flake-related operations m_d->view->document()->addCommand(command); } KoShapeManager* KisCanvas2::shapeManager() const { if (!viewManager()) return globalShapeManager(); if (!viewManager()->nodeManager()) return globalShapeManager(); KisLayerSP activeLayer = viewManager()->nodeManager()->activeLayer(); if (activeLayer && activeLayer->isEditable()) { KisShapeLayer * shapeLayer = dynamic_cast(activeLayer.data()); if (shapeLayer) { return shapeLayer->shapeManager(); } KisSelectionSP selection = activeLayer->selection(); if (selection && !selection.isNull()) { if (selection->hasShapeSelection()) { KoShapeManager* m = dynamic_cast(selection->shapeSelection())->shapeManager(); return m; } } } return globalShapeManager(); } KoShapeManager * KisCanvas2::globalShapeManager() const { return &m_d->shapeManager; } void KisCanvas2::updateInputMethodInfo() { // TODO call (the protected) QWidget::updateMicroFocus() on the proper canvas widget... } const KisCoordinatesConverter* KisCanvas2::coordinatesConverter() const { return m_d->coordinatesConverter; } KoViewConverter* KisCanvas2::viewConverter() const { return m_d->coordinatesConverter; } KisInputManager* KisCanvas2::globalInputManager() const { return m_d->view->globalInputManager(); } QWidget* KisCanvas2::canvasWidget() { return m_d->canvasWidget->widget(); } const QWidget* KisCanvas2::canvasWidget() const { return m_d->canvasWidget->widget(); } KoUnit KisCanvas2::unit() const { KoUnit unit(KoUnit::Pixel); KisImageWSP image = m_d->view->image(); if (image) { if (!qFuzzyCompare(image->xRes(), image->yRes())) { warnKrita << "WARNING: resolution of the image is anisotropic" << ppVar(image->xRes()) << ppVar(image->yRes()); } const qreal resolution = image->xRes(); unit.setFactor(resolution); } return unit; } KoToolProxy * KisCanvas2::toolProxy() const { return &m_d->toolProxy; } void KisCanvas2::createQPainterCanvas() { m_d->currentCanvasIsOpenGL = false; KisQPainterCanvas * canvasWidget = new KisQPainterCanvas(this, m_d->coordinatesConverter, m_d->view); m_d->prescaledProjection = new KisPrescaledProjection(); m_d->prescaledProjection->setCoordinatesConverter(m_d->coordinatesConverter); m_d->prescaledProjection->setMonitorProfile(m_d->displayColorConverter.monitorProfile(), m_d->displayColorConverter.renderingIntent(), m_d->displayColorConverter.conversionFlags()); m_d->prescaledProjection->setDisplayFilter(m_d->displayColorConverter.displayFilter()); canvasWidget->setPrescaledProjection(m_d->prescaledProjection); setCanvasWidget(canvasWidget); } void KisCanvas2::createOpenGLCanvas() { KisConfig cfg; m_d->openGLFilterMode = cfg.openGLFilteringMode(); m_d->currentCanvasIsOpenGL = true; KisOpenGLCanvas2 *canvasWidget = new KisOpenGLCanvas2(this, m_d->coordinatesConverter, 0, m_d->view->image(), &m_d->displayColorConverter); m_d->frameCache = KisAnimationFrameCache::getFrameCache(canvasWidget->openGLImageTextures()); setCanvasWidget(canvasWidget); if (canvasWidget->needsFpsDebugging() && !decoration(KisFpsDecoration::idTag)) { addDecoration(new KisFpsDecoration(imageView())); } } void KisCanvas2::createCanvas(bool useOpenGL) { // deinitialize previous canvas structures m_d->prescaledProjection = 0; m_d->frameCache = 0; KisConfig cfg; QDesktopWidget dw; const KoColorProfile *profile = cfg.displayProfile(dw.screenNumber(imageView())); m_d->displayColorConverter.setMonitorProfile(profile); if (useOpenGL) { if (KisOpenGL::hasOpenGL()) { createOpenGLCanvas(); if (cfg.canvasState() == "OPENGL_FAILED") { // Creating the opengl canvas failed, fall back warnKrita << "OpenGL Canvas initialization returned OPENGL_FAILED. Falling back to QPainter."; createQPainterCanvas(); } } else { warnKrita << "Tried to create OpenGL widget when system doesn't have OpenGL\n"; createQPainterCanvas(); } - } + } else { createQPainterCanvas(); } - + if (m_d->popupPalette) { m_d->popupPalette->setParent(m_d->canvasWidget->widget()); } } void KisCanvas2::initializeImage() { KisImageWSP image = m_d->view->image(); m_d->coordinatesConverter->setImage(image); connect(image, SIGNAL(sigImageUpdated(QRect)), SLOT(startUpdateCanvasProjection(QRect)), Qt::DirectConnection); connect(this, SIGNAL(sigCanvasCacheUpdated()), SLOT(updateCanvasProjection())); connect(image, SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)), SLOT(startResizingImage()), Qt::DirectConnection); connect(this, SIGNAL(sigContinueResizeImage(qint32,qint32)), SLOT(finishResizingImage(qint32,qint32))); connectCurrentCanvas(); } void KisCanvas2::connectCurrentCanvas() { KisImageWSP image = m_d->view->image(); if (!m_d->currentCanvasIsOpenGL) { Q_ASSERT(m_d->prescaledProjection); m_d->prescaledProjection->setImage(image); } startResizingImage(); emit imageChanged(image); setLodAllowedInCanvas(m_d->lodAllowedInCanvas); } void KisCanvas2::resetCanvas(bool useOpenGL) { // we cannot reset the canvas before it's created, but this method might be called, // for instance when setting the monitor profile. if (!m_d->canvasWidget) { return; } KisConfig cfg; bool needReset = (m_d->currentCanvasIsOpenGL != useOpenGL) || (m_d->currentCanvasIsOpenGL && m_d->openGLFilterMode != cfg.openGLFilteringMode()); if (needReset) { createCanvas(useOpenGL); connectCurrentCanvas(); notifyZoomChanged(); } updateCanvasWidgetImpl(); } void KisCanvas2::startUpdateInPatches(const QRect &imageRect) { if (m_d->currentCanvasIsOpenGL) { startUpdateCanvasProjection(imageRect); } else { KisImageConfig imageConfig; int patchWidth = imageConfig.updatePatchWidth(); int patchHeight = imageConfig.updatePatchHeight(); for (int y = 0; y < imageRect.height(); y += patchHeight) { for (int x = 0; x < imageRect.width(); x += patchWidth) { QRect patchRect(x, y, patchWidth, patchHeight); startUpdateCanvasProjection(patchRect); } } } } void KisCanvas2::setDisplayFilter(KisDisplayFilter *displayFilter) { m_d->displayColorConverter.setDisplayFilter(displayFilter); KisImageWSP image = this->image(); image->barrierLock(); m_d->canvasWidget->setDisplayFilter(displayFilter); image->unlock(); } KisDisplayFilter *KisCanvas2::displayFilter() const { return m_d->displayColorConverter.displayFilter(); } KisDisplayColorConverter* KisCanvas2::displayColorConverter() const { return &m_d->displayColorConverter; } KisExposureGammaCorrectionInterface* KisCanvas2::exposureGammaCorrectionInterface() const { KisDisplayFilter *displayFilter = m_d->displayColorConverter.displayFilter(); return displayFilter ? displayFilter->correctionInterface() : KisDumbExposureGammaCorrectionInterface::instance(); } void KisCanvas2::startResizingImage() { KisImageWSP image = this->image(); qint32 w = image->width(); qint32 h = image->height(); emit sigContinueResizeImage(w, h); QRect imageBounds(0, 0, w, h); startUpdateInPatches(imageBounds); } void KisCanvas2::finishResizingImage(qint32 w, qint32 h) { m_d->canvasWidget->finishResizingImage(w, h); } void KisCanvas2::startUpdateCanvasProjection(const QRect & rc) { KisUpdateInfoSP info = m_d->canvasWidget->startUpdateCanvasProjection(rc, m_d->channelFlags); if (m_d->projectionUpdatesCompressor.putUpdateInfo(info)) { emit sigCanvasCacheUpdated(); } } void KisCanvas2::updateCanvasProjection() { while (KisUpdateInfoSP info = m_d->projectionUpdatesCompressor.takeUpdateInfo()) { QRect vRect = m_d->canvasWidget->updateCanvasProjection(info); if (!vRect.isEmpty()) { updateCanvasWidgetImpl(m_d->coordinatesConverter->viewportToWidget(vRect).toAlignedRect()); } } // TODO: Implement info->dirtyViewportRect() for KisOpenGLCanvas2 to avoid updating whole canvas if (m_d->currentCanvasIsOpenGL) { updateCanvasWidgetImpl(); } } void KisCanvas2::slotDoCanvasUpdate() { if (m_d->canvasWidget->isBusy()) { // just restarting the timer updateCanvasWidgetImpl(m_d->savedUpdateRect); return; } if (m_d->savedUpdateRect.isEmpty()) { m_d->canvasWidget->widget()->update(); emit updateCanvasRequested(m_d->canvasWidget->widget()->rect()); } else { emit updateCanvasRequested(m_d->savedUpdateRect); m_d->canvasWidget->widget()->update(m_d->savedUpdateRect); } m_d->savedUpdateRect = QRect(); } void KisCanvas2::updateCanvasWidgetImpl(const QRect &rc) { if (!m_d->updateSignalCompressor.isActive() || !m_d->savedUpdateRect.isEmpty()) { m_d->savedUpdateRect |= rc; } m_d->updateSignalCompressor.start(); } void KisCanvas2::updateCanvas() { updateCanvasWidgetImpl(); } void KisCanvas2::updateCanvas(const QRectF& documentRect) { if (m_d->currentCanvasIsOpenGL && m_d->canvasWidget->decorations().size() > 0) { updateCanvasWidgetImpl(); } else { // updateCanvas is called from tools, never from the projection // updates, so no need to prescale! QRect widgetRect = m_d->coordinatesConverter->documentToWidget(documentRect).toAlignedRect(); widgetRect.adjust(-2, -2, 2, 2); if (!widgetRect.isEmpty()) { updateCanvasWidgetImpl(widgetRect); } } } void KisCanvas2::disconnectCanvasObserver(QObject *object) { KoCanvasBase::disconnectCanvasObserver(object); m_d->view->disconnect(object); } void KisCanvas2::notifyZoomChanged() { if (!m_d->currentCanvasIsOpenGL) { Q_ASSERT(m_d->prescaledProjection); m_d->prescaledProjection->notifyZoomChanged(); } notifyLevelOfDetailChange(); updateCanvas(); // update the canvas, because that isn't done when zooming using KoZoomAction } void KisCanvas2::notifyLevelOfDetailChange() { if (!m_d->effectiveLodAllowedInCanvas()) return; KisImageSP image = this->image(); const qreal effectiveZoom = m_d->coordinatesConverter->effectiveZoom(); KisConfig cfg; const int maxLod = cfg.numMipmapLevels(); int lod = KisLodTransform::scaleToLod(effectiveZoom, maxLod); image->setDesiredLevelOfDetail(lod); } void KisCanvas2::preScale() { if (!m_d->currentCanvasIsOpenGL) { Q_ASSERT(m_d->prescaledProjection); m_d->prescaledProjection->preScale(); } } const KoColorProfile * KisCanvas2::monitorProfile() { return m_d->displayColorConverter.monitorProfile(); } KisViewManager* KisCanvas2::viewManager() const { if (m_d->view) { return m_d->view->viewManager(); } return 0; } QPointerKisCanvas2::imageView() const { return m_d->view; } KisImageWSP KisCanvas2::image() const { return m_d->view->image(); } KisImageWSP KisCanvas2::currentImage() const { return m_d->view->image(); } void KisCanvas2::documentOffsetMoved(const QPoint &documentOffset) { QPointF offsetBefore = m_d->coordinatesConverter->imageRectInViewportPixels().topLeft(); m_d->coordinatesConverter->setDocumentOffset(documentOffset); QPointF offsetAfter = m_d->coordinatesConverter->imageRectInViewportPixels().topLeft(); QPointF moveOffset = offsetAfter - offsetBefore; if (!m_d->currentCanvasIsOpenGL) m_d->prescaledProjection->viewportMoved(moveOffset); emit documentOffsetUpdateFinished(); updateCanvas(); } void KisCanvas2::slotConfigChanged() { KisConfig cfg; m_d->vastScrolling = cfg.vastScrolling(); resetCanvas(cfg.useOpenGL()); slotSetDisplayProfile(cfg.displayProfile(QApplication::desktop()->screenNumber(this->canvasWidget()))); } void KisCanvas2::refetchDataFromImage() { KisImageSP image = this->image(); KisImageBarrierLocker l(image); startUpdateInPatches(image->bounds()); } void KisCanvas2::slotSetDisplayProfile(const KoColorProfile *monitorProfile) { if (m_d->displayColorConverter.monitorProfile() == monitorProfile) return; m_d->displayColorConverter.setMonitorProfile(monitorProfile); KisImageWSP image = this->image(); image->barrierLock(); m_d->canvasWidget->setDisplayProfile(&m_d->displayColorConverter); refetchDataFromImage(); } -void KisCanvas2::addDecoration(KisCanvasDecoration* deco) +void KisCanvas2::addDecoration(KisCanvasDecorationSP deco) { m_d->canvasWidget->addDecoration(deco); } -KisCanvasDecoration* KisCanvas2::decoration(const QString& id) const +KisCanvasDecorationSP KisCanvas2::decoration(const QString& id) const { return m_d->canvasWidget->decoration(id); } QPoint KisCanvas2::documentOrigin() const { /** * In Krita we don't use document origin anymore. * All the centering when needed (vastScrolling < 0.5) is done * automatically by the KisCoordinatesConverter. */ return QPoint(); } QPoint KisCanvas2::documentOffset() const { return m_d->coordinatesConverter->documentOffset(); } void KisCanvas2::setFavoriteResourceManager(KisFavoriteResourceManager* favoriteResourceManager) { m_d->popupPalette = new KisPopupPalette(favoriteResourceManager, displayColorConverter()->displayRendererInterface(), m_d->canvasWidget->widget()); m_d->popupPalette->showPopupPalette(false); } void KisCanvas2::setCursor(const QCursor &cursor) { canvasWidget()->setCursor(cursor); } KisAnimationFrameCacheSP KisCanvas2::frameCache() const { return m_d->frameCache; } KisAnimationPlayer *KisCanvas2::animationPlayer() const { return m_d->animationPlayer; } void KisCanvas2::slotSelectionChanged() { KisShapeLayer* shapeLayer = dynamic_cast(viewManager()->activeLayer().data()); if (!shapeLayer) { return; } m_d->shapeManager.selection()->deselectAll(); Q_FOREACH (KoShape* shape, shapeLayer->shapeManager()->selection()->selectedShapes()) { m_d->shapeManager.selection()->select(shape); } } bool KisCanvas2::isPopupPaletteVisible() const { if (!m_d->popupPalette) { return false; } return m_d->popupPalette->isVisible(); } void KisCanvas2::setWrapAroundViewingMode(bool value) { - KisCanvasDecoration *infinityDecoration = + KisCanvasDecorationSP infinityDecoration = m_d->canvasWidget->decoration(INFINITY_DECORATION_ID); if (infinityDecoration) { infinityDecoration->setVisible(!value); } m_d->canvasWidget->setWrapAroundViewingMode(value); } bool KisCanvas2::wrapAroundViewingMode() const { - KisCanvasDecoration *infinityDecoration = + KisCanvasDecorationSP infinityDecoration = m_d->canvasWidget->decoration(INFINITY_DECORATION_ID); if (infinityDecoration) { return !(infinityDecoration->visible()); } return false; } void KisCanvas2::bootstrapFinished() { - if (!m_d->bootsrapLodBlocked) return; + if (!m_d->bootstrapLodBlocked) return; - m_d->bootsrapLodBlocked = false; + m_d->bootstrapLodBlocked = false; setLodAllowedInCanvas(m_d->lodAllowedInCanvas); } void KisCanvas2::setLodAllowedInCanvas(bool value) { + if (!KisOpenGL::supportsGLSL13()) { + qWarning() << "WARNING: Level of Detail functionality is available only with openGL + GLSL 1.3 support"; + } + m_d->lodAllowedInCanvas = - value && - m_d->currentCanvasIsOpenGL && - (m_d->openGLFilterMode == KisOpenGL::TrilinearFilterMode || - m_d->openGLFilterMode == KisOpenGL::HighQualityFiltering); + value && + m_d->currentCanvasIsOpenGL && + KisOpenGL::supportsGLSL13() && + (m_d->openGLFilterMode == KisOpenGL::TrilinearFilterMode || + m_d->openGLFilterMode == KisOpenGL::HighQualityFiltering); KisImageSP image = this->image(); if (m_d->effectiveLodAllowedInCanvas() != !image->levelOfDetailBlocked()) { image->setLevelOfDetailBlocked(!m_d->effectiveLodAllowedInCanvas()); notifyLevelOfDetailChange(); } KisConfig cfg; cfg.setLevelOfDetailEnabled(m_d->lodAllowedInCanvas); } bool KisCanvas2::lodAllowedInCanvas() const { return m_d->lodAllowedInCanvas; } void KisCanvas2::slotShowPopupPalette(const QPoint &p) { if (!m_d->popupPalette) { return; } m_d->popupPalette->showPopupPalette(p); } -KisPaintingAssistantsDecoration* KisCanvas2::paintingAssistantsDecoration() const +KisPaintingAssistantsDecorationSP KisCanvas2::paintingAssistantsDecoration() const { - KisCanvasDecoration* deco = decoration("paintingAssistantsDecoration"); - return qobject_cast(deco); + KisCanvasDecorationSP deco = decoration("paintingAssistantsDecoration"); + return qobject_cast(deco.data()); } diff --git a/libs/ui/canvas/kis_canvas2.h b/libs/ui/canvas/kis_canvas2.h index b147d2f83b..93b0c188de 100644 --- a/libs/ui/canvas/kis_canvas2.h +++ b/libs/ui/canvas/kis_canvas2.h @@ -1,275 +1,276 @@ /* This file is part of the KDE project * Copyright (C) 2006, 2010 Boudewijn Rempt * 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. */ #ifndef KIS_CANVAS_H #define KIS_CANVAS_H #include #include #include #include #include #include #include #include #include #include #include "opengl/kis_opengl.h" #include "kis_ui_types.h" #include "kis_coordinates_converter.h" +#include "kis_canvas_decoration.h" +#include "kis_painting_assistants_decoration.h" class KoToolProxy; class KoColorProfile; -class KisCanvasDecoration; + class KisViewManager; class KisFavoriteResourceManager; class KisDisplayFilter; class KisDisplayColorConverter; struct KisExposureGammaCorrectionInterface; -class KisPaintingAssistantsDecoration; class KisView; class KisInputManager; class KisAnimationPlayer; class KisShapeController; class KisCoordinatesConverter; class KoViewConverter; /** * KisCanvas2 is not an actual widget class, but rather an adapter for * the widget it contains, which may be either a QPainter based * canvas, or an OpenGL based canvas: that are the real widgets. */ class KRITAUI_EXPORT KisCanvas2 : public QObject, public KoCanvasBase { Q_OBJECT public: /** * Create a new canvas. The canvas manages a widget that will do * the actual painting: the canvas itself is not a widget. * * @param viewConverter the viewconverter for converting between * window and document coordinates. */ KisCanvas2(KisCoordinatesConverter* coordConverter, KoCanvasResourceManager *resourceManager, KisView *view, KoShapeBasedDocumentBase* sc); virtual ~KisCanvas2(); void notifyZoomChanged(); virtual void disconnectCanvasObserver(QObject *object); public: // KoCanvasBase implementation bool canvasIsOpenGL() const; KisOpenGL::FilterMode openGLFilterMode() const; void gridSize(QPointF *offset, QSizeF *spacing) const; bool snapToGrid() const; // XXX: Why? void addCommand(KUndo2Command *command); virtual QPoint documentOrigin() const; QPoint documentOffset() const; /** * Return the right shape manager for the current layer. That is * to say, if the current layer is a vector layer, return the shape * layer's canvas' shapemanager, else the shapemanager associated * with the global krita canvas. */ KoShapeManager * shapeManager() const; /** * Return the shape manager associated with this canvas */ KoShapeManager * globalShapeManager() const; void updateCanvas(const QRectF& rc); virtual void updateInputMethodInfo(); const KisCoordinatesConverter* coordinatesConverter() const; virtual KoViewConverter *viewConverter() const; virtual QWidget* canvasWidget(); virtual const QWidget* canvasWidget() const; virtual KoUnit unit() const; virtual KoToolProxy* toolProxy() const; const KoColorProfile* monitorProfile(); /** * Prescale the canvas represention of the image (if necessary, it * is for QPainter, not for OpenGL). */ void preScale(); // FIXME: // Temporary! Either get the current layer and image from the // resource provider, or use this, which gets them from the // current shape selection. KisImageWSP currentImage() const; /** * Filters events and sends them to canvas actions. Shared * among all the views/canvases * * NOTE: May be null while initialization! */ KisInputManager* globalInputManager() const; - KisPaintingAssistantsDecoration* paintingAssistantsDecoration() const; + KisPaintingAssistantsDecorationSP paintingAssistantsDecoration() const; public: // KisCanvas2 methods KisImageWSP image() const; KisViewManager* viewManager() const; QPointer imageView() const; /// @return true if the canvas image should be displayed in vertically mirrored mode - void addDecoration(KisCanvasDecoration* deco); - KisCanvasDecoration* decoration(const QString& id) const; + void addDecoration(KisCanvasDecorationSP deco); + KisCanvasDecorationSP decoration(const QString& id) const; void setDisplayFilter(KisDisplayFilter *displayFilter); KisDisplayFilter *displayFilter() const; KisDisplayColorConverter* displayColorConverter() const; KisExposureGammaCorrectionInterface* exposureGammaCorrectionInterface() const; void setCursor(const QCursor &cursor); KisAnimationFrameCacheSP frameCache() const; KisAnimationPlayer *animationPlayer() const; void refetchDataFromImage(); Q_SIGNALS: void imageChanged(KisImageWSP image); void sigCanvasCacheUpdated(); void sigContinueResizeImage(qint32 w, qint32 h); void documentOffsetUpdateFinished(); // emitted whenever the canvas widget thinks sketch should update void updateCanvasRequested(const QRect &rc); public Q_SLOTS: /// Update the entire canvas area void updateCanvas(); void startResizingImage(); void finishResizingImage(qint32 w, qint32 h); /// canvas rotation in degrees qreal rotationAngle() const; /// Bools indicating canvasmirroring. bool xAxisMirrored() const; bool yAxisMirrored() const; void channelSelectionChanged(); /** * Called whenever the display monitor profile resource changes */ void slotSetDisplayProfile(const KoColorProfile *profile); void startUpdateInPatches(const QRect &imageRect); private Q_SLOTS: /// The image projection has changed, now start an update /// of the canvas representation. void startUpdateCanvasProjection(const QRect & rc); void updateCanvasProjection(); /** * Called whenever the view widget needs to show a different part of * the document * * @param documentOffset the offset in widget pixels */ void documentOffsetMoved(const QPoint &documentOffset); /** * Called whenever the configuration settings change. */ void slotConfigChanged(); void slotSelectionChanged(); void slotDoCanvasUpdate(); void bootstrapFinished(); public: bool isPopupPaletteVisible() const; void slotShowPopupPalette(const QPoint& = QPoint(0,0)); // interface for KisCanvasController only void setWrapAroundViewingMode(bool value); bool wrapAroundViewingMode() const; void setLodAllowedInCanvas(bool value); bool lodAllowedInCanvas() const; void initializeImage(); void setFavoriteResourceManager(KisFavoriteResourceManager* favoriteResourceManager); private: Q_DISABLE_COPY(KisCanvas2) void connectCurrentCanvas(); void createCanvas(bool useOpenGL); void createQPainterCanvas(); void createOpenGLCanvas(); void updateCanvasWidgetImpl(const QRect &rc = QRect()); void setCanvasWidget(QWidget *widget); void resetCanvas(bool useOpenGL); void notifyLevelOfDetailChange(); // Completes construction of canvas. // To be called by KisView in its constructor, once it has been setup enough // (to be defined what that means) for things KisCanvas2 expects from KisView // TODO: see to avoid that void setup(); private: friend class KisView; // calls setup() class KisCanvas2Private; KisCanvas2Private * const m_d; }; #endif diff --git a/libs/ui/canvas/kis_canvas_controller.cpp b/libs/ui/canvas/kis_canvas_controller.cpp index be22eedc02..f3fb686920 100644 --- a/libs/ui/canvas/kis_canvas_controller.cpp +++ b/libs/ui/canvas/kis_canvas_controller.cpp @@ -1,296 +1,296 @@ /* * Copyright (c) 2010 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_canvas_controller.h" #include #include #include #include "kis_canvas_decoration.h" #include "kis_paintop_transformation_connector.h" #include "kis_coordinates_converter.h" #include "kis_canvas2.h" #include "kis_image.h" #include "KisViewManager.h" #include "KisView.h" #include "krita_utils.h" #include "kis_signal_compressor_with_param.h" static const int gRulersUpdateDelay = 80 /* ms */; struct KisCanvasController::Private { Private(KisCanvasController *qq) : q(qq), paintOpTransformationConnector(0) { using namespace std::placeholders; std::function callback( std::bind(&KisCanvasController::Private::emitPointerPositionChangedSignals, this, _1)); mousePositionCompressor.reset( new KisSignalCompressorWithParam( gRulersUpdateDelay, callback, KisSignalCompressor::FIRST_ACTIVE)); } QPointer view; KisCoordinatesConverter *coordinatesConverter; KisCanvasController *q; KisPaintopTransformationConnector *paintOpTransformationConnector; QScopedPointer > mousePositionCompressor; void emitPointerPositionChangedSignals(QPoint pointerPos); void updateDocumentSizeAfterTransform(); void showRotationValueOnCanvas(); void showMirrorStateOnCanvas(); }; void KisCanvasController::Private::emitPointerPositionChangedSignals(QPoint pointerPos) { if (!coordinatesConverter) return; QPointF documentPos = coordinatesConverter->widgetToDocument(pointerPos); q->proxyObject->emitDocumentMousePositionChanged(documentPos); q->proxyObject->emitCanvasMousePositionChanged(pointerPos); } void KisCanvasController::Private::updateDocumentSizeAfterTransform() { // round the size of the area to the nearest integer instead of getting aligned rect QSize widgetSize = coordinatesConverter->imageRectInWidgetPixels().toRect().size(); q->updateDocumentSize(widgetSize, true); KisCanvas2 *kritaCanvas = dynamic_cast(q->canvas()); Q_ASSERT(kritaCanvas); kritaCanvas->notifyZoomChanged(); } KisCanvasController::KisCanvasController(QPointerparent, KActionCollection * actionCollection) : KoCanvasControllerWidget(actionCollection, parent), m_d(new Private(this)) { m_d->view = parent; } KisCanvasController::~KisCanvasController() { delete m_d; } void KisCanvasController::setCanvas(KoCanvasBase *canvas) { KisCanvas2 *kritaCanvas = dynamic_cast(canvas); Q_ASSERT(kritaCanvas); m_d->coordinatesConverter = const_cast(kritaCanvas->coordinatesConverter()); KoCanvasControllerWidget::setCanvas(canvas); m_d->paintOpTransformationConnector = new KisPaintopTransformationConnector(kritaCanvas, this); } void KisCanvasController::changeCanvasWidget(QWidget *widget) { KoCanvasControllerWidget::changeCanvasWidget(widget); } void KisCanvasController::activate() { KoCanvasControllerWidget::activate(); } void KisCanvasController::keyPressEvent(QKeyEvent *event) { /** * Dirty Hack Alert: * Do not call the KoCanvasControllerWidget::keyPressEvent() * to avoid activation of Pan and Default tool activation shortcuts */ Q_UNUSED(event); } void KisCanvasController::wheelEvent(QWheelEvent *event) { /** * Dirty Hack Alert: * Do not call the KoCanvasControllerWidget::wheelEvent() * to disable the default behavior of KoCanvasControllerWidget and QAbstractScrollArea */ Q_UNUSED(event); } bool KisCanvasController::eventFilter(QObject *watched, QEvent *event) { KoCanvasBase *canvas = this->canvas(); if (!canvas || !canvas->canvasWidget() || canvas->canvasWidget() != watched) return false; if (event->type() == QEvent::MouseMove) { QMouseEvent *mevent = static_cast(event); m_d->mousePositionCompressor->start(mevent->pos()); } else if (event->type() == QEvent::TabletMove) { QTabletEvent *tevent = static_cast(event); m_d->mousePositionCompressor->start(tevent->pos()); } return false; } void KisCanvasController::updateDocumentSize(const QSize &sz, bool recalculateCenter) { KoCanvasControllerWidget::updateDocumentSize(sz, recalculateCenter); emit documentSizeChanged(); } void KisCanvasController::Private::showMirrorStateOnCanvas() { bool isXMirrored = coordinatesConverter->xAxisMirrored(); view->viewManager()-> showFloatingMessage( i18nc("floating message about mirroring", "Horizontal mirroring: %1 ", isXMirrored ? i18n("ON") : i18n("OFF")), QIcon(), 500, KisFloatingMessage::Low); } void KisCanvasController::mirrorCanvas(bool enable) { - KisCanvasDecoration *decorator = dynamic_cast(this->canvas())->decoration("mirror_axis"); + KisCanvasDecorationSP decorator = dynamic_cast(this->canvas())->decoration("mirror_axis"); KIS_ASSERT_RECOVER_RETURN(decorator); decorator->setVisible(enable); QPoint newOffset = m_d->coordinatesConverter->mirror(m_d->coordinatesConverter->widgetCenterPoint(), enable, false); m_d->updateDocumentSizeAfterTransform(); setScrollBarValue(newOffset); m_d->paintOpTransformationConnector->notifyTransformationChanged(); m_d->showMirrorStateOnCanvas(); } void KisCanvasController::Private::showRotationValueOnCanvas() { qreal rotationAngle = coordinatesConverter->rotationAngle(); view->viewManager()-> showFloatingMessage( i18nc("floating message about rotation", "Rotation: %1° ", KritaUtils::prettyFormatReal(rotationAngle)), QIcon(), 500, KisFloatingMessage::Low, Qt::AlignCenter); } void KisCanvasController::rotateCanvas(qreal angle) { QPoint newOffset = m_d->coordinatesConverter->rotate(m_d->coordinatesConverter->widgetCenterPoint(), angle); m_d->updateDocumentSizeAfterTransform(); setScrollBarValue(newOffset); m_d->paintOpTransformationConnector->notifyTransformationChanged(); m_d->showRotationValueOnCanvas(); } void KisCanvasController::rotateCanvasRight15() { rotateCanvas(15.0); } void KisCanvasController::rotateCanvasLeft15() { rotateCanvas(-15.0); } void KisCanvasController::resetCanvasRotation() { QPoint newOffset = m_d->coordinatesConverter->resetRotation(m_d->coordinatesConverter->widgetCenterPoint()); m_d->updateDocumentSizeAfterTransform(); setScrollBarValue(newOffset); m_d->paintOpTransformationConnector->notifyTransformationChanged(); m_d->showRotationValueOnCanvas(); } void KisCanvasController::slotToggleWrapAroundMode(bool value) { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); if (!canvas()->canvasIsOpenGL() && value) { m_d->view->viewManager()->showFloatingMessage(i18n("You are activating wrap-around mode, but have not enabled OpenGL.\n" "To visualize wrap-around mode, enable OpenGL."), QIcon()); } kritaCanvas->setWrapAroundViewingMode(value); kritaCanvas->image()->setWrapAroundModePermitted(value); } bool KisCanvasController::wrapAroundMode() const { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); return kritaCanvas->wrapAroundViewingMode(); } void KisCanvasController::slotToggleLevelOfDetailMode(bool value) { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); kritaCanvas->setLodAllowedInCanvas(value); bool result = levelOfDetailMode(); if (!value || result) { m_d->view->viewManager()->showFloatingMessage( i18n("Instant Preview Mode: %1", result ? i18n("ON") : i18n("OFF")), QIcon(), 500, KisFloatingMessage::Low); } else { QString reason; if (!kritaCanvas->canvasIsOpenGL()) { reason = i18n("Instant Preview is only supported with OpenGL activated"); } else if (kritaCanvas->openGLFilterMode() == KisOpenGL::BilinearFilterMode || kritaCanvas->openGLFilterMode() == KisOpenGL::NearestFilterMode) { QString filteringMode = kritaCanvas->openGLFilterMode() == KisOpenGL::BilinearFilterMode ? i18n("Bilinear") : i18n("Nearest Neighbour"); reason = i18n("Instant Preview is supported\n in Trilinear of High Quality filtering modes.\nCurrent mode is %1", filteringMode); } m_d->view->viewManager()->showFloatingMessage( i18n("Failed activating Instant Preview mode!\n\n%1", reason), QIcon(), 5000, KisFloatingMessage::Low); } } bool KisCanvasController::levelOfDetailMode() const { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); return kritaCanvas->lodAllowedInCanvas(); } diff --git a/libs/ui/canvas/kis_canvas_decoration.cc b/libs/ui/canvas/kis_canvas_decoration.cc index 33fddd7d77..30c73de42d 100644 --- a/libs/ui/canvas/kis_canvas_decoration.cc +++ b/libs/ui/canvas/kis_canvas_decoration.cc @@ -1,93 +1,94 @@ /* * Copyright (c) 2008 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_canvas_decoration.h" - +#include "kis_canvas2.h" #include "kis_debug.h" struct KisCanvasDecoration::Private { bool visible; QPointer view; QString id; }; KisCanvasDecoration::KisCanvasDecoration(const QString& id, QPointerparent) : QObject(parent) , d(new Private) { d->visible = false; d->view = parent; d->id = id; } KisCanvasDecoration::~KisCanvasDecoration() { delete d; } void KisCanvasDecoration::setView(QPointerimageView) { d->view = imageView; } const QString& KisCanvasDecoration::id() const { return d->id; } void KisCanvasDecoration::setVisible(bool v) { d->visible = v; if (d->view && - d->view->canvasBase()) { + d->view->canvasBase()) { d->view->canvasBase()->updateCanvas(); } } bool KisCanvasDecoration::visible() const { return d->visible; } void KisCanvasDecoration::toggleVisibility() { setVisible(!visible()); } void KisCanvasDecoration::paint(QPainter& gc, const QRectF& updateArea, const KisCoordinatesConverter *converter, KisCanvas2 *canvas = 0) { if (!canvas) { dbgFile<<"canvas does not exist:"<KisCanvasDecoration::imageView() { return d->view; } QPointerKisCanvasDecoration::view() const { return d->view; } diff --git a/libs/ui/canvas/kis_canvas_decoration.h b/libs/ui/canvas/kis_canvas_decoration.h index ab4f887ef9..a15ec2e971 100644 --- a/libs/ui/canvas/kis_canvas_decoration.h +++ b/libs/ui/canvas/kis_canvas_decoration.h @@ -1,86 +1,90 @@ /* * Copyright (c) 2008 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_CANVAS_DECORATION_H_ #define _KIS_CANVAS_DECORATION_H_ #include #include #include #include -#include "kis_canvas2.h" #include "KisView.h" +#include +class KisCanvas2; class QRectF; class QPainter; class KisCoordinatesConverter; +class KisCanvasDecoration; +typedef KisSharedPtr KisCanvasDecorationSP; /** * This class is the base class for object that draw a decoration on the canvas, * for instance, selections, grids, tools, ... */ -class KRITAUI_EXPORT KisCanvasDecoration : public QObject +class KRITAUI_EXPORT KisCanvasDecoration : public QObject, public KisShared { Q_OBJECT public: KisCanvasDecoration(const QString& id, QPointerparent); + ~KisCanvasDecoration(); void setView(QPointer imageView); const QString& id() const; /** * @return whether the decoration is visible. */ bool visible() const; /** * Will paint the decoration on the QPainter, if the visible is set to true. * * @param updateRect dirty rect in document pixels */ void paint(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter,KisCanvas2* canvas); public Q_SLOTS: /** * Set if the decoration is visible or not. */ virtual void setVisible(bool v); /** * If decoration is visible, hide it, if not show it. */ void toggleVisibility(); protected: virtual void drawDecoration(QPainter& gc, const QRectF& updateArea, const KisCoordinatesConverter *converter,KisCanvas2* canvas) = 0; /// XXX: unify view and imageview! QPointerimageView(); /** * @return the parent KisView */ QPointer view() const; private: struct Private; Private* const d; }; #endif diff --git a/libs/ui/canvas/kis_canvas_widget_base.cpp b/libs/ui/canvas/kis_canvas_widget_base.cpp index 3b4c81d597..69de9130b5 100644 --- a/libs/ui/canvas/kis_canvas_widget_base.cpp +++ b/libs/ui/canvas/kis_canvas_widget_base.cpp @@ -1,250 +1,250 @@ /* * 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" 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; + 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 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. Oherwise they might decide * to process some events or signals after the canvas has been * destroyed */ - qDeleteAll(m_d->decorations); + //5qDeleteAll(m_d->decorations); m_d->decorations.clear(); delete m_d; } void KisCanvasWidgetBase::drawDecorations(QPainter & gc, const QRect &updateWidgetRect) const { gc.save(); if (!m_d->canvas) { dbgFile<<"canvas doesn't exist, in canvas widget base!"; } // 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 reenable 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 // 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(); } // - 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(); // ask the decorations to paint themselves - Q_FOREACH (KisCanvasDecoration* deco, m_d->decorations) { + Q_FOREACH (KisCanvasDecorationSP deco, m_d->decorations) { deco->paint(gc, m_d->coordinatesConverter->widgetToDocument(updateWidgetRect), m_d->coordinatesConverter,m_d->canvas); } gc.restore(); } -void KisCanvasWidgetBase::addDecoration(KisCanvasDecoration* deco) +void KisCanvasWidgetBase::addDecoration(KisCanvasDecorationSP deco) { m_d->decorations.push_back(deco); } -KisCanvasDecoration* KisCanvasWidgetBase::decoration(const QString& id) const +KisCanvasDecorationSP KisCanvasWidgetBase::decoration(const QString& id) const { - Q_FOREACH (QPointer deco, m_d->decorations) { + Q_FOREACH (KisCanvasDecorationSP deco, m_d->decorations) { if (deco->id() == id) { return deco; } } return 0; } -void KisCanvasWidgetBase::setDecorations(const QList > &decorations) +void KisCanvasWidgetBase::setDecorations(const QList &decorations) { m_d->decorations=decorations; } -QList > KisCanvasWidgetBase::decorations() const +QList KisCanvasWidgetBase::decorations() const { return m_d->decorations; } void KisCanvasWidgetBase::setWrapAroundViewingMode(bool value) { Q_UNUSED(value); } QImage KisCanvasWidgetBase::createCheckersImage(qint32 checkSize) { KisConfig cfg; 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; 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; } KoToolProxy *KisCanvasWidgetBase::toolProxy() const { return m_d->toolProxy; } void KisCanvasWidgetBase::setDisplayFilter(KisDisplayFilter */*displayFilter*/) { } 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/canvas/kis_canvas_widget_base.h b/libs/ui/canvas/kis_canvas_widget_base.h index c01f2c1ef9..a7889bf1bc 100644 --- a/libs/ui/canvas/kis_canvas_widget_base.h +++ b/libs/ui/canvas/kis_canvas_widget_base.h @@ -1,102 +1,102 @@ /* * Copyright (C) 2007 Boudewijn Rempt , (C) * Copyright (C) 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. */ #ifndef _KIS_CANVAS_WIDGET_BASE_ #define _KIS_CANVAS_WIDGET_BASE_ #include #include #include "kis_abstract_canvas_widget.h" class QColor; class QImage; class QInputMethodEvent; class QVariant; class KisCoordinatesConverter; class KisDisplayFilter; class KisCanvas2; #include "kritaui_export.h" class KRITAUI_EXPORT KisCanvasWidgetBase : public KisAbstractCanvasWidget { public: KisCanvasWidgetBase(KisCanvas2 * canvas, KisCoordinatesConverter *coordinatesConverter); virtual ~KisCanvasWidgetBase(); public: // KisAbstractCanvasWidget virtual KoToolProxy *toolProxy() const; /// set the specified display filter on the canvas virtual void setDisplayFilter(KisDisplayFilter *displayFilter); /** * Draw the specified decorations on the view. */ virtual void drawDecorations(QPainter & gc, const QRect &updateWidgetRect) const; - virtual void addDecoration(KisCanvasDecoration* deco); - virtual KisCanvasDecoration* decoration(const QString& id) const; + virtual void addDecoration(KisCanvasDecorationSP deco); + virtual KisCanvasDecorationSP decoration(const QString& id) const; - virtual void setDecorations(const QList > &); - virtual QList > decorations() const; + virtual void setDecorations(const QList &); + virtual QList decorations() const; virtual void setWrapAroundViewingMode(bool value); /** * Returns the color of the border, i.e. the part of the canvas * outside the image contents. * */ QColor borderColor() const; /** * Returns one check of the background checkerboard pattern. */ static QImage createCheckersImage(qint32 checkSize = -1); KisCoordinatesConverter* coordinatesConverter() const; protected: KisCanvas2 *canvas() const; /** * Event handlers to be called by derived canvas event handlers. * All common event processing is carried out by these * functions. */ QVariant processInputMethodQuery(Qt::InputMethodQuery query) const; void processInputMethodEvent(QInputMethodEvent *event); void notifyConfigChanged(); /// To be implemented by the derived canvas virtual bool callFocusNextPrevChild(bool next) = 0; private: struct Private; Private * const m_d; }; #endif // _KIS_CANVAS_WIDGET_BASE_ diff --git a/libs/ui/canvas/kis_grid_decoration.cpp b/libs/ui/canvas/kis_grid_decoration.cpp index b0b72c0e3a..10e95cad71 100644 --- a/libs/ui/canvas/kis_grid_decoration.cpp +++ b/libs/ui/canvas/kis_grid_decoration.cpp @@ -1,126 +1,128 @@ /* * This file is part of Krita * * Copyright (c) 2006 Cyrille Berger * Copyright (c) 2014 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_grid_decoration.h" #include #include #include #include #include + #include "kis_grid_config.h" +#include "kis_coordinates_converter.h" struct KisGridDecoration::Private { KisGridConfig config; }; KisGridDecoration::KisGridDecoration(KisView* parent) : KisCanvasDecoration("grid", parent), m_d(new Private) { } KisGridDecoration::~KisGridDecoration() { } void KisGridDecoration::setGridConfig(const KisGridConfig &config) { m_d->config = config; } void KisGridDecoration::drawDecoration(QPainter& gc, const QRectF& updateArea, const KisCoordinatesConverter* converter, KisCanvas2* canvas) { if (!m_d->config.showGrid()) return; Q_UNUSED(canvas); QTransform transform = converter->imageToWidgetTransform(); const qreal scale = KoUnit::approxTransformScale(transform); const int minWidgetSize = 3; const int effectiveSize = qMin(m_d->config.spacing().x(), m_d->config.spacing().y()); int scaleCoeff = 1; quint32 subdivision = m_d->config.subdivision(); while (qFloor(scale * scaleCoeff * effectiveSize) <= minWidgetSize) { if (subdivision > 1) { scaleCoeff = subdivision; subdivision = 1; } else { scaleCoeff *= 2; } if (scaleCoeff > 32768) { qWarning() << "WARNING: Grid Scale Coeff is too high! That is surely a bug!"; return; } } const QPen mainPen = m_d->config.penMain(); const QPen subdivisionPen = m_d->config.penSubdivision(); gc.save(); gc.setTransform(transform); gc.setRenderHints(QPainter::Antialiasing, false); gc.setRenderHints(QPainter::HighQualityAntialiasing, false); qreal x1, y1, x2, y2; QRectF imageRect = converter->documentToImage(updateArea) & converter->imageRectInImagePixels(); imageRect.getCoords(&x1, &y1, &x2, &y2); { // vertical lines const int offset = m_d->config.offset().x(); const int step = scaleCoeff * m_d->config.spacing().x(); const int lineIndexFirst = qCeil((x1 - offset) / step); const int lineIndexLast = qFloor((x2 - offset) / step); for (int i = lineIndexFirst; i <= lineIndexLast; i++) { int w = offset + i * step; gc.setPen(i % subdivision == 0 ? mainPen : subdivisionPen); gc.drawLine(QPointF(w, y1),QPointF(w, y2)); } } { // horizontal lines const int offset = m_d->config.offset().y(); const int step = scaleCoeff * m_d->config.spacing().y(); const int lineIndexFirst = qCeil((y1 - offset) / step); const int lineIndexLast = qFloor((y2 - offset) / step); for (int i = lineIndexFirst; i <= lineIndexLast; i++) { int w = offset + i * step; gc.setPen(i % subdivision == 0 ? mainPen : subdivisionPen); gc.drawLine(QPointF(x1, w),QPointF(x2, w)); } } gc.restore(); } diff --git a/libs/ui/canvas/kis_grid_manager.cpp b/libs/ui/canvas/kis_grid_manager.cpp index 96f9165a51..2ed25b61ef 100644 --- a/libs/ui/canvas/kis_grid_manager.cpp +++ b/libs/ui/canvas/kis_grid_manager.cpp @@ -1,137 +1,138 @@ /* * This file is part of Krita * * Copyright (c) 2006 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_grid_manager.h" #include #include #include #include #include +#include "kis_canvas2.h" #include "kis_coordinates_converter.h" #include "kis_config.h" #include "kis_grid_decoration.h" #include "kis_image.h" #include "KisViewManager.h" #include "KisDocument.h" #include "KisView.h" #include "kis_grid_config.h" #include "kis_signals_blocker.h" KisGridManager::KisGridManager(KisViewManager * parent) : QObject(parent) { } KisGridManager::~KisGridManager() { } void KisGridManager::setGridConfig(const KisGridConfig &config) { setGridConfigImpl(config, true); } void KisGridManager::setGridConfigImpl(const KisGridConfig &config, bool emitModified) { if (!m_imageView) return; config.saveStaticData(); m_imageView->document()->setGridConfig(config); if (emitModified) { // TODO: make editing guides undoable, so that no // setModified() will be needed m_imageView->document()->setModified(true); } m_gridDecoration->setGridConfig(config); m_gridDecoration->setVisible(config.showGrid()); m_toggleGrid->setChecked(config.showGrid()); m_toggleSnapToGrid->setChecked(config.snapToGrid()); } void KisGridManager::setup(KisActionManager* actionManager) { m_toggleGrid = actionManager->createAction("view_grid"); connect(m_toggleGrid, SIGNAL(toggled(bool)), this, SLOT(slotChangeGridVisibilityTriggered(bool))); m_toggleSnapToGrid = actionManager->createAction("view_snap_to_grid"); connect(m_toggleSnapToGrid, SIGNAL(toggled(bool)), this, SLOT(slotSnapToGridTriggered(bool))); } void KisGridManager::updateGUI() { } void KisGridManager::setView(QPointer imageView) { if (m_imageView) { m_gridDecoration = 0; } m_imageView = imageView; if (imageView) { - m_gridDecoration = qobject_cast(imageView->canvasBase()->decoration("grid")); + m_gridDecoration = qobject_cast(imageView->canvasBase()->decoration("grid").data()); if (!m_gridDecoration) { m_gridDecoration = new KisGridDecoration(imageView); imageView->canvasBase()->addDecoration(m_gridDecoration); } KisGridConfig config = imageView->document()->gridConfig(); setGridConfigImpl(config, false); emit sigRequestUpdateGridConfig(config); KisSignalsBlocker b1(m_toggleGrid, m_toggleSnapToGrid); m_toggleGrid->setChecked(config.showGrid()); m_toggleSnapToGrid->setChecked(config.snapToGrid()); } } void KisGridManager::slotChangeGridVisibilityTriggered(bool value) { if (!m_imageView) return; KisGridConfig config = m_imageView->document()->gridConfig(); config.setShowGrid(value); setGridConfig(config); emit sigRequestUpdateGridConfig(config); } void KisGridManager::slotSnapToGridTriggered(bool value) { if (!m_imageView) return; KisGridConfig config = m_imageView->document()->gridConfig(); config.setSnapToGrid(value); setGridConfig(config); emit sigRequestUpdateGridConfig(config); } diff --git a/libs/ui/canvas/kis_guides_decoration.cpp b/libs/ui/canvas/kis_guides_decoration.cpp index f7d391347b..39a1469799 100644 --- a/libs/ui/canvas/kis_guides_decoration.cpp +++ b/libs/ui/canvas/kis_guides_decoration.cpp @@ -1,90 +1,90 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_guides_decoration.h" #include #include "kis_config.h" #include "kis_guides_config.h" - +#include "kis_coordinates_converter.h" struct KisGuidesDecoration::Private { KisGuidesConfig guidesConfig; }; KisGuidesDecoration::KisGuidesDecoration(QPointer view) : KisCanvasDecoration(GUIDES_DECORATION_ID, view), m_d(new Private) { } KisGuidesDecoration::~KisGuidesDecoration() { } void KisGuidesDecoration::setGuidesConfig(const KisGuidesConfig &value) { m_d->guidesConfig = value; } const KisGuidesConfig& KisGuidesDecoration::guidesConfig() const { return m_d->guidesConfig; } void KisGuidesDecoration::drawDecoration(QPainter &painter, const QRectF& updateArea, const KisCoordinatesConverter *converter, KisCanvas2 *canvas) { Q_UNUSED(canvas); const qreal borderDelta = 2.0; const QPen guidesPen(m_d->guidesConfig.guidesPen()); painter.save(); painter.setPen(guidesPen); painter.setTransform(QTransform()); painter.setRenderHints(QPainter::Antialiasing, false); painter.setRenderHints(QPainter::HighQualityAntialiasing, false); Q_FOREACH (qreal guide, m_d->guidesConfig.horizontalGuideLines()) { if (guide < updateArea.top() - borderDelta || guide > updateArea.bottom() + borderDelta) { continue; } const QPoint p0 = converter->documentToWidget(QPointF(updateArea.left() - borderDelta, guide)).toPoint(); const QPoint p1 = converter->documentToWidget(QPointF(updateArea.right() + borderDelta, guide)).toPoint(); painter.drawLine(p0, p1); } Q_FOREACH (qreal guide, m_d->guidesConfig.verticalGuideLines()) { if (guide < updateArea.left() - borderDelta || guide > updateArea.right() + borderDelta) { continue; } const QPoint p0 = converter->documentToWidget(QPointF(guide, updateArea.top() - borderDelta)).toPoint(); const QPoint p1 = converter->documentToWidget(QPointF(guide, updateArea.bottom() + borderDelta)).toPoint(); painter.drawLine(p0, p1); } painter.restore(); } diff --git a/libs/ui/canvas/kis_guides_manager.cpp b/libs/ui/canvas/kis_guides_manager.cpp index ec68d6bc0b..bd251f4271 100644 --- a/libs/ui/canvas/kis_guides_manager.cpp +++ b/libs/ui/canvas/kis_guides_manager.cpp @@ -1,742 +1,743 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_guides_manager.h" #include #include #include "kis_guides_decoration.h" #include #include "kis_guides_config.h" #include "kis_action_manager.h" #include "kis_action.h" #include "kis_signals_blocker.h" #include "input/kis_input_manager.h" #include "kis_coordinates_converter.h" #include "kis_zoom_manager.h" #include "kis_signal_auto_connection.h" #include "KisViewManager.h" #include "KisDocument.h" #include "kis_algebra_2d.h" #include #include "kis_snap_line_strategy.h" #include "kis_change_guides_command.h" #include "kis_snap_config.h" - +#include "kis_coordinates_converter.h" +#include "kis_canvas2.h" struct KisGuidesManager::Private { Private(KisGuidesManager *_q) : q(_q), decoration(0), invalidGuide(Qt::Horizontal, -1), currentGuide(invalidGuide), cursorSwitched(false), dragStartGuidePos(0) {} KisGuidesManager *q; KisGuidesDecoration *decoration; KisGuidesConfig guidesConfig; KisSnapConfig snapConfig; QPointer view; typedef QPair GuideHandle; GuideHandle findGuide(const QPointF &docPos); bool isGuideValid(const GuideHandle &h); qreal guideValue(const GuideHandle &h); void setGuideValue(const GuideHandle &h, qreal value); void deleteGuide(const GuideHandle &h); const GuideHandle invalidGuide; bool updateCursor(const QPointF &docPos); void initDragStart(const GuideHandle &guide, const QPointF &dragStart, qreal guideValue, bool snapToStart); bool mouseMoveHandler(const QPointF &docPos, Qt::KeyboardModifiers modifiers); bool mouseReleaseHandler(const QPointF &docPos); void updateSnappingStatus(const KisGuidesConfig &value); QPointF alignToPixels(const QPointF docPoint); QPointF getDocPointFromEvent(QEvent *event); Qt::MouseButton getButtonFromEvent(QEvent *event); QAction* createShortenedAction(const QString &text, const QString &parentId, QObject *parent); void syncAction(const QString &actionName, bool value); GuideHandle currentGuide; bool cursorSwitched; QCursor oldCursor; QPointF dragStartDoc; QPointF dragPointerOffset; qreal dragStartGuidePos; KisSignalAutoConnectionsStore viewConnections; }; KisGuidesManager::KisGuidesManager(QObject *parent) : QObject(parent), m_d(new Private(this)) { } KisGuidesManager::~KisGuidesManager() { } void KisGuidesManager::setGuidesConfig(const KisGuidesConfig &config) { if (config == m_d->guidesConfig) return; setGuidesConfigImpl(config, true); } void KisGuidesManager::slotDocumentRequestedConfig(const KisGuidesConfig &config) { if (config == m_d->guidesConfig) return; setGuidesConfigImpl(config, false); } void KisGuidesManager::setGuidesConfigImpl(const KisGuidesConfig &value, bool emitModified) { m_d->guidesConfig = value; if (m_d->decoration && value != m_d->decoration->guidesConfig()) { m_d->decoration->setVisible(value.showGuides()); m_d->decoration->setGuidesConfig(value); } KisDocument *doc = m_d->view ? m_d->view->document() : 0; if (doc) { KisSignalsBlocker b(doc); if (emitModified) { KUndo2Command *cmd = new KisChangeGuidesCommand(doc, value); doc->addCommand(cmd); } else { doc->setGuidesConfig(value); } value.saveStaticData(); } const bool shouldFilterEvent = value.showGuides() && !value.lockGuides() && value.hasGuides(); attachEventFilterImpl(shouldFilterEvent); syncActionsStatus(); if (!m_d->isGuideValid(m_d->currentGuide)) { m_d->updateSnappingStatus(value); } emit sigRequestUpdateGuidesConfig(m_d->guidesConfig); } void KisGuidesManager::attachEventFilterImpl(bool value) { if (!m_d->view) return; KisInputManager *inputManager = m_d->view->globalInputManager(); if (inputManager) { if (value) { inputManager->attachPriorityEventFilter(this, 100); } else { inputManager->detachPriorityEventFilter(this); } } } void KisGuidesManager::Private::syncAction(const QString &actionName, bool value) { KisActionManager *actionManager = view->viewManager()->actionManager(); KisAction *action = actionManager->actionByName(actionName); KIS_ASSERT_RECOVER_RETURN(action); KisSignalsBlocker b(action); action->setChecked(value); } void KisGuidesManager::syncActionsStatus() { if (!m_d->view) return; m_d->syncAction("view_show_guides", m_d->guidesConfig.showGuides()); m_d->syncAction("view_lock_guides", m_d->guidesConfig.lockGuides()); m_d->syncAction("view_snap_to_guides", m_d->guidesConfig.snapToGuides()); m_d->syncAction("view_snap_orthogonal", m_d->snapConfig.orthogonal()); m_d->syncAction("view_snap_node", m_d->snapConfig.node()); m_d->syncAction("view_snap_extension", m_d->snapConfig.extension()); m_d->syncAction("view_snap_intersection", m_d->snapConfig.intersection()); m_d->syncAction("view_snap_bounding_box", m_d->snapConfig.boundingBox()); m_d->syncAction("view_snap_image_bounds", m_d->snapConfig.imageBounds()); m_d->syncAction("view_snap_image_center", m_d->snapConfig.imageCenter()); } void KisGuidesManager::Private::updateSnappingStatus(const KisGuidesConfig &value) { if (!view) return; KoSnapGuide *snapGuide = view->canvasBase()->snapGuide(); KisSnapLineStrategy *guidesSnap = 0; if (value.snapToGuides()) { guidesSnap = new KisSnapLineStrategy(KoSnapGuide::GuideLineSnapping); guidesSnap->setHorizontalLines(value.horizontalGuideLines()); guidesSnap->setVerticalLines(value.verticalGuideLines()); } snapGuide->overrideSnapStrategy(KoSnapGuide::GuideLineSnapping, guidesSnap); snapGuide->enableSnapStrategy(KoSnapGuide::GuideLineSnapping, guidesSnap); snapGuide->enableSnapStrategy(KoSnapGuide::OrthogonalSnapping, snapConfig.orthogonal()); snapGuide->enableSnapStrategy(KoSnapGuide::NodeSnapping, snapConfig.node()); snapGuide->enableSnapStrategy(KoSnapGuide::ExtensionSnapping, snapConfig.extension()); snapGuide->enableSnapStrategy(KoSnapGuide::IntersectionSnapping, snapConfig.intersection()); snapGuide->enableSnapStrategy(KoSnapGuide::BoundingBoxSnapping, snapConfig.boundingBox()); snapGuide->enableSnapStrategy(KoSnapGuide::DocumentBoundsSnapping, snapConfig.imageBounds()); snapGuide->enableSnapStrategy(KoSnapGuide::DocumentCenterSnapping, snapConfig.imageCenter()); snapConfig.saveStaticData(); } bool KisGuidesManager::showGuides() const { return m_d->guidesConfig.showGuides(); } void KisGuidesManager::setShowGuides(bool value) { m_d->guidesConfig.setShowGuides(value); setGuidesConfigImpl(m_d->guidesConfig); } bool KisGuidesManager::lockGuides() const { return m_d->guidesConfig.lockGuides(); } void KisGuidesManager::setLockGuides(bool value) { m_d->guidesConfig.setLockGuides(value); setGuidesConfigImpl(m_d->guidesConfig); } bool KisGuidesManager::snapToGuides() const { return m_d->guidesConfig.snapToGuides(); } void KisGuidesManager::setSnapToGuides(bool value) { m_d->guidesConfig.setSnapToGuides(value); setGuidesConfigImpl(m_d->guidesConfig); } void KisGuidesManager::setup(KisActionManager *actionManager) { KisAction *action = 0; action = actionManager->createAction("view_show_guides"); connect(action, SIGNAL(toggled(bool)), this, SLOT(setShowGuides(bool))); action = actionManager->createAction("view_lock_guides"); connect(action, SIGNAL(toggled(bool)), this, SLOT(setLockGuides(bool))); action = actionManager->createAction("view_snap_to_guides"); connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapToGuides(bool))); action = actionManager->createAction("show_snap_options_popup"); connect(action, SIGNAL(triggered()), this, SLOT(slotShowSnapOptions())); action = actionManager->createAction("view_snap_orthogonal"); connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapOrthogonal(bool))); action = actionManager->createAction("view_snap_node"); connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapNode(bool))); action = actionManager->createAction("view_snap_extension"); connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapExtension(bool))); action = actionManager->createAction("view_snap_intersection"); connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapIntersection(bool))); action = actionManager->createAction("view_snap_bounding_box"); connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapBoundingBox(bool))); action = actionManager->createAction("view_snap_image_bounds"); connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapImageBounds(bool))); action = actionManager->createAction("view_snap_image_center"); connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapImageCenter(bool))); m_d->updateSnappingStatus(m_d->guidesConfig); syncActionsStatus(); } void KisGuidesManager::setView(QPointer view) { if (m_d->view) { KoSnapGuide *snapGuide = m_d->view->canvasBase()->snapGuide(); snapGuide->overrideSnapStrategy(KoSnapGuide::GuideLineSnapping, 0); snapGuide->enableSnapStrategy(KoSnapGuide::GuideLineSnapping, false); m_d->decoration = 0; m_d->viewConnections.clear(); attachEventFilterImpl(false); } m_d->view = view; if (m_d->view) { - KisGuidesDecoration* decoration = qobject_cast(m_d->view->canvasBase()->decoration(GUIDES_DECORATION_ID)); + KisGuidesDecoration* decoration = qobject_cast(m_d->view->canvasBase()->decoration(GUIDES_DECORATION_ID).data()); if (!decoration) { decoration = new KisGuidesDecoration(m_d->view); m_d->view->canvasBase()->addDecoration(decoration); } m_d->decoration = decoration; m_d->guidesConfig = m_d->view->document()->guidesConfig(); setGuidesConfigImpl(m_d->guidesConfig, false); m_d->viewConnections.addUniqueConnection( m_d->view->zoomManager()->horizontalRuler(), SIGNAL(guideCreationInProgress(Qt::Orientation, const QPoint&)), this, SLOT(slotGuideCreationInProgress(Qt::Orientation, const QPoint&))); m_d->viewConnections.addUniqueConnection( m_d->view->zoomManager()->horizontalRuler(), SIGNAL(guideCreationFinished(Qt::Orientation, const QPoint&)), this, SLOT(slotGuideCreationFinished(Qt::Orientation, const QPoint&))); m_d->viewConnections.addUniqueConnection( m_d->view->zoomManager()->verticalRuler(), SIGNAL(guideCreationInProgress(Qt::Orientation, const QPoint&)), this, SLOT(slotGuideCreationInProgress(Qt::Orientation, const QPoint&))); m_d->viewConnections.addUniqueConnection( m_d->view->zoomManager()->verticalRuler(), SIGNAL(guideCreationFinished(Qt::Orientation, const QPoint&)), this, SLOT(slotGuideCreationFinished(Qt::Orientation, const QPoint&))); m_d->viewConnections.addUniqueConnection( m_d->view->document(), SIGNAL(sigGuidesConfigChanged(const KisGuidesConfig &)), this, SLOT(slotDocumentRequestedConfig(const KisGuidesConfig &))); } } KisGuidesManager::Private::GuideHandle KisGuidesManager::Private::findGuide(const QPointF &docPos) { const int snapRadius = 16; GuideHandle nearestGuide = invalidGuide; qreal nearestRadius = std::numeric_limits::max(); for (int i = 0; i < guidesConfig.horizontalGuideLines().size(); i++) { const qreal guide = guidesConfig.horizontalGuideLines()[i]; const qreal radius = qAbs(docPos.y() - guide); if (radius < snapRadius && radius < nearestRadius) { nearestGuide = GuideHandle(Qt::Horizontal, i); nearestRadius = radius; } } for (int i = 0; i < guidesConfig.verticalGuideLines().size(); i++) { const qreal guide = guidesConfig.verticalGuideLines()[i]; const qreal radius = qAbs(docPos.x() - guide); if (radius < snapRadius && radius < nearestRadius) { nearestGuide = GuideHandle(Qt::Vertical, i); nearestRadius = radius; } } return nearestGuide; } bool KisGuidesManager::Private::isGuideValid(const GuideHandle &h) { return h.second >= 0; } qreal KisGuidesManager::Private::guideValue(const GuideHandle &h) { return h.first == Qt::Horizontal ? guidesConfig.horizontalGuideLines()[h.second] : guidesConfig.verticalGuideLines()[h.second]; } void KisGuidesManager::Private::setGuideValue(const GuideHandle &h, qreal value) { if (h.first == Qt::Horizontal) { QList guides = guidesConfig.horizontalGuideLines(); guides[h.second] = value; guidesConfig.setHorizontalGuideLines(guides); } else { QList guides = guidesConfig.verticalGuideLines(); guides[h.second] = value; guidesConfig.setVerticalGuideLines(guides); } } void KisGuidesManager::Private::deleteGuide(const GuideHandle &h) { if (h.first == Qt::Horizontal) { QList guides = guidesConfig.horizontalGuideLines(); guides.removeAt(h.second); guidesConfig.setHorizontalGuideLines(guides); } else { QList guides = guidesConfig.verticalGuideLines(); guides.removeAt(h.second); guidesConfig.setVerticalGuideLines(guides); } } bool KisGuidesManager::Private::updateCursor(const QPointF &docPos) { KisCanvas2 *canvas = view->canvasBase(); const GuideHandle guide = findGuide(docPos); const bool guideValid = isGuideValid(guide); if (guideValid && !cursorSwitched) { oldCursor = canvas->canvasWidget()->cursor(); } if (guideValid) { cursorSwitched = true; QCursor newCursor = guide.first == Qt::Horizontal ? Qt::SizeVerCursor : Qt::SizeHorCursor; canvas->canvasWidget()->setCursor(newCursor); } if (!guideValid && cursorSwitched) { canvas->canvasWidget()->setCursor(oldCursor); cursorSwitched = false; } return guideValid; } void KisGuidesManager::Private::initDragStart(const GuideHandle &guide, const QPointF &dragStart, qreal guideValue, bool snapToStart) { currentGuide = guide; dragStartDoc = dragStart; dragStartGuidePos = guideValue; dragPointerOffset = guide.first == Qt::Horizontal ? QPointF(0, dragStartGuidePos - dragStartDoc.y()) : QPointF(dragStartGuidePos - dragStartDoc.x(), 0); KoSnapGuide *snapGuide = view->canvasBase()->snapGuide(); snapGuide->reset(); if (snapToStart) { KisSnapLineStrategy *strategy = new KisSnapLineStrategy(); strategy->addLine(guide.first, guideValue); snapGuide->addCustomSnapStrategy(strategy); } } QPointF KisGuidesManager::Private::alignToPixels(const QPointF docPoint) { KisCanvas2 *canvas = view->canvasBase(); const KisCoordinatesConverter *converter = canvas->coordinatesConverter(); QPoint imagePoint = converter->documentToImage(docPoint).toPoint(); return converter->imageToDocument(imagePoint); } bool KisGuidesManager::Private::mouseMoveHandler(const QPointF &docPos, Qt::KeyboardModifiers modifiers) { if (isGuideValid(currentGuide)) { KoSnapGuide *snapGuide = view->canvasBase()->snapGuide(); const QPointF snappedPos = snapGuide->snap(docPos, dragPointerOffset, modifiers); const QPointF offset = snappedPos - dragStartDoc; const qreal newValue = dragStartGuidePos + (currentGuide.first == Qt::Horizontal ? offset.y() : offset.x()); setGuideValue(currentGuide, newValue); q->setGuidesConfigImpl(guidesConfig); } return updateCursor(docPos); } bool KisGuidesManager::Private::mouseReleaseHandler(const QPointF &docPos) { bool result = false; KisCanvas2 *canvas = view->canvasBase(); const KisCoordinatesConverter *converter = canvas->coordinatesConverter(); if (isGuideValid(currentGuide)) { const QRectF docRect = converter->imageRectInDocumentPixels(); // TODO: enable work rect after we fix painting guides // outside canvas in openGL mode const QRectF workRect = KisAlgebra2D::blowRect(docRect, 0 /*0.2*/); if (!workRect.contains(docPos)) { deleteGuide(currentGuide); q->setGuidesConfigImpl(guidesConfig); /** * When we delete a guide, it might happen that we are * delting the last guide. Therefore we should eat the * corresponding event so that the event filter would stop * the filter processing. */ result = true; } currentGuide = invalidGuide; dragStartDoc = QPointF(); dragPointerOffset = QPointF(); dragStartGuidePos = 0; KoSnapGuide *snapGuide = view->canvasBase()->snapGuide(); snapGuide->reset(); updateSnappingStatus(guidesConfig); } return updateCursor(docPos) | result; } QPointF KisGuidesManager::Private::getDocPointFromEvent(QEvent *event) { QPointF result; KisCanvas2 *canvas = view->canvasBase(); const KisCoordinatesConverter *converter = canvas->coordinatesConverter(); if (event->type() == QEvent::MouseMove || event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease) { QMouseEvent *mouseEvent = static_cast(event); result = alignToPixels(converter->widgetToDocument(mouseEvent->pos())); } else if (event->type() == QEvent::TabletMove || event->type() == QEvent::TabletPress || event->type() == QEvent::TabletRelease) { QTabletEvent *tabletEvent = static_cast(event); result = alignToPixels(converter->widgetToDocument(tabletEvent->pos())); } return result; } Qt::MouseButton KisGuidesManager::Private::getButtonFromEvent(QEvent *event) { Qt::MouseButton button = Qt::NoButton; if (event->type() == QEvent::MouseMove || event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease) { QMouseEvent *mouseEvent = static_cast(event); button = mouseEvent->button(); } else if (event->type() == QEvent::TabletMove || event->type() == QEvent::TabletPress || event->type() == QEvent::TabletRelease) { QTabletEvent *tabletEvent = static_cast(event); button = tabletEvent->button(); } return button; } bool KisGuidesManager::eventFilter(QObject *obj, QEvent *event) { if (!m_d->view || obj != m_d->view->canvasBase()->canvasWidget()) return false; bool retval = false; switch (event->type()) { case QEvent::Enter: case QEvent::Leave: case QEvent::TabletMove: case QEvent::MouseMove: { const QPointF docPos = m_d->getDocPointFromEvent(event); const Qt::KeyboardModifiers modifiers = qApp->keyboardModifiers(); retval = m_d->mouseMoveHandler(docPos, modifiers); break; } case QEvent::TabletPress: case QEvent::MouseButtonPress: { if (m_d->getButtonFromEvent(event) != Qt::LeftButton) break; const QPointF docPos = m_d->getDocPointFromEvent(event); const Private::GuideHandle guide = m_d->findGuide(docPos); const bool guideValid = m_d->isGuideValid(guide); if (guideValid) { m_d->initDragStart(guide, docPos, m_d->guideValue(guide), true); } retval = m_d->updateCursor(docPos); break; } case QEvent::TabletRelease: case QEvent::MouseButtonRelease: { if (m_d->getButtonFromEvent(event) != Qt::LeftButton) break; const QPointF docPos = m_d->getDocPointFromEvent(event); retval = m_d->mouseReleaseHandler(docPos); break; } default: break; } return !retval ? QObject::eventFilter(obj, event) : true; } void KisGuidesManager::slotGuideCreationInProgress(Qt::Orientation orientation, const QPoint &globalPos) { if (m_d->guidesConfig.lockGuides()) return; KisCanvas2 *canvas = m_d->view->canvasBase(); const KisCoordinatesConverter *converter = canvas->coordinatesConverter(); const QPointF widgetPos = canvas->canvasWidget()->mapFromGlobal(globalPos); const QPointF docPos = m_d->alignToPixels(converter->widgetToDocument(widgetPos)); if (m_d->isGuideValid(m_d->currentGuide)) { const Qt::KeyboardModifiers modifiers = qApp->keyboardModifiers(); m_d->mouseMoveHandler(docPos, modifiers); } else { m_d->guidesConfig.setShowGuides(true); if (orientation == Qt::Horizontal) { QList guides = m_d->guidesConfig.horizontalGuideLines(); guides.append(docPos.y()); m_d->currentGuide.first = orientation; m_d->currentGuide.second = guides.size() - 1; m_d->guidesConfig.setHorizontalGuideLines(guides); m_d->initDragStart(m_d->currentGuide, docPos, docPos.y(), false); } else { QList guides = m_d->guidesConfig.verticalGuideLines(); guides.append(docPos.x()); m_d->currentGuide.first = orientation; m_d->currentGuide.second = guides.size() - 1; m_d->guidesConfig.setVerticalGuideLines(guides); m_d->initDragStart(m_d->currentGuide, docPos, docPos.x(), false); } setGuidesConfigImpl(m_d->guidesConfig); } } void KisGuidesManager::slotGuideCreationFinished(Qt::Orientation orientation, const QPoint &globalPos) { Q_UNUSED(orientation); if (m_d->guidesConfig.lockGuides()) return; KisCanvas2 *canvas = m_d->view->canvasBase(); const KisCoordinatesConverter *converter = canvas->coordinatesConverter(); const QPointF widgetPos = canvas->canvasWidget()->mapFromGlobal(globalPos); const QPointF docPos = m_d->alignToPixels(converter->widgetToDocument(widgetPos)); m_d->mouseReleaseHandler(docPos); } QAction* KisGuidesManager::Private::createShortenedAction(const QString &text, const QString &parentId, QObject *parent) { KisActionManager *actionManager = view->viewManager()->actionManager(); QAction *action = 0; KisAction *parentAction = 0; action = new QAction(text, parent); action->setCheckable(true); parentAction = actionManager->actionByName(parentId); action->setChecked(parentAction->isChecked()); connect(action, SIGNAL(toggled(bool)), parentAction, SLOT(setChecked(bool))); return action; } void KisGuidesManager::slotShowSnapOptions() { const QPoint pos = QCursor::pos(); QMenu menu; menu.addSection(i18n("Snap to:")); menu.addAction(m_d->createShortenedAction(i18n("Grid"), "view_snap_to_grid", &menu)); menu.addAction(m_d->createShortenedAction(i18n("Guides"), "view_snap_to_guides", &menu)); menu.addAction(m_d->createShortenedAction(i18n("Orthogonal"), "view_snap_orthogonal", &menu)); menu.addAction(m_d->createShortenedAction(i18n("Node"), "view_snap_node", &menu)); menu.addAction(m_d->createShortenedAction(i18n("Extension"), "view_snap_extension", &menu)); menu.addAction(m_d->createShortenedAction(i18n("Intersection"), "view_snap_intersection", &menu)); menu.addAction(m_d->createShortenedAction(i18n("Bounding Box"), "view_snap_bounding_box", &menu)); menu.addAction(m_d->createShortenedAction(i18n("Image Bounds"), "view_snap_image_bounds", &menu)); menu.addAction(m_d->createShortenedAction(i18n("Image Center"), "view_snap_image_center", &menu)); menu.exec(pos); } void KisGuidesManager::setSnapOrthogonal(bool value) { m_d->snapConfig.setOrthogonal(value); m_d->updateSnappingStatus(m_d->guidesConfig); } void KisGuidesManager::setSnapNode(bool value) { m_d->snapConfig.setNode(value); m_d->updateSnappingStatus(m_d->guidesConfig); } void KisGuidesManager::setSnapExtension(bool value) { m_d->snapConfig.setExtension(value); m_d->updateSnappingStatus(m_d->guidesConfig); } void KisGuidesManager::setSnapIntersection(bool value) { m_d->snapConfig.setIntersection(value); m_d->updateSnappingStatus(m_d->guidesConfig); } void KisGuidesManager::setSnapBoundingBox(bool value) { m_d->snapConfig.setBoundingBox(value); m_d->updateSnappingStatus(m_d->guidesConfig); } void KisGuidesManager::setSnapImageBounds(bool value) { m_d->snapConfig.setImageBounds(value); m_d->updateSnappingStatus(m_d->guidesConfig); } void KisGuidesManager::setSnapImageCenter(bool value) { m_d->snapConfig.setImageCenter(value); m_d->updateSnappingStatus(m_d->guidesConfig); } diff --git a/libs/ui/canvas/kis_mirror_axis.cpp b/libs/ui/canvas/kis_mirror_axis.cpp index eaa9dcce97..468dbf4ad8 100644 --- a/libs/ui/canvas/kis_mirror_axis.cpp +++ b/libs/ui/canvas/kis_mirror_axis.cpp @@ -1,358 +1,359 @@ /* * Copyright (c) 2014 Arjen Hiemstra * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "kis_mirror_axis.h" #include "KoConfig.h" #include #include #include #include #include #include #include +#include "kis_canvas2.h" #include "kis_canvas_resource_provider.h" #include "KisViewManager.h" #include "KisView.h" #include "kis_image.h" #include "canvas/kis_canvas_controller.h" #include "input/kis_input_manager.h" #include "kis_algebra_2d.h" class KisMirrorAxis::Private { public: Private(KisMirrorAxis* qq) : q(qq) , resourceProvider(0) , mirrorHorizontal(false) , mirrorVertical(false) , handleSize(32.f) , xActive(false) , yActive(false) , horizontalHandlePosition(64.f) , verticalHandlePosition(64.f) , sideMargin(10.f) , minHandlePosition(10.f + 32.f) , horizontalContainsCursor(false) , verticalContainsCursor(false) , horizontalAxis(QLineF()) , verticalAxis(QLineF()) { } void setAxisPosition(float x, float y); void recomputeVisibleAxes(QRect viewport); KisMirrorAxis* q; KisCanvasResourceProvider* resourceProvider; bool mirrorHorizontal; bool mirrorVertical; float handleSize; QPixmap horizontalHandleIcon; QPixmap verticalHandleIcon; QPixmap horizontalIcon; QPixmap verticalIcon; QPointF axisPosition; QRectF horizontalHandle; QRectF verticalHandle; bool xActive; bool yActive; float horizontalHandlePosition; float verticalHandlePosition; float sideMargin; float minHandlePosition; bool horizontalContainsCursor; bool verticalContainsCursor; QLineF horizontalAxis; QLineF verticalAxis; }; KisMirrorAxis::KisMirrorAxis(KisCanvasResourceProvider* provider, QPointerparent) : KisCanvasDecoration("mirror_axis", parent) , d(new Private(this)) { d->resourceProvider = provider; connect(d->resourceProvider, SIGNAL(mirrorModeChanged()), SLOT(mirrorModeChanged())); d->mirrorHorizontal = d->resourceProvider->mirrorHorizontal(); d->mirrorVertical = d->resourceProvider->mirrorVertical(); d->horizontalIcon = KisIconUtils::loadIcon("mirrorAxis-HorizontalMove").pixmap(d->handleSize, QIcon::Normal, QIcon::On); d->verticalIcon = KisIconUtils::loadIcon("mirrorAxis-VerticalMove").pixmap(d->handleSize, QIcon::Normal, QIcon::On); d->horizontalHandleIcon = KisIconUtils::loadIcon("transform-move").pixmap(d->handleSize, QIcon::Normal, QIcon::On); d->verticalHandleIcon = KisIconUtils::loadIcon("transform-move").pixmap(d->handleSize, QIcon::Normal, QIcon::On); setVisible(d->mirrorHorizontal || d->mirrorVertical); int imageWidth = parent->canvasBase()->image()->width(); int imageHeight = parent->canvasBase()->image()->height(); QPointF point(imageWidth / 2, imageHeight / 2); d->resourceProvider->resourceManager()->setResource(KisCanvasResourceProvider::MirrorAxesCenter, point); } KisMirrorAxis::~KisMirrorAxis() { delete d; } float KisMirrorAxis::handleSize() const { return d->handleSize; } void KisMirrorAxis::setHandleSize(float newSize) { if(d->handleSize != newSize) { d->handleSize = newSize; d->horizontalIcon = KisIconUtils::loadIcon("symmetry-horyzontal").pixmap(d->handleSize, QIcon::Normal, QIcon::On); d->verticalIcon = KisIconUtils::loadIcon("symmetry-vertical").pixmap(d->handleSize, QIcon::Normal, QIcon::On); d->horizontalHandleIcon = KisIconUtils::loadIcon("transform-move").pixmap(d->handleSize, QIcon::Normal, QIcon::On); d->verticalHandleIcon = KisIconUtils::loadIcon("transform-move").pixmap(d->handleSize, QIcon::Normal, QIcon::On); d->minHandlePosition = d->sideMargin + newSize; emit handleSizeChanged(); } } void KisMirrorAxis::drawDecoration(QPainter& gc, const QRectF& updateArea, const KisCoordinatesConverter* converter, KisCanvas2* canvas) { Q_UNUSED(updateArea); Q_UNUSED(converter); Q_UNUSED(canvas); gc.setPen(QPen(QColor(0, 0, 0, 128), 1)); gc.setBrush(Qt::white); gc.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); QOpenGLContext *ctx = QOpenGLContext::currentContext(); bool hasMultisample = ((gc.paintEngine()->type() == QPaintEngine::OpenGL2) && (ctx->hasExtension("GL_ARB_multisample"))); // QPainter cannot anti-alias the edges of circles etc. when using OpenGL // So instead, use native OpenGL anti-aliasing when available. if (hasMultisample) { gc.beginNativePainting(); ctx->functions()->glEnable(GL_MULTISAMPLE); gc.endNativePainting(); } float halfHandleSize = d->handleSize / 2; d->recomputeVisibleAxes(gc.viewport()); if(d->mirrorHorizontal) { if (!d->horizontalAxis.isNull()) { // QPointF horizontalIndicatorCenter = d->horizontalAxis.unitVector().pointAt(15); // QRectF horizontalIndicator = QRectF(horizontalIndicatorCenter.x() - halfHandleSize, horizontalIndicatorCenter.y() - halfHandleSize, d->handleSize, d->handleSize); float horizontalHandlePosition = qBound(d->minHandlePosition, d->horizontalHandlePosition, d->horizontalAxis.length() - d->minHandlePosition); QPointF horizontalHandleCenter = d->horizontalAxis.unitVector().pointAt(horizontalHandlePosition); d->horizontalHandle = QRectF(horizontalHandleCenter.x() - halfHandleSize, horizontalHandleCenter.y() - halfHandleSize, d->handleSize, d->handleSize); gc.setPen(QPen(QColor(0, 0, 0, 64), 2, Qt::DashDotDotLine, Qt::RoundCap, Qt::RoundJoin)); gc.drawLine(d->horizontalAxis); // gc.drawEllipse(horizontalIndicator); // gc.drawPixmap(horizontalIndicator.adjusted(5, 5, -5, -5).toRect(), d->horizontalIcon); gc.setPen(QPen(QColor(0, 0, 0, 128), 2)); gc.drawEllipse(d->horizontalHandle); gc.drawPixmap(d->horizontalHandle.adjusted(5, 5, -5, -5).toRect(), d->horizontalIcon); } else { d->horizontalHandle = QRectF(); } } if(d->mirrorVertical) { if (!d->verticalAxis.isNull()) { gc.setPen(QPen(QColor(0, 0, 0, 64), 2, Qt::DashDotDotLine, Qt::RoundCap, Qt::RoundJoin)); gc.drawLine(d->verticalAxis); // QPointF verticalIndicatorCenter = d->verticalAxis.unitVector().pointAt(15); // QRectF verticalIndicator = QRectF(verticalIndicatorCenter.x() - halfHandleSize, verticalIndicatorCenter.y() - halfHandleSize, d->handleSize, d->handleSize); float verticalHandlePosition = qBound(d->minHandlePosition, d->verticalHandlePosition, d->verticalAxis.length() - d->minHandlePosition); QPointF verticalHandleCenter = d->verticalAxis.unitVector().pointAt(verticalHandlePosition); d->verticalHandle = QRectF(verticalHandleCenter.x() - halfHandleSize, verticalHandleCenter.y() - halfHandleSize, d->handleSize, d->handleSize); // gc.drawEllipse(verticalIndicator); // gc.drawPixmap(verticalIndicator.adjusted(5, 5, -5, -5).toRect(), d->verticalIcon); gc.setPen(QPen(QColor(0, 0, 0, 128), 2)); gc.drawEllipse(d->verticalHandle); gc.drawPixmap(d->verticalHandle.adjusted(5, 5, -5, -5).toRect(), d->verticalIcon); } else { d->verticalHandle = QRectF(); } } if (hasMultisample) { gc.beginNativePainting(); ctx->functions()->glDisable(GL_MULTISAMPLE); gc.endNativePainting(); } } bool KisMirrorAxis::eventFilter(QObject* target, QEvent* event) { if (!visible()) return false; QObject *expectedCanvasWidget = view() ? view()->canvasBase()->canvasWidget() : 0; if (!expectedCanvasWidget || target != expectedCanvasWidget) return false; if(event->type() == QEvent::MouseButtonPress || event->type() == QEvent::TabletPress) { QMouseEvent *me = dynamic_cast(event); QTabletEvent *te = dynamic_cast(event); QPoint pos = me ? me->pos() : (te ? te->pos() : QPoint(77,77)); if(d->mirrorHorizontal && d->horizontalHandle.contains(pos)) { d->xActive = true; QApplication::setOverrideCursor(Qt::ClosedHandCursor); event->accept(); return true; } if(d->mirrorVertical && d->verticalHandle.contains(pos)) { d->yActive = true; QApplication::setOverrideCursor(Qt::ClosedHandCursor); event->accept(); return true; } } if(event->type() == QEvent::MouseMove || event->type() == QEvent::TabletMove) { QMouseEvent *me = dynamic_cast(event); QTabletEvent *te = dynamic_cast(event); QPoint pos = me ? me->pos() : (te ? te->pos() : QPoint(77,77)); if(d->xActive) { float axisX = view()->viewConverter()->widgetToImage(pos).x(); d->setAxisPosition(axisX, d->axisPosition.y()); d->horizontalHandlePosition = KisAlgebra2D::dotProduct(pos - d->horizontalAxis.p1(), d->horizontalAxis.unitVector().p2() - d->horizontalAxis.p1()); event->accept(); return true; } if(d->yActive) { float axisY = view()->viewConverter()->widgetToImage(pos).y(); d->setAxisPosition(d->axisPosition.x(), axisY); d->verticalHandlePosition = KisAlgebra2D::dotProduct(pos - d->verticalAxis.p1(), d->verticalAxis.unitVector().p2() - d->verticalAxis.p1()); event->accept(); return true; } if(d->mirrorHorizontal) { if(d->horizontalHandle.contains(pos)) { if(!d->horizontalContainsCursor) { QApplication::setOverrideCursor(Qt::OpenHandCursor); d->horizontalContainsCursor = true; } } else if(d->horizontalContainsCursor) { QApplication::restoreOverrideCursor(); d->horizontalContainsCursor = false; } } if(d->mirrorVertical) { if(d->verticalHandle.contains(pos)) { if(!d->verticalContainsCursor) { QApplication::setOverrideCursor(Qt::OpenHandCursor); d->verticalContainsCursor = true; } } else if(d->verticalContainsCursor) { QApplication::restoreOverrideCursor(); d->verticalContainsCursor = false; } } } if(event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::TabletRelease) { if(d->xActive) { QApplication::restoreOverrideCursor(); d->xActive = false; event->accept(); return true; } if(d->yActive) { QApplication::restoreOverrideCursor(); d->yActive = false; event->accept(); return true; } } return QObject::eventFilter(target, event); } void KisMirrorAxis::mirrorModeChanged() { d->mirrorHorizontal = d->resourceProvider->mirrorHorizontal(); d->mirrorVertical = d->resourceProvider->mirrorVertical(); setVisible(d->mirrorHorizontal || d->mirrorVertical); } void KisMirrorAxis::setVisible(bool v) { KisCanvasDecoration::setVisible(v); KisInputManager *inputManager = view() ? view()->canvasBase()->globalInputManager() : 0; if (!inputManager) return; if (v) { inputManager->attachPriorityEventFilter(this); } else { inputManager->detachPriorityEventFilter(this); } } void KisMirrorAxis::Private::setAxisPosition(float x, float y) { QPointF newPosition = QPointF(x, y); resourceProvider->resourceManager()->setResource(KisCanvasResourceProvider::MirrorAxesCenter, newPosition); q->view()->canvasBase()->updateCanvas(); } void KisMirrorAxis::Private::recomputeVisibleAxes(QRect viewport) { KisCoordinatesConverter *converter = q->view()->viewConverter(); axisPosition = resourceProvider->resourceManager()->resource(KisCanvasResourceProvider::MirrorAxesCenter).toPointF(); QPointF samplePt1 = converter->imageToWidget(axisPosition); QPointF samplePt2 = converter->imageToWidget(QPointF(axisPosition.x(), axisPosition.y() - 100)); horizontalAxis = QLineF(samplePt1, samplePt2); if (!KisAlgebra2D::intersectLineRect(horizontalAxis, viewport)) horizontalAxis = QLineF(); samplePt2 = converter->imageToWidget(QPointF(axisPosition.x() - 100, axisPosition.y())); verticalAxis = QLineF(samplePt1, samplePt2); if (!KisAlgebra2D::intersectLineRect(verticalAxis, viewport)) verticalAxis = QLineF(); } diff --git a/libs/ui/canvas/kis_qpainter_canvas.cpp b/libs/ui/canvas/kis_qpainter_canvas.cpp index 9ad9f0d775..33ce919373 100644 --- a/libs/ui/canvas/kis_qpainter_canvas.cpp +++ b/libs/ui/canvas/kis_qpainter_canvas.cpp @@ -1,257 +1,261 @@ /* * Copyright (C) Boudewijn Rempt , (C) 2006 * Copyright (C) Lukas Tvrdy , (C) 2009 * * This program is free software; you can redistribute 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_qpainter_canvas.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_coordinates_converter.h" #include #include #include #include #include #include "KisViewManager.h" #include "kis_canvas2.h" #include "kis_prescaled_projection.h" #include "kis_config.h" #include "kis_canvas_resource_provider.h" #include "KisDocument.h" #include "kis_selection_manager.h" #include "kis_selection.h" #include "kis_canvas_updates_compressor.h" #include "kis_config_notifier.h" #include "kis_group_layer.h" #include "canvas/kis_display_color_converter.h" //#define DEBUG_REPAINT #include class KisQPainterCanvas::Private { public: KisPrescaledProjectionSP prescaledProjection; QBrush checkBrush; QImage buffer; }; KisQPainterCanvas::KisQPainterCanvas(KisCanvas2 *canvas, KisCoordinatesConverter *coordinatesConverter, QWidget * parent) : QWidget(parent) , KisCanvasWidgetBase(canvas, coordinatesConverter) , m_d(new Private()) { setAutoFillBackground(true); setAcceptDrops(true); setFocusPolicy(Qt::StrongFocus); setAttribute(Qt::WA_InputMethodEnabled, true); setAttribute(Qt::WA_StaticContents); setAttribute(Qt::WA_OpaquePaintEvent); - +#ifdef Q_OS_MAC + setAttribute(Qt::WA_AcceptTouchEvents, false); +#else + setAttribute(Qt::WA_AcceptTouchEvents, true); +#endif connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); slotConfigChanged(); } KisQPainterCanvas::~KisQPainterCanvas() { delete m_d; } void KisQPainterCanvas::setPrescaledProjection(KisPrescaledProjectionSP prescaledProjection) { m_d->prescaledProjection = prescaledProjection; } void KisQPainterCanvas::paintEvent(QPaintEvent * ev) { KisImageWSP image = canvas()->image(); if (image == 0) return; setAutoFillBackground(false); if (m_d->buffer.size() != size()) { m_d->buffer = QImage(size(), QImage::Format_ARGB32_Premultiplied); } QPainter gc(&m_d->buffer); // we double buffer, so we paint on an image first, then from the image onto the canvas, // so copy the clip region since otherwise we're filling the whole buffer every time with // the background color _and_ the transparent squares. gc.setClipRegion(ev->region()); KisCoordinatesConverter *converter = coordinatesConverter(); gc.save(); gc.setCompositionMode(QPainter::CompositionMode_Source); gc.fillRect(QRect(QPoint(0, 0), size()), borderColor()); QTransform checkersTransform; QPointF brushOrigin; QPolygonF polygon; converter->getQPainterCheckersInfo(&checkersTransform, &brushOrigin, &polygon); gc.setPen(Qt::NoPen); gc.setBrush(m_d->checkBrush); gc.setBrushOrigin(brushOrigin); gc.setTransform(checkersTransform); gc.drawPolygon(polygon); drawImage(gc, ev->rect()); gc.restore(); #ifdef DEBUG_REPAINT QColor color = QColor(random() % 255, random() % 255, random() % 255, 150); gc.fillRect(ev->rect(), color); #endif drawDecorations(gc, ev->rect()); gc.end(); QPainter painter(this); painter.drawImage(ev->rect(), m_d->buffer, ev->rect()); } void KisQPainterCanvas::drawImage(QPainter & gc, const QRect &updateWidgetRect) const { KisCoordinatesConverter *converter = coordinatesConverter(); QTransform imageTransform = converter->viewportToWidgetTransform(); gc.setTransform(imageTransform); gc.setRenderHint(QPainter::SmoothPixmapTransform, true); QRectF viewportRect = converter->widgetToViewport(updateWidgetRect); gc.setCompositionMode(QPainter::CompositionMode_SourceOver); gc.drawImage(viewportRect, m_d->prescaledProjection->prescaledQImage(), viewportRect); } QVariant KisQPainterCanvas::inputMethodQuery(Qt::InputMethodQuery query) const { return processInputMethodQuery(query); } void KisQPainterCanvas::inputMethodEvent(QInputMethodEvent *event) { processInputMethodEvent(event); } void KisQPainterCanvas::channelSelectionChanged(const QBitArray &channelFlags) { Q_ASSERT(m_d->prescaledProjection); m_d->prescaledProjection->setChannelFlags(channelFlags); } void KisQPainterCanvas::setDisplayProfile(KisDisplayColorConverter *colorConverter) { Q_ASSERT(m_d->prescaledProjection); m_d->prescaledProjection->setMonitorProfile(colorConverter->monitorProfile(), colorConverter->renderingIntent(), colorConverter->conversionFlags()); } void KisQPainterCanvas::setDisplayFilter(KisDisplayFilter* displayFilter) { Q_ASSERT(m_d->prescaledProjection); m_d->prescaledProjection->setDisplayFilter(displayFilter); canvas()->startUpdateInPatches(canvas()->image()->bounds()); } void KisQPainterCanvas::setWrapAroundViewingMode(bool value) { Q_UNUSED(value); dbgKrita << "Wrap around viewing mode not implemented in QPainter Canvas."; return; } void KisQPainterCanvas::finishResizingImage(qint32 w, qint32 h) { m_d->prescaledProjection->slotImageSizeChanged(w, h); } KisUpdateInfoSP KisQPainterCanvas::startUpdateCanvasProjection(const QRect & rc, const QBitArray &channelFlags) { Q_UNUSED(channelFlags); return m_d->prescaledProjection->updateCache(rc); } QRect KisQPainterCanvas::updateCanvasProjection(KisUpdateInfoSP info) { /** * It might happen that the canvas type is switched while the * update info is being stuck in the Qt's signals queue. Than a wrong * type of the info may come. So just check it here. */ bool isPPUpdateInfo = dynamic_cast(info.data()); if (isPPUpdateInfo) { m_d->prescaledProjection->recalculateCache(info); return info->dirtyViewportRect(); } else { return QRect(); } } void KisQPainterCanvas::resizeEvent(QResizeEvent *e) { QSize size(e->size()); if (size.width() <= 0) { size.setWidth(1); } if (size.height() <= 0) { size.setHeight(1); } coordinatesConverter()->setCanvasWidgetSize(size); m_d->prescaledProjection->notifyCanvasSizeChanged(size); } void KisQPainterCanvas::slotConfigChanged() { m_d->checkBrush = QBrush(createCheckersImage()); notifyConfigChanged(); } bool KisQPainterCanvas::callFocusNextPrevChild(bool next) { return focusNextPrevChild(next); } diff --git a/libs/ui/dialogs/kis_dlg_file_layer.cpp b/libs/ui/dialogs/kis_dlg_file_layer.cpp index b1452d56e5..3b5ee82a1a 100644 --- a/libs/ui/dialogs/kis_dlg_file_layer.cpp +++ b/libs/ui/dialogs/kis_dlg_file_layer.cpp @@ -1,94 +1,94 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2013 * * 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_dlg_file_layer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include KisDlgFileLayer::KisDlgFileLayer(const QString &basePath, const QString & name, QWidget * parent) : KoDialog(parent) , m_basePath(basePath) { setButtons(Ok | Cancel); setDefaultButton(Ok); QWidget * page = new QWidget(this); dlgWidget.setupUi(page); - dlgWidget.wdgUrlRequester->setMimeTypeFilters(KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Import)); + dlgWidget.wdgUrlRequester->setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import)); setMainWidget(page); //dlgWidget.wdgUrlRequester->setBasePath(m_basePath); dlgWidget.wdgUrlRequester->setStartDir(m_basePath); dlgWidget.txtLayerName->setText(name); connect(dlgWidget.wdgUrlRequester, SIGNAL(textChanged(const QString &)), SLOT(slotNameChanged(const QString &))); enableButtonOk(false); } void KisDlgFileLayer::slotNameChanged(const QString & text) { enableButtonOk(!text.isEmpty()); } QString KisDlgFileLayer::layerName() const { return dlgWidget.txtLayerName->text(); } KisFileLayer::ScalingMethod KisDlgFileLayer::scaleToImageResolution() const { if (dlgWidget.radioDontScale->isChecked()) { return KisFileLayer::None; } else if (dlgWidget.radioScaleToImageSize->isChecked()) { return KisFileLayer::ToImageSize; } else { return KisFileLayer::ToImagePPI; } } QString KisDlgFileLayer::fileName() const { QString path = dlgWidget.wdgUrlRequester->fileName(); if (!m_basePath.isEmpty() && QFileInfo(path).isAbsolute()) { QDir directory(m_basePath); path = directory.relativeFilePath(path); } return path; } diff --git a/libs/ui/dialogs/kis_dlg_image_properties.cc b/libs/ui/dialogs/kis_dlg_image_properties.cc index f30b41c994..8a795e55d9 100644 --- a/libs/ui/dialogs/kis_dlg_image_properties.cc +++ b/libs/ui/dialogs/kis_dlg_image_properties.cc @@ -1,144 +1,132 @@ /* * Copyright (c) 2004 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_dlg_image_properties.h" #include #include #include #include #include #include #include #include #include #include #include #include "KoColorProfile.h" #include "KoColor.h" #include "KoColorPopupAction.h" #include "kis_icon_utils.h" #include "KoID.h" #include "kis_image.h" #include "kis_annotation.h" #include "kis_config.h" #include "kis_signal_compressor.h" -#include "kis_processing_applicator.h" -#include "commands_new/kis_change_projection_color_command.h" #include "widgets/kis_cmb_idlist.h" #include "widgets/squeezedcombobox.h" +#include "kis_layer_utils.h" KisDlgImageProperties::KisDlgImageProperties(KisImageWSP image, QWidget *parent, const char *name) : KoDialog(parent) { setButtons(Ok | Cancel); setDefaultButton(Ok); setObjectName(name); setCaption(i18n("Image Properties")); m_page = new WdgImageProperties(this); m_image = image; setMainWidget(m_page); resize(m_page->sizeHint()); KisConfig cfg; m_page->lblWidthValue->setText(QString::number(image->width())); m_page->lblHeightValue->setText(QString::number(image->height())); m_page->lblResolutionValue->setText(QLocale().toString(image->xRes()*72, 2)); // XXX: separate values for x & y? m_defaultColorAction = new KoColorPopupAction(this); m_defaultColorAction->setCurrentColor(m_image->defaultProjectionColor()); m_defaultColorAction->setIcon(KisIconUtils::loadIcon("format-stroke-color")); m_defaultColorAction->setToolTip(i18n("Change the background color of the image")); m_page->bnBackgroundColor->setDefaultAction(m_defaultColorAction); KisSignalCompressor *compressor = new KisSignalCompressor(500 /* ms */, KisSignalCompressor::POSTPONE, this); connect(m_defaultColorAction, SIGNAL(colorChanged(const KoColor&)), compressor, SLOT(start())); connect(compressor, SIGNAL(timeout()), this, SLOT(setCurrentColor())); connect(m_defaultColorAction, SIGNAL(colorChanged(const KoColor&)), this, SLOT(setCurrentColor())); m_page->colorSpaceSelector->setCurrentColorSpace(image->colorSpace()); vKisAnnotationSP_it beginIt = image->beginAnnotations(); vKisAnnotationSP_it endIt = image->endAnnotations(); vKisAnnotationSP_it it = beginIt; while (it != endIt) { if (!(*it) || (*it)->type().isEmpty()) { dbgFile << "Warning: empty annotation"; it++; continue; } m_page->cmbAnnotations->addItem((*it) -> type()); it++; } connect(m_page->cmbAnnotations, SIGNAL(activated(QString)), SLOT(setAnnotation(QString))); setAnnotation(m_page->cmbAnnotations->currentText()); } KisDlgImageProperties::~KisDlgImageProperties() { delete m_page; } const KoColorSpace * KisDlgImageProperties::colorSpace() { return m_page->colorSpaceSelector->currentColorSpace(); } void KisDlgImageProperties::setCurrentColor() { - KisImageSignalVector emitSignals; - emitSignals << ModifiedSignal; - - KisProcessingApplicator applicator(m_image, - m_image->root(), - KisProcessingApplicator::RECURSIVE, - emitSignals, - kundo2_i18n("Change projection color"), - 0, - 142857 + 1); - applicator.applyCommand(new KisChangeProjectionColorCommand(m_image, m_defaultColorAction->currentKoColor())); - applicator.end(); + KisLayerUtils::changeImageDefaultProjectionColor(m_image, m_defaultColorAction->currentKoColor()); } void KisDlgImageProperties::setAnnotation(const QString &type) { KisAnnotationSP annotation = m_image->annotation(type); if (annotation) { m_page->lblDescription->clear(); m_page->txtAnnotation->clear(); m_page->lblDescription->setText(annotation->description()); m_page->txtAnnotation->appendPlainText(annotation->displayText()); } else { m_page->lblDescription->clear(); m_page->txtAnnotation->clear(); } } diff --git a/libs/ui/dialogs/kis_dlg_layer_properties.cc b/libs/ui/dialogs/kis_dlg_layer_properties.cc index 5c0343a830..32d387c2ff 100644 --- a/libs/ui/dialogs/kis_dlg_layer_properties.cc +++ b/libs/ui/dialogs/kis_dlg_layer_properties.cc @@ -1,292 +1,296 @@ /* * Copyright (c) 2005 Boudewijn Rempt * Copyright (c) 2011 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_dlg_layer_properties.h" #include #include #include #include #include #include #include #include #include "KisViewManager.h" #include #include #include "widgets/kis_cmb_composite.h" #include "KoColorProfile.h" #include "kis_multinode_property.h" #include "kis_layer_utils.h" #include "kis_image.h" #include "kis_layer_properties_icons.h" #include "kis_signal_compressor.h" #include "commands_new/kis_saved_commands.h" #include "kis_post_execution_undo_adapter.h" struct KisDlgLayerProperties::Private { Private() : updatesCompressor(500, KisSignalCompressor::POSTPONE) {} KisNodeList nodes; const KoColorSpace *colorSpace; KisViewManager *view; WdgLayerProperties *page; QSharedPointer compositeOpProperty; QSharedPointer opacityProperty; QSharedPointer nameProperty; QSharedPointer colorLabelProperty; QList layerProperties; QList > layerPropCheckboxes; QList channelFlagsProps; QList > channelFlagsCheckboxes; KisSignalCompressor updatesCompressor; QList allProperties() const { QList props; props << compositeOpProperty; props << opacityProperty; props << nameProperty; props << layerProperties; props << channelFlagsProps; props << colorLabelProperty; return props; } }; KisDlgLayerProperties::KisDlgLayerProperties(KisNodeList nodes, KisViewManager *view, QWidget *parent, const char *name, Qt::WFlags f) : KoDialog(parent) , d(new Private()) { nodes = KisLayerUtils::sortMergableNodes(view->image()->root(), nodes); d->nodes = nodes; Q_UNUSED(f); setCaption(i18n("Layer Properties")); setButtons(Ok | Cancel); setDefaultButton(Ok); setModal(false); setObjectName(name); d->page = new WdgLayerProperties(this); setMainWidget(d->page); d->view = view; d->colorSpace = d->nodes.first()->colorSpace(); d->page->editName->setFocus(); d->nameProperty.reset(new KisMultinodeNameProperty(nodes)); d->nameProperty->connectIgnoreCheckBox(d->page->chkName); + d->nameProperty->connectAutoEnableWidget(d->page->editName); d->nameProperty->connectValueChangedSignal(this, SLOT(slotNameValueChangedInternally())); connect(d->page->editName, SIGNAL(textChanged(const QString &)), SLOT(slotNameValueChangedExternally())); d->page->intOpacity->setRange(0, 100); d->page->intOpacity->setSuffix("%"); d->opacityProperty.reset(new KisMultinodeOpacityProperty(nodes)); d->opacityProperty->connectIgnoreCheckBox(d->page->chkOpacity); + d->opacityProperty->connectAutoEnableWidget(d->page->intOpacity); d->opacityProperty->connectValueChangedSignal(this, SLOT(slotOpacityValueChangedInternally())); d->opacityProperty->connectValueChangedSignal(&d->updatesCompressor, SLOT(start())); connect(d->page->intOpacity, SIGNAL(valueChanged(int)), SLOT(slotOpacityValueChangedExternally())); d->compositeOpProperty.reset(new KisMultinodeCompositeOpProperty(nodes)); d->compositeOpProperty->connectIgnoreCheckBox(d->page->chkCompositeOp); + d->compositeOpProperty->connectAutoEnableWidget(d->page->cmbComposite); d->compositeOpProperty->connectValueChangedSignal(this, SLOT(slotCompositeOpValueChangedInternally())); d->compositeOpProperty->connectValueChangedSignal(&d->updatesCompressor, SLOT(start())); connect(d->page->cmbComposite, SIGNAL(currentIndexChanged(int)), SLOT(slotCompositeOpValueChangedExternally())); d->page->colorLabelSelector->setFocusPolicy(Qt::StrongFocus); d->colorLabelProperty.reset(new KisMultinodeColorLabelProperty(nodes)); d->colorLabelProperty->connectIgnoreCheckBox(d->page->chkColorLabel); + d->colorLabelProperty->connectAutoEnableWidget(d->page->colorLabelSelector); d->colorLabelProperty->connectValueChangedSignal(this, SLOT(slotColorLabelValueChangedInternally())); d->colorLabelProperty->connectValueChangedSignal(&d->updatesCompressor, SLOT(start())); connect(d->page->colorLabelSelector, SIGNAL(currentIndexChanged(int)), SLOT(slotColorLabelValueChangedExternally())); if (!KisLayerUtils::checkNodesDiffer(d->nodes, [](KisNodeSP node) { return node->colorSpace(); })) { d->page->lblColorSpace->setText(d->colorSpace->name()); if (const KoColorProfile* profile = d->colorSpace->profile()) { d->page->lblProfile->setText(profile->name()); } ChannelFlagAdapter::PropertyList props = ChannelFlagAdapter::adaptersList(nodes); if (!props.isEmpty()) { QVBoxLayout *vbox = new QVBoxLayout; Q_FOREACH (const ChannelFlagAdapter::Property &prop, props) { QCheckBox *chk = new QCheckBox(prop.name, this); vbox->addWidget(chk); KisMultinodePropertyInterface *multiprop = new KisMultinodeProperty( nodes, ChannelFlagAdapter(prop)); multiprop->connectIgnoreCheckBox(chk); multiprop->connectValueChangedSignal(this, SLOT(slotFlagsValueChangedInternally())); multiprop->connectValueChangedSignal(&d->updatesCompressor, SLOT(start())); d->channelFlagsCheckboxes << chk; d->channelFlagsProps << toQShared(multiprop); } d->page->grpActiveChannels->setLayout(vbox); } else { d->page->grpActiveChannels->setVisible(false); d->page->lineActiveChannels->setVisible(false); } } else { d->page->grpActiveChannels->setVisible(false); d->page->lineActiveChannels->setVisible(false); d->page->cmbComposite->setEnabled(false); d->page->chkCompositeOp->setEnabled(false); d->page->lblColorSpace->setText(i18n("*varies*")); d->page->lblProfile->setText(i18n("*varies*")); } { QVBoxLayout *vbox = new QVBoxLayout; KisBaseNode::PropertyList props = LayerPropertyAdapter::adaptersList(nodes); Q_FOREACH (const KisBaseNode::Property &prop, props) { QCheckBox *chk = new QCheckBox(prop.name, this); chk->setIcon(prop.onIcon); vbox->addWidget(chk); KisMultinodePropertyInterface *multiprop = new KisMultinodeProperty( nodes, LayerPropertyAdapter(prop.name)); multiprop->connectIgnoreCheckBox(chk); multiprop->connectValueChangedSignal(this, SLOT(slotPropertyValueChangedInternally())); multiprop->connectValueChangedSignal(&d->updatesCompressor, SLOT(start())); d->layerPropCheckboxes << chk; d->layerProperties << toQShared(multiprop); } d->page->grpProperties->setLayout(vbox); } connect(&d->updatesCompressor, SIGNAL(timeout()), SLOT(updatePreview())); } KisDlgLayerProperties::~KisDlgLayerProperties() { if (result() == QDialog::Accepted) { if (d->updatesCompressor.isActive()) { d->updatesCompressor.stop(); updatePreview(); } KisPostExecutionUndoAdapter *adapter = d->view->image()->postExecutionUndoAdapter(); KisSavedMacroCommand *macro = adapter->createMacro(kundo2_i18n("Change Layer Properties")); macro->addCommand(toQShared(new KisLayerUtils::KisSimpleUpdateCommand(d->nodes, false))); Q_FOREACH(auto prop, d->allProperties()) { if (!prop->isIgnored()) { macro->addCommand(toQShared(prop->createPostExecutionUndoCommand())); } } macro->addCommand(toQShared(new KisLayerUtils::KisSimpleUpdateCommand(d->nodes, true))); adapter->addMacro(macro); } else /* if (result() == QDialog::Rejected) */ { Q_FOREACH(auto prop, d->allProperties()) { prop->setIgnored(true); } updatePreview(); } } void KisDlgLayerProperties::slotCompositeOpValueChangedInternally() { d->page->cmbComposite->validate(d->colorSpace); d->page->cmbComposite->selectCompositeOp(KoID(d->compositeOpProperty->value())); d->page->cmbComposite->setEnabled(!d->compositeOpProperty->isIgnored()); } void KisDlgLayerProperties::slotCompositeOpValueChangedExternally() { if (d->compositeOpProperty->isIgnored()) return; d->compositeOpProperty->setValue(d->page->cmbComposite->selectedCompositeOp().id()); } void KisDlgLayerProperties::slotColorLabelValueChangedInternally() { d->page->colorLabelSelector->setCurrentIndex(d->colorLabelProperty->value()); d->page->colorLabelSelector->setEnabled(!d->colorLabelProperty->isIgnored()); } void KisDlgLayerProperties::slotColorLabelValueChangedExternally() { if (d->colorLabelProperty->isIgnored()) return; d->colorLabelProperty->setValue(d->page->colorLabelSelector->currentIndex()); } void KisDlgLayerProperties::slotOpacityValueChangedInternally() { d->page->intOpacity->setValue(d->opacityProperty->value()); d->page->intOpacity->setEnabled(!d->opacityProperty->isIgnored()); } void KisDlgLayerProperties::slotOpacityValueChangedExternally() { if (d->opacityProperty->isIgnored()) return; d->opacityProperty->setValue(d->page->intOpacity->value()); } void KisDlgLayerProperties::slotNameValueChangedInternally() { d->page->editName->setText(d->nameProperty->value()); d->page->editName->setEnabled(!d->nameProperty->isIgnored()); } void KisDlgLayerProperties::slotNameValueChangedExternally() { if (d->nameProperty->isIgnored()) return; d->nameProperty->setValue(d->page->editName->text()); } void KisDlgLayerProperties::slotPropertyValueChangedInternally() { Q_FOREACH (KisMultinodePropertyInterfaceSP prop, d->channelFlagsProps) { prop->rereadCurrentValue(); } } void KisDlgLayerProperties::slotFlagsValueChangedInternally() { Q_FOREACH (KisMultinodePropertyInterfaceSP prop, d->layerProperties) { prop->rereadCurrentValue(); } } void KisDlgLayerProperties::updatePreview() { KisLayerUtils::KisSimpleUpdateCommand::updateNodes(d->nodes); } diff --git a/libs/ui/dialogs/kis_dlg_preferences.cc b/libs/ui/dialogs/kis_dlg_preferences.cc index c9894540b3..841a6e6932 100644 --- a/libs/ui/dialogs/kis_dlg_preferences.cc +++ b/libs/ui/dialogs/kis_dlg_preferences.cc @@ -1,1055 +1,1052 @@ /* * preferencesdlg.cc - part of KImageShop * * Copyright (c) 1999 Michael Koch * Copyright (c) 2003-2011 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_dlg_preferences.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoID.h" #include #include #include #include #include #include "kis_action_registry.h" #include "widgets/squeezedcombobox.h" #include "kis_clipboard.h" #include "widgets/kis_cmb_idlist.h" #include "KoColorSpace.h" #include "KoColorSpaceRegistry.h" #include "kis_cursor.h" #include "kis_config.h" #include "kis_canvas_resource_provider.h" #include "kis_preference_set_registry.h" #include "kis_color_manager.h" #include "slider_and_spin_box_sync.h" // for the performance update #include #include "input/config/kis_input_configuration_page.h" GeneralTab::GeneralTab(QWidget *_parent, const char *_name) : WdgGeneralSettings(_parent, _name) { KisConfig cfg; m_cmbCursorShape->addItem(i18n("No Cursor")); m_cmbCursorShape->addItem(i18n("Tool Icon")); m_cmbCursorShape->addItem(i18n("Arrow")); m_cmbCursorShape->addItem(i18n("Small Circle")); m_cmbCursorShape->addItem(i18n("Crosshair")); m_cmbCursorShape->addItem(i18n("Triangle Righthanded")); m_cmbCursorShape->addItem(i18n("Triangle Lefthanded")); m_cmbCursorShape->addItem(i18n("Black Pixel")); m_cmbCursorShape->addItem(i18n("White Pixel")); m_cmbOutlineShape->addItem(i18n("No Outline")); m_cmbOutlineShape->addItem(i18n("Circle Outline")); m_cmbOutlineShape->addItem(i18n("Preview Outline")); m_cmbOutlineShape->addItem(i18n("Tilt Outline")); m_cmbCursorShape->setCurrentIndex(cfg.newCursorStyle()); m_cmbOutlineShape->setCurrentIndex(cfg.newOutlineStyle()); chkShowRootLayer->setChecked(cfg.showRootLayer()); int autosaveInterval = cfg.autoSaveInterval(); //convert to minutes m_autosaveSpinBox->setValue(autosaveInterval / 60); m_autosaveCheckBox->setChecked(autosaveInterval > 0); m_undoStackSize->setValue(cfg.undoStackLimit()); m_backupFileCheckBox->setChecked(cfg.backupFile()); m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting()); m_hideSplashScreen->setChecked(cfg.hideSplashScreen()); m_cmbMDIType->setCurrentIndex(cfg.readEntry("mdi_viewmode", (int)QMdiArea::TabbedView)); m_chkRubberBand->setChecked(cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); m_favoritePresetsSpinBox->setValue(cfg.favoritePresets()); m_mdiColor->setColor(cfg.getMDIBackgroundColor()); m_backgroundimage->setText(cfg.getMDIBackgroundImage()); m_chkCanvasMessages->setChecked(cfg.showCanvasMessages()); m_chkCompressKra->setChecked(cfg.compressKra()); m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker()); m_chkSwitchSelectionCtrlAlt->setChecked(cfg.switchSelectionCtrlAlt()); m_chkConvertOnImport->setChecked(cfg.convertToImageColorspaceOnImport()); connect(m_bnFileName, SIGNAL(clicked()), SLOT(getBackgroundImage())); connect(clearBgImageButton, SIGNAL(clicked()), SLOT(clearBackgroundImage())); } void GeneralTab::setDefault() { KisConfig cfg; m_cmbCursorShape->setCurrentIndex(cfg.newCursorStyle(true)); m_cmbOutlineShape->setCurrentIndex(cfg.newOutlineStyle(true)); chkShowRootLayer->setChecked(cfg.showRootLayer(true)); m_autosaveCheckBox->setChecked(cfg.autoSaveInterval(true) > 0); //convert to minutes m_autosaveSpinBox->setValue(cfg.autoSaveInterval(true) / 60); m_undoStackSize->setValue(cfg.undoStackLimit(true)); m_backupFileCheckBox->setChecked(cfg.backupFile(true)); m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting(true)); m_hideSplashScreen->setChecked(cfg.hideSplashScreen(true)); m_cmbMDIType->setCurrentIndex((int)QMdiArea::TabbedView); m_chkRubberBand->setChecked(cfg.useOpenGL(true)); m_favoritePresetsSpinBox->setValue(cfg.favoritePresets(true)); m_mdiColor->setColor(cfg.getMDIBackgroundColor(true)); m_backgroundimage->setText(cfg.getMDIBackgroundImage(true)); m_chkCanvasMessages->setChecked(cfg.showCanvasMessages(true)); m_chkCompressKra->setChecked(cfg.compressKra(true)); m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker(true)); m_chkSwitchSelectionCtrlAlt->setChecked(cfg.switchSelectionCtrlAlt(true)); m_chkConvertOnImport->setChecked(cfg.convertToImageColorspaceOnImport(true)); } CursorStyle GeneralTab::cursorStyle() { return (CursorStyle)m_cmbCursorShape->currentIndex(); } OutlineStyle GeneralTab::outlineStyle() { return (OutlineStyle)m_cmbOutlineShape->currentIndex(); } bool GeneralTab::showRootLayer() { return chkShowRootLayer->isChecked(); } int GeneralTab::autoSaveInterval() { //convert to seconds return m_autosaveCheckBox->isChecked() ? m_autosaveSpinBox->value()*60 : 0; } int GeneralTab::undoStackSize() { return m_undoStackSize->value(); } bool GeneralTab::showOutlineWhilePainting() { return m_showOutlinePainting->isChecked(); } bool GeneralTab::hideSplashScreen() { return m_hideSplashScreen->isChecked(); } int GeneralTab::mdiMode() { return m_cmbMDIType->currentIndex(); } int GeneralTab::favoritePresets() { return m_favoritePresetsSpinBox->value(); } bool GeneralTab::showCanvasMessages() { return m_chkCanvasMessages->isChecked(); } bool GeneralTab::compressKra() { return m_chkCompressKra->isChecked(); } bool GeneralTab::toolOptionsInDocker() { return m_radioToolOptionsInDocker->isChecked(); } bool GeneralTab::switchSelectionCtrlAlt() { return m_chkSwitchSelectionCtrlAlt->isChecked(); } bool GeneralTab::convertToImageColorspaceOnImport() { return m_chkConvertOnImport->isChecked(); } void GeneralTab::getBackgroundImage() { KoFileDialog dialog(this, KoFileDialog::OpenFile, "BackgroundImages"); dialog.setCaption(i18n("Select a Background Image")); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); dialog.setImageFilters(); QString fn = dialog.filename(); // dialog box was canceled or somehow no file was selected if (fn.isEmpty()) { return; } QImage image(fn); if (image.isNull()) { QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("%1 is not a valid image file!", fn)); } else { m_backgroundimage->setText(fn); } } void GeneralTab::clearBackgroundImage() { // clearing the background image text will implicitly make the background color be used m_backgroundimage->setText(""); } ShortcutSettingsTab::ShortcutSettingsTab(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(name); QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgShortcutSettings(this); l->addWidget(m_page, 0, 0); KisPart::instance()->loadActions(); KisActionRegistry::instance()->setupDialog(m_page); } void ShortcutSettingsTab::setDefault() { m_page->allDefault(); } void ShortcutSettingsTab::saveChanges() { m_page->save(); KisActionRegistry::instance()->settingsPageSaved(); } void ShortcutSettingsTab::revertChanges() { m_page->allDefault(); } ColorSettingsTab::ColorSettingsTab(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(name); // XXX: Make sure only profiles that fit the specified color model // are shown in the profile combos QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgColorSettings(this); l->addWidget(m_page, 0, 0); KisConfig cfg; m_page->chkUseSystemMonitorProfile->setChecked(cfg.useSystemMonitorProfile()); connect(m_page->chkUseSystemMonitorProfile, SIGNAL(toggled(bool)), this, SLOT(toggleAllowMonitorProfileSelection(bool))); // XXX: no color management integration on Windows or OSX yet #ifndef HAVE_X11 m_page->chkUseSystemMonitorProfile->setVisible(false); #endif m_page->cmbWorkingColorSpace->setIDList(KoColorSpaceRegistry::instance()->listKeys()); m_page->cmbWorkingColorSpace->setCurrent(cfg.workingColorSpace()); m_page->cmbPrintingColorSpace->setIDList(KoColorSpaceRegistry::instance()->listKeys()); m_page->cmbPrintingColorSpace->setCurrent(cfg.printerColorSpace()); m_page->bnAddColorProfile->setIcon(KisIconUtils::loadIcon("document-open")); m_page->bnAddColorProfile->setToolTip( i18n("Open Color Profile") ); connect(m_page->bnAddColorProfile, SIGNAL(clicked()), SLOT(installProfile())); refillPrintProfiles(KoID(cfg.printerColorSpace(), "")); //hide printing settings m_page->groupBox2->hide(); QGridLayout *monitorProfileGrid = new QGridLayout(m_page->monitorprofileholder); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { QLabel *lbl = new QLabel(i18nc("The number of the screen", "Screen %1:", i + 1)); monitorProfileGrid->addWidget(lbl, i, 0); m_monitorProfileLabels << lbl; SqueezedComboBox *cmb = new SqueezedComboBox(); monitorProfileGrid->addWidget(cmb, i, 1); m_monitorProfileWidgets << cmb; } refillMonitorProfiles(KoID("RGBA", "")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } if (m_page->cmbPrintProfile->contains(cfg.printerProfile())) m_page->cmbPrintProfile->setCurrentIndex(m_page->cmbPrintProfile->findText(cfg.printerProfile())); m_page->chkBlackpoint->setChecked(cfg.useBlackPointCompensation()); m_page->chkAllowLCMSOptimization->setChecked(cfg.allowLCMSOptimization()); m_pasteBehaviourGroup.addButton(m_page->radioPasteWeb, PASTE_ASSUME_WEB); m_pasteBehaviourGroup.addButton(m_page->radioPasteMonitor, PASTE_ASSUME_MONITOR); m_pasteBehaviourGroup.addButton(m_page->radioPasteAsk, PASTE_ASK); QAbstractButton *button = m_pasteBehaviourGroup.button(cfg.pasteBehaviour()); Q_ASSERT(button); if (button) { button->setChecked(true); } m_page->cmbMonitorIntent->setCurrentIndex(cfg.monitorRenderIntent()); toggleAllowMonitorProfileSelection(cfg.useSystemMonitorProfile()); connect(m_page->cmbPrintingColorSpace, SIGNAL(activated(const KoID &)), this, SLOT(refillPrintProfiles(const KoID &))); } void ColorSettingsTab::installProfile() { KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocumentICC"); dialog.setCaption(i18n("Install Color Profiles")); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::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()); } KisConfig cfg; refillMonitorProfiles(KoID("RGBA", "")); refillPrintProfiles(KoID(cfg.printerColorSpace(), "")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } if (m_page->cmbPrintProfile->contains(cfg.printerProfile())) m_page->cmbPrintProfile->setCurrentIndex(m_page->cmbPrintProfile->findText(cfg.printerProfile())); } void ColorSettingsTab::toggleAllowMonitorProfileSelection(bool useSystemProfile) { if (useSystemProfile) { KisConfig cfg; QStringList devices = KisColorManager::instance()->devices(); if (devices.size() == QApplication::desktop()->screenCount()) { for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileWidgets[i]->clear(); QString monitorForScreen = cfg.monitorForScreen(i, devices[i]); Q_FOREACH (const QString &device, devices) { m_monitorProfileLabels[i]->setText(i18nc("The display/screen we got from Qt", "Screen %1:", i + 1)); m_monitorProfileWidgets[i]->addSqueezedItem(KisColorManager::instance()->deviceName(device), device); if (devices[i] == monitorForScreen) { m_monitorProfileWidgets[i]->setCurrentIndex(i); } } } } } else { KisConfig cfg; refillMonitorProfiles(KoID("RGBA", "")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } } } void ColorSettingsTab::setDefault() { m_page->cmbWorkingColorSpace->setCurrent("RGBA"); m_page->cmbPrintingColorSpace->setCurrent("CMYK"); refillPrintProfiles(KoID("CMYK", "")); refillMonitorProfiles(KoID("RGBA", "")); KisConfig cfg; m_page->chkBlackpoint->setChecked(cfg.useBlackPointCompensation(true)); m_page->chkAllowLCMSOptimization->setChecked(cfg.allowLCMSOptimization(true)); m_page->cmbMonitorIntent->setCurrentIndex(cfg.monitorRenderIntent(true)); m_page->chkUseSystemMonitorProfile->setChecked(cfg.useSystemMonitorProfile(true)); QAbstractButton *button = m_pasteBehaviourGroup.button(cfg.pasteBehaviour(true)); Q_ASSERT(button); if (button) { button->setChecked(true); } } void ColorSettingsTab::refillMonitorProfiles(const KoID & s) { const KoColorSpaceFactory * csf = KoColorSpaceRegistry::instance()->colorSpaceFactory(s.id()); for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileWidgets[i]->clear(); } if (!csf) return; QList profileList = KoColorSpaceRegistry::instance()->profilesFor(csf); Q_FOREACH (const KoColorProfile *profile, profileList) { // //dbgKrita << "Profile" << profile->name() << profile->isSuitableForDisplay() << csf->defaultProfile(); if (profile->isSuitableForDisplay()) { for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileWidgets[i]->addSqueezedItem(profile->name()); } } } for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileLabels[i]->setText(i18nc("The number of the screen", "Screen %1:", i + 1)); m_monitorProfileWidgets[i]->setCurrent(csf->defaultProfile()); } } void ColorSettingsTab::refillPrintProfiles(const KoID & s) { const KoColorSpaceFactory * csf = KoColorSpaceRegistry::instance()->colorSpaceFactory(s.id()); m_page->cmbPrintProfile->clear(); if (!csf) return; QList profileList = KoColorSpaceRegistry::instance()->profilesFor(csf); Q_FOREACH (const KoColorProfile *profile, profileList) { if (profile->isSuitableForPrinting()) m_page->cmbPrintProfile->addSqueezedItem(profile->name()); } m_page->cmbPrintProfile->setCurrent(csf->defaultProfile()); } //--------------------------------------------------------------------------------------------------- void TabletSettingsTab::setDefault() { KisCubicCurve curve; curve.fromString(DEFAULT_CURVE_STRING); m_page->pressureCurve->setCurve(curve); } TabletSettingsTab::TabletSettingsTab(QWidget* parent, const char* name): QWidget(parent) { setObjectName(name); QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgTabletSettings(this); l->addWidget(m_page, 0, 0); KisConfig cfg; KisCubicCurve curve; curve.fromString( cfg.pressureTabletCurve() ); m_page->pressureCurve->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); m_page->pressureCurve->setCurve(curve); } //--------------------------------------------------------------------------------------------------- #include "kis_image_config.h" #include "kis_acyclic_signal_connector.h" int getTotalRAM() { KisImageConfig cfg; return cfg.totalRAM(); } int PerformanceTab::realTilesRAM() { return intMemoryLimit->value() - intPoolLimit->value(); } PerformanceTab::PerformanceTab(QWidget *parent, const char *name) : WdgPerformanceSettings(parent, name) { KisImageConfig cfg; const int totalRAM = cfg.totalRAM(); lblTotalMemory->setText(i18n("%1 MiB", totalRAM)); sliderMemoryLimit->setSuffix(i18n(" %")); sliderMemoryLimit->setRange(1, 100, 2); sliderMemoryLimit->setSingleStep(0.01); sliderPoolLimit->setSuffix(i18n(" %")); sliderPoolLimit->setRange(0, 20, 2); sliderMemoryLimit->setSingleStep(0.01); sliderUndoLimit->setSuffix(i18n(" %")); sliderUndoLimit->setRange(0, 50, 2); sliderMemoryLimit->setSingleStep(0.01); intMemoryLimit->setMinimumWidth(80); intPoolLimit->setMinimumWidth(80); intUndoLimit->setMinimumWidth(80); SliderAndSpinBoxSync *sync1 = new SliderAndSpinBoxSync(sliderMemoryLimit, intMemoryLimit, getTotalRAM); sync1->slotParentValueChanged(); m_syncs << sync1; SliderAndSpinBoxSync *sync2 = new SliderAndSpinBoxSync(sliderPoolLimit, intPoolLimit, std::bind(&QSpinBox::value, intMemoryLimit)); connect(intMemoryLimit, SIGNAL(valueChanged(int)), sync2, SLOT(slotParentValueChanged())); sync2->slotParentValueChanged(); m_syncs << sync2; SliderAndSpinBoxSync *sync3 = new SliderAndSpinBoxSync(sliderUndoLimit, intUndoLimit, std::bind(&PerformanceTab::realTilesRAM, this)); connect(intPoolLimit, SIGNAL(valueChanged(int)), sync3, SLOT(slotParentValueChanged())); sync3->slotParentValueChanged(); m_syncs << sync3; sliderSwapSize->setSuffix(i18n(" GiB")); sliderSwapSize->setRange(1, 64); intSwapSize->setRange(1, 64); KisAcyclicSignalConnector *swapSizeConnector = new KisAcyclicSignalConnector(this); swapSizeConnector->connectForwardInt(sliderSwapSize, SIGNAL(valueChanged(int)), intSwapSize, SLOT(setValue(int))); swapSizeConnector->connectBackwardInt(intSwapSize, SIGNAL(valueChanged(int)), sliderSwapSize, SLOT(setValue(int))); lblSwapFileLocation->setText(cfg.swapDir()); connect(bnSwapFile, SIGNAL(clicked()), SLOT(selectSwapDir())); load(false); } PerformanceTab::~PerformanceTab() { qDeleteAll(m_syncs); } void PerformanceTab::load(bool requestDefault) { KisImageConfig cfg; sliderMemoryLimit->setValue(cfg.memoryHardLimitPercent(requestDefault)); sliderPoolLimit->setValue(cfg.memoryPoolLimitPercent(requestDefault)); sliderUndoLimit->setValue(cfg.memorySoftLimitPercent(requestDefault)); chkPerformanceLogging->setChecked(cfg.enablePerfLog(requestDefault)); chkProgressReporting->setChecked(cfg.enableProgressReporting(requestDefault)); sliderSwapSize->setValue(cfg.maxSwapSize(requestDefault) / 1024); lblSwapFileLocation->setText(cfg.swapDir(requestDefault)); { KisConfig cfg2; chkOpenGLLogging->setChecked(cfg2.enableOpenGLDebugging(requestDefault)); chkDisableVectorOptimizations->setChecked(cfg2.enableAmdVectorizationWorkaround(requestDefault)); } } void PerformanceTab::save() { KisImageConfig cfg; cfg.setMemoryHardLimitPercent(sliderMemoryLimit->value()); cfg.setMemorySoftLimitPercent(sliderUndoLimit->value()); cfg.setMemoryPoolLimitPercent(sliderPoolLimit->value()); cfg.setEnablePerfLog(chkPerformanceLogging->isChecked()); cfg.setEnableProgressReporting(chkProgressReporting->isChecked()); cfg.setMaxSwapSize(sliderSwapSize->value() * 1024); cfg.setSwapDir(lblSwapFileLocation->text()); { KisConfig cfg2; cfg2.setEnableOpenGLDebugging(chkOpenGLLogging->isChecked()); cfg2.setEnableAmdVectorizationWorkaround(chkDisableVectorOptimizations->isChecked()); } } void PerformanceTab::selectSwapDir() { KisImageConfig cfg; QString swapDir = cfg.swapDir(); swapDir = QFileDialog::getExistingDirectory(0, i18nc("@title:window", "Select a swap directory"), swapDir); lblSwapFileLocation->setText(swapDir); } //--------------------------------------------------------------------------------------------------- #include "KoColor.h" #include "KoColorPopupAction.h" DisplaySettingsTab::DisplaySettingsTab(QWidget *parent, const char *name) : WdgDisplaySettings(parent, name) { KisConfig cfg; if (!KisOpenGL::hasOpenGL()) { grpOpenGL->setEnabled(false); grpOpenGL->setChecked(false); chkUseTextureBuffer->setEnabled(false); - chkDisableDoubleBuffering->setEnabled(false); chkDisableVsync->setEnabled(false); cmbFilterMode->setEnabled(false); } else { grpOpenGL->setEnabled(true); grpOpenGL->setChecked(cfg.useOpenGL()); chkUseTextureBuffer->setEnabled(cfg.useOpenGL()); chkUseTextureBuffer->setChecked(cfg.useOpenGLTextureBuffer()); - chkDisableDoubleBuffering->setVisible(cfg.showAdvancedOpenGLSettings()); - chkDisableDoubleBuffering->setEnabled(cfg.useOpenGL()); - chkDisableDoubleBuffering->setChecked(cfg.disableDoubleBuffering()); chkDisableVsync->setVisible(cfg.showAdvancedOpenGLSettings()); chkDisableVsync->setEnabled(cfg.useOpenGL()); chkDisableVsync->setChecked(cfg.disableVSync()); cmbFilterMode->setEnabled(cfg.useOpenGL()); cmbFilterMode->setCurrentIndex(cfg.openGLFilteringMode()); + // Don't show the high quality filtering mode if it's not available + if (!KisOpenGL::supportsGLSL13()) { + cmbFilterMode->removeItem(3); + } } if (qApp->applicationName() == "kritasketch" || qApp->applicationName() == "kritagemini") { grpOpenGL->setVisible(false); grpOpenGL->setMaximumHeight(0); } KoColor c; c.fromQColor(cfg.selectionOverlayMaskColor()); m_selectionOverlayColorAction = new KoColorPopupAction(this); m_selectionOverlayColorAction->setCurrentColor(c); m_selectionOverlayColorAction->setIcon(KisIconUtils::loadIcon("format-stroke-color")); m_selectionOverlayColorAction->setToolTip(i18n("Change the background color of the image")); btnSelectionOverlayColor->setDefaultAction(m_selectionOverlayColorAction); intCheckSize->setValue(cfg.checkSize()); chkMoving->setChecked(cfg.scrollCheckers()); colorChecks1->setColor(cfg.checkersColor1()); colorChecks2->setColor(cfg.checkersColor2()); canvasBorder->setColor(cfg.canvasBorderColor()); hideScrollbars->setChecked(cfg.hideScrollbars()); chkCurveAntialiasing->setChecked(cfg.antialiasCurves()); chkSelectionOutlineAntialiasing->setChecked(cfg.antialiasSelectionOutline()); chkChannelsAsColor->setChecked(cfg.showSingleChannelAsColor()); chkHidePopups->setChecked(cfg.hidePopups()); connect(grpOpenGL, SIGNAL(toggled(bool)), SLOT(slotUseOpenGLToggled(bool))); } void DisplaySettingsTab::setDefault() { KisConfig cfg; if (!KisOpenGL::hasOpenGL()) { grpOpenGL->setEnabled(false); grpOpenGL->setChecked(false); chkUseTextureBuffer->setEnabled(false); - chkDisableDoubleBuffering->setEnabled(false); chkDisableVsync->setEnabled(false); cmbFilterMode->setEnabled(false); } else { grpOpenGL->setEnabled(true); grpOpenGL->setChecked(cfg.useOpenGL(true)); chkUseTextureBuffer->setChecked(cfg.useOpenGLTextureBuffer(true)); chkUseTextureBuffer->setEnabled(true); - chkDisableDoubleBuffering->setEnabled(true); - chkDisableDoubleBuffering->setChecked(cfg.disableDoubleBuffering(true)); chkDisableVsync->setEnabled(true); chkDisableVsync->setChecked(cfg.disableVSync(true)); cmbFilterMode->setEnabled(true); cmbFilterMode->setCurrentIndex(cfg.openGLFilteringMode(true)); } chkMoving->setChecked(cfg.scrollCheckers(true)); intCheckSize->setValue(cfg.checkSize(true)); colorChecks1->setColor(cfg.checkersColor1(true)); colorChecks2->setColor(cfg.checkersColor2(true)); canvasBorder->setColor(cfg.canvasBorderColor(true)); hideScrollbars->setChecked(cfg.hideScrollbars(true)); chkCurveAntialiasing->setChecked(cfg.antialiasCurves(true)); chkSelectionOutlineAntialiasing->setChecked(cfg.antialiasSelectionOutline(true)); chkChannelsAsColor->setChecked(cfg.showSingleChannelAsColor(true)); chkHidePopups->setChecked(cfg.hidePopups(true)); } void DisplaySettingsTab::slotUseOpenGLToggled(bool isChecked) { chkUseTextureBuffer->setEnabled(isChecked); - chkDisableDoubleBuffering->setEnabled(isChecked); chkDisableVsync->setEnabled(isChecked); cmbFilterMode->setEnabled(isChecked); } //--------------------------------------------------------------------------------------------------- FullscreenSettingsTab::FullscreenSettingsTab(QWidget* parent) : WdgFullscreenSettingsBase(parent) { KisConfig cfg; chkDockers->setChecked(cfg.hideDockersFullscreen()); chkMenu->setChecked(cfg.hideMenuFullscreen()); chkScrollbars->setChecked(cfg.hideScrollbarsFullscreen()); chkStatusbar->setChecked(cfg.hideStatusbarFullscreen()); +#ifdef Q_OS_WINDOWS + chkTitlebar->setVisible(false); +#endif chkTitlebar->setChecked(cfg.hideTitlebarFullscreen()); chkToolbar->setChecked(cfg.hideToolbarFullscreen()); } void FullscreenSettingsTab::setDefault() { KisConfig cfg; chkDockers->setChecked(cfg.hideDockersFullscreen(true)); chkMenu->setChecked(cfg.hideMenuFullscreen(true)); chkScrollbars->setChecked(cfg.hideScrollbarsFullscreen(true)); chkStatusbar->setChecked(cfg.hideStatusbarFullscreen(true)); chkTitlebar->setChecked(cfg.hideTitlebarFullscreen(true)); chkToolbar->setChecked(cfg.hideToolbarFullscreen(true)); } //--------------------------------------------------------------------------------------------------- KisDlgPreferences::KisDlgPreferences(QWidget* parent, const char* name) : KPageDialog(parent) { Q_UNUSED(name); setWindowTitle(i18n("Preferences")); // QT5TODO: help button needs custom wiring up to whatever help should be shown setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::RestoreDefaults); button(QDialogButtonBox::Ok)->setDefault(true); setFaceType(KPageDialog::List); // General KoVBox *vbox = new KoVBox(); KPageWidgetItem *page = new KPageWidgetItem(vbox, i18n("General")); page->setObjectName("general"); page->setHeader(i18n("General")); page->setIcon(KisIconUtils::loadIcon("go-home")); addPage(page); m_general = new GeneralTab(vbox); // Shortcuts vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Keyboard Shortcuts")); page->setObjectName("shortcuts"); page->setHeader(i18n("Shortcuts")); page->setIcon(KisIconUtils::loadIcon("document-export")); addPage(page); m_shortcutSettings = new ShortcutSettingsTab(vbox); connect(this, SIGNAL(accepted()), m_shortcutSettings, SLOT(saveChanges())); - connect(this, SIGNAL(rejected()), m_shortcutSettings, SLOT(revertChanges())); // Canvas input settings m_inputConfiguration = new KisInputConfigurationPage(); page = addPage(m_inputConfiguration, i18n("Canvas Input Settings")); page->setHeader(i18n("Canvas Input")); page->setObjectName("canvasinput"); page->setIcon(KisIconUtils::loadIcon("configure")); // Display vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Display")); page->setObjectName("display"); page->setHeader(i18n("Display")); page->setIcon(KisIconUtils::loadIcon("preferences-desktop-display")); addPage(page); m_displaySettings = new DisplaySettingsTab(vbox); // Color vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Color Management")); page->setObjectName("colormanagement"); page->setHeader(i18n("Color")); page->setIcon(KisIconUtils::loadIcon("preferences-desktop-color")); addPage(page); m_colorSettings = new ColorSettingsTab(vbox); // Performance vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Performance")); page->setObjectName("performance"); page->setHeader(i18n("Performance")); page->setIcon(KisIconUtils::loadIcon("applications-system")); addPage(page); m_performanceSettings = new PerformanceTab(vbox); // Tablet vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Tablet settings")); page->setObjectName("tablet"); page->setHeader(i18n("Tablet")); page->setIcon(KisIconUtils::loadIcon("document-edit")); addPage(page); m_tabletSettings = new TabletSettingsTab(vbox); // full-screen mode vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Canvas-only settings")); page->setObjectName("canvasonly"); page->setHeader(i18n("Canvas-only")); page->setIcon(KisIconUtils::loadIcon("folder-pictures")); addPage(page); m_fullscreenSettings = new FullscreenSettingsTab(vbox); // Author profiles m_authorPage = new KoConfigAuthorPage(); page = addPage(m_authorPage, i18nc("@title:tab Author page", "Author" )); page->setObjectName("author"); page->setHeader(i18n("Author")); page->setIcon(KisIconUtils::loadIcon("im-user")); QPushButton *restoreDefaultsButton = button(QDialogButtonBox::RestoreDefaults); connect(this, SIGNAL(accepted()), m_inputConfiguration, SLOT(saveChanges())); connect(this, SIGNAL(rejected()), m_inputConfiguration, SLOT(revertChanges())); KisPreferenceSetRegistry *preferenceSetRegistry = KisPreferenceSetRegistry::instance(); Q_FOREACH (KisAbstractPreferenceSetFactory *preferenceSetFactory, preferenceSetRegistry->values()) { KisPreferenceSet* preferenceSet = preferenceSetFactory->createPreferenceSet(); vbox = new KoVBox(); page = new KPageWidgetItem(vbox, preferenceSet->name()); page->setHeader(preferenceSet->header()); page->setIcon(preferenceSet->icon()); addPage(page); preferenceSet->setParent(vbox); preferenceSet->loadPreferences(); connect(restoreDefaultsButton, SIGNAL(clicked(bool)), preferenceSet, SLOT(loadDefaultPreferences()), Qt::UniqueConnection); connect(this, SIGNAL(accepted()), preferenceSet, SLOT(savePreferences()), Qt::UniqueConnection); } connect(restoreDefaultsButton, SIGNAL(clicked(bool)), this, SLOT(slotDefault())); } KisDlgPreferences::~KisDlgPreferences() { } void KisDlgPreferences::slotDefault() { if (currentPage()->objectName() == "general") { m_general->setDefault(); } else if (currentPage()->objectName() == "shortcuts") { m_shortcutSettings->setDefault(); } else if (currentPage()->objectName() == "display") { m_displaySettings->setDefault(); } else if (currentPage()->objectName() == "colormanagement") { m_colorSettings->setDefault(); } else if (currentPage()->objectName() == "performance") { m_performanceSettings->load(true); } else if (currentPage()->objectName() == "tablet") { m_tabletSettings->setDefault(); } else if (currentPage()->objectName() == "canvasonly") { m_fullscreenSettings->setDefault(); } else if (currentPage()->objectName() == "canvasinput") { m_inputConfiguration->setDefaults(); } } bool KisDlgPreferences::editPreferences() { KisDlgPreferences* dialog; dialog = new KisDlgPreferences(); bool baccept = (dialog->exec() == Accepted); if (baccept) { // General settings KisConfig cfg; cfg.setNewCursorStyle(dialog->m_general->cursorStyle()); cfg.setNewOutlineStyle(dialog->m_general->outlineStyle()); cfg.setShowRootLayer(dialog->m_general->showRootLayer()); cfg.setShowOutlineWhilePainting(dialog->m_general->showOutlineWhilePainting()); cfg.setHideSplashScreen(dialog->m_general->hideSplashScreen()); cfg.writeEntry("mdi_viewmode", dialog->m_general->mdiMode()); cfg.setMDIBackgroundColor(dialog->m_general->m_mdiColor->color()); cfg.setMDIBackgroundImage(dialog->m_general->m_backgroundimage->text()); cfg.setAutoSaveInterval(dialog->m_general->autoSaveInterval()); cfg.setBackupFile(dialog->m_general->m_backupFileCheckBox->isChecked()); cfg.setShowCanvasMessages(dialog->m_general->showCanvasMessages()); cfg.setCompressKra(dialog->m_general->compressKra()); cfg.setToolOptionsInDocker(dialog->m_general->toolOptionsInDocker()); cfg.setSwitchSelectionCtrlAlt(dialog->m_general->switchSelectionCtrlAlt()); cfg.setConvertToImageColorspaceOnImport(dialog->m_general->convertToImageColorspaceOnImport()); KisPart *part = KisPart::instance(); if (part) { Q_FOREACH (QPointer doc, part->documents()) { if (doc) { doc->setAutoSave(dialog->m_general->autoSaveInterval()); doc->setBackupFile(dialog->m_general->m_backupFileCheckBox->isChecked()); doc->undoStack()->setUndoLimit(dialog->m_general->undoStackSize()); } } } cfg.setUndoStackLimit(dialog->m_general->undoStackSize()); cfg.setFavoritePresets(dialog->m_general->favoritePresets()); // Color settings cfg.setUseSystemMonitorProfile(dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()); for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()) { int currentIndex = dialog->m_colorSettings->m_monitorProfileWidgets[i]->currentIndex(); QString monitorid = dialog->m_colorSettings->m_monitorProfileWidgets[i]->itemData(currentIndex).toString(); cfg.setMonitorForScreen(i, monitorid); } else { cfg.setMonitorProfile(i, dialog->m_colorSettings->m_monitorProfileWidgets[i]->itemHighlighted(), dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()); } } cfg.setWorkingColorSpace(dialog->m_colorSettings->m_page->cmbWorkingColorSpace->currentItem().id()); cfg.setPrinterColorSpace(dialog->m_colorSettings->m_page->cmbPrintingColorSpace->currentItem().id()); cfg.setPrinterProfile(dialog->m_colorSettings->m_page->cmbPrintProfile->itemHighlighted()); cfg.setUseBlackPointCompensation(dialog->m_colorSettings->m_page->chkBlackpoint->isChecked()); cfg.setAllowLCMSOptimization(dialog->m_colorSettings->m_page->chkAllowLCMSOptimization->isChecked()); cfg.setPasteBehaviour(dialog->m_colorSettings->m_pasteBehaviourGroup.checkedId()); cfg.setRenderIntent(dialog->m_colorSettings->m_page->cmbMonitorIntent->currentIndex()); // Tablet settings cfg.setPressureTabletCurve( dialog->m_tabletSettings->m_page->pressureCurve->curve().toString() ); dialog->m_performanceSettings->save(); if (!cfg.useOpenGL() && dialog->m_displaySettings->grpOpenGL->isChecked()) cfg.setCanvasState("TRY_OPENGL"); cfg.setUseOpenGL(dialog->m_displaySettings->grpOpenGL->isChecked()); cfg.setUseOpenGLTextureBuffer(dialog->m_displaySettings->chkUseTextureBuffer->isChecked()); cfg.setOpenGLFilteringMode(dialog->m_displaySettings->cmbFilterMode->currentIndex()); - cfg.setDisableDoubleBuffering(dialog->m_displaySettings->chkDisableDoubleBuffering->isChecked()); cfg.setDisableVSync(dialog->m_displaySettings->chkDisableVsync->isChecked()); cfg.setCheckSize(dialog->m_displaySettings->intCheckSize->value()); cfg.setScrollingCheckers(dialog->m_displaySettings->chkMoving->isChecked()); cfg.setCheckersColor1(dialog->m_displaySettings->colorChecks1->color()); cfg.setCheckersColor2(dialog->m_displaySettings->colorChecks2->color()); cfg.setCanvasBorderColor(dialog->m_displaySettings->canvasBorder->color()); cfg.setHideScrollbars(dialog->m_displaySettings->hideScrollbars->isChecked()); cfg.setSelectionOverlayMaskColor(dialog->m_displaySettings->m_selectionOverlayColorAction->currentKoColor().toQColor()); cfg.setAntialiasCurves(dialog->m_displaySettings->chkCurveAntialiasing->isChecked()); cfg.setAntialiasSelectionOutline(dialog->m_displaySettings->chkSelectionOutlineAntialiasing->isChecked()); cfg.setShowSingleChannelAsColor(dialog->m_displaySettings->chkChannelsAsColor->isChecked()); cfg.setHidePopups(dialog->m_displaySettings->chkHidePopups->isChecked()); cfg.setHideDockersFullscreen(dialog->m_fullscreenSettings->chkDockers->checkState()); cfg.setHideMenuFullscreen(dialog->m_fullscreenSettings->chkMenu->checkState()); cfg.setHideScrollbarsFullscreen(dialog->m_fullscreenSettings->chkScrollbars->checkState()); cfg.setHideStatusbarFullscreen(dialog->m_fullscreenSettings->chkStatusbar->checkState()); cfg.setHideTitlebarFullscreen(dialog->m_fullscreenSettings->chkTitlebar->checkState()); cfg.setHideToolbarFullscreen(dialog->m_fullscreenSettings->chkToolbar->checkState()); dialog->m_authorPage->apply(); } delete dialog; return baccept; } diff --git a/libs/ui/flake/kis_shape_layer.cc b/libs/ui/flake/kis_shape_layer.cc index 4297822a10..f69a325cde 100644 --- a/libs/ui/flake/kis_shape_layer.cc +++ b/libs/ui/flake/kis_shape_layer.cc @@ -1,631 +1,637 @@ /* * Copyright (c) 2006-2008 Boudewijn Rempt * Copyright (c) 2007 Thomas Zander * Copyright (c) 2009 Cyrille Berger * Copyright (c) 2011 Jan Hambrecht * * This program is free software; you can redistribute 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_shape_layer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_default_bounds.h" #include #include "kis_shape_layer_canvas.h" #include "kis_image_view_converter.h" #include #include "kis_node_visitor.h" #include "kis_processing_visitor.h" #include "kis_effect_mask.h" #include "kis_shape_layer_paste.h" #include class ShapeLayerContainerModel : public SimpleShapeContainerModel { public: ShapeLayerContainerModel(KisShapeLayer *parent) : q(parent) {} void add(KoShape *child) { SimpleShapeContainerModel::add(child); q->shapeManager()->addShape(child); } void remove(KoShape *child) { q->shapeManager()->remove(child); SimpleShapeContainerModel::remove(child); } private: KisShapeLayer *q; }; struct KisShapeLayer::Private { public: Private() : converter(0) , canvas(0) , controller(0) , x(0) , y(0) {} KoViewConverter * converter; KisPaintDeviceSP paintDevice; KisShapeLayerCanvas * canvas; KoShapeBasedDocumentBase* controller; int x; int y; }; KisShapeLayer::KisShapeLayer(KoShapeBasedDocumentBase* controller, KisImageWSP image, const QString &name, quint8 opacity) : KisExternalLayer(image, name, opacity), KoShapeLayer(new ShapeLayerContainerModel(this)), m_d(new Private()) { initShapeLayer(controller); } -KisShapeLayer::KisShapeLayer(const KisShapeLayer& _rhs) +KisShapeLayer::KisShapeLayer(const KisShapeLayer& rhs) + : KisShapeLayer(rhs, rhs.m_d->controller) +{ +} + +KisShapeLayer::KisShapeLayer(const KisShapeLayer& _rhs, KoShapeBasedDocumentBase* controller) : KisExternalLayer(_rhs) , KoShapeLayer(new ShapeLayerContainerModel(this)) //no _rhs here otherwise both layer have the same KoShapeContainerModel , m_d(new Private()) { // Make sure our new layer is visible otherwise the shapes cannot be painted. setVisible(true); - initShapeLayer(_rhs.m_d->controller); + initShapeLayer(controller); KoShapeOdfSaveHelper saveHelper(_rhs.shapes()); KoDrag drag; drag.setOdf(KoOdf::mimeType(KoOdf::Text), saveHelper); QMimeData* mimeData = drag.mimeData(); Q_ASSERT(mimeData->hasFormat(KoOdf::mimeType(KoOdf::Text))); KisShapeLayerShapePaste paste(this, m_d->controller); bool success = paste.paste(KoOdf::Text, mimeData); Q_ASSERT(success); Q_UNUSED(success); // for release build } KisShapeLayer::KisShapeLayer(const KisShapeLayer& _rhs, const KisShapeLayer &_addShapes) : KisExternalLayer(_rhs) , KoShapeLayer(new ShapeLayerContainerModel(this)) //no _merge here otherwise both layer have the same KoShapeContainerModel , m_d(new Private()) { // Make sure our new layer is visible otherwise the shapes cannot be painted. setVisible(true); initShapeLayer(_rhs.m_d->controller); // copy in _rhs's shapes { KoShapeOdfSaveHelper saveHelper(_rhs.shapes()); KoDrag drag; drag.setOdf(KoOdf::mimeType(KoOdf::Text), saveHelper); QMimeData* mimeData = drag.mimeData(); Q_ASSERT(mimeData->hasFormat(KoOdf::mimeType(KoOdf::Text))); KisShapeLayerShapePaste paste(this, m_d->controller); bool success = paste.paste(KoOdf::Text, mimeData); Q_ASSERT(success); Q_UNUSED(success); // for release build } // copy in _addShapes's shapes { KoShapeOdfSaveHelper saveHelper(_addShapes.shapes()); KoDrag drag; drag.setOdf(KoOdf::mimeType(KoOdf::Text), saveHelper); QMimeData* mimeData = drag.mimeData(); Q_ASSERT(mimeData->hasFormat(KoOdf::mimeType(KoOdf::Text))); KisShapeLayerShapePaste paste(this, m_d->controller); bool success = paste.paste(KoOdf::Text, mimeData); Q_ASSERT(success); Q_UNUSED(success); // for release build } } KisShapeLayer::~KisShapeLayer() { /** * Small hack alert: we set the image to null to disable * updates those will be emitted on shape deletion */ KisLayer::setImage(0); Q_FOREACH (KoShape *shape, shapes()) { shape->setParent(0); delete shape; } delete m_d->converter; delete m_d->canvas; delete m_d; } void KisShapeLayer::initShapeLayer(KoShapeBasedDocumentBase* controller) { setSupportsLodMoves(false); setShapeId(KIS_SHAPE_LAYER_ID); m_d->converter = new KisImageViewConverter(image()); KIS_ASSERT_RECOVER_NOOP(this->image()); m_d->paintDevice = new KisPaintDevice(image()->colorSpace()); m_d->paintDevice->setDefaultBounds(new KisDefaultBounds(this->image())); m_d->paintDevice->setParentNode(this); m_d->canvas = new KisShapeLayerCanvas(this, m_d->converter); m_d->canvas->setProjection(m_d->paintDevice); + m_d->canvas->moveToThread(this->thread()); m_d->controller = controller; m_d->canvas->shapeManager()->selection()->disconnect(this); connect(m_d->canvas->shapeManager()->selection(), SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged())); connect(m_d->canvas->shapeManager()->selection(), SIGNAL(currentLayerChanged(const KoShapeLayer*)), this, SIGNAL(currentLayerChanged(const KoShapeLayer*))); connect(this, SIGNAL(sigMoveShapes(const QPointF&)), SLOT(slotMoveShapes(const QPointF&))); } bool KisShapeLayer::allowAsChild(KisNodeSP node) const { return node->inherits("KisMask"); } void KisShapeLayer::setImage(KisImageWSP _image) { KisLayer::setImage(_image); delete m_d->converter; m_d->converter = new KisImageViewConverter(image()); m_d->paintDevice = new KisPaintDevice(image()->colorSpace()); } KisLayerSP KisShapeLayer::createMergedLayerTemplate(KisLayerSP prevLayer) { KisShapeLayer *prevShape = dynamic_cast(prevLayer.data()); if (prevShape) return new KisShapeLayer(*prevShape, *this); else return KisExternalLayer::createMergedLayerTemplate(prevLayer); } void KisShapeLayer::fillMergedLayerTemplate(KisLayerSP dstLayer, KisLayerSP prevLayer) { if (!dynamic_cast(dstLayer.data())) { KisLayer::fillMergedLayerTemplate(dstLayer, prevLayer); } } void KisShapeLayer::setParent(KoShapeContainer *parent) { Q_UNUSED(parent) KIS_ASSERT_RECOVER_RETURN(0) } QIcon KisShapeLayer::icon() const { return KisIconUtils::loadIcon("vectorLayer"); } KisPaintDeviceSP KisShapeLayer::original() const { return m_d->paintDevice; } KisPaintDeviceSP KisShapeLayer::paintDevice() const { return 0; } qint32 KisShapeLayer::x() const { return m_d->x; } qint32 KisShapeLayer::y() const { return m_d->y; } void KisShapeLayer::setX(qint32 x) { qint32 delta = x - this->x(); QPointF diff = QPointF(m_d->converter->viewToDocumentX(delta), 0); emit sigMoveShapes(diff); // Save new value to satisfy LSP m_d->x = x; } void KisShapeLayer::setY(qint32 y) { qint32 delta = y - this->y(); QPointF diff = QPointF(0, m_d->converter->viewToDocumentY(delta)); emit sigMoveShapes(diff); // Save new value to satisfy LSP m_d->y = y; } void KisShapeLayer::slotMoveShapes(const QPointF &diff) { QList prevPos; QList newPos; QList shapes; Q_FOREACH (KoShape* shape, shapeManager()->shapes()) { if (!dynamic_cast(shape)) { shapes.append(shape); } } Q_FOREACH (KoShape* shape, shapes) { QPointF pos = shape->position(); prevPos << pos; newPos << pos + diff; } KoShapeMoveCommand cmd(shapes, prevPos, newPos); cmd.redo(); } bool KisShapeLayer::accept(KisNodeVisitor& visitor) { return visitor.visit(this); } void KisShapeLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) { return visitor.visit(this, undoAdapter); } KoShapeManager* KisShapeLayer::shapeManager() const { return m_d->canvas->shapeManager(); } KoViewConverter* KisShapeLayer::converter() const { return m_d->converter; } bool KisShapeLayer::visible(bool recursive) const { return KisExternalLayer::visible(recursive); } void KisShapeLayer::setVisible(bool visible, bool isLoading) { KisExternalLayer::setVisible(visible, isLoading); } bool KisShapeLayer::saveLayer(KoStore * store) const { KoOdfWriteStore odfStore(store); KoXmlWriter* manifestWriter = odfStore.manifestWriter("application/vnd.oasis.opendocument.graphics"); KoEmbeddedDocumentSaver embeddedSaver; KisDocument::SavingContext documentContext(odfStore, embeddedSaver); if (!store->open("content.xml")) return false; KoStoreDevice storeDev(store); KoXmlWriter * docWriter = KoOdfWriteStore::createOasisXmlWriter(&storeDev, "office:document-content"); // for office:master-styles QTemporaryFile masterStyles; masterStyles.open(); KoXmlWriter masterStylesTmpWriter(&masterStyles, 1); KoPageLayout page; page.format = KoPageFormat::defaultFormat(); QRectF rc = boundingRect(); page.width = rc.width(); page.height = rc.height(); if (page.width > page.height) { page.orientation = KoPageFormat::Landscape; } else { page.orientation = KoPageFormat::Portrait; } KoGenStyles mainStyles; KoGenStyle pageLayout = page.saveOdf(); QString layoutName = mainStyles.insert(pageLayout, "PL"); KoGenStyle masterPage(KoGenStyle::MasterPageStyle); masterPage.addAttribute("style:page-layout-name", layoutName); mainStyles.insert(masterPage, "Default", KoGenStyles::DontAddNumberToName); QTemporaryFile contentTmpFile; contentTmpFile.open(); KoXmlWriter contentTmpWriter(&contentTmpFile, 1); contentTmpWriter.startElement("office:body"); contentTmpWriter.startElement("office:drawing"); KoShapeSavingContext shapeContext(contentTmpWriter, mainStyles, documentContext.embeddedSaver); shapeContext.xmlWriter().startElement("draw:page"); shapeContext.xmlWriter().addAttribute("draw:name", ""); KoElementReference elementRef("page", 1); elementRef.saveOdf(&shapeContext.xmlWriter(), KoElementReference::DrawId); shapeContext.xmlWriter().addAttribute("draw:master-page-name", "Default"); saveOdf(shapeContext); shapeContext.xmlWriter().endElement(); // draw:page contentTmpWriter.endElement(); // office:drawing contentTmpWriter.endElement(); // office:body mainStyles.saveOdfStyles(KoGenStyles::DocumentAutomaticStyles, docWriter); // And now we can copy over the contents from the tempfile to the real one contentTmpFile.seek(0); docWriter->addCompleteElement(&contentTmpFile); docWriter->endElement(); // Root element docWriter->endDocument(); delete docWriter; if (!store->close()) return false; embeddedSaver.saveEmbeddedDocuments(documentContext); manifestWriter->addManifestEntry("content.xml", "text/xml"); if (! mainStyles.saveOdfStylesDotXml(store, manifestWriter)) { return false; } manifestWriter->addManifestEntry("settings.xml", "text/xml"); if (! shapeContext.saveDataCenter(documentContext.odfStore.store(), documentContext.odfStore.manifestWriter())) return false; // Write out manifest file if (!odfStore.closeManifestWriter()) { dbgImage << "closing manifestWriter failed"; return false; } return true; } bool KisShapeLayer::loadLayer(KoStore* store) { KoOdfReadStore odfStore(store); QString errorMessage; odfStore.loadAndParse(errorMessage); if (!errorMessage.isEmpty()) { warnKrita << errorMessage; return false; } KoXmlElement contents = odfStore.contentDoc().documentElement(); // dbgKrita <<"Start loading OASIS document..." << contents.text(); // dbgKrita <<"Start loading OASIS contents..." << contents.lastChild().localName(); // dbgKrita <<"Start loading OASIS contents..." << contents.lastChild().namespaceURI(); // dbgKrita <<"Start loading OASIS contents..." << contents.lastChild().isElement(); KoXmlElement body(KoXml::namedItemNS(contents, KoXmlNS::office, "body")); if (body.isNull()) { //setErrorMessage( i18n( "Invalid OASIS document. No office:body tag found." ) ); return false; } body = KoXml::namedItemNS(body, KoXmlNS::office, "drawing"); if (body.isNull()) { //setErrorMessage( i18n( "Invalid OASIS document. No office:drawing tag found." ) ); return false; } KoXmlElement page(KoXml::namedItemNS(body, KoXmlNS::draw, "page")); if (page.isNull()) { //setErrorMessage( i18n( "Invalid OASIS document. No draw:page tag found." ) ); return false; } KoXmlElement * master = 0; if (odfStore.styles().masterPages().contains("Standard")) master = odfStore.styles().masterPages().value("Standard"); else if (odfStore.styles().masterPages().contains("Default")) master = odfStore.styles().masterPages().value("Default"); else if (! odfStore.styles().masterPages().empty()) master = odfStore.styles().masterPages().begin().value(); if (master) { const KoXmlElement *style = odfStore.styles().findStyle( master->attributeNS(KoXmlNS::style, "page-layout-name", QString())); KoPageLayout pageLayout; pageLayout.loadOdf(*style); setSize(QSizeF(pageLayout.width, pageLayout.height)); } // We work fine without a master page KoOdfLoadingContext context(odfStore.styles(), odfStore.store()); context.setManifestFile(QString("tar:/") + odfStore.store()->currentPath() + "META-INF/manifest.xml"); KoShapeLoadingContext shapeContext(context, m_d->controller->resourceManager()); KoXmlElement layerElement; forEachElement(layerElement, context.stylesReader().layerSet()) { // FIXME: investigate what is this // KoShapeLayer * l = new KoShapeLayer(); if (!loadOdf(layerElement, shapeContext)) { dbgKrita << "Could not load vector layer!"; return false; } } KoXmlElement child; forEachElement(child, page) { KoShape * shape = KoShapeRegistry::instance()->createShapeFromOdf(child, shapeContext); if (shape) { addShape(shape); } } return true; } void KisShapeLayer::resetCache() { m_d->paintDevice->clear(); QList shapes = m_d->canvas->shapeManager()->shapes(); Q_FOREACH (const KoShape* shape, shapes) { shape->update(); } } KUndo2Command* KisShapeLayer::crop(const QRect & rect) { QPoint oldPos(x(), y()); QPoint newPos = oldPos - rect.topLeft(); return new KisNodeMoveCommand2(this, oldPos, newPos); } KUndo2Command* KisShapeLayer::transform(const QTransform &transform) { QList shapes = m_d->canvas->shapeManager()->shapes(); if(shapes.isEmpty()) return 0; KisImageViewConverter *converter = dynamic_cast(m_d->converter); QTransform realTransform = converter->documentToView() * transform * converter->viewToDocument(); QList oldTransformations; QList newTransformations; QList newShadows; const qreal transformBaseScale = KoUnit::approxTransformScale(transform); // this code won't work if there are shapes, that inherit the transformation from the parent container. // the chart and tree shapes are examples for that, but they aren't used in krita and there are no other shapes like that. Q_FOREACH (const KoShape* shape, shapes) { QTransform oldTransform = shape->transformation(); oldTransformations.append(oldTransform); if (dynamic_cast(shape)) { newTransformations.append(oldTransform); } else { QTransform globalTransform = shape->absoluteTransformation(0); QTransform localTransform = globalTransform * realTransform * globalTransform.inverted(); newTransformations.append(localTransform*oldTransform); } KoShapeShadow *shadow = 0; if (shape->shadow()) { shadow = new KoShapeShadow(*shape->shadow()); shadow->setOffset(transformBaseScale * shadow->offset()); shadow->setBlur(transformBaseScale * shadow->blur()); } newShadows.append(shadow); } KUndo2Command *parentCommand = new KUndo2Command(); new KoShapeTransformCommand(shapes, oldTransformations, newTransformations, parentCommand); new KoShapeShadowCommand(shapes, newShadows, parentCommand); return parentCommand; } diff --git a/libs/ui/flake/kis_shape_layer.h b/libs/ui/flake/kis_shape_layer.h index 9ab63bab60..35fb081dfd 100644 --- a/libs/ui/flake/kis_shape_layer.h +++ b/libs/ui/flake/kis_shape_layer.h @@ -1,149 +1,150 @@ /* * Copyright (c) 2006 Boudewijn Rempt * Copyright (c) 2007 Thomas Zander * * This program is free software; you can redistribute 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_SHAPE_LAYER_H_ #define KIS_SHAPE_LAYER_H_ #include #include #include #include class QRect; class QIcon; class QRect; class QString; class KoShapeManager; class KoStore; class KoViewConverter; class KoShapeBasedDocumentBase; const QString KIS_SHAPE_LAYER_ID = "KisShapeLayer"; /** A KisShapeLayer contains any number of non-krita flakes, such as path shapes, text shapes and anything else people come up with. The KisShapeLayer has a shapemanager and a canvas of its own. The canvas paints onto the projection, and the projection is what we render in Krita. This means that no matter how many views you have, you cannot have a different view on your shapes per view. XXX: what about removing shapes? */ class KRITAUI_EXPORT KisShapeLayer : public KisExternalLayer, public KoShapeLayer { Q_OBJECT public: KisShapeLayer(KoShapeBasedDocumentBase* shapeController, KisImageWSP image, const QString &name, quint8 opacity); KisShapeLayer(const KisShapeLayer& _rhs); + KisShapeLayer(const KisShapeLayer& _rhs, KoShapeBasedDocumentBase* controller); /** * Merge constructor. * * Creates a new layer as a merge of two existing layers. * * This is used by createMergedLayer() */ KisShapeLayer(const KisShapeLayer& _merge, const KisShapeLayer &_addShapes); virtual ~KisShapeLayer(); private: void initShapeLayer(KoShapeBasedDocumentBase* controller); public: KisNodeSP clone() const { return new KisShapeLayer(*this); } bool allowAsChild(KisNodeSP) const; virtual void setImage(KisImageWSP image); virtual KisLayerSP createMergedLayerTemplate(KisLayerSP prevLayer); virtual void fillMergedLayerTemplate(KisLayerSP dstLayer, KisLayerSP prevLayer); public: // KoShape overrides bool isSelectable() const { return false; } void setParent(KoShapeContainer *parent); // KisExternalLayer implementation QIcon icon() const; void resetCache(); KisPaintDeviceSP original() const; KisPaintDeviceSP paintDevice() const; qint32 x() const; qint32 y() const; void setX(qint32); void setY(qint32); bool accept(KisNodeVisitor&); void accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter); KoShapeManager *shapeManager() const; bool saveLayer(KoStore * store) const; bool loadLayer(KoStore* store); KUndo2Command* crop(const QRect & rect); KUndo2Command* transform(const QTransform &transform); bool visible(bool recursive = false) const; void setVisible(bool visible, bool isLoading = false); protected: using KoShape::isVisible; friend class ShapeLayerContainerModel; KoViewConverter* converter() const; Q_SIGNALS: /** * These signals are forwarded from the local shape manager * This is done because we switch KoShapeManager and therefore * KoSelection in KisCanvas2, so we need to connect local managers * to the UI as well. * * \see comment in the constructor of KisCanvas2 */ void selectionChanged(); void currentLayerChanged(const KoShapeLayer *layer); Q_SIGNALS: /** * A signal + slot to synchronize UI and image * threads. Image thread emits the signal, UI * thread performes the action */ void sigMoveShapes(const QPointF &diff); private Q_SLOTS: void slotMoveShapes(const QPointF &diff); private: struct Private; Private * const m_d; }; #endif diff --git a/libs/ui/flake/kis_shape_layer_canvas.cpp b/libs/ui/flake/kis_shape_layer_canvas.cpp index 5f5caa6bad..9f8605c341 100644 --- a/libs/ui/flake/kis_shape_layer_canvas.cpp +++ b/libs/ui/flake/kis_shape_layer_canvas.cpp @@ -1,169 +1,169 @@ /* * Copyright (c) 2007 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_shape_layer_canvas.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#define DEBUG_REPAINT KisShapeLayerCanvas::KisShapeLayerCanvas(KisShapeLayer *parent, KoViewConverter * viewConverter) - : QObject(parent) + : QObject() , KoCanvasBase(0) , m_viewConverter(viewConverter) , m_shapeManager(new KoShapeManager(this)) , m_projection(0) , m_parentLayer(parent) { m_shapeManager->selection()->setActiveLayer(parent); connect(this, SIGNAL(forwardRepaint()), SLOT(repaint()), Qt::QueuedConnection); } KisShapeLayerCanvas::~KisShapeLayerCanvas() { delete m_shapeManager; } void KisShapeLayerCanvas::gridSize(QPointF *offset, QSizeF *spacing) const { Q_ASSERT(false); // This should never be called as this canvas should have no tools. Q_UNUSED(offset); Q_UNUSED(spacing); } bool KisShapeLayerCanvas::snapToGrid() const { Q_ASSERT(false); // This should never be called as this canvas should have no tools. return false; } void KisShapeLayerCanvas::addCommand(KUndo2Command *) { Q_ASSERT(false); // This should never be called as this canvas should have no tools. } KoShapeManager *KisShapeLayerCanvas::shapeManager() const { return m_shapeManager; } #ifdef DEBUG_REPAINT # include #endif void KisShapeLayerCanvas::updateCanvas(const QRectF& rc) { dbgUI << "KisShapeLayerCanvas::updateCanvas()" << rc; //image is 0, if parentLayer is being deleted so don't update if (!m_parentLayer->image()) { return; } QRect r = m_viewConverter->documentToView(rc).toRect(); r.adjust(-2, -2, 2, 2); // for antialias { QMutexLocker locker(&m_dirtyRegionMutex); m_dirtyRegion += r; qreal x, y; m_viewConverter->zoom(&x, &y); } emit forwardRepaint(); } void KisShapeLayerCanvas::repaint() { QRect r; { QMutexLocker locker(&m_dirtyRegionMutex); r = m_dirtyRegion.boundingRect(); m_dirtyRegion = QRegion(); } if (r.isEmpty()) return; r = r.intersected(m_parentLayer->image()->bounds()); QImage image(r.width(), r.height(), QImage::Format_ARGB32); image.fill(0); QPainter p(&image); p.setRenderHint(QPainter::Antialiasing); p.setRenderHint(QPainter::TextAntialiasing); p.translate(-r.x(), -r.y()); p.setClipRect(r); #ifdef DEBUG_REPAINT QColor color = QColor(random() % 255, random() % 255, random() % 255); p.fillRect(r, color); #endif m_shapeManager->paint(p, *m_viewConverter, false); p.end(); KisPaintDeviceSP dev = new KisPaintDevice(m_projection->colorSpace()); dev->convertFromQImage(image, 0); KisPainter::copyAreaOptimized(r.topLeft(), dev, m_projection, QRect(QPoint(), r.size())); m_parentLayer->setDirty(r); } KoToolProxy * KisShapeLayerCanvas::toolProxy() const { // Q_ASSERT(false); // This should never be called as this canvas should have no tools. return 0; } KoViewConverter *KisShapeLayerCanvas::viewConverter() const { return m_viewConverter; } QWidget* KisShapeLayerCanvas::canvasWidget() { return 0; } const QWidget* KisShapeLayerCanvas::canvasWidget() const { return 0; } KoUnit KisShapeLayerCanvas::unit() const { Q_ASSERT(false); // This should never be called as this canvas should have no tools. return KoUnit(KoUnit::Point); } diff --git a/libs/ui/forms/wdgdisplaysettings.ui b/libs/ui/forms/wdgdisplaysettings.ui index 06158084ec..9538a90321 100644 --- a/libs/ui/forms/wdgdisplaysettings.ui +++ b/libs/ui/forms/wdgdisplaysettings.ui @@ -1,374 +1,355 @@ WdgDisplaySettings 0 0 - 446 - 563 + 500 + 685 0 0 Display Miscellaneous Color channels in color false Enable curve anti-aliasing Enable selection outline anti-aliasing Hide layer thumbnail popup Transparency Checkerboard Pattern Size: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter intCheckSize px 256 32 Qt::Horizontal 40 20 Colors: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Horizontal 40 20 If checked, the checkers will move when scrolling the canvas. Determines whether the checks will stay put or whether they will scroll together with the canvas &Move checkers when scrolling true Qt::Vertical 20 40 0 0 OpenGL true - - - - - 0 - 0 - - - - Scaling Mode: - - - - - - - - 0 - 0 - - - - <html><head/><body><p>Try to disable double buffering. This makes painting more responsive, but might cause crashes with some ATI GPU/driver combinations.</p></body></html> - - - Disable double buffering (needs restart) - - - true - - - 0 0 0 Nearest Neighbour Bilinear Filtering Trilinear Filtering High Quality Filtering <html><head/><body><p>Try to disable vsync for Krita. This makes painting more responsive. Uncheck only when experiencing crashes with some GPU/driver combinations.</p></body></html> Disable vsync (needs restart) true 0 0 <html><head/><body><p>Use Texture Buffering. This can be faster on some GPU/Driver combinations (like Intel) or broken on some others (like AMD/Radeon).</p></body></html> Use texture buffer + + + + + 0 + 0 + + + + Scaling Mode: + + + Canvas border Color: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Horizontal 40 20 Hide Scrollbars false Selection Overlay Color: ... Qt::Horizontal 358 20 KColorButton QPushButton
kcolorbutton.h
1
diff --git a/libs/ui/forms/wdggeneralsettings.ui b/libs/ui/forms/wdggeneralsettings.ui index 0db86c423d..24877ff8d6 100644 --- a/libs/ui/forms/wdggeneralsettings.ui +++ b/libs/ui/forms/wdggeneralsettings.ui @@ -1,674 +1,710 @@ WdgGeneralSettings 0 0 552 295 0 0 552 295 Qt::LeftToRight 0 Cursor 10 10 - + + 10 + + + 10 + + + 10 + + 10 0 0 Cursor Shape: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 Outline Shape: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 200 0 Show brush outline while painting Qt::Horizontal 40 20 Qt::Vertical 20 40 Window Qt::Vertical 20 40 - + + 10 + + + 10 + + + 10 + + 10 10 0 0 Multiple Document Mode: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Window Background: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 200 0 QFrame::StyledPanel QFrame::Sunken ... 0 0 Clear 0 0 - + 0 0 0 - + 0 0 0 Background Image (overrides color): 0 0 1 Subwindows Tabs 0 0 - Don't show contents when moving sub-windows: + Don't show contents when moving sub-windows Qt::Horizontal 40 20 Show on-canvas popup messages: Qt::Horizontal 40 20 Qt::Horizontal 40 20 Tools 10 - + + 10 + + + 10 + + + 10 + + 10 Tool Options Location (needs restart) In Doc&ker In Toolbar true Switch Control/Alt Selection Modifiers Qt::Vertical 20 40 Qt::Horizontal 40 20 Miscellaneous - + + 10 + + + 10 + + + 10 + + 10 10 0 0 Qt::RightToLeft Autosave every: true 0 0 Undo stack size: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Compress .kra files more (slows loading/saving) Create backup file 0 0 75 0 10 30 0 0 Favorite presets: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 75 0 min 1 1440 5 15 0 0 75 0 0 1000 5 30 Show root layer Hide splash screen on startup On importing images as layers, convert to the image colorspace Qt::Vertical 20 40 Qt::Vertical 20 40 KColorButton QPushButton
kcolorbutton.h
1
diff --git a/libs/ui/forms/wdgpaintopsettings.ui b/libs/ui/forms/wdgpaintopsettings.ui index 9628dabe1c..faa02dcb66 100644 --- a/libs/ui/forms/wdgpaintopsettings.ui +++ b/libs/ui/forms/wdgpaintopsettings.ui @@ -1,326 +1,320 @@ WdgPaintOpSettings 0 0 - 961 + 1175 107 true 6 0 0 0 0 QFrame::StyledPanel QFrame::Raised - + 0 0 250 0 Fill preset area with current icon Fill area with gradient Fill area with background color Reset area to white 0 4 - - - 100 - 16777215 - - QFrame::StyledPanel QFrame::Raised 0 0 - + 0 0 0 0 16777215 60 Name: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Current Brush Preset. Typed in italic when the preset has unsaved settings. Save the current brush settings under this name &Save to Presets Reload - + 0 0 200 0 200 16777215 0 0 Default preset 0 0 Temporarily Save Tweaks To Presets true 0 0 Erase mode will use a separate brush size Eraser switch size false - + 60 0 KisScratchPad QWidget
kis_scratch_pad.h
1
KisPresetSelectorStrip QWidget
kis_preset_selector_strip.h
1
KisPaintOpListWidget QWidget
kis_paintop_list_widget.h
1
KisLodAvailabilityWidget QWidget
kis_lod_availability_widget.h
1
diff --git a/libs/ui/input/kis_input_manager_p.cpp b/libs/ui/input/kis_input_manager_p.cpp index 2d4c7a28e4..05b8875498 100644 --- a/libs/ui/input/kis_input_manager_p.cpp +++ b/libs/ui/input/kis_input_manager_p.cpp @@ -1,475 +1,511 @@ /* * Copyright (C) 2015 Michael Abrahams * * This program is free software; you can redistribute 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_input_manager_p.h" #include #include #include #include #include "kis_input_manager.h" #include "kis_config.h" #include "kis_abstract_input_action.h" #include "kis_tool_invocation_action.h" #include "kis_stroke_shortcut.h" #include "kis_touch_shortcut.h" #include "kis_input_profile_manager.h" /** * This hungry class EventEater encapsulates event masking logic. * * Its basic role is to kill synthetic mouseMove events sent by Xorg or Qt after * tablet events. Those events are sent in order to allow widgets that haven't * implemented tablet specific functionality to seamlessly behave as if one were * using a mouse. These synthetic events are *supposed* to be optional, or at * least come with a flag saying "This is a fake event!!" but neither of those * methods is trustworthy. (This is correct as of Qt 5.4 + Xorg.) * * Qt 5.4 provides no reliable way to see if a user's tablet is being hovered * over the pad, since it converts all tablethover events into mousemove, with * no option to turn this off. Moreover, sometimes the MouseButtonPress event * from the tapping their tablet happens BEFORE the TabletPress event. This * means we have to resort to a somewhat complicated logic. What makes this * truly a joke is that we are not guaranteed to observe TabletProximityEnter * events when we're using a tablet, either, you may only see an Enter event. * * Once we see tablet events heading our way, we can say pretty confidently that * every mouse event is fake. There are two painful cases to consider - a * mousePress event could arrive before the tabletPress event, or it could * arrive much later, e.g. after tabletRelease. The first was only seen on Linux * with Qt's XInput2 code, the solution was to hold onto mousePress events * temporarily and wait for tabletPress later, this is contained in git history * but is now removed. The second case is currently handled by the * eatOneMousePress function, which waits as long as necessary to detect and * block a single mouse press event. */ static bool isMouseEventType(QEvent::Type t) { return (t == QEvent::MouseMove || t == QEvent::MouseButtonPress || t == QEvent::MouseButtonRelease || t == QEvent::MouseButtonDblClick); } bool KisInputManager::Private::EventEater::eventFilter(QObject* target, QEvent* event ) { Q_UNUSED(target) auto debugEvent = [&]() { if (KisTabletDebugger::instance()->debugEnabled()) { QString pre = QString("[BLOCKED]"); QMouseEvent *ev = static_cast(event); dbgTablet << KisTabletDebugger::instance()->eventToString(*ev,pre); } }; if (peckish && event->type() == QEvent::MouseButtonPress // Drop one mouse press following tabletPress or touchBegin && (static_cast(event)->button() == Qt::LeftButton)) { peckish = false; debugEvent(); return true; } else if (isMouseEventType(event->type()) && (hungry || (eatSyntheticEvents && static_cast(event)->source() != 0))) { // Drop mouse events if enabled or event was synthetic & synthetic events are disabled debugEvent(); return true; } return false; // All clear - let this one through! } void KisInputManager::Private::EventEater::activate() { if (!hungry && (KisTabletDebugger::instance()->debugEnabled())) qDebug() << "Start ignoring mouse events."; hungry = true; } void KisInputManager::Private::EventEater::deactivate() { if (hungry && (KisTabletDebugger::instance()->debugEnabled())) dbgTablet << "Stop ignoring mouse events."; hungry = false; } void KisInputManager::Private::EventEater::eatOneMousePress() { // #if defined(Q_OS_WIN) // Enable on other platforms if getting full-pressure splotches peckish = true; // #endif } bool KisInputManager::Private::ignoringQtCursorEvents() { return eventEater.hungry; } void KisInputManager::Private::maskSyntheticEvents(bool value) { eventEater.eatSyntheticEvents = value; } KisInputManager::Private::Private(KisInputManager *qq) : q(qq) , moveEventCompressor(10 /* ms */, KisSignalCompressor::FIRST_ACTIVE) , priorityEventFilterSeqNo(0) , canvasSwitcher(this, qq) { KisConfig cfg; disableTouchOnCanvas = cfg.disableTouchOnCanvas(); moveEventCompressor.setDelay(cfg.tabletEventsDelay()); testingAcceptCompressedTabletEvents = cfg.testingAcceptCompressedTabletEvents(); testingCompressBrushEvents = cfg.testingCompressBrushEvents(); } +static const int InputWidgetsThreshold = 2000; +static const int OtherWidgetsThreshold = 400; KisInputManager::Private::CanvasSwitcher::CanvasSwitcher(Private *_d, QObject *p) : QObject(p), d(_d), eatOneMouseStroke(false), - focusSwitchThreshold(2000) + focusSwitchThreshold(InputWidgetsThreshold) { } void KisInputManager::Private::CanvasSwitcher::setupFocusThreshold(QObject* object) { QWidget *widget = qobject_cast(object); KIS_SAFE_ASSERT_RECOVER_RETURN(widget); thresholdConnections.clear(); thresholdConnections.addConnection(&focusSwitchThreshold, SIGNAL(timeout()), widget, SLOT(setFocus())); } void KisInputManager::Private::CanvasSwitcher::addCanvas(KisCanvas2 *canvas) { QObject *canvasWidget = canvas->canvasWidget(); if (!canvasResolver.contains(canvasWidget)) { canvasResolver.insert(canvasWidget, canvas); d->q->setupAsEventFilter(canvasWidget); canvasWidget->installEventFilter(this); setupFocusThreshold(canvasWidget); focusSwitchThreshold.setEnabled(false); d->canvas = canvas; d->toolProxy = dynamic_cast(canvas->toolProxy()); } else { KIS_ASSERT_RECOVER_RETURN(d->canvas == canvas); } } void KisInputManager::Private::CanvasSwitcher::removeCanvas(KisCanvas2 *canvas) { QObject *widget = canvas->canvasWidget(); canvasResolver.remove(widget); if (d->eventsReceiver == widget) { d->q->setupAsEventFilter(0); } widget->removeEventFilter(this); } +bool isInputWidget(QWidget *w) +{ + if (!w) return false; + + + QList types; + types << QLatin1String("QAbstractSlider"); + types << QLatin1String("QAbstractSpinBox"); + types << QLatin1String("QLineEdit"); + types << QLatin1String("QTextEdit"); + types << QLatin1String("QPlainTextEdit"); + types << QLatin1String("QComboBox"); + types << QLatin1String("QKeySequenceEdit"); + + Q_FOREACH (const QLatin1String &type, types) { + if (w->inherits(type.data())) { + return true; + } + } + + return false; +} + bool KisInputManager::Private::CanvasSwitcher::eventFilter(QObject* object, QEvent* event ) { if (canvasResolver.contains(object)) { switch (event->type()) { case QEvent::FocusIn: { QFocusEvent *fevent = static_cast(event); KisCanvas2 *canvas = canvasResolver.value(object); if (canvas != d->canvas) { eatOneMouseStroke = 2 * (fevent->reason() == Qt::MouseFocusReason); } d->canvas = canvas; d->toolProxy = dynamic_cast(canvas->toolProxy()); d->q->setupAsEventFilter(object); object->removeEventFilter(this); object->installEventFilter(this); setupFocusThreshold(object); focusSwitchThreshold.setEnabled(false); QEvent event(QEvent::Enter); d->q->eventFilter(object, &event); break; } case QEvent::FocusOut: { focusSwitchThreshold.setEnabled(true); break; } case QEvent::Enter: { break; } case QEvent::Leave: { focusSwitchThreshold.stop(); break; } case QEvent::Wheel: { QWidget *widget = static_cast(object); widget->setFocus(); break; } case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::TabletPress: case QEvent::TabletRelease: focusSwitchThreshold.forceDone(); if (eatOneMouseStroke) { eatOneMouseStroke--; return true; } break; case QEvent::MouseButtonDblClick: focusSwitchThreshold.forceDone(); if (eatOneMouseStroke) { return true; } break; case QEvent::MouseMove: - case QEvent::TabletMove: - focusSwitchThreshold.start(); + case QEvent::TabletMove: { + + QWidget *widget = static_cast(object); + + if (!widget->hasFocus()) { + const int delay = + isInputWidget(QApplication::focusWidget()) ? + InputWidgetsThreshold : OtherWidgetsThreshold; + + focusSwitchThreshold.setDelayThreshold(delay); + focusSwitchThreshold.start(); + } + } break; default: break; } } return QObject::eventFilter(object, event); } KisInputManager::Private::ProximityNotifier::ProximityNotifier(KisInputManager::Private *_d, QObject *p) : QObject(p), d(_d) {} bool KisInputManager::Private::ProximityNotifier::eventFilter(QObject* object, QEvent* event ) { switch (event->type()) { case QEvent::TabletEnterProximity: d->debugEvent(event); // Tablet proximity events are unreliable AND fake mouse events do not // necessarily come after tablet events, so this is insufficient. // d->eventEater.eatOneMousePress(); // Qt sends fake mouse events instead of hover events, so not very useful. d->blockMouseEvents(); break; case QEvent::TabletLeaveProximity: d->debugEvent(event); d->allowMouseEvents(); break; default: break; } return QObject::eventFilter(object, event); } void KisInputManager::Private::addStrokeShortcut(KisAbstractInputAction* action, int index, const QList &modifiers, Qt::MouseButtons buttons) { KisStrokeShortcut *strokeShortcut = new KisStrokeShortcut(action, index); QList buttonList; if(buttons & Qt::LeftButton) { buttonList << Qt::LeftButton; } if(buttons & Qt::RightButton) { buttonList << Qt::RightButton; } if(buttons & Qt::MidButton) { buttonList << Qt::MidButton; } if(buttons & Qt::XButton1) { buttonList << Qt::XButton1; } if(buttons & Qt::XButton2) { buttonList << Qt::XButton2; } if (buttonList.size() > 0) { strokeShortcut->setButtons(QSet::fromList(modifiers), QSet::fromList(buttonList)); matcher.addShortcut(strokeShortcut); } else { delete strokeShortcut; } } void KisInputManager::Private::addKeyShortcut(KisAbstractInputAction* action, int index, const QList &keys) { if (keys.size() == 0) return; KisSingleActionShortcut *keyShortcut = new KisSingleActionShortcut(action, index); //Note: Ordering is important here, Shift + V is different from V + Shift, //which is the reason we use the last key here since most users will enter //shortcuts as "Shift + V". Ideally this should not happen, but this is //the way the shortcut matcher is currently implemented. QList allKeys = keys; Qt::Key key = allKeys.takeLast(); QSet modifiers = QSet::fromList(allKeys); keyShortcut->setKey(modifiers, key); matcher.addShortcut(keyShortcut); } void KisInputManager::Private::addWheelShortcut(KisAbstractInputAction* action, int index, const QList &modifiers, KisShortcutConfiguration::MouseWheelMovement wheelAction) { KisSingleActionShortcut *keyShortcut = new KisSingleActionShortcut(action, index); KisSingleActionShortcut::WheelAction a; switch(wheelAction) { case KisShortcutConfiguration::WheelUp: a = KisSingleActionShortcut::WheelUp; break; case KisShortcutConfiguration::WheelDown: a = KisSingleActionShortcut::WheelDown; break; case KisShortcutConfiguration::WheelLeft: a = KisSingleActionShortcut::WheelLeft; break; case KisShortcutConfiguration::WheelRight: a = KisSingleActionShortcut::WheelRight; break; default: return; } keyShortcut->setWheel(QSet::fromList(modifiers), a); matcher.addShortcut(keyShortcut); } void KisInputManager::Private::addTouchShortcut( KisAbstractInputAction* action, int index, KisShortcutConfiguration::GestureAction gesture) { KisTouchShortcut *shortcut = new KisTouchShortcut(action, index); switch(gesture) { case KisShortcutConfiguration::PinchGesture: shortcut->setMinimumTouchPoints(2); shortcut->setMaximumTouchPoints(2); break; case KisShortcutConfiguration::PanGesture: shortcut->setMinimumTouchPoints(3); shortcut->setMaximumTouchPoints(10); break; default: break; } matcher.addShortcut(shortcut); } void KisInputManager::Private::setupActions() { QList actions = KisInputProfileManager::instance()->actions(); Q_FOREACH (KisAbstractInputAction *action, actions) { KisToolInvocationAction *toolAction = dynamic_cast(action); if(toolAction) { defaultInputAction = toolAction; } } connect(KisInputProfileManager::instance(), SIGNAL(currentProfileChanged()), q, SLOT(profileChanged())); if(KisInputProfileManager::instance()->currentProfile()) { q->profileChanged(); } } bool KisInputManager::Private::processUnhandledEvent(QEvent *event) { bool retval = false; if (forwardAllEventsToTool || event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) { defaultInputAction->processUnhandledEvent(event); retval = true; } return retval && !forwardAllEventsToTool; } bool KisInputManager::Private::tryHidePopupPalette() { if (canvas->isPopupPaletteVisible()) { canvas->slotShowPopupPalette(); return true; } return false; } #ifdef HAVE_X11 inline QPointF dividePoints(const QPointF &pt1, const QPointF &pt2) { return QPointF(pt1.x() / pt2.x(), pt1.y() / pt2.y()); } inline QPointF multiplyPoints(const QPointF &pt1, const QPointF &pt2) { return QPointF(pt1.x() * pt2.x(), pt1.y() * pt2.y()); } #endif void KisInputManager::Private::saveTouchEvent( QTouchEvent* event ) { delete lastTouchEvent; lastTouchEvent = new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints()); } void KisInputManager::Private::blockMouseEvents() { eventEater.activate(); } void KisInputManager::Private::allowMouseEvents() { eventEater.deactivate(); } void KisInputManager::Private::eatOneMousePress() { eventEater.eatOneMousePress(); } bool KisInputManager::Private::handleCompressedTabletEvent(QEvent *event) { bool retval = false; if (!matcher.pointerMoved(event)) { toolProxy->forwardHoverEvent(event); } retval = true; event->setAccepted(true); return retval; } diff --git a/libs/ui/input/kis_tablet_debugger.cpp b/libs/ui/input/kis_tablet_debugger.cpp index bf2e36b5d1..f1deb1259e 100644 --- a/libs/ui/input/kis_tablet_debugger.cpp +++ b/libs/ui/input/kis_tablet_debugger.cpp @@ -1,229 +1,237 @@ /* * 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_tablet_debugger.h" #include #include #include +#include #include #include #include Q_GLOBAL_STATIC(KisTabletDebugger, s_instance) inline QString button(const QWheelEvent &ev) { Q_UNUSED(ev); return "-"; } template QString button(const T &ev) { return QString::number(ev.button()); } template QString buttons(const T &ev) { return QString::number(ev.buttons()); } template void dumpBaseParams(QTextStream &s, const Event &ev, const QString &prefix) { s << qSetFieldWidth(5) << left << prefix << reset << " "; s << qSetFieldWidth(17) << left << KisTabletDebugger::exTypeToString(ev.type()) << reset; } template void dumpMouseRelatedParams(QTextStream &s, const Event &ev) { s << "btn: " << button(ev) << " "; s << "btns: " << buttons(ev) << " "; s << "pos: " << qSetFieldWidth(4) << ev.x() << qSetFieldWidth(0) << "," << qSetFieldWidth(4) << ev.y() << qSetFieldWidth(0) << " "; s << "gpos: " << qSetFieldWidth(4) << ev.globalX() << qSetFieldWidth(0) << "," << qSetFieldWidth(4) << ev.globalY() << qSetFieldWidth(0) << " "; } QString KisTabletDebugger::exTypeToString(QEvent::Type type) { return type == QEvent::TabletEnterProximity ? "TabletEnterProximity" : type == QEvent::TabletLeaveProximity ? "TabletLeaveProximity" : type == QEvent::Enter ? "Enter" : type == QEvent::Leave ? "Leave" : type == QEvent::FocusIn ? "FocusIn" : type == QEvent::Wheel ? "Wheel" : type == QEvent::KeyPress ? "KeyPress" : type == QEvent::KeyRelease ? "KeyRelease" : type == QEvent::ShortcutOverride ? "ShortcutOverride" : type == QMouseEvent::MouseButtonPress ? "MouseButtonPress" : type == QMouseEvent::MouseButtonRelease ? "MouseButtonRelease" : type == QMouseEvent::MouseButtonDblClick ? "MouseButtonDblClick" : type == QMouseEvent::MouseMove ? "MouseMove" : type == QTabletEvent::TabletMove ? "TabletMove" : type == QTabletEvent::TabletPress ? "TabletPress" : type == QTabletEvent::TabletRelease ? "TabletRelease" : "unknown"; } KisTabletDebugger::KisTabletDebugger() : m_debugEnabled(false) { + KisConfig cfg; + m_shouldEatDriverShortcuts = cfg.shouldEatDriverShortcuts(); } KisTabletDebugger* KisTabletDebugger::instance() { return s_instance; } void KisTabletDebugger::toggleDebugging() { m_debugEnabled = !m_debugEnabled; QMessageBox::information(0, i18nc("@title:window", "Krita"), m_debugEnabled ? i18n("Tablet Event Logging Enabled") : i18n("Tablet Event Logging Disabled")); if (m_debugEnabled) { dbgTablet << "vvvvvvvvvvvvvvvvvvvvvvv START TABLET EVENT LOG vvvvvvvvvvvvvvvvvvvvvvv"; } else { dbgTablet << "^^^^^^^^^^^^^^^^^^^^^^^ END TABLET EVENT LOG ^^^^^^^^^^^^^^^^^^^^^^^"; } } bool KisTabletDebugger::debugEnabled() const { return m_debugEnabled; } bool KisTabletDebugger::initializationDebugEnabled() const { // FIXME: make configurable! return true; } bool KisTabletDebugger::debugRawTabletValues() const { // FIXME: make configurable! return m_debugEnabled; } +bool KisTabletDebugger::shouldEatDriverShortcuts() const +{ + return m_shouldEatDriverShortcuts; +} + QString KisTabletDebugger::eventToString(const QMouseEvent &ev, const QString &prefix) { QString string; QTextStream s(&string); dumpBaseParams(s, ev, prefix); dumpMouseRelatedParams(s, ev); s << "Source:" << ev.source(); return string; } QString KisTabletDebugger::eventToString(const QKeyEvent &ev, const QString &prefix) { QString string; QTextStream s(&string); dumpBaseParams(s, ev, prefix); s << "key: 0x" << hex << ev.key() << reset << " "; s << "mod: 0x" << hex << ev.modifiers() << reset << " "; s << "text: " << (ev.text().isEmpty() ? "none" : ev.text()); return string; } QString KisTabletDebugger::eventToString(const QWheelEvent &ev, const QString &prefix) { QString string; QTextStream s(&string); dumpBaseParams(s, ev, prefix); dumpMouseRelatedParams(s, ev); s << "delta: " << ev.delta() << " "; s << "orientation: " << (ev.orientation() == Qt::Horizontal ? "H" : "V") << " "; return string; } QString KisTabletDebugger::eventToString(const QEvent &ev, const QString &prefix) { QString string; QTextStream s(&string); dumpBaseParams(s, ev, prefix); return string; } template QString tabletEventToString(const Event &ev, const QString &prefix) { QString string; QTextStream s(&string); dumpBaseParams(s, ev, prefix); dumpMouseRelatedParams(s, ev); s << "hires: " << qSetFieldWidth(8) << ev.hiResGlobalX() << qSetFieldWidth(0) << "," << qSetFieldWidth(8) << ev.hiResGlobalY() << qSetFieldWidth(0) << " "; s << "prs: " << qSetFieldWidth(4) << fixed << ev.pressure() << reset << " "; s << KisTabletDebugger::tabletDeviceToString((QTabletEvent::TabletDevice) ev.device()) << " "; s << KisTabletDebugger::pointerTypeToString((QTabletEvent::PointerType) ev.pointerType()) << " "; s << "id: " << ev.uniqueId() << " "; s << "xTilt: " << ev.xTilt() << " "; s << "yTilt: " << ev.yTilt() << " "; s << "rot: " << ev.rotation() << " "; s << "z: " << ev.z() << " "; s << "tp: " << ev.tangentialPressure() << " "; return string; } QString KisTabletDebugger::eventToString(const QTabletEvent &ev, const QString &prefix) { return tabletEventToString(ev, prefix); } QString KisTabletDebugger::tabletDeviceToString(QTabletEvent::TabletDevice device) { return device == QTabletEvent::NoDevice ? "NoDevice" : device == QTabletEvent::Puck ? "Puck" : device == QTabletEvent::Stylus ? "Stylus" : device == QTabletEvent::Airbrush ? "Airbrush" : device == QTabletEvent::FourDMouse ? "FourDMouse" : device == QTabletEvent::XFreeEraser ? "XFreeEraser" : device == QTabletEvent::RotationStylus ? "RotationStylus" : "unknown"; } QString KisTabletDebugger::pointerTypeToString(QTabletEvent::PointerType pointer) { return pointer == QTabletEvent::UnknownPointer ? "UnknownPointer" : pointer == QTabletEvent::Pen ? "Pen" : pointer == QTabletEvent::Cursor ? "Cursor" : pointer == QTabletEvent::Eraser ? "Eraser" : "unknown"; } diff --git a/libs/ui/input/kis_tablet_debugger.h b/libs/ui/input/kis_tablet_debugger.h index 3f0e84ef4f..738673cfb8 100644 --- a/libs/ui/input/kis_tablet_debugger.h +++ b/libs/ui/input/kis_tablet_debugger.h @@ -1,55 +1,58 @@ /* * 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_TABLET_DEBUGGER_H #define __KIS_TABLET_DEBUGGER_H #include #include #include #include class KisTabletDebugger { public: KisTabletDebugger(); static KisTabletDebugger* instance(); void toggleDebugging(); bool debugEnabled() const; bool initializationDebugEnabled() const; bool debugRawTabletValues() const; + bool shouldEatDriverShortcuts() const; + QString eventToString(const QMouseEvent &ev, const QString &prefix); QString eventToString(const QKeyEvent &ev, const QString &prefix); QString eventToString(const QWheelEvent &ev, const QString &prefix); QString eventToString(const QTabletEvent &ev, const QString &prefix); QString eventToString(const KisTabletEvent &ev, const QString &prefix); QString eventToString(const QEvent &ev, const QString &prefix); static QString tabletDeviceToString(QTabletEvent::TabletDevice device); static QString pointerTypeToString(QTabletEvent::PointerType pointer); static QString exTypeToString(QEvent::Type type); private: bool m_debugEnabled; + bool m_shouldEatDriverShortcuts; }; #endif /* __KIS_TABLET_DEBUGGER_H */ diff --git a/libs/ui/input/wintab/kis_tablet_support_win.cpp b/libs/ui/input/wintab/kis_tablet_support_win.cpp index 65e49755fe..d11a14cb81 100644 --- a/libs/ui/input/wintab/kis_tablet_support_win.cpp +++ b/libs/ui/input/wintab/kis_tablet_support_win.cpp @@ -1,873 +1,959 @@ /* * Copyright (c) 2013 Digia Plc and/or its subsidiary(-ies). * Copyright (c) 2013 Boudewijn Rempt * Copyright (c) 2013 Dmitry Kazakov * Copyright (c) 2015 Michael Abrahams * Copyright (c) 2015 The Qt Company Ltd. * * This program is free software; you can redistribute 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_tablet_support_win_p.h" #include #include "kis_tablet_support_win.h" // #include "kis_tablet_support.h" #include #include #include #include #include #include #include #include #include #include #include #define Q_PI M_PI #include #include // For "inline tool switches" #include #include #include "kis_screen_size_choice_dialog.h" // NOTE: we stub out qwindowcontext.cpp::347 to disable Qt's own tablet support. // Note: The definition of the PACKET structure in pktdef.h depends on this define. #define PACKETDATA (PK_X | PK_Y | PK_BUTTONS | PK_TIME | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION | PK_CURSOR | PK_Z) #include "pktdef.h" QT_BEGIN_NAMESPACE enum { PacketMode = 0, TabletPacketQSize = 128, DeviceIdMask = 0xFF6, // device type mask && device color mask CursorTypeBitMask = 0x0F06 // bitmask to find the specific cursor type (see Wacom FAQ) }; /* * * Krita extensions begin here * * */ QWindowsTabletSupport *QTAB = 0; static QPointer targetWindow = 0; //< Window receiving last tablet event static QPointer qt_tablet_target = 0; //< Widget receiving last tablet event +static bool dialogOpen = false; //< KisTabletSupportWin is not a Q_OBJECT and can't accept dialog signals HWND createDummyWindow(const QString &className, const wchar_t *windowName, WNDPROC wndProc) { if (!wndProc) wndProc = DefWindowProc; WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = wndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = (HINSTANCE)GetModuleHandle(0); wc.hCursor = 0; wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW); wc.hIcon = 0; wc.hIconSm = 0; wc.lpszMenuName = 0; wc.lpszClassName = (wchar_t*)className.utf16(); ATOM atom = RegisterClassEx(&wc); if (!atom) qErrnoWarning("Registering tablet fake window class failed."); return CreateWindowEx(0, (wchar_t*)className.utf16(), windowName, WS_OVERLAPPED, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_MESSAGE, NULL, (HINSTANCE)GetModuleHandle(0), NULL); } void printContext(const LOGCONTEXT &lc) { dbgTablet << "# Getting current context data:"; dbgTablet << ppVar(lc.lcName); dbgTablet << ppVar(lc.lcDevice); dbgTablet << ppVar(lc.lcInOrgX); dbgTablet << ppVar(lc.lcInOrgY); dbgTablet << ppVar(lc.lcInExtX); dbgTablet << ppVar(lc.lcInExtY); dbgTablet << ppVar(lc.lcOutOrgX); dbgTablet << ppVar(lc.lcOutOrgY); dbgTablet << ppVar(lc.lcOutExtX); dbgTablet << ppVar(lc.lcOutExtY); dbgTablet << ppVar(lc.lcSysOrgX); dbgTablet << ppVar(lc.lcSysOrgY); dbgTablet << ppVar(lc.lcSysExtX); dbgTablet << ppVar(lc.lcSysExtY); dbgTablet << "Qt Desktop Geometry" << QApplication::desktop()->geometry(); } static QRect mapToNative(const QRect &qRect, int m_factor) { return QRect(qRect.x() * m_factor, qRect.y() * m_factor, qRect.width() * m_factor, qRect.height() * m_factor); } static inline QEvent::Type mouseEventType(QEvent::Type t) { return (t == QEvent::TabletMove ? QEvent::MouseMove : t == QEvent::TabletPress ? QEvent::MouseButtonPress : t == QEvent::TabletRelease ? QEvent::MouseButtonRelease : QEvent::None); } static inline bool isMouseEventType(QEvent::Type t) { return (t == QEvent::MouseMove || t == QEvent::MouseButtonPress || t == QEvent::MouseButtonRelease); } static QPoint mousePosition() { POINT p; GetCursorPos(&p); return QPoint(p.x, p.y); } QWindowsWinTab32DLL QWindowsTabletSupport::m_winTab32DLL; void KisTabletSupportWin::init() { if (!QWindowsTabletSupport::m_winTab32DLL.init()) { qWarning() << "Failed to initialize Wintab"; return; } QTAB = QWindowsTabletSupport::create(); // Refresh tablet context after tablet rotated, screen added, etc. QObject::connect(qApp->primaryScreen(), &QScreen::geometryChanged, [=](const QRect & geometry){ delete QTAB; QTAB = QWindowsTabletSupport::create(); }); } // Derived from qwidgetwindow. // // The work done by processTabletEvent from qguiapplicationprivate is divided // between here and translateTabletPacketEvent. static void handleTabletEvent(QWidget *windowWidget, const QPointF &local, const QPointF &global, int device, int pointerType, Qt::MouseButton button, Qt::MouseButtons buttons, qreal pressure,int xTilt, int yTilt, qreal tangentialPressure, qreal rotation, int z, qint64 uniqueId, Qt::KeyboardModifiers modifiers, QEvent::Type type, LONG time) { // Lock in target window if (type == QEvent::TabletPress) { targetWindow = windowWidget; dbgInput << "Locking target window" << targetWindow; } else if ((type == QEvent::TabletRelease || buttons == Qt::NoButton) && (targetWindow != 0)) { dbgInput << "Releasing target window" << targetWindow; targetWindow = 0; } if (!windowWidget) // Should never happen return; // We do this instead of constructing the event e beforehand const QPoint localPos = local.toPoint(); const QPoint globalPos = global.toPoint(); if (type == QEvent::TabletPress) { QWidget *widget = windowWidget->childAt(localPos); if (!widget) widget = windowWidget; qt_tablet_target = widget; } QWidget *finalDestination = qt_tablet_target; if (!finalDestination) { finalDestination = windowWidget->childAt(localPos); } if ((type == QEvent::TabletRelease || buttons == Qt::NoButton) && (qt_tablet_target != 0)) { dbgTablet << "releasing tablet target" << qt_tablet_target; qt_tablet_target = 0; } if (finalDestination) { // The event was specified relative to windowWidget, so we remap it QPointF delta = global - globalPos; QPointF mapped = finalDestination->mapFromGlobal(global.toPoint()) + delta; QTabletEvent ev(type, mapped, global, device, pointerType, pressure, xTilt, yTilt, tangentialPressure, rotation, z, modifiers, uniqueId, button, buttons); ev.setTimestamp(time); QGuiApplication::sendEvent(finalDestination, &ev); if (ev.isAccepted()) { // dbgTablet << "Tablet event" << type << "accepted" << "by target widget" << finalDestination; } else { // Turn off eventEater send a synthetic mouse event. // dbgTablet << "Tablet event" << type << "rejected; sending mouse event to" << finalDestination; qt_tablet_target = 0; // We shouldn't ever get a widget accepting a tablet event from this // call, so we won't worry about any interactions with our own // widget-locking code. // QWindow *target = platformScreen->topLevelAt(globalPos); // if (!target) return; // QPointF windowLocal = global - QPointF(target->mapFromGlobal(QPoint())) + delta; // QWindowSystemInterface::handleTabletEvent(target, ev.timestamp(), windowLocal, // global, device, pointerType, // buttons, pressure, xTilt, yTilt, // tangentialPressure, rotation, z, // uniqueId, modifiers); } } else { qt_tablet_target = 0; targetWindow = 0; } } /** * This is a default implementation of a class for converting the * WinTab value of the buttons pressed to the Qt buttons. This class * may be substituted from the UI. */ struct DefaultButtonsConverter { void convert(DWORD btnOld, DWORD btnNew, Qt::MouseButton *button, Qt::MouseButtons *buttons, const QWindowsTabletDeviceData &tdd) { int pressedButtonValue = btnNew ^ btnOld; *button = buttonValueToEnum(pressedButtonValue, tdd); *buttons = Qt::NoButton; for (int i = 0; i < 3; i++) { int btn = 0x1 << i; if (btn & btnNew) { Qt::MouseButton convertedButton = buttonValueToEnum(btn, tdd); *buttons |= convertedButton; /** * If a button that is present in hardware input is * mapped to a Qt::NoButton, it means that it is going * to be eaten by the driver, for example by its * "Pan/Scroll" feature. Therefore we shouldn't handle * any of the events associated to it. So just return * Qt::NoButton here. */ if (convertedButton == Qt::NoButton) { - *button = Qt::NoButton; - *buttons = Qt::NoButton; - break; + + /** + * Sometimes the driver-handled sortcuts are just + * keyboard modifiers, so ideally we should handle + * them as well. The problem is that we cannot + * know if the shortcut was a pan/zoom action or a + * shortcut. So here we use a "hackish" approash. + * We just check if any modifier has been pressed + * and, if so, pass the button to Krita. Of + * course, if the driver uses some really complex + * shortcuts like "Shift + stylus btn" to generate + * some recorded shortcut, it will not work. But I + * guess it will be ok for th emost of the + * usecases. + * + * WARNING: this hack will *not* work if you bind + * any non-modifier key to the stylus + * button, e.g. Space. + */ + + const Qt::KeyboardModifiers keyboardModifiers = QApplication::queryKeyboardModifiers(); + + if (KisTabletDebugger::instance()->shouldEatDriverShortcuts() || + keyboardModifiers == Qt::NoModifier) { + + *button = Qt::NoButton; + *buttons = Qt::NoButton; + break; + } } } } } private: Qt::MouseButton buttonValueToEnum(DWORD button, const QWindowsTabletDeviceData &tdd) { const int leftButtonValue = 0x1; const int middleButtonValue = 0x2; const int rightButtonValue = 0x4; const int doubleClickButtonValue = 0x7; button = tdd.buttonsMap.value(button); return button == leftButtonValue ? Qt::LeftButton : button == rightButtonValue ? Qt::RightButton : button == doubleClickButtonValue ? Qt::MiddleButton : button == middleButtonValue ? Qt::MiddleButton : button ? Qt::LeftButton /* fallback item */ : Qt::NoButton; } }; static DefaultButtonsConverter *globalButtonsConverter = new DefaultButtonsConverter(); /* * * Krita extensions end here * * */ // This is the WndProc for a single additional hidden window used to collect tablet events. extern "C" LRESULT QT_WIN_CALLBACK qWindowsTabletSupportWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WT_PROXIMITY: if (QTAB->translateTabletProximityEvent(wParam, lParam)) return 0; break; case WT_PACKET: if (QTAB->translateTabletPacketEvent()) return 0; break; } return DefWindowProc(hwnd, message, wParam, lParam); } // Scale tablet coordinates to screen coordinates. static inline int sign(int x) { return x >= 0 ? 1 : -1; } inline QPointF QWindowsTabletDeviceData::scaleCoordinates(int coordX, int coordY, const QRect &targetArea) const { const int targetX = targetArea.x(); const int targetY = targetArea.y(); const int targetWidth = targetArea.width(); const int targetHeight = targetArea.height(); const qreal x = sign(targetWidth) == sign(maxX) ? ((coordX - minX) * qAbs(targetWidth) / qAbs(qreal(maxX - minX))) + targetX : ((qAbs(maxX) - (coordX - minX)) * qAbs(targetWidth) / qAbs(qreal(maxX - minX))) + targetX; const qreal y = sign(targetHeight) == sign(maxY) ? ((coordY - minY) * qAbs(targetHeight) / qAbs(qreal(maxY - minY))) + targetY : ((qAbs(maxY) - (coordY - minY)) * qAbs(targetHeight) / qAbs(qreal(maxY - minY))) + targetY; return QPointF(x, y); } /*! \class QWindowsWinTab32DLL QWindowsTabletSupport \brief Functions from wintabl32.dll shipped with WACOM tablets used by QWindowsTabletSupport. \internal \ingroup qt-lighthouse-win */ bool QWindowsWinTab32DLL::init() { if (wTInfo) return true; QLibrary library(QStringLiteral("wintab32")); if (!library.load()) return false; wTOpen = (PtrWTOpen) library.resolve("WTOpenW"); wTClose = (PtrWTClose) library.resolve("WTClose"); wTInfo = (PtrWTInfo) library.resolve("WTInfoW"); wTEnable = (PtrWTEnable) library.resolve("WTEnable"); wTOverlap = (PtrWTOverlap) library.resolve("WTOverlap"); wTPacketsGet = (PtrWTPacketsGet) library.resolve("WTPacketsGet"); wTGet = (PtrWTGet) library.resolve("WTGetW"); wTQueueSizeGet = (PtrWTQueueSizeGet) library.resolve("WTQueueSizeGet"); wTQueueSizeSet = (PtrWTQueueSizeSet) library.resolve("WTQueueSizeSet"); return wTOpen && wTClose && wTInfo && wTEnable && wTOverlap && wTPacketsGet && wTQueueSizeGet && wTQueueSizeSet; } /*! \class QWindowsTabletSupport \brief Tablet support for Windows. Support for WACOM tablets. \sa http://www.wacomeng.com/windows/docs/Wintab_v140.htm \internal \since 5.2 \ingroup qt-lighthouse-win */ QWindowsTabletSupport::QWindowsTabletSupport(HWND window, HCTX context) : m_window(window) , m_context(context) , m_absoluteRange(20) , m_tiltSupport(false) , m_currentDevice(-1) { AXIS orientation[3]; // Some tablets don't support tilt, check if it is possible, if (QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES, DVC_ORIENTATION, &orientation)) m_tiltSupport = orientation[0].axResolution && orientation[1].axResolution; } QWindowsTabletSupport::~QWindowsTabletSupport() { QWindowsTabletSupport::m_winTab32DLL.wTClose(m_context); DestroyWindow(m_window); } QWindowsTabletSupport *QWindowsTabletSupport::create() { const HWND window = createDummyWindow(QStringLiteral("TabletDummyWindow"), L"TabletDummyWindow", qWindowsTabletSupportWndProc); LOGCONTEXT lcMine; // build our context from the default context QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEFSYSCTX, 0, &lcMine); // Go for the raw coordinates, the tablet event will return good stuff. The // defaults for lcOut rectangle are the desktop dimensions in pixels, which // means Wintab will do lossy rounding. Instead we specify this trivial // scaling for the output context, then do the scaling ourselves later to // obtain higher resolution coordinates. lcMine.lcOptions |= CXO_MESSAGES | CXO_CSRMESSAGES; lcMine.lcPktData = lcMine.lcMoveMask = PACKETDATA; lcMine.lcPktMode = PacketMode; lcMine.lcOutOrgX = 0; lcMine.lcOutExtX = lcMine.lcInExtX; lcMine.lcOutOrgY = 0; lcMine.lcOutExtY = -lcMine.lcInExtY; const HCTX context = QWindowsTabletSupport::m_winTab32DLL.wTOpen(window, &lcMine, true); if (!context) { dbgTablet << __FUNCTION__ << "Unable to open tablet."; DestroyWindow(window); return 0; } // Set the size of the Packet Queue to the correct size const int currentQueueSize = QWindowsTabletSupport::m_winTab32DLL.wTQueueSizeGet(context); if (currentQueueSize != TabletPacketQSize) { if (!QWindowsTabletSupport::m_winTab32DLL.wTQueueSizeSet(context, TabletPacketQSize)) { if (!QWindowsTabletSupport::m_winTab32DLL.wTQueueSizeSet(context, currentQueueSize)) { qWarning() << "Unable to set queue size on tablet. The tablet will not work."; QWindowsTabletSupport::m_winTab32DLL.wTClose(context); DestroyWindow(window); return 0; } // cannot restore old size } // cannot set } // mismatch dbgTablet << "Opened tablet context " << context << " on window " << window << "changed packet queue size " << currentQueueSize << "->" << TabletPacketQSize; return new QWindowsTabletSupport(window, context); } unsigned QWindowsTabletSupport::options() const { UINT result = 0; m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_CTXOPTIONS, &result); return result; } QString QWindowsTabletSupport::description() const { const unsigned size = m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_WINTABID, 0); if (!size) return QString(); QScopedPointer winTabId(new TCHAR[size + 1]); m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_WINTABID, winTabId.data()); WORD implementationVersion = 0; m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_IMPLVERSION, &implementationVersion); WORD specificationVersion = 0; m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_SPECVERSION, &specificationVersion); const unsigned opts = options(); QString result = QString::fromLatin1("%1 specification: v%2.%3 implementation: v%4.%5 options: 0x%6") .arg(QString::fromWCharArray(winTabId.data())) .arg(specificationVersion >> 8).arg(specificationVersion & 0xFF) .arg(implementationVersion >> 8).arg(implementationVersion & 0xFF) .arg(opts, 0, 16); if (opts & CXO_MESSAGES) result += QStringLiteral(" CXO_MESSAGES"); if (opts & CXO_CSRMESSAGES) result += QStringLiteral(" CXO_CSRMESSAGES"); if (m_tiltSupport) result += QStringLiteral(" tilt"); return result; } void QWindowsTabletSupport::notifyActivate() { // Cooperate with other tablet applications, but when we get focus, I want to use the tablet. const bool result = QWindowsTabletSupport::m_winTab32DLL.wTEnable(m_context, true) && QWindowsTabletSupport::m_winTab32DLL.wTOverlap(m_context, true); dbgTablet << __FUNCTION__ << result; } static inline int indexOfDevice(const QVector &devices, qint64 uniqueId) { for (int i = 0; i < devices.size(); ++i) if (devices.at(i).uniqueId == uniqueId) return i; return -1; } static inline QTabletEvent::TabletDevice deviceType(const UINT cursorType) { if (((cursorType & 0x0006) == 0x0002) && ((cursorType & CursorTypeBitMask) != 0x0902)) return QTabletEvent::Stylus; if (cursorType == 0x4020) // Surface Pro 2 tablet device return QTabletEvent::Stylus; switch (cursorType & CursorTypeBitMask) { case 0x0802: return QTabletEvent::Stylus; case 0x0902: return QTabletEvent::Airbrush; case 0x0004: return QTabletEvent::FourDMouse; case 0x0006: return QTabletEvent::Puck; case 0x0804: return QTabletEvent::RotationStylus; default: break; } return QTabletEvent::NoDevice; }; static inline QTabletEvent::PointerType pointerType(unsigned pkCursor) { switch (pkCursor % 3) { // %3 for dual track case 0: return QTabletEvent::Cursor; case 1: return QTabletEvent::Pen; case 2: return QTabletEvent::Eraser; default: break; } return QTabletEvent::UnknownPointer; } QDebug operator<<(QDebug d, const QWindowsTabletDeviceData &t) { d << "TabletDevice id:" << t.uniqueId << " pressure: " << t.minPressure << ".." << t.maxPressure << " tan pressure: " << t.minTanPressure << ".." << t.maxTanPressure << " area:" << t.minX << t.minY << t.minZ << ".." << t.maxX << t.maxY << t.maxZ << " device " << t.currentDevice << " pointer " << t.currentPointerType; return d; } QWindowsTabletDeviceData QWindowsTabletSupport::tabletInit(const quint64 uniqueId, const UINT cursorType) const { QWindowsTabletDeviceData result; result.uniqueId = uniqueId; /* browse WinTab's many info items to discover pressure handling. */ AXIS axis; LOGCONTEXT lc; /* get the current context for its device variable. */ QWindowsTabletSupport::m_winTab32DLL.wTGet(m_context, &lc); if (KisTabletDebugger::instance()->initializationDebugEnabled()) { printContext(lc); } /* get the size of the pressure axis. */ QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES + lc.lcDevice, DVC_NPRESSURE, &axis); result.minPressure = int(axis.axMin); result.maxPressure = int(axis.axMax); QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES + lc.lcDevice, DVC_TPRESSURE, &axis); result.minTanPressure = int(axis.axMin); result.maxTanPressure = int(axis.axMax); LOGCONTEXT defaultLc; + /* get default region */ QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEFCONTEXT, 0, &defaultLc); result.maxX = int(defaultLc.lcInExtX) - int(defaultLc.lcInOrgX); result.maxY = int(defaultLc.lcInExtY) - int(defaultLc.lcInOrgY); result.maxZ = int(defaultLc.lcInExtZ) - int(defaultLc.lcInOrgZ); result.currentDevice = deviceType(cursorType); + // Define a rectangle representing the whole screen as seen by Wintab. + QRect qtDesktopRect = QApplication::desktop()->geometry(); + QRect wintabDesktopRect(defaultLc.lcSysOrgX, defaultLc.lcSysOrgY, + defaultLc.lcSysExtX, defaultLc.lcSysExtY); + qDebug() << ppVar(qtDesktopRect); + qDebug() << ppVar(wintabDesktopRect); + + // Show screen choice dialog + { + KisScreenSizeChoiceDialog dlg(0, + wintabDesktopRect, + qtDesktopRect); + + KisExtendedModifiersMapper mapper; + KisExtendedModifiersMapper::ExtendedModifiers modifiers = + mapper.queryExtendedModifiers(); + + if (modifiers.contains(Qt::Key_Shift) || + (!dlg.canUseDefaultSettings() && + qtDesktopRect != wintabDesktopRect)) { + + dialogOpen = true; + dlg.exec(); + } + + result.virtualDesktopArea = dlg.screenRect(); + dialogOpen = false; + } - // These define a rectangle representing the whole screen as seen by Wintab. - result.virtualDesktopArea = QRect(defaultLc.lcSysOrgX, defaultLc.lcSysOrgY, - defaultLc.lcSysExtX, defaultLc.lcSysExtY); return result; }; bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, LPARAM lParam) { auto sendProximityEvent = [&](QEvent::Type type) { QPointF emptyPos; qreal zero = 0.0; QTabletEvent e(type, emptyPos, emptyPos, 0, m_devices.at(m_currentDevice).currentPointerType, zero, 0, 0, zero, zero, 0, Qt::NoModifier, m_devices.at(m_currentDevice).uniqueId, Qt::NoButton, (Qt::MouseButtons)0); qApp->sendEvent(qApp, &e); }; if (!LOWORD(lParam)) { // dbgTablet << "leave proximity for device #" << m_currentDevice; sendProximityEvent(QEvent::TabletLeaveProximity); return true; } PACKET proximityBuffer[1]; // we are only interested in the first packet in this case const int totalPacks = QWindowsTabletSupport::m_winTab32DLL.wTPacketsGet(m_context, 1, proximityBuffer); if (!totalPacks) return false; UINT pkCursor = proximityBuffer[0].pkCursor; // initializing and updating the cursor should be done in response to // WT_CSRCHANGE. We do it in WT_PROXIMITY because some wintab never send // the event WT_CSRCHANGE even if asked with CXO_CSRMESSAGES tabletUpdateCursor(pkCursor); // dbgTablet << "enter proximity for device #" << m_currentDevice << m_devices.at(m_currentDevice); sendProximityEvent(QEvent::TabletEnterProximity); return true; } bool QWindowsTabletSupport::translateTabletPacketEvent() { static PACKET localPacketBuf[TabletPacketQSize]; // our own tablet packet queue. const int packetCount = QWindowsTabletSupport::m_winTab32DLL.wTPacketsGet(m_context, TabletPacketQSize, &localPacketBuf); - if (!packetCount || m_currentDevice < 0) + if (!packetCount || m_currentDevice < 0 || dialogOpen) return false; // In contrast to Qt, these will not be "const" during our loop. // This is because the Surface Pro 3 may cause us to switch devices. QWindowsTabletDeviceData tabletData = m_devices.at(m_currentDevice); int currentDevice = tabletData.currentDevice; int currentPointerType = tabletData.currentPointerType; // static Qt::MouseButtons buttons = Qt::NoButton, btnOld, btnChange; static DWORD btnNew, btnOld, btnChange; // The tablet can be used in 2 different modes, depending on its settings: // 1) Absolute (pen) mode: // The coordinates are scaled to the virtual desktop (by default). The user // can also choose to scale to the monitor or a region of the screen. // When entering proximity, the tablet driver snaps the mouse pointer to the // tablet position scaled to that area and keeps it in sync. // 2) Relative (mouse) mode: // The pen follows the mouse. The constant 'absoluteRange' specifies the // manhattanLength difference for detecting if a tablet input device is in this mode, // in which case we snap the position to the mouse position. // It seems there is no way to find out the mode programmatically, the LOGCONTEXT orgX/Y/Ext // area is always the virtual desktop. static qreal dpr = 1.0; auto activeWindow = qApp->activeWindow(); if (activeWindow) { dpr = activeWindow->devicePixelRatio(); } const Qt::KeyboardModifiers keyboardModifiers = QApplication::queryKeyboardModifiers(); for (int i = 0; i < packetCount; ++i) { const PACKET &packet = localPacketBuf[i]; btnOld = btnNew; btnNew = localPacketBuf[i].pkButtons; btnChange = btnOld ^ btnNew; bool buttonPressed = btnChange && btnNew > btnOld; bool buttonReleased = btnChange && btnNew < btnOld; bool anyButtonsStillPressed = btnNew; Qt::MouseButton button = Qt::NoButton; Qt::MouseButtons buttons; globalButtonsConverter->convert(btnOld, btnNew, &button, &buttons, tabletData); QEvent::Type type = QEvent::TabletMove; if (buttonPressed && button != Qt::NoButton) { type = QEvent::TabletPress; } else if (buttonReleased && button != Qt::NoButton) { type = QEvent::TabletRelease; } const int z = currentDevice == QTabletEvent::FourDMouse ? int(packet.pkZ) : 0; // This code is to delay the tablet data one cycle to sync with the mouse location. QPointF globalPosF = m_oldGlobalPosF / dpr; // Convert from "native" to "device independent pixels." m_oldGlobalPosF = tabletData.scaleCoordinates(packet.pkX, packet.pkY, tabletData.virtualDesktopArea); QPoint globalPos = globalPosF.toPoint(); // Find top-level window QWidget *w = targetWindow; // If we had a target already, use it. if (!w) { w = qApp->activePopupWidget(); if (!w) w = qApp->activeModalWidget(); if (!w) w = qApp->topLevelAt(globalPos); if (!w) continue; w = w->window(); } const QPoint localPos = w->mapFromGlobal(globalPos); const QPointF delta = globalPosF - globalPos; const QPointF localPosF = globalPosF + QPointF(localPos - globalPos) + delta; const qreal pressureNew = packet.pkButtons && (currentPointerType == QTabletEvent::Pen || currentPointerType == QTabletEvent::Eraser) ? m_devices.at(m_currentDevice).scalePressure(packet.pkNormalPressure) : qreal(0); const qreal tangentialPressure = currentDevice == QTabletEvent::Airbrush ? m_devices.at(m_currentDevice).scaleTangentialPressure(packet.pkTangentPressure) : qreal(0); int tiltX = 0; int tiltY = 0; qreal rotation = 0; if (m_tiltSupport) { // Convert from azimuth and altitude to x tilt and y tilt. What // follows is the optimized version. Here are the equations used: // X = sin(azimuth) * cos(altitude) // Y = cos(azimuth) * cos(altitude) // Z = sin(altitude) // X Tilt = arctan(X / Z) // Y Tilt = arctan(Y / Z) const double radAzim = (packet.pkOrientation.orAzimuth / 10.0) * (M_PI / 180); const double tanAlt = std::tan((std::abs(packet.pkOrientation.orAltitude / 10.0)) * (M_PI / 180)); const double degX = std::atan(std::sin(radAzim) / tanAlt); const double degY = std::atan(std::cos(radAzim) / tanAlt); tiltX = int(degX * (180 / M_PI)); tiltY = int(-degY * (180 / M_PI)); rotation = 360.0 - (packet.pkOrientation.orTwist / 10.0); if (rotation > 180.0) rotation -= 360.0; } // This is adds *a lot* of noise to the output log if (false) { dbgTablet << "Packet #" << (i+1) << '/' << packetCount << "button:" << packet.pkButtons << globalPosF << z << "to:" << w << localPos << "(packet" << packet.pkX << packet.pkY << ") dev:" << currentDevice << "pointer:" << currentPointerType << "P:" << pressureNew << "tilt:" << tiltX << ',' << tiltY << "tanP:" << tangentialPressure << "rotation:" << rotation; } // Reusable helper function. Better than compiler macros! auto sendTabletEvent = [&](QTabletEvent::Type t){ handleTabletEvent(w, localPosF, globalPosF, currentDevice, currentPointerType, button, buttons, pressureNew, tiltX, tiltY, tangentialPressure, rotation, z, m_devices.at(m_currentDevice).uniqueId, keyboardModifiers, t, packet.pkTime); }; /** * Workaround to deal with "inline" tool switches. * These are caused by the eraser trigger button on the Surface Pro 3. * We shoot out a tabletUpdateCursor request and a switchInputDevice request. */ if (isSurfacePro3 && (packet.pkCursor != currentPkCursor)) { dbgTablet << "Got an inline tool switch."; // Send tablet release event. sendTabletEvent(QTabletEvent::TabletRelease); // Read the new cursor info. UINT pkCursor = packet.pkCursor; tabletUpdateCursor(pkCursor); // Update the local loop variables. tabletData = m_devices.at(m_currentDevice); currentDevice = deviceType(tabletData.currentDevice); currentPointerType = pointerType(pkCursor); sendTabletEvent(QTabletEvent::TabletPress); } sendTabletEvent(type); } // Loop over packets return true; } void QWindowsTabletSupport::tabletUpdateCursor(const int pkCursor) { UINT physicalCursorId; QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_CURSORS + pkCursor, CSR_PHYSID, &physicalCursorId); UINT cursorType; QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_CURSORS + pkCursor, CSR_TYPE, &cursorType); const qint64 uniqueId = (qint64(cursorType & DeviceIdMask) << 32L) | qint64(physicalCursorId); m_currentDevice = indexOfDevice(m_devices, uniqueId); if (m_currentDevice < 0) { m_currentDevice = m_devices.size(); m_devices.push_back(tabletInit(uniqueId, cursorType)); // Note: ideally we might check this button map for changes every // update. However there seems to be an issue with Wintab altering the // button map when the user right-clicks in Krita while another // application has focus. This forces Krita to load button settings only // once, when the tablet is first detected. // // See https://bugs.kde.org/show_bug.cgi?id=359561 BYTE logicalButtons[32]; memset(logicalButtons, 0, 32); m_winTab32DLL.wTInfo(WTI_CURSORS + pkCursor, CSR_SYSBTNMAP, &logicalButtons); m_devices[m_currentDevice].buttonsMap[0x1] = logicalButtons[0]; m_devices[m_currentDevice].buttonsMap[0x2] = logicalButtons[1]; m_devices[m_currentDevice].buttonsMap[0x4] = logicalButtons[2]; } m_devices[m_currentDevice].currentPointerType = pointerType(pkCursor); currentPkCursor = pkCursor; // Check tablet name to enable Surface Pro 3 workaround. #ifdef UNICODE if (!isSurfacePro3) { - UINT nameLength = QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES, DVC_NAME, 0); + /** + * Some really "nice" tablet drivers don't know that trhey are + * supposed to return their name length when the buffer is + * null and they try to write into it effectively causing a + * suicide. So we cannot rely on it :( + * + * We workaround it by just allocating a big array and hoping + * for the best. + * + * Failing tablets: + * - Adesso Cybertablet M14 + * - Peritab-302 + * - Trust Tablet TB7300 + * - VisTablet Realm Pro + * - Aiptek 14000u (latest driver: v5.03, 2013-10-21) + * - Genius G-Pen F350 + * - Genius G-Pen 560 (supported on win7 only) + */ + + // we cannot use the correct api :( + // UINT nameLength = QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES, DVC_NAME, 0); + + // 1024 chars should be enough for everyone! (c) + UINT nameLength = 1024; + TCHAR* dvcName = new TCHAR[nameLength + 1]; - QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES, DVC_NAME, dvcName); + memset(dvcName, 0, sizeof(TCHAR) * nameLength); + UINT writtenBytes = QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES, DVC_NAME, dvcName); + + if (writtenBytes > sizeof(TCHAR) * nameLength) { + qWarning() << "WINTAB WARNING: tablet name is too long!" << writtenBytes; + + // avoid crash when trying to read it + dvcName[nameLength - 1] = (TCHAR)0; + } + QString qDvcName = QString::fromWCharArray((const wchar_t*)dvcName); dbgInput << "DVC_NAME =" << qDvcName; // Name changed between older and newer Surface Pro 3 drivers if (qDvcName == QString::fromLatin1("N-trig DuoSense device") || qDvcName == QString::fromLatin1("Microsoft device")) { dbgInput << "This looks like a Surface Pro 3. Enabling eraser workaround."; isSurfacePro3 = true; } delete[] dvcName; } #endif } QT_END_NAMESPACE diff --git a/libs/ui/input/wintab/qxcbconnection.cpp b/libs/ui/input/wintab/qxcbconnection.cpp index 6dd7115b26..2ac662f15c 100644 --- a/libs/ui/input/wintab/qxcbconnection.cpp +++ b/libs/ui/input/wintab/qxcbconnection.cpp @@ -1,765 +1,765 @@ /**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** 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 The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://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. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qxcbconnection_xi2.h" #include #include #include #include #include #include #include #include Q_LOGGING_CATEGORY(lcQpaXInput, "qt.qpa.input") Q_LOGGING_CATEGORY(lcQpaXInputDevices, "qt.qpa.input.devices") Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen") QXcbConnection::QXcbConnection(bool canGrabServer, const char *displayName) : m_connection(0) , m_canGrabServer(canGrabServer) , m_displayName(displayName ? QByteArray(displayName) : qgetenv("DISPLAY")) #ifdef XCB_USE_XLIB , m_xlib_display(0) #endif { m_connection = QX11Info::connection(); m_xlib_display = QX11Info::display(); if (!m_connection || xcb_connection_has_error(m_connection)) { qFatal("QXcbConnection: Could not connect to display %s", m_displayName.constData()); } initializeAllAtoms(); #if defined(XCB_USE_XINPUT2) initializeXInput2(); #endif } QXcbConnection::~QXcbConnection() { #if defined(XCB_USE_XINPUT2) finalizeXInput2(); #endif } QXcbAtom::Atom QXcbConnection::qatom(xcb_atom_t xatom) const { return static_cast(std::find(m_allAtoms, m_allAtoms + QXcbAtom::NAtoms, xatom) - m_allAtoms); } void *QXcbConnection::xlib_display() const { return m_xlib_display; } QByteArray QXcbConnection::atomName(xcb_atom_t atom) { if (!atom) return QByteArray(); xcb_generic_error_t *error = 0; xcb_get_atom_name_cookie_t cookie = Q_XCB_CALL(xcb_get_atom_name(xcb_connection(), atom)); xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(xcb_connection(), cookie, &error); if (error) { qWarning() << "QXcbConnection::atomName: bad Atom" << atom; free(error); } if (reply) { QByteArray result(xcb_get_atom_name_name(reply), xcb_get_atom_name_name_length(reply)); free(reply); return result; } return QByteArray(); } static const char * xcb_atomnames = { // window-manager <-> client protocols "WM_PROTOCOLS\0" "WM_DELETE_WINDOW\0" "WM_TAKE_FOCUS\0" "_NET_WM_PING\0" "_NET_WM_CONTEXT_HELP\0" "_NET_WM_SYNC_REQUEST\0" "_NET_WM_SYNC_REQUEST_COUNTER\0" "MANAGER\0" "_NET_SYSTEM_TRAY_OPCODE\0" // ICCCM window state "WM_STATE\0" "WM_CHANGE_STATE\0" "WM_CLASS\0" "WM_NAME\0" // Session management "WM_CLIENT_LEADER\0" "WM_WINDOW_ROLE\0" "SM_CLIENT_ID\0" // Clipboard "CLIPBOARD\0" "INCR\0" "TARGETS\0" "MULTIPLE\0" "TIMESTAMP\0" "SAVE_TARGETS\0" "CLIP_TEMPORARY\0" "_QT_SELECTION\0" "_QT_CLIPBOARD_SENTINEL\0" "_QT_SELECTION_SENTINEL\0" "CLIPBOARD_MANAGER\0" "RESOURCE_MANAGER\0" "_XSETROOT_ID\0" "_QT_SCROLL_DONE\0" "_QT_INPUT_ENCODING\0" "_QT_CLOSE_CONNECTION\0" "_MOTIF_WM_HINTS\0" "DTWM_IS_RUNNING\0" "ENLIGHTENMENT_DESKTOP\0" "_DT_SAVE_MODE\0" "_SGI_DESKS_MANAGER\0" // EWMH (aka NETWM) "_NET_SUPPORTED\0" "_NET_VIRTUAL_ROOTS\0" "_NET_WORKAREA\0" "_NET_MOVERESIZE_WINDOW\0" "_NET_WM_MOVERESIZE\0" "_NET_WM_NAME\0" "_NET_WM_ICON_NAME\0" "_NET_WM_ICON\0" "_NET_WM_PID\0" "_NET_WM_WINDOW_OPACITY\0" "_NET_WM_STATE\0" "_NET_WM_STATE_ABOVE\0" "_NET_WM_STATE_BELOW\0" "_NET_WM_STATE_FULLSCREEN\0" "_NET_WM_STATE_MAXIMIZED_HORZ\0" "_NET_WM_STATE_MAXIMIZED_VERT\0" "_NET_WM_STATE_MODAL\0" "_NET_WM_STATE_STAYS_ON_TOP\0" "_NET_WM_STATE_DEMANDS_ATTENTION\0" "_NET_WM_USER_TIME\0" "_NET_WM_USER_TIME_WINDOW\0" "_NET_WM_FULL_PLACEMENT\0" "_NET_WM_WINDOW_TYPE\0" "_NET_WM_WINDOW_TYPE_DESKTOP\0" "_NET_WM_WINDOW_TYPE_DOCK\0" "_NET_WM_WINDOW_TYPE_TOOLBAR\0" "_NET_WM_WINDOW_TYPE_MENU\0" "_NET_WM_WINDOW_TYPE_UTILITY\0" "_NET_WM_WINDOW_TYPE_SPLASH\0" "_NET_WM_WINDOW_TYPE_DIALOG\0" "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU\0" "_NET_WM_WINDOW_TYPE_POPUP_MENU\0" "_NET_WM_WINDOW_TYPE_TOOLTIP\0" "_NET_WM_WINDOW_TYPE_NOTIFICATION\0" "_NET_WM_WINDOW_TYPE_COMBO\0" "_NET_WM_WINDOW_TYPE_DND\0" "_NET_WM_WINDOW_TYPE_NORMAL\0" "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE\0" "_KDE_NET_WM_FRAME_STRUT\0" "_NET_FRAME_EXTENTS\0" "_NET_STARTUP_INFO\0" "_NET_STARTUP_INFO_BEGIN\0" "_NET_SUPPORTING_WM_CHECK\0" "_NET_WM_CM_S0\0" "_NET_SYSTEM_TRAY_VISUAL\0" "_NET_ACTIVE_WINDOW\0" // Property formats "TEXT\0" "UTF8_STRING\0" "CARDINAL\0" // xdnd "XdndEnter\0" "XdndPosition\0" "XdndStatus\0" "XdndLeave\0" "XdndDrop\0" "XdndFinished\0" "XdndTypeList\0" "XdndActionList\0" "XdndSelection\0" "XdndAware\0" "XdndProxy\0" "XdndActionCopy\0" "XdndActionLink\0" "XdndActionMove\0" "XdndActionPrivate\0" // Motif DND "_MOTIF_DRAG_AND_DROP_MESSAGE\0" "_MOTIF_DRAG_INITIATOR_INFO\0" "_MOTIF_DRAG_RECEIVER_INFO\0" "_MOTIF_DRAG_WINDOW\0" "_MOTIF_DRAG_TARGETS\0" "XmTRANSFER_SUCCESS\0" "XmTRANSFER_FAILURE\0" // Xkb "_XKB_RULES_NAMES\0" // XEMBED "_XEMBED\0" "_XEMBED_INFO\0" // XInput2 "Button Left\0" "Button Middle\0" "Button Right\0" "Button Wheel Up\0" "Button Wheel Down\0" "Button Horiz Wheel Left\0" "Button Horiz Wheel Right\0" "Abs MT Position X\0" "Abs MT Position Y\0" "Abs MT Touch Major\0" "Abs MT Touch Minor\0" "Abs MT Pressure\0" "Abs MT Tracking ID\0" "Max Contacts\0" "Rel X\0" "Rel Y\0" // XInput2 tablet "Abs X\0" "Abs Y\0" "Abs Pressure\0" "Abs Tilt X\0" "Abs Tilt Y\0" "Abs Wheel\0" "Abs Distance\0" "Wacom Serial IDs\0" "INTEGER\0" "Rel Horiz Wheel\0" "Rel Vert Wheel\0" "Rel Horiz Scroll\0" "Rel Vert Scroll\0" "_XSETTINGS_SETTINGS\0" "_COMPIZ_DECOR_PENDING\0" "_COMPIZ_DECOR_REQUEST\0" "_COMPIZ_DECOR_DELETE_PIXMAP\0" // \0\0 terminates loop. }; void QXcbConnection::initializeAllAtoms() { const char *names[QXcbAtom::NAtoms]; const char *ptr = xcb_atomnames; int i = 0; while (*ptr) { names[i++] = ptr; while (*ptr) ++ptr; ++ptr; } Q_ASSERT(i == QXcbAtom::NPredefinedAtoms); QByteArray settings_atom_name("_QT_SETTINGS_TIMESTAMP_"); settings_atom_name += m_displayName; names[i++] = settings_atom_name; xcb_intern_atom_cookie_t cookies[QXcbAtom::NAtoms]; Q_ASSERT(i == QXcbAtom::NAtoms); for (i = 0; i < QXcbAtom::NAtoms; ++i) cookies[i] = xcb_intern_atom(xcb_connection(), false, strlen(names[i]), names[i]); for (i = 0; i < QXcbAtom::NAtoms; ++i) { xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xcb_connection(), cookies[i], 0); m_allAtoms[i] = reply->atom; free(reply); } } bool QXcbConnection::xi2MouseEvents() const { static bool mouseViaXI2 = !qEnvironmentVariableIsSet("QT_XCB_NO_XI2_MOUSE"); return mouseViaXI2; } void QXcbConnection::notifyEnterEvent(xcb_enter_notify_event_t *event) { xcb_window_t window; // first cleaning up deleted windows: assuming 0 is not a valid window id while ((window = m_windowMapper.key(0,0)) != 0) { m_windowMapper.remove(window); } window = event->event; if (!m_windowMapper.contains(window)) { QWidget *widget = QWidget::find(window); if (widget) { QWindow *windowHandle = widget->windowHandle(); m_windowMapper.insert(window, windowHandle); } } } QWindow* QXcbConnection::windowFromId(xcb_window_t id) { return m_windowMapper.value(id, 0); } static int xi2ValuatorOffset(unsigned char *maskPtr, int maskLen, int number) { int offset = 0; for (int i = 0; i < maskLen; i++) { if (number < 8) { if ((maskPtr[i] & (1 << number)) == 0) return -1; } for (int j = 0; j < 8; j++) { if (j == number) return offset; if (maskPtr[i] & (1 << j)) offset++; } number -= 8; } return -1; } bool QXcbConnection::xi2GetValuatorValueIfSet(void *event, int valuatorNum, double *value) { xXIDeviceEvent *xideviceevent = static_cast(event); unsigned char *buttonsMaskAddr = (unsigned char*)&xideviceevent[1]; unsigned char *valuatorsMaskAddr = buttonsMaskAddr + xideviceevent->buttons_len * 4; FP3232 *valuatorsValuesAddr = (FP3232*)(valuatorsMaskAddr + xideviceevent->valuators_len * 4); int valuatorOffset = xi2ValuatorOffset(valuatorsMaskAddr, xideviceevent->valuators_len, valuatorNum); if (valuatorOffset < 0) return false; *value = valuatorsValuesAddr[valuatorOffset].integral; *value += ((double)valuatorsValuesAddr[valuatorOffset].frac / (1 << 16) / (1 << 16)); return true; } // Starting from the xcb version 1.9.3 struct xcb_ge_event_t has changed: // - "pad0" became "extension" // - "pad1" and "pad" became "pad0" // New and old version of this struct share the following fields: // NOTE: API might change again in the next release of xcb in which case this comment will // need to be updated to reflect the reality. typedef struct qt_xcb_ge_event_t { uint8_t response_type; uint8_t extension; uint16_t sequence; uint32_t length; uint16_t event_type; } qt_xcb_ge_event_t; bool QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *ev, int opCode) { qt_xcb_ge_event_t *event = (qt_xcb_ge_event_t *)ev; // xGenericEvent has "extension" on the second byte, the same is true for xcb_ge_event_t starting from // the xcb version 1.9.3, prior to that it was called "pad0". if (event->extension == opCode) { // xcb event structs contain stuff that wasn't on the wire, the full_sequence field // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes. // Move this data back to have the same layout in memory as it was on the wire // and allow casting, overwriting the full_sequence field. memmove((char*) event + 32, (char*) event + 36, event->length * 4); return true; } return false; } class Q_GUI_EXPORT QWindowSystemInterfacePrivate { public: enum EventType { UserInputEvent = 0x100, Close = UserInputEvent | 0x01, GeometryChange = 0x02, Enter = UserInputEvent | 0x03, Leave = UserInputEvent | 0x04, ActivatedWindow = 0x05, WindowStateChanged = 0x06, Mouse = UserInputEvent | 0x07, FrameStrutMouse = UserInputEvent | 0x08, Wheel = UserInputEvent | 0x09, Key = UserInputEvent | 0x0a, Touch = UserInputEvent | 0x0b, ScreenOrientation = 0x0c, ScreenGeometry = 0x0d, ScreenAvailableGeometry = 0x0e, ScreenLogicalDotsPerInch = 0x0f, ScreenRefreshRate = 0x10, ThemeChange = 0x11, Expose_KRITA_XXX = 0x12, FileOpen = UserInputEvent | 0x13, Tablet = UserInputEvent | 0x14, TabletEnterProximity = UserInputEvent | 0x15, TabletLeaveProximity = UserInputEvent | 0x16, PlatformPanel = UserInputEvent | 0x17, ContextMenu = UserInputEvent | 0x18, EnterWhatsThisMode = UserInputEvent | 0x19, #ifndef QT_NO_GESTURES Gesture = UserInputEvent | 0x1a, #endif ApplicationStateChanged = 0x19, FlushEvents = 0x20, WindowScreenChanged = 0x21 }; class WindowSystemEvent { public: enum { Synthetic = 0x1, NullWindow = 0x2 }; explicit WindowSystemEvent(EventType t) : type(t), flags(0) { } virtual ~WindowSystemEvent() { } bool synthetic() const { return flags & Synthetic; } bool nullWindow() const { return flags & NullWindow; } EventType type; int flags; }; class UserEvent : public WindowSystemEvent { public: UserEvent(QWindow * w, ulong time, EventType t) : WindowSystemEvent(t), window(w), timestamp(time) { if (!w) flags |= NullWindow; } QPointer window; unsigned long timestamp; }; class InputEvent: public UserEvent { public: InputEvent(QWindow * w, ulong time, EventType t, Qt::KeyboardModifiers mods) : UserEvent(w, time, t), modifiers(mods) {} Qt::KeyboardModifiers modifiers; }; class TabletEvent : public InputEvent { public: static void handleTabletEvent(QWindow *w, const QPointF &local, const QPointF &global, int device, int pointerType, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt, qreal tangentialPressure, qreal rotation, int z, qint64 uid, Qt::KeyboardModifiers modifiers = Qt::NoModifier); TabletEvent(QWindow *w, ulong time, const QPointF &local, const QPointF &global, int device, int pointerType, Qt::MouseButtons b, qreal pressure, int xTilt, int yTilt, qreal tpressure, qreal rotation, int z, qint64 uid, Qt::KeyboardModifiers mods) : InputEvent(w, time, Tablet, mods), buttons(b), local(local), global(global), device(device), pointerType(pointerType), pressure(pressure), xTilt(xTilt), yTilt(yTilt), tangentialPressure(tpressure), rotation(rotation), z(z), uid(uid) { } Qt::MouseButtons buttons; QPointF local; QPointF global; int device; int pointerType; qreal pressure; int xTilt; int yTilt; qreal tangentialPressure; qreal rotation; int z; qint64 uid; }; class WheelEvent : public InputEvent { public: WheelEvent(QWindow *w, ulong time, const QPointF & local, const QPointF & global, QPoint pixelD, QPoint angleD, int qt4D, Qt::Orientation qt4O, Qt::KeyboardModifiers mods, Qt::ScrollPhase phase = Qt::ScrollUpdate, Qt::MouseEventSource src = Qt::MouseEventNotSynthesized) : InputEvent(w, time, Wheel, mods), pixelDelta(pixelD), angleDelta(angleD), qt4Delta(qt4D), qt4Orientation(qt4O), localPos(local), globalPos(global), phase(phase), source(src) { } QPoint pixelDelta; QPoint angleDelta; int qt4Delta; Qt::Orientation qt4Orientation; QPointF localPos; QPointF globalPos; Qt::ScrollPhase phase; Qt::MouseEventSource source; }; }; void processTabletEvent(QWindowSystemInterfacePrivate::TabletEvent *e); static QElapsedTimer g_eventTimer; struct EventTimerStaticInitializer { EventTimerStaticInitializer() { g_eventTimer.start(); } }; EventTimerStaticInitializer __timerStaticInitializer; Qt::MouseButtons tabletState = Qt::NoButton; -QWidget *tabletPressWidget = 0; +QPointer tabletPressWidget = 0; void QWindowSystemInterface::handleTabletEvent(QWindow *w, const QPointF &local, const QPointF &global, int device, int pointerType, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt, qreal tangentialPressure, qreal rotation, int z, qint64 uid, Qt::KeyboardModifiers modifiers) { qint64 timestamp = g_eventTimer.elapsed(); QWindowSystemInterfacePrivate::TabletEvent *e = new QWindowSystemInterfacePrivate::TabletEvent(w, timestamp, local, global, device, pointerType, buttons, pressure, xTilt, yTilt, tangentialPressure, rotation, z, uid, modifiers); processTabletEvent(e); } void processTabletEvent(QWindowSystemInterfacePrivate::TabletEvent *e) { #ifndef QT_NO_TABLETEVENT QEvent::Type type = QEvent::TabletMove; if (e->buttons != tabletState) type = (e->buttons > tabletState) ? QEvent::TabletPress : QEvent::TabletRelease; bool localValid = true; QWidget *targetWidget = 0; if (tabletPressWidget) { targetWidget = tabletPressWidget; localValid = false; } else if (e->window) { /** * Here we use a weird way of converting QWindow into a * QWidget. The problem is that the Qt itself does it by just * converting QWindow into QWidgetWindow. But the latter one * is private, so we cannot use it. * * We also cannot use QApplication::widegtAt(). We *MUST NOT*! * There is some but in XCB: if we call * QApplication::topLevelAt() during the event processing, the * Enter/Leave events stop arriving. Or, more precisely, they * start to errive at random points in time. Which makes * KisShortcutMatcher go crazy of course. * * So instead of just fetching the toplevel window we decrypt * the pointer using WinId mapping. */ targetWidget = QWidget::find(e->window->winId()); if (targetWidget) { QWidget *childWidget = targetWidget->childAt(e->local.toPoint()); if (childWidget) { targetWidget = childWidget; localValid = false; } } } if (!targetWidget) { targetWidget = QApplication::widgetAt(e->global.toPoint()); localValid = false; if (!targetWidget) return; } if (type == QEvent::TabletPress) { tabletPressWidget = targetWidget; } else if (type == QEvent::TabletRelease) { tabletPressWidget = 0; } QPointF local = e->local; if (!localValid) { QPointF delta = e->global - e->global.toPoint(); local = targetWidget->mapFromGlobal(e->global.toPoint()) + delta; } Qt::MouseButtons stateChange = e->buttons ^ tabletState; Qt::MouseButton button = Qt::NoButton; for (int check = Qt::LeftButton; check <= int(Qt::MaxMouseButton); check = check << 1) { if (check & stateChange) { button = Qt::MouseButton(check); break; } } QTabletEvent ev(type, local, e->global, e->device, e->pointerType, e->pressure, e->xTilt, e->yTilt, e->tangentialPressure, e->rotation, e->z, e->modifiers, e->uid, button, e->buttons); ev.setTimestamp(e->timestamp); QGuiApplication::sendEvent(targetWidget, &ev); tabletState = e->buttons; #else Q_UNUSED(e) #endif } void QWindowSystemInterface::handleTabletEnterProximityEvent(int device, int pointerType, qint64 uid) { qint64 timestamp = g_eventTimer.elapsed(); QTabletEvent ev(QEvent::TabletEnterProximity, QPointF(), QPointF(), device, pointerType, 0, 0, 0, 0, 0, 0, Qt::NoModifier, uid, Qt::NoButton, tabletState); ev.setTimestamp(timestamp); QGuiApplication::sendEvent(qGuiApp, &ev); } void QWindowSystemInterface::handleTabletLeaveProximityEvent(int device, int pointerType, qint64 uid) { qint64 timestamp = g_eventTimer.elapsed(); QTabletEvent ev(QEvent::TabletLeaveProximity, QPointF(), QPointF(), device, pointerType, 0, 0, 0, 0, 0, 0, Qt::NoModifier, uid, Qt::NoButton, tabletState); ev.setTimestamp(timestamp); QGuiApplication::sendEvent(qGuiApp, &ev); } void processWheelEvent(QWindowSystemInterfacePrivate::WheelEvent *e); void QWindowSystemInterface::handleWheelEvent(QWindow *tlw, ulong timestamp, const QPointF & local, const QPointF & global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods, Qt::ScrollPhase phase, Qt::MouseEventSource source) { // Qt 4 sends two separate wheel events for horizontal and vertical // deltas. For Qt 5 we want to send the deltas in one event, but at the // same time preserve source and behavior compatibility with Qt 4. // // In addition high-resolution pixel-based deltas are also supported. // Platforms that does not support these may pass a null point here. // Angle deltas must always be sent in addition to pixel deltas. QScopedPointer e; // Pass Qt::ScrollBegin and Qt::ScrollEnd through // even if the wheel delta is null. if (angleDelta.isNull() && phase == Qt::ScrollUpdate) return; // Simple case: vertical deltas only: if (angleDelta.y() != 0 && angleDelta.x() == 0) { e.reset(new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, local, global, pixelDelta, angleDelta, angleDelta.y(), Qt::Vertical, mods, phase, source)); processWheelEvent(e.data()); return; } // Simple case: horizontal deltas only: if (angleDelta.y() == 0 && angleDelta.x() != 0) { e.reset(new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, local, global, pixelDelta, angleDelta, angleDelta.x(), Qt::Horizontal, mods, phase, source)); processWheelEvent(e.data()); return; } // Both horizontal and vertical deltas: Send two wheel events. // The first event contains the Qt 5 pixel and angle delta as points, // and in addition the Qt 4 compatibility vertical angle delta. e.reset(new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, local, global, pixelDelta, angleDelta, angleDelta.y(), Qt::Vertical, mods, phase, source)); processWheelEvent(e.data()); // The second event contains null pixel and angle points and the // Qt 4 compatibility horizontal angle delta. e.reset(new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, local, global, QPoint(), QPoint(), angleDelta.x(), Qt::Horizontal, mods, phase, source)); processWheelEvent(e.data()); } void processWheelEvent(QWindowSystemInterfacePrivate::WheelEvent *e) { #ifndef QT_NO_WHEELEVENT QWindow *window = e->window.data(); QPointF globalPoint = e->globalPos; QPointF localPoint = e->localPos; if (e->nullWindow()) { window = QGuiApplication::topLevelAt(globalPoint.toPoint()); if (window) { QPointF delta = globalPoint - globalPoint.toPoint(); localPoint = window->mapFromGlobal(globalPoint.toPoint()) + delta; } } if (!window) return; // Cut off in Krita... // // QGuiApplicationPrivate::lastCursorPosition = globalPoint; // modifier_buttons = e->modifiers; //if (window->d_func()->blockedByModalWindow) { if (QGuiApplication::modalWindow() && QGuiApplication::modalWindow() != window) { // a modal window is blocking this window, don't allow wheel events through return; } #if QT_VERSION >= 0x050500 QWheelEvent ev(localPoint, globalPoint, e->pixelDelta, e->angleDelta, e->qt4Delta, e->qt4Orientation, QGuiApplication::mouseButtons(), e->modifiers, e->phase, e->source); #else QWheelEvent ev(localPoint, globalPoint, e->pixelDelta, e->angleDelta, e->qt4Delta, e->qt4Orientation, QGuiApplication::mouseButtons(), e->modifiers, e->phase); #endif ev.setTimestamp(e->timestamp); QGuiApplication::sendEvent(window, &ev); #endif /* ifndef QT_NO_WHEELEVENT */ } diff --git a/libs/ui/input/wintab/qxcbconnection_xi2.cpp b/libs/ui/input/wintab/qxcbconnection_xi2.cpp index 8693ac000d..547e8b05d9 100644 --- a/libs/ui/input/wintab/qxcbconnection_xi2.cpp +++ b/libs/ui/input/wintab/qxcbconnection_xi2.cpp @@ -1,968 +1,972 @@ /**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** 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 The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://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. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qxcbconnection_xi2.h" //#include "qxcbconnection.h" //#include "qxcbkeyboard.h" //#include "qxcbscreen.h" //#include "qxcbwindow.h" //#include "qtouchdevice.h" //#include #include #include #include #ifdef XCB_USE_XINPUT2 #include #include struct XInput2TouchDeviceData { XInput2TouchDeviceData() : xiDeviceInfo(0) , qtTouchDevice(0) { } XIDeviceInfo *xiDeviceInfo; QTouchDevice *qtTouchDevice; QHash touchPoints; // Stuff that is relevant only for touchpads QHash pointPressedPosition; // in screen coordinates where each point was pressed QPointF firstPressedPosition; // in screen coordinates where the first point was pressed QPointF firstPressedNormalPosition; // device coordinates (0 to 1, 0 to 1) where the first point was pressed QSizeF size; // device size in mm }; void QXcbConnection::initializeXInput2() { // TODO Qt 6 (or perhaps earlier): remove these redundant env variables if (qEnvironmentVariableIsSet("KIS_QT_XCB_DEBUG_XINPUT")) const_cast(lcQpaXInput()).setEnabled(QtDebugMsg, true); if (qEnvironmentVariableIsSet("KIS_QT_XCB_DEBUG_XINPUT_DEVICES")) const_cast(lcQpaXInputDevices()).setEnabled(QtDebugMsg, true); Display *xDisplay = static_cast(m_xlib_display); if (XQueryExtension(xDisplay, "XInputExtension", &m_xiOpCode, &m_xiEventBase, &m_xiErrorBase)) { int xiMajor = 2; m_xi2Minor = 2; // try 2.2 first, needed for TouchBegin/Update/End if (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) == BadRequest) { m_xi2Minor = 1; // for smooth scrolling 2.1 is enough if (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) == BadRequest) { m_xi2Minor = 0; // for tablet support 2.0 is enough m_xi2Enabled = XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) != BadRequest; } else m_xi2Enabled = true; } else m_xi2Enabled = true; if (m_xi2Enabled) { #ifdef XCB_USE_XINPUT22 qCDebug(lcQpaXInputDevices, "XInput version %d.%d is available and Qt supports 2.2 or greater", xiMajor, m_xi2Minor); #else qCDebug(lcQpaXInputDevices, "XInput version %d.%d is available and Qt supports 2.0", xiMajor, m_xi2Minor); #endif } xi2SetupDevices(); } } void QXcbConnection::xi2SetupDevices() { #ifndef QT_NO_TABLETEVENT m_tabletData.clear(); #endif m_scrollingDevices.clear(); if (!m_xi2Enabled) return; Display *xDisplay = static_cast(m_xlib_display); int deviceCount = 0; XIDeviceInfo *devices = XIQueryDevice(xDisplay, XIAllDevices, &deviceCount); for (int i = 0; i < deviceCount; ++i) { // Only non-master pointing devices are relevant here. if (devices[i].use != XISlavePointer) continue; qCDebug(lcQpaXInputDevices) << "input device "<< devices[i].name; #ifndef QT_NO_TABLETEVENT TabletData tabletData; #endif ScrollingDevice scrollingDevice; for (int c = 0; c < devices[i].num_classes; ++c) { switch (devices[i].classes[c]->type) { case XIValuatorClass: { XIValuatorClassInfo *vci = reinterpret_cast(devices[i].classes[c]); const int valuatorAtom = qatom(vci->label); qCDebug(lcQpaXInputDevices) << " has valuator" << atomName(vci->label) << "recognized?" << (valuatorAtom < QXcbAtom::NAtoms); #ifndef QT_NO_TABLETEVENT if (valuatorAtom < QXcbAtom::NAtoms) { TabletData::ValuatorClassInfo info; info.minVal = vci->min; info.maxVal = vci->max; info.number = vci->number; tabletData.valuatorInfo[valuatorAtom] = info; } #endif // QT_NO_TABLETEVENT if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel) scrollingDevice.lastScrollPosition.setX(vci->value); else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel) scrollingDevice.lastScrollPosition.setY(vci->value); break; } #ifdef XCB_USE_XINPUT21 case XIScrollClass: { XIScrollClassInfo *sci = reinterpret_cast(devices[i].classes[c]); if (sci->scroll_type == XIScrollTypeVertical) { scrollingDevice.orientations |= Qt::Vertical; scrollingDevice.verticalIndex = sci->number; scrollingDevice.verticalIncrement = sci->increment; } else if (sci->scroll_type == XIScrollTypeHorizontal) { scrollingDevice.orientations |= Qt::Horizontal; scrollingDevice.horizontalIndex = sci->number; scrollingDevice.horizontalIncrement = sci->increment; } break; } case XIButtonClass: { XIButtonClassInfo *bci = reinterpret_cast(devices[i].classes[c]); if (bci->num_buttons >= 5) { Atom label4 = bci->labels[3]; Atom label5 = bci->labels[4]; // Some drivers have no labels on the wheel buttons, some have no label on just one and some have no label on // button 4 and the wrong one on button 5. So we just check that they are not labelled with unrelated buttons. if ((!label4 || qatom(label4) == QXcbAtom::ButtonWheelUp || qatom(label4) == QXcbAtom::ButtonWheelDown) && (!label5 || qatom(label5) == QXcbAtom::ButtonWheelUp || qatom(label5) == QXcbAtom::ButtonWheelDown)) scrollingDevice.legacyOrientations |= Qt::Vertical; } if (bci->num_buttons >= 7) { Atom label6 = bci->labels[5]; Atom label7 = bci->labels[6]; if ((!label6 || qatom(label6) == QXcbAtom::ButtonHorizWheelLeft) && (!label7 || qatom(label7) == QXcbAtom::ButtonHorizWheelRight)) scrollingDevice.legacyOrientations |= Qt::Horizontal; } qCDebug(lcQpaXInputDevices, " has %d buttons", bci->num_buttons); break; } #endif case XIKeyClass: qCDebug(lcQpaXInputDevices) << " it's a keyboard"; break; #ifdef XCB_USE_XINPUT22 case XITouchClass: // will be handled in deviceForId() break; #endif default: qCDebug(lcQpaXInputDevices) << " has class" << devices[i].classes[c]->type; break; } } bool isTablet = false; #ifndef QT_NO_TABLETEVENT // If we have found the valuators which we expect a tablet to have, it might be a tablet. if (tabletData.valuatorInfo.contains(QXcbAtom::AbsX) && tabletData.valuatorInfo.contains(QXcbAtom::AbsY) && tabletData.valuatorInfo.contains(QXcbAtom::AbsPressure)) isTablet = true; // But we need to be careful not to take the touch and tablet-button devices as tablets. QByteArray name = QByteArray(devices[i].name).toLower(); QString dbgType = QLatin1String("UNKNOWN"); if (name.contains("eraser")) { isTablet = true; tabletData.pointerType = QTabletEvent::Eraser; dbgType = QLatin1String("eraser"); } else if (name.contains("cursor")) { isTablet = true; tabletData.pointerType = QTabletEvent::Cursor; dbgType = QLatin1String("cursor"); } else if ((name.contains("pen") || name.contains("stylus")) && isTablet) { tabletData.pointerType = QTabletEvent::Pen; dbgType = QLatin1String("pen"); } else if (name.contains("wacom") && isTablet && !name.contains("touch")) { // combined device (evdev) rather than separate pen/eraser (wacom driver) tabletData.pointerType = QTabletEvent::Pen; dbgType = QLatin1String("pen"); } else if (name.contains("aiptek") /* && device == QXcbAtom::KEYBOARD */) { // some "Genius" tablets isTablet = true; tabletData.pointerType = QTabletEvent::Pen; dbgType = QLatin1String("pen"); + } else if (name.contains("uc-logic") && isTablet) { + isTablet = true; + tabletData.pointerType = QTabletEvent::Pen; + dbgType = QLatin1String("pen"); } else { isTablet = false; } if (isTablet) { tabletData.deviceId = devices[i].deviceid; m_tabletData.append(tabletData); qCDebug(lcQpaXInputDevices) << " it's a tablet with pointer type" << dbgType; } #endif // QT_NO_TABLETEVENT #ifdef XCB_USE_XINPUT21 if (scrollingDevice.orientations || scrollingDevice.legacyOrientations) { scrollingDevice.deviceId = devices[i].deviceid; // Only use legacy wheel button events when we don't have real scroll valuators. scrollingDevice.legacyOrientations &= ~scrollingDevice.orientations; m_scrollingDevices.insert(scrollingDevice.deviceId, scrollingDevice); qCDebug(lcQpaXInputDevices) << " it's a scrolling device"; } #endif if (!isTablet) { // touchDeviceForId populates XInput2DeviceData the first time it is called // with a new deviceId. On subsequent calls it will return the cached object. XInput2TouchDeviceData *dev = touchDeviceForId(devices[i].deviceid); if (dev && lcQpaXInputDevices().isDebugEnabled()) { if (dev->qtTouchDevice->type() == QTouchDevice::TouchScreen) qCDebug(lcQpaXInputDevices, " it's a touchscreen with type %d capabilities 0x%X max touch points %d", dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(), dev->qtTouchDevice->maximumTouchPoints()); else if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad) qCDebug(lcQpaXInputDevices, " it's a touchpad with type %d capabilities 0x%X max touch points %d size %f x %f", dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(), dev->qtTouchDevice->maximumTouchPoints(), dev->size.width(), dev->size.height()); } } } XIFreeDeviceInfo(devices); } void QXcbConnection::finalizeXInput2() { foreach (XInput2TouchDeviceData *dev, m_touchDevices) { if (dev->xiDeviceInfo) XIFreeDeviceInfo(dev->xiDeviceInfo); delete dev->qtTouchDevice; delete dev; } } void QXcbConnection::xi2Select(xcb_window_t window) { if (!m_xi2Enabled) return; Display *xDisplay = static_cast(m_xlib_display); unsigned int bitMask = 0; unsigned char *xiBitMask = reinterpret_cast(&bitMask); #ifdef XCB_USE_XINPUT22 if (isAtLeastXI22()) { bitMask |= XI_TouchBeginMask; bitMask |= XI_TouchUpdateMask; bitMask |= XI_TouchEndMask; bitMask |= XI_PropertyEventMask; // for tablets if (xi2MouseEvents()) { // We want both mouse and touch through XI2 if touch is supported (>= 2.2). // The plain xcb press and motion events will not be delivered after this. bitMask |= XI_ButtonPressMask; bitMask |= XI_ButtonReleaseMask; bitMask |= XI_MotionMask; qCDebug(lcQpaXInput, "XInput 2.2: Selecting press/release/motion events in addition to touch"); } XIEventMask mask; mask.mask_len = sizeof(bitMask); mask.mask = xiBitMask; // When xi2MouseEvents() is true (the default), pointer emulation for touch and tablet // events will get disabled. This is preferable for touch, as Qt Quick handles touch events // directly while for others QtGui synthesizes mouse events, not so much for tablets. For // the latter we will synthesize the events ourselves. mask.deviceid = XIAllMasterDevices; Status result = XISelectEvents(xDisplay, window, &mask, 1); if (result != Success) qCDebug(lcQpaXInput, "XInput 2.2: failed to select pointer/touch events, window %x, result %d", window, result); } #endif // XCB_USE_XINPUT22 const bool pointerSelected = isAtLeastXI22() && xi2MouseEvents(); QSet tabletDevices; #ifndef QT_NO_TABLETEVENT if (!m_tabletData.isEmpty()) { unsigned int tabletBitMask; unsigned char *xiTabletBitMask = reinterpret_cast(&tabletBitMask); QVector xiEventMask(m_tabletData.count()); tabletBitMask = XI_PropertyEventMask; if (!pointerSelected) tabletBitMask |= XI_ButtonPressMask | XI_ButtonReleaseMask | XI_MotionMask; for (int i = 0; i < m_tabletData.count(); ++i) { int deviceId = m_tabletData.at(i).deviceId; tabletDevices.insert(deviceId); xiEventMask[i].deviceid = deviceId; xiEventMask[i].mask_len = sizeof(tabletBitMask); xiEventMask[i].mask = xiTabletBitMask; } XISelectEvents(xDisplay, window, xiEventMask.data(), m_tabletData.count()); } #endif // QT_NO_TABLETEVENT #ifdef XCB_USE_XINPUT21 // Enable each scroll device if (!m_scrollingDevices.isEmpty() && !pointerSelected) { // Only when XI2 mouse events are not enabled, otherwise motion and release are selected already. QVector xiEventMask(m_scrollingDevices.size()); unsigned int scrollBitMask; unsigned char *xiScrollBitMask = reinterpret_cast(&scrollBitMask); scrollBitMask = XI_MotionMask; scrollBitMask |= XI_ButtonReleaseMask; int i=0; Q_FOREACH (const ScrollingDevice& scrollingDevice, m_scrollingDevices) { if (tabletDevices.contains(scrollingDevice.deviceId)) continue; // All necessary events are already captured. xiEventMask[i].deviceid = scrollingDevice.deviceId; xiEventMask[i].mask_len = sizeof(scrollBitMask); xiEventMask[i].mask = xiScrollBitMask; i++; } XISelectEvents(xDisplay, window, xiEventMask.data(), i); } #else Q_UNUSED(xiBitMask); #endif { // Listen for hotplug events XIEventMask xiEventMask; bitMask = XI_HierarchyChangedMask; bitMask |= XI_DeviceChangedMask; xiEventMask.deviceid = XIAllDevices; xiEventMask.mask_len = sizeof(bitMask); xiEventMask.mask = xiBitMask; XISelectEvents(xDisplay, window, &xiEventMask, 1); } } XInput2TouchDeviceData *QXcbConnection::touchDeviceForId(int id) { XInput2TouchDeviceData *dev = 0; QHash::const_iterator devIt = m_touchDevices.constFind(id); if (devIt != m_touchDevices.cend()) { dev = devIt.value(); } else { int nrDevices = 0; QTouchDevice::Capabilities caps = 0; dev = new XInput2TouchDeviceData; dev->xiDeviceInfo = XIQueryDevice(static_cast(m_xlib_display), id, &nrDevices); if (nrDevices <= 0) { delete dev; return 0; } int type = -1; int maxTouchPoints = 1; bool hasRelativeCoords = false; for (int i = 0; i < dev->xiDeviceInfo->num_classes; ++i) { XIAnyClassInfo *classinfo = dev->xiDeviceInfo->classes[i]; switch (classinfo->type) { #ifdef XCB_USE_XINPUT22 case XITouchClass: { XITouchClassInfo *tci = reinterpret_cast(classinfo); maxTouchPoints = tci->num_touches; qCDebug(lcQpaXInputDevices, " has touch class with mode %d", tci->mode); switch (tci->mode) { case XIDependentTouch: type = QTouchDevice::TouchPad; break; case XIDirectTouch: type = QTouchDevice::TouchScreen; break; } break; } #endif // XCB_USE_XINPUT22 case XIValuatorClass: { XIValuatorClassInfo *vci = reinterpret_cast(classinfo); if (vci->label == atom(QXcbAtom::AbsMTPositionX)) caps |= QTouchDevice::Position | QTouchDevice::NormalizedPosition; else if (vci->label == atom(QXcbAtom::AbsMTTouchMajor)) caps |= QTouchDevice::Area; else if (vci->label == atom(QXcbAtom::AbsMTPressure) || vci->label == atom(QXcbAtom::AbsPressure)) caps |= QTouchDevice::Pressure; else if (vci->label == atom(QXcbAtom::RelX)) { hasRelativeCoords = true; dev->size.setWidth((vci->max - vci->min) * 1000.0 / vci->resolution); } else if (vci->label == atom(QXcbAtom::RelY)) { hasRelativeCoords = true; dev->size.setHeight((vci->max - vci->min) * 1000.0 / vci->resolution); } else if (vci->label == atom(QXcbAtom::AbsX)) { dev->size.setHeight((vci->max - vci->min) * 1000.0 / vci->resolution); } else if (vci->label == atom(QXcbAtom::AbsY)) { dev->size.setWidth((vci->max - vci->min) * 1000.0 / vci->resolution); } break; } default: break; } } if (type < 0 && caps && hasRelativeCoords) { type = QTouchDevice::TouchPad; if (dev->size.width() < 10 || dev->size.height() < 10 || dev->size.width() > 10000 || dev->size.height() > 10000) dev->size = QSizeF(130, 110); } // WARNING: Krita edit // if (!isAtLeastXI22() || type == QTouchDevice::TouchPad) // caps |= QTouchDevice::MouseEmulation; if (type >= QTouchDevice::TouchScreen && type <= QTouchDevice::TouchPad) { dev->qtTouchDevice = new QTouchDevice; dev->qtTouchDevice->setName(QString::fromUtf8(dev->xiDeviceInfo->name)); dev->qtTouchDevice->setType((QTouchDevice::DeviceType)type); dev->qtTouchDevice->setCapabilities(caps); dev->qtTouchDevice->setMaximumTouchPoints(maxTouchPoints); if (caps != 0) QWindowSystemInterface::registerTouchDevice(dev->qtTouchDevice); m_touchDevices[id] = dev; } else { XIFreeDeviceInfo(dev->xiDeviceInfo); delete dev; dev = 0; } } return dev; } #if defined(XCB_USE_XINPUT21) || !defined(QT_NO_TABLETEVENT) static inline qreal fixed1616ToReal(FP1616 val) { return qreal(val) / 0x10000; } #endif // defined(XCB_USE_XINPUT21) || !defined(QT_NO_TABLETEVENT) bool QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) { if (xi2PrepareXIGenericDeviceEvent(event, m_xiOpCode)) { xXIGenericDeviceEvent *xiEvent = reinterpret_cast(event); int sourceDeviceId = xiEvent->deviceid; // may be the master id xXIDeviceEvent *xiDeviceEvent = 0; QWindow *window = 0; switch (xiEvent->evtype) { case XI_ButtonPress: case XI_ButtonRelease: case XI_Motion: #ifdef XCB_USE_XINPUT22 case XI_TouchBegin: case XI_TouchUpdate: case XI_TouchEnd: #endif { xiDeviceEvent = reinterpret_cast(event); window = windowFromId(xiDeviceEvent->event); sourceDeviceId = xiDeviceEvent->sourceid; // use the actual device id instead of the master break; } case XI_HierarchyChanged: xi2HandleHierachyEvent(xiEvent); return false; case XI_DeviceChanged: xi2HandleDeviceChangedEvent(xiEvent); return false; default: break; } #ifndef QT_NO_TABLETEVENT for (int i = 0; i < m_tabletData.count(); ++i) { if (m_tabletData.at(i).deviceId == sourceDeviceId) { if (xi2HandleTabletEvent(xiEvent, &m_tabletData[i], window)) { return true; } } } #endif // QT_NO_TABLETEVENT #ifdef XCB_USE_XINPUT21 QHash::iterator device = m_scrollingDevices.find(sourceDeviceId); if (device != m_scrollingDevices.end()) xi2HandleScrollEvent(xiEvent, device.value()); // Removed from Qt... #endif // XCB_USE_XINPUT21 #ifdef XCB_USE_XINPUT22 // Removed from Qt... #endif // XCB_USE_XINPUT22 } return false; } #ifdef XCB_USE_XINPUT22 bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab) { if (grab && !canGrab()) return false; int num_devices = 0; Display *xDisplay = static_cast(xlib_display()); XIDeviceInfo *info = XIQueryDevice(xDisplay, XIAllMasterDevices, &num_devices); if (!info) return false; XIEventMask evmask; unsigned char mask[XIMaskLen(XI_LASTEVENT)]; evmask.mask = mask; evmask.mask_len = sizeof(mask); memset(mask, 0, sizeof(mask)); evmask.deviceid = XIAllMasterDevices; XISetMask(mask, XI_ButtonPress); XISetMask(mask, XI_ButtonRelease); XISetMask(mask, XI_Motion); XISetMask(mask, XI_TouchBegin); XISetMask(mask, XI_TouchUpdate); XISetMask(mask, XI_TouchEnd); bool grabbed = true; for (int i = 0; i < num_devices; i++) { int id = info[i].deviceid, n = 0; XIDeviceInfo *deviceInfo = XIQueryDevice(xDisplay, id, &n); if (deviceInfo) { const bool grabbable = deviceInfo->use != XIMasterKeyboard; XIFreeDeviceInfo(deviceInfo); if (!grabbable) continue; } if (!grab) { Status result = XIUngrabDevice(xDisplay, id, CurrentTime); if (result != Success) { grabbed = false; qCDebug(lcQpaXInput, "XInput 2.2: failed to ungrab events for device %d (result %d)", id, result); } } else { Status result = XIGrabDevice(xDisplay, id, w, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &evmask); if (result != Success) { grabbed = false; qCDebug(lcQpaXInput, "XInput 2.2: failed to grab events for device %d on window %x (result %d)", id, w, result); } } } XIFreeDeviceInfo(info); m_xiGrab = grabbed; return grabbed; } #endif // XCB_USE_XINPUT22 void QXcbConnection::xi2HandleHierachyEvent(void *event) { xXIHierarchyEvent *xiEvent = reinterpret_cast(event); // We only care about hotplugged devices if (!(xiEvent->flags & (XISlaveRemoved | XISlaveAdded))) return; xi2SetupDevices(); // Reselect events for all event-listening windows. Q_FOREACH (xcb_window_t window, m_windowMapper.keys()) { xi2Select(window); } } void QXcbConnection::xi2HandleDeviceChangedEvent(void *event) { xXIDeviceChangedEvent *xiEvent = reinterpret_cast(event); // ### If a slave device changes (XIDeviceChange), we should probably run setup on it again. if (xiEvent->reason != XISlaveSwitch) return; #ifdef XCB_USE_XINPUT21 // This code handles broken scrolling device drivers that reset absolute positions // when they are made active. Whenever a new slave device is made active the // primary pointer sends a DeviceChanged event with XISlaveSwitch, and the new // active slave in sourceid. QHash::iterator device = m_scrollingDevices.find(xiEvent->sourceid); if (device == m_scrollingDevices.end()) return; int nrDevices = 0; XIDeviceInfo* xiDeviceInfo = XIQueryDevice(static_cast(m_xlib_display), xiEvent->sourceid, &nrDevices); if (nrDevices <= 0) { qCDebug(lcQpaXInputDevices, "scrolling device %d no longer present", xiEvent->sourceid); return; } updateScrollingDevice(*device, xiDeviceInfo->num_classes, xiDeviceInfo->classes); XIFreeDeviceInfo(xiDeviceInfo); #endif } void QXcbConnection::updateScrollingDevice(ScrollingDevice &scrollingDevice, int num_classes, void *classInfo) { #ifdef XCB_USE_XINPUT21 XIAnyClassInfo **classes = reinterpret_cast(classInfo); QPointF lastScrollPosition; if (lcQpaXInput().isDebugEnabled()) lastScrollPosition = scrollingDevice.lastScrollPosition; for (int c = 0; c < num_classes; ++c) { if (classes[c]->type == XIValuatorClass) { XIValuatorClassInfo *vci = reinterpret_cast(classes[c]); const int valuatorAtom = qatom(vci->label); if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel) scrollingDevice.lastScrollPosition.setX(vci->value); else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel) scrollingDevice.lastScrollPosition.setY(vci->value); } } if (lcQpaXInput().isDebugEnabled() && lastScrollPosition != scrollingDevice.lastScrollPosition) qCDebug(lcQpaXInput, "scrolling device %d moved from (%f, %f) to (%f, %f)", scrollingDevice.deviceId, lastScrollPosition.x(), lastScrollPosition.y(), scrollingDevice.lastScrollPosition.x(), scrollingDevice.lastScrollPosition.y()); #else Q_UNUSED(scrollingDevice); Q_UNUSED(num_classes); Q_UNUSED(classInfo); #endif } Qt::MouseButton QXcbConnection::xiToQtMouseButton(uint32_t b) { switch (b) { case 1: return Qt::LeftButton; case 2: return Qt::MiddleButton; case 3: return Qt::RightButton; // 4-7 are for scrolling default: break; } if (b >= 8 && b <= Qt::MaxMouseButton) return static_cast(Qt::BackButton << (b - 8)); return Qt::NoButton; } static QTabletEvent::TabletDevice toolIdToTabletDevice(quint32 toolId) { // keep in sync with wacom_intuos_inout() in Linux kernel driver wacom_wac.c switch (toolId) { case 0xd12: case 0x912: case 0x112: case 0x913: /* Intuos3 Airbrush */ case 0x91b: /* Intuos3 Airbrush Eraser */ case 0x902: /* Intuos4/5 13HD/24HD Airbrush */ case 0x90a: /* Intuos4/5 13HD/24HD Airbrush Eraser */ case 0x100902: /* Intuos4/5 13HD/24HD Airbrush */ case 0x10090a: /* Intuos4/5 13HD/24HD Airbrush Eraser */ return QTabletEvent::Airbrush; case 0x007: /* Mouse 4D and 2D */ case 0x09c: case 0x094: return QTabletEvent::FourDMouse; case 0x017: /* Intuos3 2D Mouse */ case 0x806: /* Intuos4 Mouse */ case 0x096: /* Lens cursor */ case 0x097: /* Intuos3 Lens cursor */ case 0x006: /* Intuos4 Lens cursor */ return QTabletEvent::Puck; case 0x885: /* Intuos3 Art Pen (Marker Pen) */ case 0x100804: /* Intuos4/5 13HD/24HD Art Pen */ case 0x10080c: /* Intuos4/5 13HD/24HD Art Pen Eraser */ return QTabletEvent::RotationStylus; case 0: return QTabletEvent::NoDevice; } return QTabletEvent::Stylus; // Safe default assumption if nonzero } #ifndef QT_NO_TABLETEVENT bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData, QWindow *window) { bool handled = true; Display *xDisplay = static_cast(m_xlib_display); xXIGenericDeviceEvent *xiEvent = static_cast(event); xXIDeviceEvent *xiDeviceEvent = reinterpret_cast(xiEvent); switch (xiEvent->evtype) { case XI_ButtonPress: { Qt::MouseButton b = xiToQtMouseButton(xiDeviceEvent->detail); tabletData->buttons |= b; xi2ReportTabletEvent(*tabletData, xiEvent); break; } case XI_ButtonRelease: { Qt::MouseButton b = xiToQtMouseButton(xiDeviceEvent->detail); tabletData->buttons ^= b; xi2ReportTabletEvent(*tabletData, xiEvent); break; } case XI_Motion: xi2ReportTabletEvent(*tabletData, xiEvent); break; case XI_PropertyEvent: { // This is the wacom driver's way of reporting tool proximity. // The evdev driver doesn't do it this way. xXIPropertyEvent *ev = reinterpret_cast(event); if (ev->what == XIPropertyModified) { if (ev->property == atom(QXcbAtom::WacomSerialIDs)) { enum WacomSerialIndex { _WACSER_USB_ID = 0, _WACSER_LAST_TOOL_SERIAL, _WACSER_LAST_TOOL_ID, _WACSER_TOOL_SERIAL, _WACSER_TOOL_ID, _WACSER_COUNT }; Atom propType; int propFormat; unsigned long numItems, bytesAfter; unsigned char *data; if (XIGetProperty(xDisplay, tabletData->deviceId, ev->property, 0, 100, 0, AnyPropertyType, &propType, &propFormat, &numItems, &bytesAfter, &data) == Success) { if (propType == atom(QXcbAtom::INTEGER) && propFormat == 32 && numItems == _WACSER_COUNT) { quint32 *ptr = reinterpret_cast(data); quint32 tool = ptr[_WACSER_TOOL_ID]; // Workaround for http://sourceforge.net/p/linuxwacom/bugs/246/ // e.g. on Thinkpad Helix, tool ID will be 0 and serial will be 1 if (!tool && ptr[_WACSER_TOOL_SERIAL]) tool = ptr[_WACSER_TOOL_SERIAL]; // The property change event informs us which tool is in proximity or which one left proximity. if (tool) { tabletData->inProximity = true; tabletData->tool = toolIdToTabletDevice(tool); tabletData->serialId = qint64(ptr[_WACSER_USB_ID]) << 32 | qint64(ptr[_WACSER_TOOL_SERIAL]); QWindowSystemInterface::handleTabletEnterProximityEvent(tabletData->tool, tabletData->pointerType, tabletData->serialId); } else { tabletData->inProximity = false; tabletData->tool = toolIdToTabletDevice(ptr[_WACSER_LAST_TOOL_ID]); // Workaround for http://sourceforge.net/p/linuxwacom/bugs/246/ // e.g. on Thinkpad Helix, tool ID will be 0 and serial will be 1 if (!tabletData->tool) tabletData->tool = toolIdToTabletDevice(ptr[_WACSER_LAST_TOOL_SERIAL]); tabletData->serialId = qint64(ptr[_WACSER_USB_ID]) << 32 | qint64(ptr[_WACSER_LAST_TOOL_SERIAL]); QWindowSystemInterface::handleTabletLeaveProximityEvent(tabletData->tool, tabletData->pointerType, tabletData->serialId); } // TODO maybe have a hash of tabletData->deviceId to device data so we can // look up the tablet name here, and distinguish multiple tablets qCDebug(lcQpaXInput, "XI2 proximity change on tablet %d (USB %x): last tool: %x id %x current tool: %x id %x TabletDevice %d", tabletData->deviceId, ptr[_WACSER_USB_ID], ptr[_WACSER_LAST_TOOL_SERIAL], ptr[_WACSER_LAST_TOOL_ID], ptr[_WACSER_TOOL_SERIAL], ptr[_WACSER_TOOL_ID], tabletData->tool); } XFree(data); } } } break; } default: handled = false; break; } #ifdef XCB_USE_XINPUT22 // Synthesize mouse events since otherwise there are no mouse events from // the pen on the XI 2.2+ path. if (xi2MouseEvents() && window) { // DK: I have no idea why this line was introduced in Qt5.5! //eventListener->handleXIMouseEvent(reinterpret_cast(event)); } #else Q_UNUSED(eventListener); #endif return handled; } inline qreal scaleOneValuator(qreal normValue, qreal screenMin, qreal screenSize) { return screenMin + normValue * screenSize; } void QXcbConnection::xi2ReportTabletEvent(TabletData &tabletData, void *event) { xXIDeviceEvent *ev = reinterpret_cast(event); QWindow *window = windowFromId(ev->event); if (!window) return; const double scale = 65536.0; QPointF local(ev->event_x / scale, ev->event_y / scale); QPointF global(ev->root_x / scale, ev->root_y / scale); double pressure = 0, rotation = 0, tangentialPressure = 0; int xTilt = 0, yTilt = 0; QRect screenArea = qApp->desktop()->rect(); for (QHash::iterator it = tabletData.valuatorInfo.begin(), ite = tabletData.valuatorInfo.end(); it != ite; ++it) { int valuator = it.key(); TabletData::ValuatorClassInfo &classInfo(it.value()); xi2GetValuatorValueIfSet(event, classInfo.number, &classInfo.curVal); double normalizedValue = (classInfo.curVal - classInfo.minVal) / (classInfo.maxVal - classInfo.minVal); switch (valuator) { case QXcbAtom::AbsX: { const qreal value = scaleOneValuator(normalizedValue, screenArea.x(), screenArea.width()); const qreal offset = local.x() - global.x(); global.rx() = value; local.rx() = value + offset; break; } case QXcbAtom::AbsY: { qreal value = scaleOneValuator(normalizedValue, screenArea.y(), screenArea.height()); qreal offset = local.y() - global.y(); global.ry() = value; local.ry() = value + offset; break; } case QXcbAtom::AbsPressure: pressure = normalizedValue; break; case QXcbAtom::AbsTiltX: xTilt = classInfo.curVal; break; case QXcbAtom::AbsTiltY: yTilt = classInfo.curVal; break; case QXcbAtom::AbsWheel: switch (tabletData.tool) { case QTabletEvent::Airbrush: tangentialPressure = normalizedValue * 2.0 - 1.0; // Convert 0..1 range to -1..+1 range break; case QTabletEvent::RotationStylus: rotation = normalizedValue * 360.0 - 180.0; // Convert 0..1 range to -180..+180 degrees break; default: // Other types of styli do not use this valuator break; } break; default: break; } } if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled())) qCDebug(lcQpaXInput, "XI2 event on tablet %d with tool %d type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f buttons 0x%x pressure %4.2lf tilt %d, %d rotation %6.2lf", tabletData.deviceId, tabletData.tool, ev->evtype, ev->sequenceNumber, ev->detail, fixed1616ToReal(ev->event_x), fixed1616ToReal(ev->event_y), fixed1616ToReal(ev->root_x), fixed1616ToReal(ev->root_y), (int)tabletData.buttons, pressure, xTilt, yTilt, rotation); Qt::KeyboardModifiers modifiers = QApplication::queryKeyboardModifiers(); QWindowSystemInterface::handleTabletEvent(window, local, global, tabletData.tool, tabletData.pointerType, tabletData.buttons, pressure, xTilt, yTilt, tangentialPressure, rotation, 0, tabletData.serialId, modifiers); } void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice) { #ifdef XCB_USE_XINPUT21 xXIGenericDeviceEvent *xiEvent = reinterpret_cast(event); if (xiEvent->evtype == XI_Motion && scrollingDevice.orientations) { xXIDeviceEvent* xiDeviceEvent = reinterpret_cast(event); if (QWindow *platformWindow = windowFromId(xiDeviceEvent->event)) { QPoint rawDelta; QPoint angleDelta; double value; if (scrollingDevice.orientations & Qt::Vertical) { if (xi2GetValuatorValueIfSet(xiDeviceEvent, scrollingDevice.verticalIndex, &value)) { double delta = scrollingDevice.lastScrollPosition.y() - value; scrollingDevice.lastScrollPosition.setY(value); angleDelta.setY((delta / scrollingDevice.verticalIncrement) * 120); // We do not set "pixel" delta if it is only measured in ticks. if (scrollingDevice.verticalIncrement > 1) rawDelta.setY(delta); } } if (scrollingDevice.orientations & Qt::Horizontal) { if (xi2GetValuatorValueIfSet(xiDeviceEvent, scrollingDevice.horizontalIndex, &value)) { double delta = scrollingDevice.lastScrollPosition.x() - value; scrollingDevice.lastScrollPosition.setX(value); angleDelta.setX((delta / scrollingDevice.horizontalIncrement) * 120); // We do not set "pixel" delta if it is only measured in ticks. if (scrollingDevice.horizontalIncrement > 1) rawDelta.setX(delta); } } if (!angleDelta.isNull()) { const int dpr = int(platformWindow->devicePixelRatio()); QPoint local(fixed1616ToReal(xiDeviceEvent->event_x)/dpr, fixed1616ToReal(xiDeviceEvent->event_y)/dpr); QPoint global(fixed1616ToReal(xiDeviceEvent->root_x)/dpr, fixed1616ToReal(xiDeviceEvent->root_y)/dpr); Qt::KeyboardModifiers modifiers = QApplication::queryKeyboardModifiers(); if (modifiers & Qt::AltModifier) { std::swap(angleDelta.rx(), angleDelta.ry()); std::swap(rawDelta.rx(), rawDelta.ry()); } QWindowSystemInterface::handleWheelEvent(platformWindow, xiEvent->time, local, global, rawDelta, angleDelta, modifiers); } } } else if (xiEvent->evtype == XI_ButtonRelease && scrollingDevice.legacyOrientations) { xXIDeviceEvent* xiDeviceEvent = reinterpret_cast(event); if (QWindow *platformWindow = windowFromId(xiDeviceEvent->event)) { QPoint angleDelta; if (scrollingDevice.legacyOrientations & Qt::Vertical) { if (xiDeviceEvent->detail == 4) angleDelta.setY(120); else if (xiDeviceEvent->detail == 5) angleDelta.setY(-120); } if (scrollingDevice.legacyOrientations & Qt::Horizontal) { if (xiDeviceEvent->detail == 6) angleDelta.setX(120); else if (xiDeviceEvent->detail == 7) angleDelta.setX(-120); } if (!angleDelta.isNull()) { const int dpr = int(platformWindow->devicePixelRatio()); QPoint local(fixed1616ToReal(xiDeviceEvent->event_x)/dpr, fixed1616ToReal(xiDeviceEvent->event_y)/dpr); QPoint global(fixed1616ToReal(xiDeviceEvent->root_x)/dpr, fixed1616ToReal(xiDeviceEvent->root_y)/dpr); Qt::KeyboardModifiers modifiers = QApplication::queryKeyboardModifiers(); if (modifiers & Qt::AltModifier) std::swap(angleDelta.rx(), angleDelta.ry()); QWindowSystemInterface::handleWheelEvent(platformWindow, xiEvent->time, local, global, QPoint(), angleDelta, modifiers); } } } #else Q_UNUSED(event); Q_UNUSED(scrollingDevice); #endif // XCB_USE_XINPUT21 } #endif // QT_NO_TABLETEVENT #endif // XCB_USE_XINPUT2 diff --git a/libs/ui/kis_animation_exporter.cpp b/libs/ui/kis_animation_exporter.cpp index d3d36be0e1..7ca3530bea 100644 --- a/libs/ui/kis_animation_exporter.cpp +++ b/libs/ui/kis_animation_exporter.cpp @@ -1,227 +1,227 @@ /* * Copyright (c) 2015 Jouni Pentikäinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_animation_exporter.h" #include #include #include #include #include "KoFileDialog.h" #include "KisDocument.h" #include "kis_image.h" #include "KisImportExportManager.h" #include "kis_image_animation_interface.h" #include "KisPart.h" #include "kis_paint_layer.h" #include "kis_group_layer.h" #include "kis_time_range.h" #include "kis_painter.h" struct KisAnimationExporterUI::Private { QWidget *parentWidget; KisAnimationExporter *exporter; Private(QWidget *parent) : parentWidget(parent), exporter(0) {} }; KisAnimationExporterUI::KisAnimationExporterUI(QWidget *parent) : m_d(new Private(parent)) { } KisAnimationExporterUI::~KisAnimationExporterUI() { if (m_d->exporter) { delete m_d->exporter; } } KisImportExportFilter::ConversionStatus KisAnimationExporterUI::exportSequence(KisDocument *document) { KoFileDialog dialog(m_d->parentWidget, KoFileDialog::SaveFile, "exportsequence"); dialog.setCaption(i18n("Export sequence")); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); - dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Export)); + dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Export)); QString filename = dialog.filename(); // if the user presses cancel, it returns empty if (filename.isEmpty()) return KisImportExportFilter::UserCancelled; const KisTimeRange fullClipRange = document->image()->animationInterface()->fullClipRange(); int firstFrame = fullClipRange.start(); int lastFrame = fullClipRange.end(); m_d->exporter = new KisAnimationExporter(document, filename, firstFrame, lastFrame); return m_d->exporter->exportAnimation(); } struct KisAnimationExporter::Private { KisDocument *document; KisImageWSP image; QString filenamePrefix; QString filenameSuffix; int firstFrame; int currentFrame; int lastFrame; QScopedPointer tmpDoc; KisImageWSP tmpImage; KisPaintDeviceSP tmpDevice; bool exporting; bool batchMode; KisImportExportFilter::ConversionStatus status; QMutex mutex; QWaitCondition exportFinished; Private(KisDocument *document, int fromTime, int toTime) : document(document), image(document->image()), firstFrame(fromTime), lastFrame(toTime), tmpDoc(KisPart::instance()->createDocument()), exporting(false), batchMode(false) { tmpDoc->setAutoSave(0); tmpImage = new KisImage(tmpDoc->createUndoStore(), image->bounds().width(), image->bounds().height(), image->colorSpace(), QString()); tmpImage->setResolution(image->xRes(), image->yRes()); tmpDoc->setCurrentImage(tmpImage); KisPaintLayer* paintLayer = new KisPaintLayer(tmpImage, "paint device", 255); tmpImage->addNode(paintLayer, tmpImage->rootLayer(), KisLayerSP(0)); tmpDevice = paintLayer->paintDevice(); } }; KisAnimationExporter::KisAnimationExporter(KisDocument *document, const QString &baseFilename, int fromTime, int toTime) : m_d(new Private(document, fromTime, toTime)) { int baseLength = baseFilename.lastIndexOf("."); if (baseLength > -1) { m_d->filenamePrefix = baseFilename.left(baseLength); m_d->filenameSuffix = baseFilename.right(baseFilename.length() - baseLength); } else { m_d->filenamePrefix = baseFilename; } m_d->batchMode = document->fileBatchMode(); QString mimefilter = KisMimeDatabase::mimeTypeForFile(baseFilename); m_d->tmpDoc->setOutputMimeType(mimefilter.toLatin1()); m_d->tmpDoc->setFileBatchMode(true); connect(this, SIGNAL(sigFrameReadyToSave()), this, SLOT(frameReadyToSave()), Qt::QueuedConnection); } KisAnimationExporter::~KisAnimationExporter() { } KisImportExportFilter::ConversionStatus KisAnimationExporter::exportAnimation() { if (!m_d->batchMode) { emit m_d->document->statusBarMessage(i18n("Export frames")); emit m_d->document->sigProgress(0); connect(m_d->document, SIGNAL(sigProgressCanceled()), this, SLOT(cancel())); } m_d->status = KisImportExportFilter::OK; m_d->exporting = true; m_d->currentFrame = m_d->firstFrame; connect(m_d->image->animationInterface(), SIGNAL(sigFrameReady(int)), this, SLOT(frameReadyToCopy(int)), Qt::DirectConnection); m_d->image->animationInterface()->requestFrameRegeneration(m_d->currentFrame, m_d->image->bounds()); QEventLoop loop; loop.connect(this, SIGNAL(sigFinished()), SLOT(quit())); loop.exec(); return m_d->status; } void KisAnimationExporter::stopExport() { if (!m_d->exporting) return; m_d->exporting = false; disconnect(m_d->image->animationInterface(), 0, this, 0); if (!m_d->batchMode) { disconnect(m_d->document, SIGNAL(sigProgressCanceled()), this, SLOT(cancel())); emit m_d->document->sigProgress(100); emit m_d->document->clearStatusBarMessage(); } emit sigFinished(); } void KisAnimationExporter::cancel() { m_d->status = KisImportExportFilter::ProgressCancelled; stopExport(); } void KisAnimationExporter::frameReadyToCopy(int time) { if (time != m_d->currentFrame) return; QRect rc = m_d->image->bounds(); KisPainter::copyAreaOptimized(rc.topLeft(), m_d->image->projection(), m_d->tmpDevice, rc); emit sigFrameReadyToSave(); } void KisAnimationExporter::frameReadyToSave() { QString frameNumber = QString("%1").arg(m_d->currentFrame, 4, 10, QChar('0')); QString filename = m_d->filenamePrefix + frameNumber + m_d->filenameSuffix; if (m_d->tmpDoc->exportDocument(QUrl::fromLocalFile(filename))) { if (m_d->exporting && m_d->currentFrame < m_d->lastFrame) { if (!m_d->batchMode) { emit m_d->document->sigProgress((m_d->currentFrame - m_d->firstFrame) * 100 / (m_d->lastFrame - m_d->firstFrame)); } m_d->currentFrame++; m_d->image->animationInterface()->requestFrameRegeneration(m_d->currentFrame, m_d->image->bounds()); return; //continue } } else { //error m_d->status = KisImportExportFilter::InternalError; } stopExport(); //finish } diff --git a/libs/ui/kis_categorized_item_delegate.cpp b/libs/ui/kis_categorized_item_delegate.cpp index c1892aa0ab..67a37d6d09 100644 --- a/libs/ui/kis_categorized_item_delegate.cpp +++ b/libs/ui/kis_categorized_item_delegate.cpp @@ -1,122 +1,132 @@ /* * Copyright (c) 2009 Cyrille Berger * Copyright (c) 2011 Silvio Heinrich * Copyright (c) 2014 Mohit Goyal * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_categorized_item_delegate.h" #include "kis_categorized_list_model.h" #include #include #include #include #include #include #include #include +#include "kis_debug.h" -KisCategorizedItemDelegate::KisCategorizedItemDelegate(bool indicateError, QObject *parent) +KisCategorizedItemDelegate::KisCategorizedItemDelegate(QObject *parent) : QStyledItemDelegate(parent), - m_indicateError(indicateError), m_minimumItemHeight(0) { } void KisCategorizedItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { painter->resetTransform(); if(!index.data(__CategorizedListModelBase::IsHeaderRole).toBool()) { QStyleOptionViewItem sovi(option); - if(m_indicateError) - sovi.decorationPosition = QStyleOptionViewItem::Right; - - QStyledItemDelegate::paint(painter, sovi, index); if (index.data(__CategorizedListModelBase::isLockableRole).toBool()) { bool locked = index.data(__CategorizedListModelBase::isLockedRole).toBool(); const QIcon icon = locked ? KisIconUtils::loadIcon(koIconName("locked")) : KisIconUtils::loadIcon(koIconName("unlocked")); - QPixmap pixmap = icon.pixmap(QSize(sovi.rect.height() - 8, sovi.rect.height() -8)); - painter->drawPixmap(sovi.rect.width() - pixmap.width(), sovi.rect.y() + 4, pixmap); + const int iconSize = qMax(16, m_minimumItemHeight - 2); + + sovi.decorationPosition = QStyleOptionViewItem::Right; + sovi.decorationAlignment = Qt::AlignRight; + sovi.decorationSize = QSize(iconSize, iconSize); + sovi.features |= QStyleOptionViewItem::HasDecoration; + sovi.icon = icon; } + + QStyledItemDelegate::paint(painter, sovi, index); painter->setOpacity(1); } else { QPalette palette = QApplication::palette(); if(option.state & QStyle::State_MouseOver) painter->fillRect(option.rect, palette.midlight()); else painter->fillRect(option.rect, palette.button()); painter->setBrush(palette.buttonText()); painter->drawText(option.rect, index.data().toString(), QTextOption(Qt::AlignVCenter|Qt::AlignHCenter)); paintTriangle( painter, option.rect.x(), option.rect.y(), option.rect.height(), !index.data(__CategorizedListModelBase::ExpandCategoryRole).toBool() ); } painter->resetTransform(); } QSize KisCategorizedItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { //on first calling this calculates the minimal height of the items if (m_minimumItemHeight == 0) { for(int i=0; irowCount(); i++) { QSize indexSize = QStyledItemDelegate::sizeHint(option, index.model()->index(i, 0)); m_minimumItemHeight = qMax(m_minimumItemHeight, indexSize.height()); /** * Make all the items, including the ones having * checkboxes look the same. */ QStyle *style = QApplication::style(); QStyleOptionButton so; QSize size = style->sizeFromContents(QStyle::CT_CheckBox, &so, QSize(), 0); m_minimumItemHeight = qMax(size.height(), m_minimumItemHeight); } } - return QSize(QStyledItemDelegate::sizeHint(option, index).width(), m_minimumItemHeight); + + int width = QStyledItemDelegate::sizeHint(option, index).width(); + + if (index.data(__CategorizedListModelBase::isLockableRole).toBool()) { + width += m_minimumItemHeight; + } + + return QSize(width, m_minimumItemHeight); } void KisCategorizedItemDelegate::paintTriangle(QPainter* painter, qint32 x, qint32 y, qint32 size, bool rotate) const { QPolygonF triangle; triangle.push_back(QPointF(-0.2,-0.2)); triangle.push_back(QPointF( 0.2,-0.2)); triangle.push_back(QPointF( 0.0, 0.2)); QTransform transform; transform.translate(x + size/2, y + size/2); transform.scale(size, size); if(rotate) transform.rotate(-90); QPalette palette = QApplication::palette(); painter->setBrush(palette.buttonText()); painter->drawPolygon(transform.map(triangle)); } diff --git a/libs/ui/kis_categorized_item_delegate.h b/libs/ui/kis_categorized_item_delegate.h index 3185c2156b..4971138045 100644 --- a/libs/ui/kis_categorized_item_delegate.h +++ b/libs/ui/kis_categorized_item_delegate.h @@ -1,44 +1,43 @@ /* * Copyright (c) 2009 Cyrille Berger * Copyright (c) 2011 Silvio Heinrich * Copyright (c) 2014 Mohit Goyal * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_CATEGORIZED_ITEM_DELEGATE_H_ #define _KIS_CATEGORIZED_ITEM_DELEGATE_H_ #include #include #include /** * This delegate draw categories using information from a \ref KCategorizedSortFilterProxyModel . */ class KRITAUI_EXPORT KisCategorizedItemDelegate: public QStyledItemDelegate { public: - KisCategorizedItemDelegate(bool indicateError, QObject *parent); + KisCategorizedItemDelegate(QObject *parent); virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; private: void paintTriangle(QPainter* painter, qint32 x, qint32 y, qint32 size, bool rotate) const; - bool m_indicateError; mutable qint32 m_minimumItemHeight; }; #endif // _KIS_CATEGORIZED_ITEM_DELEGATE_H_ diff --git a/libs/ui/kis_config.cc b/libs/ui/kis_config.cc index a3db7f5bf7..ecdf9be771 100644 --- a/libs/ui/kis_config.cc +++ b/libs/ui/kis_config.cc @@ -1,1675 +1,1684 @@ /* * 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 "kis_canvas_resource_provider.h" #include "kis_config_notifier.h" #include "kis_snap_config.h" #include #include KisConfig::KisConfig() : m_cfg( KSharedConfig::openConfig()->group("")) { } KisConfig::~KisConfig() { if (qApp->thread() != QThread::currentThread()) { //dbgKrita << "WARNING: KisConfig: requested config synchronization from nonGUI thread! Skipping..."; return; } m_cfg.sync(); } bool KisConfig::disableTouchOnCanvas(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("disableTouchOnCanvas", false)); } void KisConfig::setDisableTouchOnCanvas(bool value) const { m_cfg.writeEntry("disableTouchOnCanvas", value); } bool KisConfig::useProjections(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("useProjections", true)); } void KisConfig::setUseProjections(bool useProj) const { m_cfg.writeEntry("useProjections", useProj); } bool KisConfig::undoEnabled(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("undoEnabled", true)); } void KisConfig::setUndoEnabled(bool undo) const { m_cfg.writeEntry("undoEnabled", undo); } int KisConfig::undoStackLimit(bool defaultValue) const { return (defaultValue ? 30 : m_cfg.readEntry("undoStackLimit", 30)); } void KisConfig::setUndoStackLimit(int limit) const { m_cfg.writeEntry("undoStackLimit", limit); } bool KisConfig::useCumulativeUndoRedo(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useCumulativeUndoRedo",false)); } void KisConfig::setCumulativeUndoRedo(bool value) { m_cfg.writeEntry("useCumulativeUndoRedo", value); } qreal KisConfig::stackT1(bool defaultValue) const { return (defaultValue ? 5 : m_cfg.readEntry("stackT1",5)); } void KisConfig::setStackT1(int T1) { m_cfg.writeEntry("stackT1", T1); } qreal KisConfig::stackT2(bool defaultValue) const { return (defaultValue ? 1 : m_cfg.readEntry("stackT2",1)); } void KisConfig::setStackT2(int T2) { m_cfg.writeEntry("stackT2", T2); } int KisConfig::stackN(bool defaultValue) const { return (defaultValue ? 5 : m_cfg.readEntry("stackN",5)); } void KisConfig::setStackN(int N) { m_cfg.writeEntry("stackN", N); } qint32 KisConfig::defImageWidth(bool defaultValue) const { return (defaultValue ? 1600 : m_cfg.readEntry("imageWidthDef", 1600)); } qint32 KisConfig::defImageHeight(bool defaultValue) const { return (defaultValue ? 1200 : m_cfg.readEntry("imageHeightDef", 1200)); } qreal KisConfig::defImageResolution(bool defaultValue) const { return (defaultValue ? 100.0 : m_cfg.readEntry("imageResolutionDef", 100.0)) / 72.0; } QString KisConfig::defColorModel(bool defaultValue) const { return (defaultValue ? KoColorSpaceRegistry::instance()->rgb8()->colorModelId().id() : m_cfg.readEntry("colorModelDef", KoColorSpaceRegistry::instance()->rgb8()->colorModelId().id())); } void KisConfig::defColorModel(const QString & model) const { m_cfg.writeEntry("colorModelDef", model); } QString KisConfig::defaultColorDepth(bool defaultValue) const { return (defaultValue ? KoColorSpaceRegistry::instance()->rgb8()->colorDepthId().id() : m_cfg.readEntry("colorDepthDef", KoColorSpaceRegistry::instance()->rgb8()->colorDepthId().id())); } void KisConfig::setDefaultColorDepth(const QString & depth) const { m_cfg.writeEntry("colorDepthDef", depth); } QString KisConfig::defColorProfile(bool defaultValue) const { return (defaultValue ? KoColorSpaceRegistry::instance()->rgb8()->profile()->name() : m_cfg.readEntry("colorProfileDef", KoColorSpaceRegistry::instance()->rgb8()->profile()->name())); } void KisConfig::defColorProfile(const QString & profile) const { m_cfg.writeEntry("colorProfileDef", profile); } void KisConfig::defImageWidth(qint32 width) const { m_cfg.writeEntry("imageWidthDef", width); } void KisConfig::defImageHeight(qint32 height) const { m_cfg.writeEntry("imageHeightDef", height); } void KisConfig::defImageResolution(qreal res) const { m_cfg.writeEntry("imageResolutionDef", res*72.0); } void cleanOldCursorStyleKeys(KConfigGroup &cfg) { if (cfg.hasKey("newCursorStyle") && cfg.hasKey("newOutlineStyle")) { cfg.deleteEntry("cursorStyleDef"); } } CursorStyle KisConfig::newCursorStyle(bool defaultValue) const { if (defaultValue) { return CURSOR_STYLE_NO_CURSOR; } int style = m_cfg.readEntry("newCursorStyle", int(-1)); if (style < 0) { // old style format style = m_cfg.readEntry("cursorStyleDef", int(OLD_CURSOR_STYLE_OUTLINE)); switch (style) { case OLD_CURSOR_STYLE_TOOLICON: style = CURSOR_STYLE_TOOLICON; break; case OLD_CURSOR_STYLE_CROSSHAIR: case OLD_CURSOR_STYLE_OUTLINE_CENTER_CROSS: style = CURSOR_STYLE_CROSSHAIR; break; case OLD_CURSOR_STYLE_POINTER: style = CURSOR_STYLE_POINTER; break; case OLD_CURSOR_STYLE_OUTLINE: case OLD_CURSOR_STYLE_NO_CURSOR: style = CURSOR_STYLE_NO_CURSOR; break; case OLD_CURSOR_STYLE_SMALL_ROUND: case OLD_CURSOR_STYLE_OUTLINE_CENTER_DOT: style = CURSOR_STYLE_SMALL_ROUND; break; case OLD_CURSOR_STYLE_TRIANGLE_RIGHTHANDED: case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_RIGHTHANDED: style = CURSOR_STYLE_TRIANGLE_RIGHTHANDED; break; case OLD_CURSOR_STYLE_TRIANGLE_LEFTHANDED: case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_LEFTHANDED: style = CURSOR_STYLE_TRIANGLE_LEFTHANDED; break; default: style = -1; } } cleanOldCursorStyleKeys(m_cfg); // compatibility with future versions if (style < 0 || style >= N_CURSOR_STYLE_SIZE) { style = CURSOR_STYLE_NO_CURSOR; } return (CursorStyle) style; } void KisConfig::setNewCursorStyle(CursorStyle style) { m_cfg.writeEntry("newCursorStyle", (int)style); } OutlineStyle KisConfig::newOutlineStyle(bool defaultValue) const { if (defaultValue) { return OUTLINE_FULL; } int style = m_cfg.readEntry("newOutlineStyle", int(-1)); if (style < 0) { // old style format style = m_cfg.readEntry("cursorStyleDef", int(OLD_CURSOR_STYLE_OUTLINE)); switch (style) { case OLD_CURSOR_STYLE_TOOLICON: case OLD_CURSOR_STYLE_CROSSHAIR: case OLD_CURSOR_STYLE_POINTER: case OLD_CURSOR_STYLE_NO_CURSOR: case OLD_CURSOR_STYLE_SMALL_ROUND: case OLD_CURSOR_STYLE_TRIANGLE_RIGHTHANDED: case OLD_CURSOR_STYLE_TRIANGLE_LEFTHANDED: style = OUTLINE_NONE; break; case OLD_CURSOR_STYLE_OUTLINE: case OLD_CURSOR_STYLE_OUTLINE_CENTER_DOT: case OLD_CURSOR_STYLE_OUTLINE_CENTER_CROSS: case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_RIGHTHANDED: case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_LEFTHANDED: style = OUTLINE_FULL; break; default: style = -1; } } cleanOldCursorStyleKeys(m_cfg); // compatibility with future versions if (style < 0 || style >= N_OUTLINE_STYLE_SIZE) { style = OUTLINE_FULL; } return (OutlineStyle) style; } void KisConfig::setNewOutlineStyle(OutlineStyle style) { m_cfg.writeEntry("newOutlineStyle", (int)style); } QRect KisConfig::colorPreviewRect() const { return m_cfg.readEntry("colorPreviewRect", QVariant(QRect(32, 32, 48, 48))).toRect(); } void KisConfig::setColorPreviewRect(const QRect &rect) { m_cfg.writeEntry("colorPreviewRect", QVariant(rect)); } bool KisConfig::useDirtyPresets(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useDirtyPresets",false)); } void KisConfig::setUseDirtyPresets(bool value) { m_cfg.writeEntry("useDirtyPresets",value); KisConfigNotifier::instance()->notifyConfigChanged(); } bool KisConfig::useEraserBrushSize(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useEraserBrushSize",false)); } void KisConfig::setUseEraserBrushSize(bool value) { m_cfg.writeEntry("useEraserBrushSize",value); KisConfigNotifier::instance()->notifyConfigChanged(); } QColor KisConfig::getMDIBackgroundColor(bool defaultValue) const { QColor col(77, 77, 77); return (defaultValue ? col : m_cfg.readEntry("mdiBackgroundColor", col)); } void KisConfig::setMDIBackgroundColor(const QColor &v) const { m_cfg.writeEntry("mdiBackgroundColor", v); } QString KisConfig::getMDIBackgroundImage(bool defaultValue) const { return (defaultValue ? "" : m_cfg.readEntry("mdiBackgroundImage", "")); } void KisConfig::setMDIBackgroundImage(const QString &filename) const { m_cfg.writeEntry("mdiBackgroundImage", filename); } QString KisConfig::monitorProfile(int screen) const { // Note: keep this in sync with the default profile for the RGB colorspaces! QString profile = m_cfg.readEntry("monitorProfile" + QString(screen == 0 ? "": QString("_%1").arg(screen)), "sRGB-elle-V2-srgbtrc.icc"); //dbgKrita << "KisConfig::monitorProfile()" << profile; return profile; } QString KisConfig::monitorForScreen(int screen, const QString &defaultMonitor, bool defaultValue) const { return (defaultValue ? defaultMonitor : m_cfg.readEntry(QString("monitor_for_screen_%1").arg(screen), defaultMonitor)); } void KisConfig::setMonitorForScreen(int screen, const QString& monitor) { m_cfg.writeEntry(QString("monitor_for_screen_%1").arg(screen), monitor); } void KisConfig::setMonitorProfile(int screen, const QString & monitorProfile, bool override) const { m_cfg.writeEntry("monitorProfile/OverrideX11", override); m_cfg.writeEntry("monitorProfile" + QString(screen == 0 ? "": QString("_%1").arg(screen)), monitorProfile); } const KoColorProfile *KisConfig::getScreenProfile(int screen) { if (screen < 0) return 0; KisConfig cfg; QString monitorId; if (KisColorManager::instance()->devices().size() > screen && screen > 0) { monitorId = cfg.monitorForScreen(screen, KisColorManager::instance()->devices()[screen]); } //dbgKrita << "getScreenProfile(). Screen" << screen << "monitor id" << monitorId; if (monitorId.isEmpty()) { return 0; } QByteArray bytes = KisColorManager::instance()->displayProfile(monitorId); //dbgKrita << "\tgetScreenProfile()" << bytes.size(); if (bytes.length() > 0) { const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), bytes); //dbgKrita << "\tKisConfig::getScreenProfile for screen" << screen << profile->name(); return profile; } else { //dbgKrita << "\tCould not get a system monitor profile"; return 0; } } const KoColorProfile *KisConfig::displayProfile(int screen) const { if (screen < 0) return 0; // if the user plays with the settings, they can override the display profile, in which case // we don't want the system setting. bool override = useSystemMonitorProfile(); //dbgKrita << "KisConfig::displayProfile(). Override X11:" << override; const KoColorProfile *profile = 0; if (override) { //dbgKrita << "\tGoing to get the screen profile"; profile = KisConfig::getScreenProfile(screen); } // if it fails. check the configuration if (!profile || !profile->isSuitableForDisplay()) { //dbgKrita << "\tGoing to get the monitor profile"; QString monitorProfileName = monitorProfile(screen); //dbgKrita << "\t\tmonitorProfileName:" << monitorProfileName; if (!monitorProfileName.isEmpty()) { profile = KoColorSpaceRegistry::instance()->profileByName(monitorProfileName); } if (profile) { //dbgKrita << "\t\tsuitable for display" << profile->isSuitableForDisplay(); } else { //dbgKrita << "\t\tstill no profile"; } } // if we still don't have a profile, or the profile isn't suitable for display, // we need to get a last-resort profile. the built-in sRGB is a good choice then. if (!profile || !profile->isSuitableForDisplay()) { //dbgKrita << "\tnothing worked, going to get sRGB built-in"; profile = KoColorSpaceRegistry::instance()->profileByName("sRGB Built-in"); } if (profile) { //dbgKrita << "\tKisConfig::displayProfile for screen" << screen << "is" << profile->name(); } else { //dbgKrita << "\tCouldn't get a display profile at all"; } return profile; } QString KisConfig::workingColorSpace(bool defaultValue) const { return (defaultValue ? "RGBA" : m_cfg.readEntry("workingColorSpace", "RGBA")); } void KisConfig::setWorkingColorSpace(const QString & workingColorSpace) const { m_cfg.writeEntry("workingColorSpace", workingColorSpace); } QString KisConfig::printerColorSpace(bool /*defaultValue*/) const { //TODO currently only rgb8 is supported //return (defaultValue ? "RGBA" : m_cfg.readEntry("printerColorSpace", "RGBA")); return QString("RGBA"); } void KisConfig::setPrinterColorSpace(const QString & printerColorSpace) const { m_cfg.writeEntry("printerColorSpace", printerColorSpace); } QString KisConfig::printerProfile(bool defaultValue) const { return (defaultValue ? "" : m_cfg.readEntry("printerProfile", "")); } void KisConfig::setPrinterProfile(const QString & printerProfile) const { m_cfg.writeEntry("printerProfile", printerProfile); } bool KisConfig::useBlackPointCompensation(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("useBlackPointCompensation", true)); } void KisConfig::setUseBlackPointCompensation(bool useBlackPointCompensation) const { m_cfg.writeEntry("useBlackPointCompensation", useBlackPointCompensation); } bool KisConfig::allowLCMSOptimization(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("allowLCMSOptimization", true)); } void KisConfig::setAllowLCMSOptimization(bool allowLCMSOptimization) { m_cfg.writeEntry("allowLCMSOptimization", allowLCMSOptimization); } bool KisConfig::showRulers(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("showrulers", false)); } void KisConfig::setShowRulers(bool rulers) const { m_cfg.writeEntry("showrulers", rulers); } bool KisConfig::rulersTrackMouse(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("rulersTrackMouse", true)); } void KisConfig::setRulersTrackMouse(bool value) const { m_cfg.writeEntry("rulersTrackMouse", value); } qint32 KisConfig::pasteBehaviour(bool defaultValue) const { return (defaultValue ? 2 : m_cfg.readEntry("pasteBehaviour", 2)); } void KisConfig::setPasteBehaviour(qint32 renderIntent) const { m_cfg.writeEntry("pasteBehaviour", renderIntent); } qint32 KisConfig::monitorRenderIntent(bool defaultValue) const { qint32 intent = m_cfg.readEntry("renderIntent", INTENT_PERCEPTUAL); if (intent > 3) intent = 3; if (intent < 0) intent = 0; return (defaultValue ? INTENT_PERCEPTUAL : intent); } void KisConfig::setRenderIntent(qint32 renderIntent) const { if (renderIntent > 3) renderIntent = 3; if (renderIntent < 0) renderIntent = 0; m_cfg.writeEntry("renderIntent", renderIntent); } bool KisConfig::useOpenGL(bool defaultValue) const { - - if (qApp->applicationName() == "krita") { - if (defaultValue) { -#ifdef Q_WS_MAC - return false; -#else - return true; -#endif - } - - //dbgKrita << "use opengl" << m_cfg.readEntry("useOpenGL", true) << "success" << m_cfg.readEntry("canvasState", "OPENGL_SUCCESS"); - QString canvasState = m_cfg.readEntry("canvasState", "OPENGL_SUCCESS"); -#ifdef Q_WS_MAC - return (m_cfg.readEntry("useOpenGL", false) && (canvasState == "OPENGL_SUCCESS" || canvasState == "TRY_OPENGL")); -#else - return (m_cfg.readEntry("useOpenGL", true) && (canvasState == "OPENGL_SUCCESS" || canvasState == "TRY_OPENGL")); -#endif - } - else if (qApp->applicationName() == "kritasketch" || qApp->applicationName() == "kritagemini") { - return true; // for sketch and gemini - } else { - return false; + if (defaultValue) { + return true; } + + //dbgKrita << "use opengl" << m_cfg.readEntry("useOpenGL", true) << "success" << m_cfg.readEntry("canvasState", "OPENGL_SUCCESS"); + QString canvasState = m_cfg.readEntry("canvasState", "OPENGL_SUCCESS"); + return (m_cfg.readEntry("useOpenGL", true) && (canvasState == "OPENGL_SUCCESS" || canvasState == "TRY_OPENGL")); } void KisConfig::setUseOpenGL(bool useOpenGL) const { m_cfg.writeEntry("useOpenGL", useOpenGL); } int KisConfig::openGLFilteringMode(bool defaultValue) const { return (defaultValue ? 3 : m_cfg.readEntry("OpenGLFilterMode", 3)); } void KisConfig::setOpenGLFilteringMode(int filteringMode) { m_cfg.writeEntry("OpenGLFilterMode", filteringMode); } bool KisConfig::useOpenGLTextureBuffer(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("useOpenGLTextureBuffer", true)); } void KisConfig::setUseOpenGLTextureBuffer(bool useBuffer) { m_cfg.writeEntry("useOpenGLTextureBuffer", useBuffer); } int KisConfig::openGLTextureSize(bool defaultValue) const { return (defaultValue ? 256 : m_cfg.readEntry("textureSize", 256)); } - -bool KisConfig::disableDoubleBuffering(bool defaultValue) const -{ - return (defaultValue ? true : m_cfg.readEntry("disableDoubleBuffering", true)); -} - -void KisConfig::setDisableDoubleBuffering(bool disableDoubleBuffering) -{ - m_cfg.writeEntry("disableDoubleBuffering", disableDoubleBuffering); -} - bool KisConfig::disableVSync(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("disableVSync", true)); } void KisConfig::setDisableVSync(bool disableVSync) { m_cfg.writeEntry("disableVSync", disableVSync); } bool KisConfig::showAdvancedOpenGLSettings(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("showAdvancedOpenGLSettings", false)); } bool KisConfig::forceOpenGLFenceWorkaround(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("forceOpenGLFenceWorkaround", false)); } int KisConfig::numMipmapLevels(bool defaultValue) const { return (defaultValue ? 4 : m_cfg.readEntry("numMipmapLevels", 4)); } int KisConfig::textureOverlapBorder() const { return 1 << qMax(0, numMipmapLevels()); } qint32 KisConfig::maxNumberOfThreads(bool defaultValue) const { return (defaultValue ? QThread::idealThreadCount() : m_cfg.readEntry("maxthreads", QThread::idealThreadCount())); } void KisConfig::setMaxNumberOfThreads(qint32 maxThreads) { m_cfg.writeEntry("maxthreads", maxThreads); } quint32 KisConfig::getGridMainStyle(bool defaultValue) const { int v = m_cfg.readEntry("gridmainstyle", 0); v = qBound(0, v, 2); return (defaultValue ? 0 : v); } void KisConfig::setGridMainStyle(quint32 v) const { m_cfg.writeEntry("gridmainstyle", v); } quint32 KisConfig::getGridSubdivisionStyle(bool defaultValue) const { quint32 v = m_cfg.readEntry("gridsubdivisionstyle", 1); if (v > 2) v = 2; return (defaultValue ? 1 : v); } void KisConfig::setGridSubdivisionStyle(quint32 v) const { m_cfg.writeEntry("gridsubdivisionstyle", v); } QColor KisConfig::getGridMainColor(bool defaultValue) const { QColor col(99, 99, 99); return (defaultValue ? col : m_cfg.readEntry("gridmaincolor", col)); } void KisConfig::setGridMainColor(const QColor & v) const { m_cfg.writeEntry("gridmaincolor", v); } QColor KisConfig::getGridSubdivisionColor(bool defaultValue) const { QColor col(150, 150, 150); return (defaultValue ? col : m_cfg.readEntry("gridsubdivisioncolor", col)); } void KisConfig::setGridSubdivisionColor(const QColor & v) const { m_cfg.writeEntry("gridsubdivisioncolor", v); } quint32 KisConfig::guidesLineStyle(bool defaultValue) const { int v = m_cfg.readEntry("guidesLineStyle", 0); v = qBound(0, v, 2); return (defaultValue ? 0 : v); } void KisConfig::setGuidesLineStyle(quint32 v) const { m_cfg.writeEntry("guidesLineStyle", v); } QColor KisConfig::guidesColor(bool defaultValue) const { QColor col(99, 99, 99); return (defaultValue ? col : m_cfg.readEntry("guidesColor", col)); } void KisConfig::setGuidesColor(const QColor & v) const { m_cfg.writeEntry("guidesColor", v); } void KisConfig::loadSnapConfig(KisSnapConfig *config, bool defaultValue) const { KisSnapConfig defaultConfig(false); if (defaultValue) { *config = defaultConfig; return; } config->setOrthogonal(m_cfg.readEntry("globalSnapOrthogonal", defaultConfig.orthogonal())); config->setNode(m_cfg.readEntry("globalSnapNode", defaultConfig.node())); config->setExtension(m_cfg.readEntry("globalSnapExtension", defaultConfig.extension())); config->setIntersection(m_cfg.readEntry("globalSnapIntersection", defaultConfig.intersection())); config->setBoundingBox(m_cfg.readEntry("globalSnapBoundingBox", defaultConfig.boundingBox())); config->setImageBounds(m_cfg.readEntry("globalSnapImageBounds", defaultConfig.imageBounds())); config->setImageCenter(m_cfg.readEntry("globalSnapImageCenter", defaultConfig.imageCenter())); } void KisConfig::saveSnapConfig(const KisSnapConfig &config) { m_cfg.writeEntry("globalSnapOrthogonal", config.orthogonal()); m_cfg.writeEntry("globalSnapNode", config.node()); m_cfg.writeEntry("globalSnapExtension", config.extension()); m_cfg.writeEntry("globalSnapIntersection", config.intersection()); m_cfg.writeEntry("globalSnapBoundingBox", config.boundingBox()); m_cfg.writeEntry("globalSnapImageBounds", config.imageBounds()); m_cfg.writeEntry("globalSnapImageCenter", config.imageCenter()); } qint32 KisConfig::checkSize(bool defaultValue) const { return (defaultValue ? 32 : m_cfg.readEntry("checksize", 32)); } void KisConfig::setCheckSize(qint32 checksize) const { m_cfg.writeEntry("checksize", checksize); } bool KisConfig::scrollCheckers(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("scrollingcheckers", false)); } void KisConfig::setScrollingCheckers(bool sc) const { m_cfg.writeEntry("scrollingcheckers", sc); } QColor KisConfig::canvasBorderColor(bool defaultValue) const { QColor color(QColor(128,128,128)); return (defaultValue ? color : m_cfg.readEntry("canvasBorderColor", color)); } void KisConfig::setCanvasBorderColor(const QColor& color) const { m_cfg.writeEntry("canvasBorderColor", color); } bool KisConfig::hideScrollbars(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("hideScrollbars", false)); } void KisConfig::setHideScrollbars(bool value) const { m_cfg.writeEntry("hideScrollbars", value); } QColor KisConfig::checkersColor1(bool defaultValue) const { QColor col(220, 220, 220); return (defaultValue ? col : m_cfg.readEntry("checkerscolor", col)); } void KisConfig::setCheckersColor1(const QColor & v) const { m_cfg.writeEntry("checkerscolor", v); } QColor KisConfig::checkersColor2(bool defaultValue) const { return (defaultValue ? QColor(Qt::white) : m_cfg.readEntry("checkerscolor2", QColor(Qt::white))); } void KisConfig::setCheckersColor2(const QColor & v) const { m_cfg.writeEntry("checkerscolor2", v); } bool KisConfig::antialiasCurves(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("antialiascurves", true)); } void KisConfig::setAntialiasCurves(bool v) const { m_cfg.writeEntry("antialiascurves", v); } QColor KisConfig::selectionOverlayMaskColor(bool defaultValue) const { QColor def(255, 0, 0, 220); return (defaultValue ? def : m_cfg.readEntry("selectionOverlayMaskColor", def)); } void KisConfig::setSelectionOverlayMaskColor(const QColor &color) { m_cfg.writeEntry("selectionOverlayMaskColor", color); } bool KisConfig::antialiasSelectionOutline(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("AntialiasSelectionOutline", false)); } void KisConfig::setAntialiasSelectionOutline(bool v) const { m_cfg.writeEntry("AntialiasSelectionOutline", v); } bool KisConfig::showRootLayer(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("ShowRootLayer", false)); } void KisConfig::setShowRootLayer(bool showRootLayer) const { m_cfg.writeEntry("ShowRootLayer", showRootLayer); } bool KisConfig::showGlobalSelection(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("ShowGlobalSelection", false)); } void KisConfig::setShowGlobalSelection(bool showGlobalSelection) const { m_cfg.writeEntry("ShowGlobalSelection", showGlobalSelection); } bool KisConfig::showOutlineWhilePainting(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("ShowOutlineWhilePainting", true)); } void KisConfig::setShowOutlineWhilePainting(bool showOutlineWhilePainting) const { m_cfg.writeEntry("ShowOutlineWhilePainting", showOutlineWhilePainting); } bool KisConfig::hideSplashScreen(bool defaultValue) const { KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen"); return (defaultValue ? true : cfg.readEntry("HideSplashAfterStartup", true)); } void KisConfig::setHideSplashScreen(bool hideSplashScreen) const { KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen"); cfg.writeEntry("HideSplashAfterStartup", hideSplashScreen); } qreal KisConfig::outlineSizeMinimum(bool defaultValue) const { return (defaultValue ? 1.0 : m_cfg.readEntry("OutlineSizeMinimum", 1.0)); } void KisConfig::setOutlineSizeMinimum(qreal outlineSizeMinimum) const { m_cfg.writeEntry("OutlineSizeMinimum", outlineSizeMinimum); } int KisConfig::autoSaveInterval(bool defaultValue) const { return (defaultValue ? KisDocument::defaultAutoSave() : m_cfg.readEntry("AutoSaveInterval", KisDocument::defaultAutoSave())); } void KisConfig::setAutoSaveInterval(int seconds) const { return m_cfg.writeEntry("AutoSaveInterval", seconds); } bool KisConfig::backupFile(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("CreateBackupFile", true)); } void KisConfig::setBackupFile(bool backupFile) const { m_cfg.writeEntry("CreateBackupFile", backupFile); } bool KisConfig::showFilterGallery(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("showFilterGallery", false)); } void KisConfig::setShowFilterGallery(bool showFilterGallery) const { m_cfg.writeEntry("showFilterGallery", showFilterGallery); } bool KisConfig::showFilterGalleryLayerMaskDialog(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("showFilterGalleryLayerMaskDialog", true)); } void KisConfig::setShowFilterGalleryLayerMaskDialog(bool showFilterGallery) const { m_cfg.writeEntry("setShowFilterGalleryLayerMaskDialog", showFilterGallery); } QString KisConfig::canvasState(bool defaultValue) const { return (defaultValue ? "OPENGL_NOT_TRIED" : m_cfg.readEntry("canvasState", "OPENGL_NOT_TRIED")); } void KisConfig::setCanvasState(const QString& state) const { static QStringList acceptableStates; if (acceptableStates.isEmpty()) { acceptableStates << "OPENGL_SUCCESS" << "TRY_OPENGL" << "OPENGL_NOT_TRIED" << "OPENGL_FAILED"; } if (acceptableStates.contains(state)) { m_cfg.writeEntry("canvasState", state); m_cfg.sync(); } } bool KisConfig::toolOptionsPopupDetached(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("ToolOptionsPopupDetached", false)); } void KisConfig::setToolOptionsPopupDetached(bool detached) const { m_cfg.writeEntry("ToolOptionsPopupDetached", detached); } bool KisConfig::paintopPopupDetached(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("PaintopPopupDetached", false)); } void KisConfig::setPaintopPopupDetached(bool detached) const { m_cfg.writeEntry("PaintopPopupDetached", detached); } QString KisConfig::pressureTabletCurve(bool defaultValue) const { return (defaultValue ? "0,0;1,1" : m_cfg.readEntry("tabletPressureCurve","0,0;1,1;")); } void KisConfig::setPressureTabletCurve(const QString& curveString) const { m_cfg.writeEntry("tabletPressureCurve", curveString); } qreal KisConfig::vastScrolling(bool defaultValue) const { return (defaultValue ? 0.9 : m_cfg.readEntry("vastScrolling", 0.9)); } void KisConfig::setVastScrolling(const qreal factor) const { m_cfg.writeEntry("vastScrolling", factor); } int KisConfig::presetChooserViewMode(bool defaultValue) const { return (defaultValue ? 0 : m_cfg.readEntry("presetChooserViewMode", 0)); } void KisConfig::setPresetChooserViewMode(const int mode) const { m_cfg.writeEntry("presetChooserViewMode", mode); } bool KisConfig::firstRun(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("firstRun", true)); } void KisConfig::setFirstRun(const bool first) const { m_cfg.writeEntry("firstRun", first); } int KisConfig::horizontalSplitLines(bool defaultValue) const { return (defaultValue ? 1 : m_cfg.readEntry("horizontalSplitLines", 1)); } void KisConfig::setHorizontalSplitLines(const int numberLines) const { m_cfg.writeEntry("horizontalSplitLines", numberLines); } int KisConfig::verticalSplitLines(bool defaultValue) const { return (defaultValue ? 1 : m_cfg.readEntry("verticalSplitLines", 1)); } void KisConfig::setVerticalSplitLines(const int numberLines) const { m_cfg.writeEntry("verticalSplitLines", numberLines); } bool KisConfig::clicklessSpacePan(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("clicklessSpacePan", true)); } void KisConfig::setClicklessSpacePan(const bool toggle) const { m_cfg.writeEntry("clicklessSpacePan", toggle); } bool KisConfig::hideDockersFullscreen(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("hideDockersFullScreen", true)); } void KisConfig::setHideDockersFullscreen(const bool value) const { m_cfg.writeEntry("hideDockersFullScreen", value); } bool KisConfig::showDockerTitleBars(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("showDockerTitleBars", true)); } void KisConfig::setShowDockerTitleBars(const bool value) const { m_cfg.writeEntry("showDockerTitleBars", value); } +bool KisConfig::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 { +#ifdef Q_OS_WIN + return false; +#else return (defaultValue ? true : m_cfg.readEntry("hideTitleBarFullscreen", true)); +#endif } void KisConfig::setHideTitlebarFullscreen(const bool value) const { m_cfg.writeEntry("hideTitleBarFullscreen", value); } bool KisConfig::hideToolbarFullscreen(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("hideToolbarFullscreen", true)); } void KisConfig::setHideToolbarFullscreen(const bool value) const { m_cfg.writeEntry("hideToolbarFullscreen", value); } bool KisConfig::fullscreenMode(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("fullscreenMode", true)); } void KisConfig::setFullscreenMode(const bool value) const { m_cfg.writeEntry("fullscreenMode", value); } QStringList KisConfig::favoriteCompositeOps(bool defaultValue) const { return (defaultValue ? QStringList() : m_cfg.readEntry("favoriteCompositeOps", QStringList())); } void KisConfig::setFavoriteCompositeOps(const QStringList& compositeOps) const { m_cfg.writeEntry("favoriteCompositeOps", compositeOps); } QString KisConfig::exportConfiguration(const QString &filterId, bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("ExportConfiguration-" + filterId, QString())); } void KisConfig::setExportConfiguration(const QString &filterId, const KisPropertiesConfiguration &properties) const { QString exportConfig = properties.toXML(); m_cfg.writeEntry("ExportConfiguration-" + filterId, exportConfig); } bool KisConfig::useOcio(bool defaultValue) const { #ifdef HAVE_OCIO return (defaultValue ? false : m_cfg.readEntry("Krita/Ocio/UseOcio", false)); #else Q_UNUSED(defaultValue); return false; #endif } void KisConfig::setUseOcio(bool useOCIO) const { m_cfg.writeEntry("Krita/Ocio/UseOcio", useOCIO); } int KisConfig::favoritePresets(bool defaultValue) const { return (defaultValue ? 10 : m_cfg.readEntry("numFavoritePresets", 10)); } void KisConfig::setFavoritePresets(const int value) { m_cfg.writeEntry("numFavoritePresets", value); } bool KisConfig::levelOfDetailEnabled(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("levelOfDetailEnabled", false)); } void KisConfig::setLevelOfDetailEnabled(bool value) { m_cfg.writeEntry("levelOfDetailEnabled", value); } KisConfig::OcioColorManagementMode KisConfig::ocioColorManagementMode(bool defaultValue) const { return (OcioColorManagementMode)(defaultValue ? INTERNAL : m_cfg.readEntry("Krita/Ocio/OcioColorManagementMode", (int) INTERNAL)); } void KisConfig::setOcioColorManagementMode(OcioColorManagementMode mode) const { m_cfg.writeEntry("Krita/Ocio/OcioColorManagementMode", (int) mode); } QString KisConfig::ocioConfigurationPath(bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("Krita/Ocio/OcioConfigPath", QString())); } void KisConfig::setOcioConfigurationPath(const QString &path) const { m_cfg.writeEntry("Krita/Ocio/OcioConfigPath", path); } QString KisConfig::ocioLutPath(bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("Krita/Ocio/OcioLutPath", QString())); } void KisConfig::setOcioLutPath(const QString &path) const { m_cfg.writeEntry("Krita/Ocio/OcioLutPath", path); } int KisConfig::ocioLutEdgeSize(bool defaultValue) const { return (defaultValue ? 64 : m_cfg.readEntry("Krita/Ocio/LutEdgeSize", 64)); } void KisConfig::setOcioLutEdgeSize(int value) { m_cfg.writeEntry("Krita/Ocio/LutEdgeSize", value); } bool KisConfig::ocioLockColorVisualRepresentation(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("Krita/Ocio/OcioLockColorVisualRepresentation", false)); } void KisConfig::setOcioLockColorVisualRepresentation(bool value) { m_cfg.writeEntry("Krita/Ocio/OcioLockColorVisualRepresentation", value); } QString KisConfig::defaultPalette(bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("defaultPalette", QString())); } void KisConfig::setDefaultPalette(const QString& name) const { m_cfg.writeEntry("defaultPalette", name); } QString KisConfig::toolbarSlider(int sliderNumber, bool defaultValue) const { QString def = "flow"; if (sliderNumber == 1) { def = "opacity"; } if (sliderNumber == 2) { def = "size"; } return (defaultValue ? def : m_cfg.readEntry(QString("toolbarslider_%1").arg(sliderNumber), def)); } void KisConfig::setToolbarSlider(int sliderNumber, const QString &slider) { m_cfg.writeEntry(QString("toolbarslider_%1").arg(sliderNumber), slider); } bool KisConfig::sliderLabels(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("sliderLabels", true)); } void KisConfig::setSliderLabels(bool enabled) { m_cfg.writeEntry("sliderLabels", enabled); } QString KisConfig::currentInputProfile(bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("currentInputProfile", QString())); } void KisConfig::setCurrentInputProfile(const QString& name) { m_cfg.writeEntry("currentInputProfile", name); } bool KisConfig::useSystemMonitorProfile(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("ColorManagement/UseSystemMonitorProfile", false)); } void KisConfig::setUseSystemMonitorProfile(bool _useSystemMonitorProfile) const { m_cfg.writeEntry("ColorManagement/UseSystemMonitorProfile", _useSystemMonitorProfile); } bool KisConfig::presetStripVisible(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("presetStripVisible", true)); } void KisConfig::setPresetStripVisible(bool visible) { m_cfg.writeEntry("presetStripVisible", visible); } bool KisConfig::scratchpadVisible(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("scratchpadVisible", true)); } void KisConfig::setScratchpadVisible(bool visible) { m_cfg.writeEntry("scratchpadVisible", visible); } bool KisConfig::showSingleChannelAsColor(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("showSingleChannelAsColor", false)); } void KisConfig::setShowSingleChannelAsColor(bool asColor) { m_cfg.writeEntry("showSingleChannelAsColor", asColor); } bool KisConfig::hidePopups(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("hidePopups", false)); } void KisConfig::setHidePopups(bool hidepopups) { m_cfg.writeEntry("hidePopups", hidepopups); } int KisConfig::numDefaultLayers(bool defaultValue) const { return (defaultValue ? 2 : m_cfg.readEntry("NumberOfLayersForNewImage", 2)); } void KisConfig::setNumDefaultLayers(int num) { m_cfg.writeEntry("NumberOfLayersForNewImage", num); } quint8 KisConfig::defaultBackgroundOpacity(bool defaultValue) const { return (defaultValue ? (int)OPACITY_OPAQUE_U8 : m_cfg.readEntry("BackgroundOpacityForNewImage", (int)OPACITY_OPAQUE_U8)); } void KisConfig::setDefaultBackgroundOpacity(quint8 value) { m_cfg.writeEntry("BackgroundOpacityForNewImage", (int)value); } QColor KisConfig::defaultBackgroundColor(bool defaultValue) const { return (defaultValue ? QColor(Qt::white) : m_cfg.readEntry("BackgroundColorForNewImage", QColor(Qt::white))); } void KisConfig::setDefaultBackgroundColor(QColor value) { m_cfg.writeEntry("BackgroundColorForNewImage", value); } KisConfig::BackgroundStyle KisConfig::defaultBackgroundStyle(bool defaultValue) const { return (KisConfig::BackgroundStyle)(defaultValue ? LAYER : m_cfg.readEntry("BackgroundStyleForNewImage", (int)LAYER)); } void KisConfig::setDefaultBackgroundStyle(KisConfig::BackgroundStyle value) { m_cfg.writeEntry("BackgroundStyleForNewImage", (int)value); } int KisConfig::lineSmoothingType(bool defaultValue) const { return (defaultValue ? 1 : m_cfg.readEntry("LineSmoothingType", 1)); } void KisConfig::setLineSmoothingType(int value) { m_cfg.writeEntry("LineSmoothingType", value); } qreal KisConfig::lineSmoothingDistance(bool defaultValue) const { return (defaultValue ? 50.0 : m_cfg.readEntry("LineSmoothingDistance", 50.0)); } void KisConfig::setLineSmoothingDistance(qreal value) { m_cfg.writeEntry("LineSmoothingDistance", value); } qreal KisConfig::lineSmoothingTailAggressiveness(bool defaultValue) const { return (defaultValue ? 0.15 : m_cfg.readEntry("LineSmoothingTailAggressiveness", 0.15)); } void KisConfig::setLineSmoothingTailAggressiveness(qreal value) { m_cfg.writeEntry("LineSmoothingTailAggressiveness", value); } bool KisConfig::lineSmoothingSmoothPressure(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("LineSmoothingSmoothPressure", false)); } void KisConfig::setLineSmoothingSmoothPressure(bool value) { m_cfg.writeEntry("LineSmoothingSmoothPressure", value); } bool KisConfig::lineSmoothingScalableDistance(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("LineSmoothingScalableDistance", true)); } void KisConfig::setLineSmoothingScalableDistance(bool value) { m_cfg.writeEntry("LineSmoothingScalableDistance", value); } qreal KisConfig::lineSmoothingDelayDistance(bool defaultValue) const { return (defaultValue ? 50.0 : m_cfg.readEntry("LineSmoothingDelayDistance", 50.0)); } void KisConfig::setLineSmoothingDelayDistance(qreal value) { m_cfg.writeEntry("LineSmoothingDelayDistance", value); } bool KisConfig::lineSmoothingUseDelayDistance(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("LineSmoothingUseDelayDistance", true)); } void KisConfig::setLineSmoothingUseDelayDistance(bool value) { m_cfg.writeEntry("LineSmoothingUseDelayDistance", value); } bool KisConfig::lineSmoothingFinishStabilizedCurve(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("LineSmoothingFinishStabilizedCurve", true)); } void KisConfig::setLineSmoothingFinishStabilizedCurve(bool value) { m_cfg.writeEntry("LineSmoothingFinishStabilizedCurve", value); } bool KisConfig::lineSmoothingStabilizeSensors(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("LineSmoothingStabilizeSensors", true)); } void KisConfig::setLineSmoothingStabilizeSensors(bool value) { m_cfg.writeEntry("LineSmoothingStabilizeSensors", value); } int KisConfig::paletteDockerPaletteViewSectionSize(bool defaultValue) const { return (defaultValue ? 12 : m_cfg.readEntry("paletteDockerPaletteViewSectionSize", 12)); } void KisConfig::setPaletteDockerPaletteViewSectionSize(int value) const { m_cfg.writeEntry("paletteDockerPaletteViewSectionSize", value); } int KisConfig::tabletEventsDelay(bool defaultValue) const { return (defaultValue ? 10 : m_cfg.readEntry("tabletEventsDelay", 10)); } void KisConfig::setTabletEventsDelay(int value) { m_cfg.writeEntry("tabletEventsDelay", value); } bool KisConfig::testingAcceptCompressedTabletEvents(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("testingAcceptCompressedTabletEvents", false)); } void KisConfig::setTestingAcceptCompressedTabletEvents(bool value) { m_cfg.writeEntry("testingAcceptCompressedTabletEvents", value); } +bool KisConfig::shouldEatDriverShortcuts(bool defaultValue) const +{ + return (defaultValue ? false : m_cfg.readEntry("shouldEatDriverShortcuts", false)); +} + bool KisConfig::testingCompressBrushEvents(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("testingCompressBrushEvents", false)); } void KisConfig::setTestingCompressBrushEvents(bool value) { m_cfg.writeEntry("testingCompressBrushEvents", value); } bool KisConfig::useVerboseOpenGLDebugOutput(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useVerboseOpenGLDebugOutput", false)); } int KisConfig::workaroundX11SmoothPressureSteps(bool defaultValue) const { return (defaultValue ? 0 : m_cfg.readEntry("workaroundX11SmoothPressureSteps", 0)); } bool KisConfig::showCanvasMessages(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("showOnCanvasMessages", true)); } void KisConfig::setShowCanvasMessages(bool show) { m_cfg.writeEntry("showOnCanvasMessages", show); } bool KisConfig::compressKra(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("compressLayersInKra", false)); } void KisConfig::setCompressKra(bool compress) { m_cfg.writeEntry("compressLayersInKra", compress); } bool KisConfig::toolOptionsInDocker(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("ToolOptionsInDocker", true)); } void KisConfig::setToolOptionsInDocker(bool inDocker) { m_cfg.writeEntry("ToolOptionsInDocker", inDocker); } const KoColorSpace* KisConfig::customColorSelectorColorSpace(bool defaultValue) const { const KoColorSpace *cs = 0; KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); if (defaultValue || cfg.readEntry("useCustomColorSpace", true)) { KoColorSpaceRegistry* csr = KoColorSpaceRegistry::instance(); QString modelID = cfg.readEntry("customColorSpaceModel", "RGBA"); QString depthID = cfg.readEntry("customColorSpaceDepthID", "U8"); QString profile = cfg.readEntry("customColorSpaceProfile", "sRGB built-in - (lcms internal)"); if (profile == "default") { // qDebug() << "Falling back to default color profile."; profile = "sRGB built-in - (lcms internal)"; } cs = csr->colorSpace(modelID, depthID, profile); } return cs; } void KisConfig::setCustomColorSelectorColorSpace(const KoColorSpace *cs) { KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); cfg.writeEntry("useCustomColorSpace", bool(cs)); if(cs) { cfg.writeEntry("customColorSpaceModel", cs->colorModelId().id()); cfg.writeEntry("customColorSpaceDepthID", cs->colorDepthId().id()); cfg.writeEntry("customColorSpaceProfile", cs->profile()->name()); } KisConfigNotifier::instance()->notifyConfigChanged(); } bool KisConfig::enableOpenGLDebugging(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("enableOpenGLDebugging", false)); } void KisConfig::setEnableOpenGLDebugging(bool value) const { m_cfg.writeEntry("enableOpenGLDebugging", value); } void KisConfig::setEnableAmdVectorizationWorkaround(bool value) { m_cfg.writeEntry("amdDisableVectorWorkaround", value); } bool KisConfig::enableAmdVectorizationWorkaround(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("amdDisableVectorWorkaround", false)); } void KisConfig::setAnimationDropFrames(bool value) { bool oldValue = animationDropFrames(); if (value == oldValue) return; m_cfg.writeEntry("animationDropFrames", value); KisConfigNotifier::instance()->notifyDropFramesModeChanged(); } bool KisConfig::animationDropFrames(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("animationDropFrames", true)); } int KisConfig::scribbingUpdatesDelay(bool defaultValue) const { return (defaultValue ? 30 : m_cfg.readEntry("scribbingUpdatesDelay", 30)); } void KisConfig::setScribbingUpdatesDelay(int value) { m_cfg.writeEntry("scribbingUpdatesDelay", 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); +} diff --git a/libs/ui/kis_config.h b/libs/ui/kis_config.h index 377845cb21..fc02b3146a 100644 --- a/libs/ui/kis_config.h +++ b/libs/ui/kis_config.h @@ -1,507 +1,513 @@ /* * Copyright (c) 2002 Patrick Julien * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_CONFIG_H_ #define KIS_CONFIG_H_ #include #include #include #include #include #include #include "kis_global.h" #include "kis_properties_configuration.h" #include "kritaui_export.h" class KoColorProfile; class KoColorSpace; class KisSnapConfig; class KRITAUI_EXPORT KisConfig { public: KisConfig(); ~KisConfig(); bool disableTouchOnCanvas(bool defaultValue = false) const; void setDisableTouchOnCanvas(bool value) const; bool useProjections(bool defaultValue = false) const; void setUseProjections(bool useProj) const; bool undoEnabled(bool defaultValue = false) const; void setUndoEnabled(bool undo) const; int undoStackLimit(bool defaultValue = false) const; void setUndoStackLimit(int limit) const; bool useCumulativeUndoRedo(bool defaultValue = false) const; void setCumulativeUndoRedo(bool value); double stackT1(bool defaultValue = false) const; void setStackT1(int T1); double stackT2(bool defaultValue = false) const; void setStackT2(int T2); int stackN(bool defaultValue = false) const; void setStackN(int N); qint32 defImageWidth(bool defaultValue = false) const; void defImageWidth(qint32 width) const; qint32 defImageHeight(bool defaultValue = false) const; void defImageHeight(qint32 height) const; qreal defImageResolution(bool defaultValue = false) const; void defImageResolution(qreal res) const; /** * @return the id of the default color model used for creating new images. */ QString defColorModel(bool defaultValue = false) const; /** * set the id of the default color model used for creating new images. */ void defColorModel(const QString & model) const; /** * @return the id of the default color depth used for creating new images. */ QString defaultColorDepth(bool defaultValue = false) const; /** * set the id of the default color depth used for creating new images. */ void setDefaultColorDepth(const QString & depth) const; /** * @return the id of the default color profile used for creating new images. */ QString defColorProfile(bool defaultValue = false) const; /** * set the id of the default color profile used for creating new images. */ void defColorProfile(const QString & depth) const; CursorStyle newCursorStyle(bool defaultValue = false) const; void setNewCursorStyle(CursorStyle style); OutlineStyle newOutlineStyle(bool defaultValue = false) const; void setNewOutlineStyle(OutlineStyle style); QRect colorPreviewRect() const; void setColorPreviewRect(const QRect &rect); /// get the profile the user has selected for the given screen QString monitorProfile(int screen) const; void setMonitorProfile(int screen, const QString & monitorProfile, bool override) const; QString monitorForScreen(int screen, const QString &defaultMonitor, bool defaultValue = true) const; void setMonitorForScreen(int screen, const QString& monitor); /// Get the actual profile to be used for the given screen, which is /// either the screen profile set by the color management system or /// the custom monitor profile set by the user, depending on the configuration const KoColorProfile *displayProfile(int screen) const; QString workingColorSpace(bool defaultValue = false) const; void setWorkingColorSpace(const QString & workingColorSpace) const; QString importProfile(bool defaultValue = false) const; void setImportProfile(const QString & importProfile) const; QString printerColorSpace(bool defaultValue = false) const; void setPrinterColorSpace(const QString & printerColorSpace) const; QString printerProfile(bool defaultValue = false) const; void setPrinterProfile(const QString & printerProfile) const; bool useBlackPointCompensation(bool defaultValue = false) const; void setUseBlackPointCompensation(bool useBlackPointCompensation) const; bool allowLCMSOptimization(bool defaultValue = false) const; void setAllowLCMSOptimization(bool allowLCMSOptimization); bool showRulers(bool defaultValue = false) const; void setShowRulers(bool rulers) const; bool rulersTrackMouse(bool defaultValue = false) const; void setRulersTrackMouse(bool value) const; qint32 pasteBehaviour(bool defaultValue = false) const; void setPasteBehaviour(qint32 behaviour) const; qint32 monitorRenderIntent(bool defaultValue = false) const; void setRenderIntent(qint32 monitorRenderIntent) const; bool useOpenGL(bool defaultValue = false) const; void setUseOpenGL(bool useOpenGL) const; int openGLFilteringMode(bool defaultValue = false) const; void setOpenGLFilteringMode(int filteringMode); bool useOpenGLTextureBuffer(bool defaultValue = false) const; void setUseOpenGLTextureBuffer(bool useBuffer); - bool disableDoubleBuffering(bool defaultValue = false) const; - void setDisableDoubleBuffering(bool disableDoubleBuffering); - bool disableVSync(bool defaultValue = false) const; void setDisableVSync(bool disableVSync); bool showAdvancedOpenGLSettings(bool defaultValue = false) const; bool forceOpenGLFenceWorkaround(bool defaultValue = false) const; int numMipmapLevels(bool defaultValue = false) const; int openGLTextureSize(bool defaultValue = false) const; int textureOverlapBorder() const; qint32 maxNumberOfThreads(bool defaultValue = false) const; void setMaxNumberOfThreads(qint32 numberOfThreads); quint32 getGridMainStyle(bool defaultValue = false) const; void setGridMainStyle(quint32 v) const; quint32 getGridSubdivisionStyle(bool defaultValue = false) const; void setGridSubdivisionStyle(quint32 v) const; QColor getGridMainColor(bool defaultValue = false) const; void setGridMainColor(const QColor & v) const; QColor getGridSubdivisionColor(bool defaultValue = false) const; void setGridSubdivisionColor(const QColor & v) const; quint32 guidesLineStyle(bool defaultValue = false) const; void setGuidesLineStyle(quint32 v) const; QColor guidesColor(bool defaultValue = false) const; void setGuidesColor(const QColor & v) const; void loadSnapConfig(KisSnapConfig *config, bool defaultValue = false) const; void saveSnapConfig(const KisSnapConfig &config); qint32 checkSize(bool defaultValue = false) const; void setCheckSize(qint32 checkSize) const; bool scrollCheckers(bool defaultValue = false) const; void setScrollingCheckers(bool scollCheckers) const; QColor checkersColor1(bool defaultValue = false) const; void setCheckersColor1(const QColor & v) const; QColor checkersColor2(bool defaultValue = false) const; void setCheckersColor2(const QColor & v) const; QColor canvasBorderColor(bool defaultValue = false) const; void setCanvasBorderColor(const QColor &color) const; bool hideScrollbars(bool defaultValue = false) const; void setHideScrollbars(bool value) const; bool antialiasCurves(bool defaultValue = false) const; void setAntialiasCurves(bool v) const; QColor selectionOverlayMaskColor(bool defaultValue = false) const; void setSelectionOverlayMaskColor(const QColor &color); bool antialiasSelectionOutline(bool defaultValue = false) const; void setAntialiasSelectionOutline(bool v) const; bool showRootLayer(bool defaultValue = false) const; void setShowRootLayer(bool showRootLayer) const; bool showGlobalSelection(bool defaultValue = false) const; void setShowGlobalSelection(bool showGlobalSelection) const; bool showOutlineWhilePainting(bool defaultValue = false) const; void setShowOutlineWhilePainting(bool showOutlineWhilePainting) const; bool hideSplashScreen(bool defaultValue = false) const; void setHideSplashScreen(bool hideSplashScreen) const; qreal outlineSizeMinimum(bool defaultValue = false) const; void setOutlineSizeMinimum(qreal outlineSizeMinimum) const; int autoSaveInterval(bool defaultValue = false) const; void setAutoSaveInterval(int seconds) const; bool backupFile(bool defaultValue = false) const; void setBackupFile(bool backupFile) const; bool showFilterGallery(bool defaultValue = false) const; void setShowFilterGallery(bool showFilterGallery) const; bool showFilterGalleryLayerMaskDialog(bool defaultValue = false) const; void setShowFilterGalleryLayerMaskDialog(bool showFilterGallery) const; // OPENGL_SUCCESS, TRY_OPENGL, OPENGL_NOT_TRIED, OPENGL_FAILED QString canvasState(bool defaultValue = false) const; void setCanvasState(const QString& state) const; bool toolOptionsPopupDetached(bool defaultValue = false) const; void setToolOptionsPopupDetached(bool detached) const; bool paintopPopupDetached(bool defaultValue = false) const; void setPaintopPopupDetached(bool detached) const; QString pressureTabletCurve(bool defaultValue = false) const; void setPressureTabletCurve(const QString& curveString) const; qreal vastScrolling(bool defaultValue = false) const; void setVastScrolling(const qreal factor) const; int presetChooserViewMode(bool defaultValue = false) const; void setPresetChooserViewMode(const int mode) const; bool firstRun(bool defaultValue = false) const; void setFirstRun(const bool firstRun) const; bool clicklessSpacePan(bool defaultValue = false) const; void setClicklessSpacePan(const bool toggle) const; int horizontalSplitLines(bool defaultValue = false) const; void setHorizontalSplitLines(const int numberLines) const; int verticalSplitLines(bool defaultValue = false) const; void setVerticalSplitLines(const int numberLines) const; bool hideDockersFullscreen(bool defaultValue = false) const; void setHideDockersFullscreen(const bool value) const; bool showDockerTitleBars(bool defaultValue = false) const; void setShowDockerTitleBars(const bool value) const; + bool showStatusBar(bool defaultValue = false) const; + void setShowStatusBar(const bool value) const; + bool hideMenuFullscreen(bool defaultValue = false) const; void setHideMenuFullscreen(const bool value) const; bool hideScrollbarsFullscreen(bool defaultValue = false) const; void setHideScrollbarsFullscreen(const bool value) const; bool hideStatusbarFullscreen(bool defaultValue = false) const; void setHideStatusbarFullscreen(const bool value) const; bool hideTitlebarFullscreen(bool defaultValue = false) const; void setHideTitlebarFullscreen(const bool value) const; bool hideToolbarFullscreen(bool defaultValue = false) const; void setHideToolbarFullscreen(const bool value) const; bool fullscreenMode(bool defaultValue = false) const; void setFullscreenMode(const bool value) const; QStringList favoriteCompositeOps(bool defaultValue = false) const; void setFavoriteCompositeOps(const QStringList& compositeOps) const; QString exportConfiguration(const QString &filterId, bool defaultValue = false) const; void setExportConfiguration(const QString &filterId, const KisPropertiesConfiguration &properties) const; bool useOcio(bool defaultValue = false) const; void setUseOcio(bool useOCIO) const; int favoritePresets(bool defaultValue = false) const; void setFavoritePresets(const int value); bool levelOfDetailEnabled(bool defaultValue = false) const; void setLevelOfDetailEnabled(bool value); enum OcioColorManagementMode { INTERNAL = 0, OCIO_CONFIG, OCIO_ENVIRONMENT }; OcioColorManagementMode ocioColorManagementMode(bool defaultValue = false) const; void setOcioColorManagementMode(OcioColorManagementMode mode) const; QString ocioConfigurationPath(bool defaultValue = false) const; void setOcioConfigurationPath(const QString &path) const; QString ocioLutPath(bool defaultValue = false) const; void setOcioLutPath(const QString &path) const; int ocioLutEdgeSize(bool defaultValue = false) const; void setOcioLutEdgeSize(int value); bool ocioLockColorVisualRepresentation(bool defaultValue = false) const; void setOcioLockColorVisualRepresentation(bool value); bool useSystemMonitorProfile(bool defaultValue = false) const; void setUseSystemMonitorProfile(bool _useSystemMonitorProfile) const; QString defaultPalette(bool defaultValue = false) const; void setDefaultPalette(const QString& name) const; QString toolbarSlider(int sliderNumber, bool defaultValue = false) const; void setToolbarSlider(int sliderNumber, const QString &slider); bool sliderLabels(bool defaultValue = false) const; void setSliderLabels(bool enabled); QString currentInputProfile(bool defaultValue = false) const; void setCurrentInputProfile(const QString& name); bool presetStripVisible(bool defaultValue = false) const; void setPresetStripVisible(bool visible); bool scratchpadVisible(bool defaultValue = false) const; void setScratchpadVisible(bool visible); bool showSingleChannelAsColor(bool defaultValue = false) const; void setShowSingleChannelAsColor(bool asColor); bool hidePopups(bool defaultValue = false) const; void setHidePopups(bool hidepopups); int numDefaultLayers(bool defaultValue = false) const; void setNumDefaultLayers(int num); quint8 defaultBackgroundOpacity(bool defaultValue = false) const; void setDefaultBackgroundOpacity(quint8 value); QColor defaultBackgroundColor(bool defaultValue = false) const; void setDefaultBackgroundColor(QColor value); enum BackgroundStyle { LAYER = 0, PROJECTION = 1 }; BackgroundStyle defaultBackgroundStyle(bool defaultValue = false) const; void setDefaultBackgroundStyle(BackgroundStyle value); int lineSmoothingType(bool defaultValue = false) const; void setLineSmoothingType(int value); qreal lineSmoothingDistance(bool defaultValue = false) const; void setLineSmoothingDistance(qreal value); qreal lineSmoothingTailAggressiveness(bool defaultValue = false) const; void setLineSmoothingTailAggressiveness(qreal value); bool lineSmoothingSmoothPressure(bool defaultValue = false) const; void setLineSmoothingSmoothPressure(bool value); bool lineSmoothingScalableDistance(bool defaultValue = false) const; void setLineSmoothingScalableDistance(bool value); qreal lineSmoothingDelayDistance(bool defaultValue = false) const; void setLineSmoothingDelayDistance(qreal value); bool lineSmoothingUseDelayDistance(bool defaultValue = false) const; void setLineSmoothingUseDelayDistance(bool value); bool lineSmoothingFinishStabilizedCurve(bool defaultValue = false) const; void setLineSmoothingFinishStabilizedCurve(bool value); bool lineSmoothingStabilizeSensors(bool defaultValue = false) const; void setLineSmoothingStabilizeSensors(bool value); int paletteDockerPaletteViewSectionSize(bool defaultValue = false) const; void setPaletteDockerPaletteViewSectionSize(int value) const; int tabletEventsDelay(bool defaultValue = false) const; void setTabletEventsDelay(int value); bool testingAcceptCompressedTabletEvents(bool defaultValue = false) const; void setTestingAcceptCompressedTabletEvents(bool value); + bool shouldEatDriverShortcuts(bool defaultValue = false) const; + bool testingCompressBrushEvents(bool defaultValue = false) const; void setTestingCompressBrushEvents(bool value); const KoColorSpace* customColorSelectorColorSpace(bool defaultValue = false) const; void setCustomColorSelectorColorSpace(const KoColorSpace *cs); bool useDirtyPresets(bool defaultValue = false) const; void setUseDirtyPresets(bool value); bool useEraserBrushSize(bool defaultValue = false) const; void setUseEraserBrushSize(bool value); QColor getMDIBackgroundColor(bool defaultValue = false) const; void setMDIBackgroundColor(const QColor & v) const; QString getMDIBackgroundImage(bool defaultValue = false) const; void setMDIBackgroundImage(const QString & fileName) const; bool useVerboseOpenGLDebugOutput(bool defaultValue = false) const; int workaroundX11SmoothPressureSteps(bool defaultValue = false) const; bool showCanvasMessages(bool defaultValue = false) const; void setShowCanvasMessages(bool show); bool compressKra(bool defaultValue = false) const; void setCompressKra(bool compress); bool toolOptionsInDocker(bool defaultValue = false) const; void setToolOptionsInDocker(bool inDocker); void setEnableOpenGLDebugging(bool value) const; bool enableOpenGLDebugging(bool defaultValue = false) const; void setEnableAmdVectorizationWorkaround(bool value); bool enableAmdVectorizationWorkaround(bool defaultValue = false) const; bool animationDropFrames(bool defaultValue = false) const; void setAnimationDropFrames(bool value); int scribbingUpdatesDelay(bool defaultValue = false) const; void setScribbingUpdatesDelay(int value); bool switchSelectionCtrlAlt(bool defaultValue = false) const; void setSwitchSelectionCtrlAlt(bool value); bool convertToImageColorspaceOnImport(bool defaultValue = false) const; void setConvertToImageColorspaceOnImport(bool value); + int stabilizerSampleSize(bool defaultValue = false) const; + void setStabilizerSampleSize(int value); + + template void writeEntry(const QString& name, const T& value) { m_cfg.writeEntry(name, value); } template void writeList(const QString& name, const QList& value) { m_cfg.writeEntry(name, value); } template T readEntry(const QString& name, const T& defaultValue=T()) { return m_cfg.readEntry(name, defaultValue); } template QList readList(const QString& name, const QList& defaultValue=QList()) { return m_cfg.readEntry(name, defaultValue); } /// get the profile the color managment system has stored for the given screen static const KoColorProfile* getScreenProfile(int screen); private: KisConfig(const KisConfig&); KisConfig& operator=(const KisConfig&) const; private: mutable KConfigGroup m_cfg; }; #endif // KIS_CONFIG_H_ diff --git a/libs/ui/kis_custom_pattern.cc b/libs/ui/kis_custom_pattern.cc index d31fcf2ece..a452a6aee6 100644 --- a/libs/ui/kis_custom_pattern.cc +++ b/libs/ui/kis_custom_pattern.cc @@ -1,175 +1,175 @@ /* * Copyright (c) 2006 Bart Coppens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_custom_pattern.h" #include #include #include #include #include #include #include #include #include #include "KisDocument.h" #include "KisViewManager.h" #include "kis_image.h" #include "kis_layer.h" #include "kis_paint_device.h" #include #include "kis_resource_server_provider.h" #include "kis_paint_layer.h" KisCustomPattern::KisCustomPattern(QWidget *parent, const char* name, const QString& caption, KisViewManager* view) : KisWdgCustomPattern(parent, name), m_view(view) { Q_ASSERT(m_view); setWindowTitle(caption); m_pattern = 0; preview->setScaledContents(true); KoResourceServer* rServer = KoResourceServerProvider::instance()->patternServer(false); m_rServerAdapter = QSharedPointer(new KoResourceServerAdapter(rServer)); connect(addButton, SIGNAL(pressed()), this, SLOT(slotAddPredefined())); connect(patternButton, SIGNAL(pressed()), this, SLOT(slotUsePattern())); connect(updateButton, SIGNAL(pressed()), this, SLOT(slotUpdateCurrentPattern())); connect(cmbSource, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateCurrentPattern())); } KisCustomPattern::~KisCustomPattern() { delete m_pattern; } void KisCustomPattern::slotUpdateCurrentPattern() { delete m_pattern; m_pattern = 0; if (m_view && m_view->image()) { createPattern(); if (m_pattern) { const qint32 maxSize = 150; if ((m_pattern->width() > maxSize) || (m_pattern->height() > maxSize)) { float aspectRatio = (float)m_pattern->width() / m_pattern->height(); qint32 scaledWidth, scaledHeight; if (m_pattern->width() > m_pattern->height()) { scaledWidth = maxSize; scaledHeight = maxSize / aspectRatio; } else { scaledWidth = maxSize * aspectRatio; scaledHeight = maxSize; } if (scaledWidth == 0) scaledWidth++; if (scaledHeight == 0) scaledHeight++; QPixmap scaledPixmap = QPixmap::fromImage(m_pattern->pattern()); - preview->setPixmap(scaledPixmap.scaled(scaledWidth, scaledHeight)); + preview->setPixmap(scaledPixmap.scaled(scaledWidth, scaledHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation)); } else { preview->setPixmap(QPixmap::fromImage(m_pattern->pattern())); } } } } void KisCustomPattern::slotAddPredefined() { if (!m_pattern) return; // Save in the directory that is likely to be: ~/.kde/share/apps/krita/patterns // a unique file with this pattern name QString dir = KoResourceServerProvider::instance()->patternServer()->saveLocation(); QString extension; QString tempFileName; { QTemporaryFile file(dir + QLatin1String("/krita_XXXXXX") + QLatin1String(".pat") ); file.setAutoRemove(false); file.open(); tempFileName = file.fileName(); } // Save it to that file m_pattern->setFilename(tempFileName); // Add it to the pattern server, so that it automatically gets to the mediators, and // so to the other pattern choosers can pick it up, if they want to m_rServerAdapter->addResource(m_pattern->clone()); } void KisCustomPattern::slotUsePattern() { if (!m_pattern) return; KoPattern* copy = m_pattern->clone(); Q_CHECK_PTR(copy); emit(activatedResource(copy)); } void KisCustomPattern::createPattern() { if (!m_view) return; KisPaintDeviceSP dev; QString name; KisImageWSP image = m_view->image(); if (!image) return; QRect rc = image->bounds(); if (cmbSource->currentIndex() == 0) { dev = m_view->activeNode()->projection(); name = m_view->activeNode()->name(); QRect rc2 = dev->exactBounds(); rc = rc.intersected(rc2); } else { image->lock(); dev = image->projection(); image->unlock(); name = image->objectName(); } if (!dev) return; // warn when creating large patterns QSize size = rc.size(); if (size.width() > 1000 || size.height() > 1000) { lblWarning->setText(i18n("The current image is too big to create a pattern. " "The pattern will be scaled down.")); size.scale(1000, 1000, Qt::KeepAspectRatio); } QString dir = KoResourceServerProvider::instance()->patternServer()->saveLocation(); m_pattern = new KoPattern(dev->createThumbnail(size.width(), size.height(), rc, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()), name, dir); } diff --git a/libs/ui/kis_filter_manager.cc b/libs/ui/kis_filter_manager.cc index b30ed3329d..5148da4a3f 100644 --- a/libs/ui/kis_filter_manager.cc +++ b/libs/ui/kis_filter_manager.cc @@ -1,349 +1,351 @@ /* * Copyright (c) 2007 Boudewijn Rempt * Copyright (c) 2007 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_filter_manager.h" #include #include #include #include #include #include #include // krita/image #include #include #include #include // krita/ui #include "KisViewManager.h" #include "kis_canvas2.h" #include #include "kis_action.h" #include "kis_action_manager.h" #include "kis_canvas_resource_provider.h" #include "dialogs/kis_dlg_filter.h" #include "strokes/kis_filter_stroke_strategy.h" #include "krita_utils.h" struct KisFilterManager::Private { Private() : reapplyAction(0) , actionCollection(0) , actionManager(0) , view(0) { } KisAction* reapplyAction; QHash filterActionMenus; QHash filters2Action; KActionCollection *actionCollection; KisActionManager *actionManager; KisViewManager *view; KisSafeFilterConfigurationSP lastConfiguration; KisSafeFilterConfigurationSP currentlyAppliedConfiguration; KisStrokeId currentStrokeId; QRect initialApplyRect; QSignalMapper actionsMapper; QPointer filterDialog; }; KisFilterManager::KisFilterManager(KisViewManager * view) : d(new Private) { d->view = view; } KisFilterManager::~KisFilterManager() { delete d; } void KisFilterManager::setView(QPointerimageView) { Q_UNUSED(imageView); } void KisFilterManager::setup(KActionCollection * ac, KisActionManager *actionManager) { d->actionCollection = ac; d->actionManager = actionManager; // Setup reapply action d->reapplyAction = d->actionManager->createAction("filter_apply_again"); d->reapplyAction->setEnabled(false); connect(d->reapplyAction, SIGNAL(triggered()), SLOT(reapplyLastFilter())); connect(&d->actionsMapper, SIGNAL(mapped(const QString&)), SLOT(showFilterDialog(const QString&))); // Setup list of filters - Q_FOREACH (const QString &filterName, KisFilterRegistry::instance()->keys()) { + QStringList keys = KisFilterRegistry::instance()->keys(); + keys.sort(); + Q_FOREACH (const QString &filterName, keys) { insertFilter(filterName); } connect(KisFilterRegistry::instance(), SIGNAL(filterAdded(QString)), SLOT(insertFilter(const QString &))); } void KisFilterManager::insertFilter(const QString & filterName) { Q_ASSERT(d->actionCollection); KisFilterSP filter = KisFilterRegistry::instance()->value(filterName); Q_ASSERT(filter); if (d->filters2Action.contains(filter.data())) { warnKrita << "Filter" << filterName << " has already been inserted"; return; } KoID category = filter->menuCategory(); KActionMenu* actionMenu = d->filterActionMenus[ category.id()]; if (!actionMenu) { actionMenu = new KActionMenu(category.name(), this); d->actionCollection->addAction(category.id(), actionMenu); d->filterActionMenus[category.id()] = actionMenu; } KisAction *action = new KisAction(filter->menuEntry(), this); action->setDefaultShortcut(filter->shortcut()); action->setActivationFlags(KisAction::ACTIVE_DEVICE); d->actionManager->addAction(QString("krita_filter_%1").arg(filterName), action); d->filters2Action[filter.data()] = action; actionMenu->addAction(action); d->actionsMapper.setMapping(action, filterName); connect(action, SIGNAL(triggered()), &d->actionsMapper, SLOT(map())); } void KisFilterManager::updateGUI() { if (!d->view) return; bool enable = false; KisNodeSP activeNode = d->view->activeNode(); enable = activeNode && activeNode->hasEditablePaintDevice(); d->reapplyAction->setEnabled(enable); for (QHash::iterator it = d->filters2Action.begin(); it != d->filters2Action.end(); ++it) { bool localEnable = enable; it.value()->setEnabled(localEnable); } } void KisFilterManager::reapplyLastFilter() { if (!d->lastConfiguration) return; apply(d->lastConfiguration); finish(); } void KisFilterManager::showFilterDialog(const QString &filterId) { if (d->filterDialog && d->filterDialog->isVisible()) { KisFilterSP filter = KisFilterRegistry::instance()->value(filterId); d->filterDialog->setFilter(filter); return; } connect(d->view->image(), SIGNAL(sigStrokeCancellationRequested()), SLOT(slotStrokeCancelRequested()), Qt::UniqueConnection); connect(d->view->image(), SIGNAL(sigStrokeEndRequested()), SLOT(slotStrokeEndRequested()), Qt::UniqueConnection); /** * The UI should show only after every running stroke is finished, * so a virtual barrier is added here. */ d->view->image()->waitForDone(); Q_ASSERT(d->view); Q_ASSERT(d->view->activeNode()); KisPaintDeviceSP dev = d->view->activeNode()->paintDevice(); if (!dev) { warnKrita << "KisFilterManager::showFilterDialog(): Filtering was requested for illegal active layer!" << d->view->activeNode(); return; } KisFilterSP filter = KisFilterRegistry::instance()->value(filterId); if (dev->colorSpace()->willDegrade(filter->colorSpaceIndependence())) { // Warning bells! if (filter->colorSpaceIndependence() == TO_LAB16) { if (QMessageBox::warning(d->view->mainWindow(), i18nc("@title:window", "Krita"), i18n("The %1 filter will convert your %2 data to 16-bit L*a*b* and vice versa. ", filter->name(), dev->colorSpace()->name()), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) != QMessageBox::Ok) return; } else if (filter->colorSpaceIndependence() == TO_RGBA16) { if (QMessageBox::warning(d->view->mainWindow(), i18nc("@title:window", "Krita"), i18n("The %1 filter will convert your %2 data to 16-bit RGBA and vice versa. ", filter->name() , dev->colorSpace()->name()), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) != QMessageBox::Ok) return; } } if (filter->showConfigurationWidget()) { if (!d->filterDialog) { d->filterDialog = new KisDlgFilter(d->view , d->view->activeNode(), this, d->view->mainWindow()); d->filterDialog->setAttribute(Qt::WA_DeleteOnClose); } d->filterDialog->setFilter(filter); d->filterDialog->setVisible(true); } else { apply(KisSafeFilterConfigurationSP(filter->defaultConfiguration(d->view->activeNode()->original()))); finish(); } } void KisFilterManager::apply(KisSafeFilterConfigurationSP filterConfig) { KisFilterSP filter = KisFilterRegistry::instance()->value(filterConfig->name()); KisImageWSP image = d->view->image(); if (d->currentStrokeId) { image->addJob(d->currentStrokeId, new KisFilterStrokeStrategy::CancelSilentlyMarker); image->cancelStroke(d->currentStrokeId); d->currentStrokeId.clear(); } else { image->waitForDone(); d->initialApplyRect = d->view->activeNode()->exactBounds(); } QRect applyRect = d->initialApplyRect; KisPaintDeviceSP paintDevice = d->view->activeNode()->paintDevice(); if (paintDevice && filter->needsTransparentPixels(filterConfig.data(), paintDevice->colorSpace())) { applyRect |= image->bounds(); } KisPostExecutionUndoAdapter *undoAdapter = image->postExecutionUndoAdapter(); KoCanvasResourceManager *resourceManager = d->view->resourceProvider()->resourceManager(); KisResourcesSnapshotSP resources = new KisResourcesSnapshot(image, d->view->activeNode(), undoAdapter, resourceManager); d->currentStrokeId = image->startStroke(new KisFilterStrokeStrategy(filter, KisSafeFilterConfigurationSP(filterConfig), resources)); QRect processRect = filter->changedRect(applyRect, filterConfig.data(), 0); processRect &= image->bounds(); if (filter->supportsThreading()) { QSize size = KritaUtils::optimalPatchSize(); QVector rects = KritaUtils::splitRectIntoPatches(processRect, size); Q_FOREACH (const QRect &rc, rects) { image->addJob(d->currentStrokeId, new KisFilterStrokeStrategy::Data(rc, true)); } } else { image->addJob(d->currentStrokeId, new KisFilterStrokeStrategy::Data(processRect, false)); } d->currentlyAppliedConfiguration = filterConfig; } void KisFilterManager::finish() { Q_ASSERT(d->currentStrokeId); d->view->image()->endStroke(d->currentStrokeId); KisFilterSP filter = KisFilterRegistry::instance()->value(d->currentlyAppliedConfiguration->name()); if (filter->bookmarkManager()) { filter->bookmarkManager()->save(KisBookmarkedConfigurationManager::ConfigLastUsed, d->currentlyAppliedConfiguration.data()); } d->lastConfiguration = d->currentlyAppliedConfiguration; d->reapplyAction->setEnabled(true); d->reapplyAction->setText(i18n("Apply Filter Again: %1", filter->name())); d->currentStrokeId.clear(); d->currentlyAppliedConfiguration.clear(); } void KisFilterManager::cancel() { Q_ASSERT(d->currentStrokeId); d->view->image()->cancelStroke(d->currentStrokeId); d->currentStrokeId.clear(); d->currentlyAppliedConfiguration.clear(); } bool KisFilterManager::isStrokeRunning() const { return d->currentStrokeId; } void KisFilterManager::slotStrokeEndRequested() { if (d->currentStrokeId && d->filterDialog) { d->filterDialog->accept(); } } void KisFilterManager::slotStrokeCancelRequested() { if (d->currentStrokeId && d->filterDialog) { d->filterDialog->reject(); } } diff --git a/libs/ui/kis_image_manager.cc b/libs/ui/kis_image_manager.cc index 4a9870cfca..2f46118b2b 100644 --- a/libs/ui/kis_image_manager.cc +++ b/libs/ui/kis_image_manager.cc @@ -1,210 +1,211 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_image_manager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_import_catcher.h" #include "KisViewManager.h" #include "KisDocument.h" #include "dialogs/kis_dlg_image_properties.h" #include "commands/kis_image_commands.h" #include "kis_action.h" #include "kis_action_manager.h" +#include "kis_layer_utils.h" #include "kis_signal_compressor_with_param.h" KisImageManager::KisImageManager(KisViewManager * view) : m_view(view) { } void KisImageManager::setView(QPointerimageView) { Q_UNUSED(imageView); } void KisImageManager::setup(KisActionManager *actionManager) { KisAction *action = actionManager->createAction("import_layer_from_file"); connect(action, SIGNAL(triggered()), this, SLOT(slotImportLayerFromFile())); action = actionManager->createAction("image_properties"); connect(action, SIGNAL(triggered()), this, SLOT(slotImageProperties())); action = actionManager->createAction("import_layer_as_paint_layer"); connect(action, SIGNAL(triggered()), this, SLOT(slotImportLayerFromFile())); action = actionManager->createAction("import_layer_as_transparency_mask"); connect(action, SIGNAL(triggered()), this, SLOT(slotImportLayerAsTransparencyMask())); action = actionManager->createAction("import_layer_as_filter_mask"); connect(action, SIGNAL(triggered()), this, SLOT(slotImportLayerAsFilterMask())); action = actionManager->createAction("import_layer_as_selection_mask"); connect(action, SIGNAL(triggered()), this, SLOT(slotImportLayerAsSelectionMask())); action = actionManager->createAction("image_color"); connect(action, SIGNAL(triggered()), this, SLOT(slotImageColor())); } void KisImageManager::slotImportLayerFromFile() { importImage(QUrl(), "KisPaintLayer"); } void KisImageManager::slotImportLayerAsTransparencyMask() { importImage(QUrl(), "KisTransparencyMask"); } void KisImageManager::slotImportLayerAsFilterMask() { importImage(QUrl(), "KisFilterMask"); } void KisImageManager::slotImportLayerAsSelectionMask() { importImage(QUrl(), "KisSelectionMask"); } qint32 KisImageManager::importImage(const QUrl &urlArg, const QString &layerType) { KisImageWSP currentImage = m_view->image(); if (!currentImage) { return 0; } QList urls; qint32 rc = 0; if (urlArg.isEmpty()) { KoFileDialog dialog(m_view->mainWindow(), KoFileDialog::OpenFiles, "OpenDocument"); dialog.setCaption(i18n("Import Image")); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); - dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Import)); + dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import)); QStringList fileNames = dialog.filenames(); Q_FOREACH (const QString &fileName, fileNames) { urls << QUrl::fromLocalFile(fileName); } } else { urls.push_back(urlArg); } if (urls.empty()) return 0; for (QList::iterator it = urls.begin(); it != urls.end(); ++it) { new KisImportCatcher(*it, m_view, layerType); } m_view->canvas()->update(); return rc; } void KisImageManager::resizeCurrentImage(qint32 w, qint32 h, qint32 xOffset, qint32 yOffset) { if (!m_view->image()) return; m_view->image()->resizeImage(QRect(-xOffset, -yOffset, w, h)); } void KisImageManager::scaleCurrentImage(const QSize &size, qreal xres, qreal yres, KisFilterStrategy *filterStrategy) { if (!m_view->image()) return; m_view->image()->scaleImage(size, xres, yres, filterStrategy); } void KisImageManager::rotateCurrentImage(double radians) { if (!m_view->image()) return; m_view->image()->rotateImage(radians); } void KisImageManager::shearCurrentImage(double angleX, double angleY) { if (!m_view->image()) return; m_view->image()->shear(angleX, angleY); } void KisImageManager::slotImageProperties() { KisImageWSP image = m_view->image(); if (!image) return; QPointer dlg = new KisDlgImageProperties(image, m_view->mainWindow()); if (dlg->exec() == QDialog::Accepted) { image->convertProjectionColorSpace(dlg->colorSpace()); } delete dlg; } void updateImageBackgroundColor(KisImageSP image, const QColorDialog *dlg) { QColor newColor = dlg->currentColor(); KoColor bg = image->defaultProjectionColor(); bg.fromQColor(newColor); - image->setDefaultProjectionColor(bg); - image->refreshGraphAsync(); + + KisLayerUtils::changeImageDefaultProjectionColor(image, bg); } void KisImageManager::slotImageColor() { KisImageWSP image = m_view->image(); if (!image) return; QColorDialog dlg; dlg.setOption(QColorDialog::ShowAlphaChannel, true); KoColor bg = image->defaultProjectionColor(); dlg.setCurrentColor(bg.toQColor()); KisSignalCompressor compressor(200, KisSignalCompressor::FIRST_INACTIVE); std::function updateCall(std::bind(updateImageBackgroundColor, image, &dlg)); SignalToFunctionProxy proxy(updateCall); connect(&dlg, SIGNAL(currentColorChanged(QColor)), &compressor, SLOT(start())); connect(&compressor, SIGNAL(timeout()), &proxy, SLOT(start())); dlg.exec(); } diff --git a/libs/ui/kis_layer_manager.cc b/libs/ui/kis_layer_manager.cc index 24f3447eb4..26f0d95dcf 100644 --- a/libs/ui/kis_layer_manager.cc +++ b/libs/ui/kis_layer_manager.cc @@ -1,772 +1,780 @@ /* * 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. */ #include "kis_layer_manager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisImportExportManager.h" #include "kis_config.h" #include "kis_cursor.h" #include "dialogs/kis_dlg_adj_layer_props.h" #include "dialogs/kis_dlg_adjustment_layer.h" #include "dialogs/kis_dlg_layer_properties.h" #include "dialogs/kis_dlg_generator_layer.h" #include "dialogs/kis_dlg_file_layer.h" #include "dialogs/kis_dlg_layer_style.h" #include "KisDocument.h" #include "kis_filter_manager.h" #include "kis_node_visitor.h" #include "kis_paint_layer.h" #include "commands/kis_image_commands.h" #include "commands/kis_layer_command.h" #include "commands/kis_node_commands.h" #include "kis_canvas_resource_provider.h" #include "kis_selection_manager.h" #include "kis_statusbar.h" #include "KisViewManager.h" #include "kis_zoom_manager.h" #include "canvas/kis_canvas2.h" #include "widgets/kis_meta_data_merge_strategy_chooser_widget.h" #include "widgets/kis_wdg_generator.h" #include "kis_progress_widget.h" #include "kis_node_commands_adapter.h" #include "kis_node_manager.h" #include "kis_action.h" #include "kis_action_manager.h" #include "KisPart.h" #include "kis_signal_compressor_with_param.h" #include "kis_abstract_projection_plane.h" #include "commands_new/kis_set_layer_style_command.h" #include "kis_post_execution_undo_adapter.h" #include "kis_selection_mask.h" #include "kis_layer_utils.h" #include "KisSaveGroupVisitor.h" KisLayerManager::KisLayerManager(KisViewManager * view) : m_view(view) , m_imageView(0) , m_imageFlatten(0) , m_imageMergeLayer(0) , m_groupLayersSave(0) , m_imageResizeToLayer(0) , m_flattenLayer(0) , m_rasterizeLayer(0) , m_commandsAdapter(new KisNodeCommandsAdapter(m_view)) , m_layerStyle(0) { } KisLayerManager::~KisLayerManager() { delete m_commandsAdapter; } void KisLayerManager::setView(QPointerview) { m_imageView = view; } KisLayerSP KisLayerManager::activeLayer() { if (m_imageView) { return m_imageView->currentLayer(); } return 0; } KisPaintDeviceSP KisLayerManager::activeDevice() { if (activeLayer()) { return activeLayer()->paintDevice(); } return 0; } void KisLayerManager::activateLayer(KisLayerSP layer) { if (m_imageView) { emit sigLayerActivated(layer); layersUpdated(); if (layer) { m_view->resourceProvider()->slotNodeActivated(layer.data()); } } } void KisLayerManager::setup(KisActionManager* actionManager) { m_imageFlatten = actionManager->createAction("flatten_image"); connect(m_imageFlatten, SIGNAL(triggered()), this, SLOT(flattenImage())); m_imageMergeLayer = actionManager->createAction("merge_layer"); connect(m_imageMergeLayer, SIGNAL(triggered()), this, SLOT(mergeLayer())); m_flattenLayer = actionManager->createAction("flatten_layer"); connect(m_flattenLayer, SIGNAL(triggered()), this, SLOT(flattenLayer())); m_rasterizeLayer = actionManager->createAction("rasterize_layer"); connect(m_rasterizeLayer, SIGNAL(triggered()), this, SLOT(rasterizeLayer())); m_groupLayersSave = actionManager->createAction("save_groups_as_images"); connect(m_groupLayersSave, SIGNAL(triggered()), this, SLOT(saveGroupLayers())); m_imageResizeToLayer = actionManager->createAction("resizeimagetolayer"); connect(m_imageResizeToLayer, SIGNAL(triggered()), this, SLOT(imageResizeToActiveLayer())); KisAction *action = actionManager->createAction("trim_to_image"); connect(action, SIGNAL(triggered()), this, SLOT(trimToImage())); m_layerStyle = actionManager->createAction("layer_style"); connect(m_layerStyle, SIGNAL(triggered()), this, SLOT(layerStyle())); } void KisLayerManager::updateGUI() { KisImageWSP image = m_view->image(); KisLayerSP layer; qint32 nlayers = 0; if (image) { layer = activeLayer(); nlayers = image->nlayers(); } // XXX these should be named layer instead of image m_imageFlatten->setEnabled(nlayers > 1); m_imageMergeLayer->setEnabled(nlayers > 1 && layer && layer->prevSibling()); m_flattenLayer->setEnabled(nlayers > 1 && layer && layer->firstChild()); if (m_view->statusBar()) m_view->statusBar()->setProfile(image); } void KisLayerManager::imageResizeToActiveLayer() { KisLayerSP layer; KisImageWSP image = m_view->image(); if (image && (layer = activeLayer())) { QRect cropRect = layer->projection()->nonDefaultPixelArea(); if (!cropRect.isEmpty()) { image->cropImage(cropRect); } else { m_view->showFloatingMessage( i18nc("floating message in layer manager", "Layer is empty "), QIcon(), 2000, KisFloatingMessage::Low); } } } void KisLayerManager::trimToImage() { KisImageWSP image = m_view->image(); if (image) { image->cropImage(image->bounds()); } } void KisLayerManager::layerProperties() { if (!m_view) return; if (!m_view->document()) return; KisLayerSP layer = activeLayer(); QList selectedNodes = m_view->nodeManager()->selectedNodes(); const bool multipleLayersSelected = selectedNodes.size() > 1; if (!layer) return; KisAdjustmentLayerSP alayer = KisAdjustmentLayerSP(dynamic_cast(layer.data())); KisGeneratorLayerSP glayer = KisGeneratorLayerSP(dynamic_cast(layer.data())); if (alayer && !multipleLayersSelected) { KisPaintDeviceSP dev = alayer->projection(); KisDlgAdjLayerProps dlg(alayer, alayer.data(), dev, m_view, alayer->filter().data(), alayer->name(), i18n("Filter Layer Properties"), m_view->mainWindow(), "dlgadjlayerprops"); dlg.resize(dlg.minimumSizeHint()); KisSafeFilterConfigurationSP configBefore(alayer->filter()); KIS_ASSERT_RECOVER_RETURN(configBefore); QString xmlBefore = configBefore->toXML(); if (dlg.exec() == QDialog::Accepted) { alayer->setName(dlg.layerName()); KisSafeFilterConfigurationSP configAfter(dlg.filterConfiguration()); Q_ASSERT(configAfter); QString xmlAfter = configAfter->toXML(); if(xmlBefore != xmlAfter) { KisChangeFilterCmd *cmd = new KisChangeFilterCmd(alayer, configBefore->name(), xmlBefore, configAfter->name(), xmlAfter, false); // FIXME: check whether is needed cmd->redo(); m_view->undoAdapter()->addCommand(cmd); m_view->document()->setModified(true); } } else { KisSafeFilterConfigurationSP configAfter(dlg.filterConfiguration()); Q_ASSERT(configAfter); QString xmlAfter = configAfter->toXML(); if(xmlBefore != xmlAfter) { alayer->setFilter(KisFilterRegistry::instance()->cloneConfiguration(configBefore.data())); alayer->setDirty(); } } } else if (glayer && !multipleLayersSelected) { KisDlgGeneratorLayer dlg(glayer->name(), m_view, m_view->mainWindow()); dlg.setCaption(i18n("Fill Layer Properties")); KisSafeFilterConfigurationSP configBefore(glayer->filter()); Q_ASSERT(configBefore); QString xmlBefore = configBefore->toXML(); dlg.setConfiguration(configBefore.data()); dlg.resize(dlg.minimumSizeHint()); if (dlg.exec() == QDialog::Accepted) { glayer->setName(dlg.layerName()); KisSafeFilterConfigurationSP configAfter(dlg.configuration()); Q_ASSERT(configAfter); QString xmlAfter = configAfter->toXML(); if(xmlBefore != xmlAfter) { KisChangeFilterCmd *cmd = new KisChangeFilterCmd(glayer, configBefore->name(), xmlBefore, configAfter->name(), xmlAfter, true); // FIXME: check whether is needed cmd->redo(); m_view->undoAdapter()->addCommand(cmd); m_view->document()->setModified(true); } } } else { // If layer == normal painting layer, vector layer, or group layer QList selectedNodes = m_view->nodeManager()->selectedNodes(); KisDlgLayerProperties *dialog = new KisDlgLayerProperties(selectedNodes, m_view); dialog->resize(dialog->minimumSizeHint()); dialog->setAttribute(Qt::WA_DeleteOnClose); Qt::WindowFlags flags = dialog->windowFlags(); dialog->setWindowFlags(flags | Qt::WindowStaysOnTopHint | Qt::Dialog); dialog->show(); } } void KisLayerManager::convertNodeToPaintLayer(KisNodeSP source) { KisImageWSP image = m_view->image(); if (!image) return; + + KisLayer *srcLayer = dynamic_cast(source.data()); + if (srcLayer) { + image->flattenLayer(srcLayer); + return; + } + + KisPaintDeviceSP srcDevice = source->paintDevice() ? source->projection() : source->original(); if (!srcDevice) return; KisPaintDeviceSP clone; if (!(*srcDevice->colorSpace() == *srcDevice->compositionSourceColorSpace())) { clone = new KisPaintDevice(srcDevice->compositionSourceColorSpace()); QRect rc(srcDevice->extent()); KisPainter::copyAreaOptimized(rc.topLeft(), srcDevice, clone, rc); } else { clone = new KisPaintDevice(*srcDevice); } KisLayerSP layer = new KisPaintLayer(image, source->name(), source->opacity(), clone); layer->setCompositeOpId(source->compositeOpId()); KisNodeSP parent = source->parent(); KisNodeSP above = source; while (parent && !parent->allowAsChild(layer)) { above = above->parent(); parent = above ? above->parent() : 0; } m_commandsAdapter->beginMacro(kundo2_i18n("Convert to a Paint Layer")); m_commandsAdapter->addNode(layer, parent, above); m_commandsAdapter->removeNode(source); m_commandsAdapter->endMacro(); } void KisLayerManager::adjustLayerPosition(KisNodeSP node, KisNodeSP activeNode, KisNodeSP &parent, KisNodeSP &above) { Q_ASSERT(activeNode); parent = activeNode; above = parent->lastChild(); while (parent && (!parent->allowAsChild(node) || parent->userLocked())) { above = parent; parent = parent->parent(); } if (!parent) { warnKrita << "KisLayerManager::adjustLayerPosition:" << "No node accepted newly created node"; parent = m_view->image()->root(); above = parent->lastChild(); } } void KisLayerManager::addLayerCommon(KisNodeSP activeNode, KisLayerSP layer, bool updateImage) { KisNodeSP parent; KisNodeSP above; adjustLayerPosition(layer, activeNode, parent, above); KisGroupLayer *group = dynamic_cast(parent.data()); const bool parentForceUpdate = group && !group->projectionIsValid(); updateImage |= parentForceUpdate; m_commandsAdapter->addNode(layer, parent, above, updateImage, updateImage); } KisLayerSP KisLayerManager::addLayer(KisNodeSP activeNode) { KisLayerSP layer = KisLayerUtils::constructDefaultLayer(m_view->image()); addLayerCommon(activeNode, layer, false); return layer; } void KisLayerManager::addGroupLayer(KisNodeSP activeNode) { KisImageWSP image = m_view->image(); addLayerCommon(activeNode, new KisGroupLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8), false); } void KisLayerManager::addCloneLayer(KisNodeSP activeNode) { KisImageWSP image = m_view->image(); addLayerCommon(activeNode, new KisCloneLayer(activeLayer(), image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8)); } void KisLayerManager::addShapeLayer(KisNodeSP activeNode) { if (!m_view) return; if (!m_view->document()) return; KisImageWSP image = m_view->image(); KisShapeLayerSP layer = new KisShapeLayer(m_view->document()->shapeController(), image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8); addLayerCommon(activeNode, layer, false); } void KisLayerManager::addAdjustmentLayer(KisNodeSP activeNode) { KisImageWSP image = m_view->image(); KisSelectionSP selection = m_view->selection(); KisAdjustmentLayerSP adjl = addAdjustmentLayer(activeNode, QString(), 0, selection); image->refreshGraph(); KisPaintDeviceSP previewDevice = new KisPaintDevice(*adjl->original()); KisDlgAdjustmentLayer dlg(adjl, adjl.data(), previewDevice, image->nextLayerName(), i18n("New Filter Layer"), m_view); dlg.resize(dlg.minimumSizeHint()); // ensure that the device may be free'd by the dialog // when it is not needed anymore previewDevice = 0; if (dlg.exec() != QDialog::Accepted || adjl->filter().isNull()) { // XXX: add messagebox warning if there's no filter set! m_commandsAdapter->undoLastCommand(); } else { adjl->setName(dlg.layerName()); } } KisAdjustmentLayerSP KisLayerManager::addAdjustmentLayer(KisNodeSP activeNode, const QString & name, KisFilterConfiguration * filter, KisSelectionSP selection) { KisImageWSP image = m_view->image(); KisAdjustmentLayerSP layer = new KisAdjustmentLayer(image, name, filter, selection); addLayerCommon(activeNode, layer); return layer; } void KisLayerManager::addGeneratorLayer(KisNodeSP activeNode) { KisImageWSP image = m_view->image(); KisDlgGeneratorLayer dlg(image->nextLayerName(), m_view, m_view->mainWindow()); dlg.resize(dlg.minimumSizeHint()); if (dlg.exec() == QDialog::Accepted) { KisSelectionSP selection = m_view->selection(); KisFilterConfiguration * generator = dlg.configuration(); QString name = dlg.layerName(); addLayerCommon(activeNode, new KisGeneratorLayer(image, name, generator, selection)); } } void KisLayerManager::rotateLayer(double radians) { if (!m_view->image()) return; KisLayerSP layer = activeLayer(); if (!layer) return; m_view->image()->rotateNode(layer, radians); } void KisLayerManager::shearLayer(double angleX, double angleY) { if (!m_view->image()) return; KisLayerSP layer = activeLayer(); if (!layer) return; m_view->image()->shearNode(layer, angleX, angleY); } void KisLayerManager::flattenImage() { KisImageWSP image = m_view->image(); if (image) { bool doIt = true; if (image->nHiddenLayers() > 0) { int answer = QMessageBox::warning(m_view->mainWindow(), i18nc("@title:window", "Flatten Image"), i18n("The image contains hidden layers that will be lost. Do you want to flatten the image?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (answer != QMessageBox::Yes) { doIt = false; } } if (doIt) { image->flatten(); } } } inline bool isSelectionMask(KisNodeSP node) { return dynamic_cast(node.data()); } bool tryMergeSelectionMasks(KisNodeSP currentNode, KisImageSP image) { bool result = false; KisNodeSP prevNode = currentNode->prevSibling(); if (isSelectionMask(currentNode) && prevNode && isSelectionMask(prevNode)) { QList mergedNodes; mergedNodes.append(currentNode); mergedNodes.append(prevNode); image->mergeMultipleLayers(mergedNodes, currentNode); result = true; } return result; } void KisLayerManager::mergeLayer() { KisImageWSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; QList selectedNodes = m_view->nodeManager()->selectedNodes(); if (selectedNodes.size() > 1) { image->mergeMultipleLayers(selectedNodes, m_view->activeNode()); } else if (!tryMergeSelectionMasks(m_view->activeNode(), image)) { if (!layer->prevSibling()) return; KisLayer *prevLayer = dynamic_cast(layer->prevSibling().data()); if (!prevLayer) return; if (layer->metaData()->isEmpty() && prevLayer->metaData()->isEmpty()) { image->mergeDown(layer, KisMetaData::MergeStrategyRegistry::instance()->get("Drop")); } else { const KisMetaData::MergeStrategy* strategy = KisMetaDataMergeStrategyChooserWidget::showDialog(m_view->mainWindow()); if (!strategy) return; image->mergeDown(layer, strategy); } } m_view->updateGUI(); } void KisLayerManager::flattenLayer() { KisImageWSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; image->flattenLayer(layer); m_view->updateGUI(); } void KisLayerManager::rasterizeLayer() { KisImageWSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; KisPaintLayerSP paintLayer = new KisPaintLayer(image, layer->name(), layer->opacity()); KisPainter gc(paintLayer->paintDevice()); QRect rc = layer->projection()->exactBounds(); gc.bitBlt(rc.topLeft(), layer->projection(), rc); m_commandsAdapter->beginMacro(kundo2_i18n("Rasterize Layer")); m_commandsAdapter->addNode(paintLayer.data(), layer->parent().data(), layer.data()); int childCount = layer->childCount(); for (int i = 0; i < childCount; i++) { m_commandsAdapter->moveNode(layer->firstChild(), paintLayer, paintLayer->lastChild()); } m_commandsAdapter->removeNode(layer); m_commandsAdapter->endMacro(); updateGUI(); } void KisLayerManager::layersUpdated() { KisLayerSP layer = activeLayer(); if (!layer) return; m_view->updateGUI(); } void KisLayerManager::saveGroupLayers() { - QStringList listMimeFilter = KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Export); + QStringList listMimeFilter = KisImportExportManager::mimeFilter(KisImportExportManager::Export); KoDialog dlg; QWidget *page = new QWidget(&dlg); dlg.setMainWidget(page); QBoxLayout *layout = new QVBoxLayout(page); KisFileNameRequester *urlRequester = new KisFileNameRequester(page); urlRequester->setMode(KoFileDialog::OpenDirectory); urlRequester->setStartDir(QFileInfo(m_view->document()->url().toLocalFile()).absolutePath()); urlRequester->setMimeTypeFilters(listMimeFilter); urlRequester->setFileName(m_view->document()->url().toLocalFile()); layout->addWidget(urlRequester); QCheckBox *chkInvisible = new QCheckBox(i18n("Convert Invisible Groups"), page); chkInvisible->setChecked(false); layout->addWidget(chkInvisible); QCheckBox *chkDepth = new QCheckBox(i18n("Export Only Toplevel Groups"), page); chkDepth->setChecked(true); layout->addWidget(chkDepth); if (!dlg.exec()) return; // selectedUrl()( does not return the expected result. So, build up the QUrl the more complicated way //return m_fileWidget->selectedUrl(); QString path = urlRequester->fileName(); if (path.isEmpty()) return; QFileInfo f(path); QString mimeType= KisMimeDatabase::mimeTypeForFile(f.fileName()); QString extension = KisMimeDatabase::suffixesForMimeType(mimeType).first(); QString basename = f.baseName(); KisImageWSP image = m_view->image(); if (!image) return; KisSaveGroupVisitor v(image, chkInvisible->isChecked(), chkDepth->isChecked(), QUrl(path), basename, extension, mimeType); image->rootLayer()->accept(v); } bool KisLayerManager::activeLayerHasSelection() { return (activeLayer()->selection() != 0); } void KisLayerManager::addFileLayer(KisNodeSP activeNode) { QString basePath; QUrl url = m_view->document()->url(); if (url.isLocalFile()) { basePath = QFileInfo(url.toLocalFile()).absolutePath(); } KisImageWSP image = m_view->image(); KisDlgFileLayer dlg(basePath, image->nextLayerName(), m_view->mainWindow()); dlg.resize(dlg.minimumSizeHint()); if (dlg.exec() == QDialog::Accepted) { QString name = dlg.layerName(); QString fileName = dlg.fileName(); if(fileName.isEmpty()){ QMessageBox::critical(m_view->mainWindow(), i18nc("@title:window", "Krita"), i18n("No file name specified")); return; } KisFileLayer::ScalingMethod scalingMethod = dlg.scaleToImageResolution(); addLayerCommon(activeNode, new KisFileLayer(image, basePath, fileName, scalingMethod, name, OPACITY_OPAQUE_U8)); } } void updateLayerStyles(KisLayerSP layer, KisDlgLayerStyle *dlg) { KisSetLayerStyleCommand::updateLayerStyle(layer, dlg->style()->clone()); } void KisLayerManager::layerStyle() { KisImageWSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; KisPSDLayerStyleSP oldStyle; if (layer->layerStyle()) { oldStyle = layer->layerStyle()->clone(); } else { oldStyle = toQShared(new KisPSDLayerStyle()); } KisDlgLayerStyle dlg(oldStyle->clone(), m_view->resourceProvider()); std::function updateCall(std::bind(updateLayerStyles, layer, &dlg)); SignalToFunctionProxy proxy(updateCall); connect(&dlg, SIGNAL(configChanged()), &proxy, SLOT(start())); if (dlg.exec() == QDialog::Accepted) { KisPSDLayerStyleSP newStyle = dlg.style(); KUndo2CommandSP command = toQShared( new KisSetLayerStyleCommand(layer, oldStyle, newStyle)); image->postExecutionUndoAdapter()->addCommand(command); } } diff --git a/libs/ui/kis_md5_generator.cpp b/libs/ui/kis_md5_generator.cpp index 1a19933a52..43854f0fb4 100644 --- a/libs/ui/kis_md5_generator.cpp +++ b/libs/ui/kis_md5_generator.cpp @@ -1,64 +1,64 @@ /* * Copyright (c) 2015 Stefano Bonicatti * * 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 "kis_md5_generator.h" #include #include KisMD5Generator::KisMD5Generator() { } KisMD5Generator::~KisMD5Generator() { } -QByteArray KisMD5Generator::generateHash(QString filename) +QByteArray KisMD5Generator::generateHash(const QString &filename) { QByteArray ba; if(filename.startsWith("bundle://")) { QString bn = filename.mid(9); int pos = bn.lastIndexOf(":"); QString fn = bn.right(bn.size() - pos - 1); bn = bn.left(pos); QScopedPointer resourceStore(KoStore::createStore(bn, KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip)); if (!resourceStore || resourceStore->bad()) { warnKrita << "Could not open store on bundle" << bn; return ba; } if (resourceStore->isOpen()) resourceStore->close(); if (!resourceStore->open(fn)) { warnKrita << "Could not open preset" << fn << "in bundle" << bn; return ba; } ba = resourceStore->device()->readAll(); resourceStore->close(); return KoMD5Generator::generateHash(ba); } return KoMD5Generator::generateHash(filename); } diff --git a/libs/ui/kis_md5_generator.h b/libs/ui/kis_md5_generator.h index 4d6e6b273d..305a38d4ca 100644 --- a/libs/ui/kis_md5_generator.h +++ b/libs/ui/kis_md5_generator.h @@ -1,29 +1,29 @@ /* * Copyright (c) 2015 Stefano Bonicatti * * 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 class KisMD5Generator : public KoMD5Generator { public: KisMD5Generator(); ~KisMD5Generator(); - QByteArray generateHash(QString filename); + QByteArray generateHash(const QString &filename); using KoMD5Generator::generateHash; }; diff --git a/libs/ui/kis_mimedata.cpp b/libs/ui/kis_mimedata.cpp index d97381f24c..1a5f0dba0f 100644 --- a/libs/ui/kis_mimedata.cpp +++ b/libs/ui/kis_mimedata.cpp @@ -1,445 +1,441 @@ /* * Copyright (c) 2011 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; 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_mimedata.h" #include "kis_config.h" #include "kis_node.h" #include "kis_paint_device.h" #include "kis_shared_ptr.h" #include "kis_image.h" #include "kis_layer.h" #include "kis_shape_layer.h" #include "kis_paint_layer.h" #include "KisDocument.h" #include "kis_shape_controller.h" #include "KisPart.h" #include "kis_layer_utils.h" #include "kis_node_insertion_adapter.h" #include "kis_dummies_facade_base.h" #include "kis_node_dummies_graph.h" #include #include #include #include #include #include #include #include #include #include #include #include #include KisMimeData::KisMimeData(QList nodes, bool forceCopy) : QMimeData() , m_nodes(nodes) , m_forceCopy(forceCopy) { Q_ASSERT(m_nodes.size() > 0); m_initialListener = m_nodes.first()->graphListener(); } void KisMimeData::deepCopyNodes() { KisNodeList newNodes; Q_FOREACH (KisNodeSP node, m_nodes) { newNodes << node->clone(); } m_nodes = newNodes; } QList KisMimeData::nodes() const { return m_nodes; } QStringList KisMimeData::formats () const { QStringList f = QMimeData::formats(); if (m_nodes.size() > 0) { f << "application/x-krita-node" << "application/x-krita-node-url" << "application/x-qt-image" << "application/zip" << "application/x-krita-node-internal-pointer"; } return f; } KisDocument *createDocument(QList nodes) { KisDocument *doc = KisPart::instance()->createDocument(); QRect rc; Q_FOREACH (KisNodeSP node, nodes) { rc |= node->exactBounds(); } KisImageSP image = new KisImage(0, rc.width(), rc.height(), nodes.first()->colorSpace(), nodes.first()->name()); Q_FOREACH (KisNodeSP node, nodes) { image->addNode(node->clone()); } doc->setCurrentImage(image); return doc; } QByteArray serializeToByteArray(QList nodes) { QByteArray byteArray; QBuffer buffer(&byteArray); KoStore *store = KoStore::createStore(&buffer, KoStore::Write); Q_ASSERT(!store->bad()); KisDocument *doc = createDocument(nodes); doc->saveNativeFormatCalligra(store); delete doc; return byteArray; } QVariant KisMimeData::retrieveData(const QString &mimetype, QVariant::Type preferredType) const { Q_ASSERT(m_nodes.size() > 0); if (mimetype == "application/x-qt-image") { KisConfig cfg; KisDocument *doc = createDocument(m_nodes); doc->image()->refreshGraph(); doc->image()->waitForDone(); return doc->image()->projection()->convertToQImage(cfg.displayProfile(QApplication::desktop()->screenNumber(qApp->activeWindow())), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); } else if (mimetype == "application/x-krita-node" || mimetype == "application/zip") { QByteArray ba = serializeToByteArray(m_nodes); return ba; } else if (mimetype == "application/x-krita-node-url") { QByteArray ba = serializeToByteArray(m_nodes); QString temporaryPath = QDir::tempPath() + QDir::separator() + QString("krita_tmp_dnd_layer_%1_%2.kra") .arg(QApplication::applicationPid()) .arg(qrand()); QFile file(temporaryPath); file.open(QFile::WriteOnly); file.write(ba); file.flush(); file.close(); return QUrl::fromLocalFile(temporaryPath).toEncoded(); } else if (mimetype == "application/x-krita-node-internal-pointer") { QDomDocument doc("krita_internal_node_pointer"); QDomElement root = doc.createElement("pointer"); root.setAttribute("application_pid", (qint64)QApplication::applicationPid()); root.setAttribute("force_copy", m_forceCopy); root.setAttribute("listener_pointer_value", (qint64)m_initialListener); doc.appendChild(root); Q_FOREACH (KisNodeSP node, m_nodes) { QDomElement element = doc.createElement("node"); element.setAttribute("pointer_value", (qint64)node.data()); root.appendChild(element); } return doc.toByteArray(); } else { return QMimeData::retrieveData(mimetype, preferredType); } } void KisMimeData::initializeExternalNode(KisNodeSP &node, KisImageWSP image, KisShapeController *shapeController) { // layers store a link to the image, so update it KisLayer *layer = dynamic_cast(node.data()); if (layer) { layer->setImage(image); } KisShapeLayer *shapeLayer = dynamic_cast(node.data()); if (shapeLayer) { - KisShapeLayer *shapeLayer2 = new KisShapeLayer(shapeController, image, node->name(), node->opacity()); - QList shapes = shapeLayer->shapes(); - shapeLayer->removeAllShapes(); - Q_FOREACH (KoShape *shape, shapes) { - shapeLayer2->addShape(shape); - } + // attach the layer to a new shape controller + KisShapeLayer *shapeLayer2 = new KisShapeLayer(*shapeLayer, shapeController); node = shapeLayer2; } } QList KisMimeData::tryLoadInternalNodes(const QMimeData *data, KisImageWSP image, KisShapeController *shapeController, bool /* IN-OUT */ ©Node) { QList nodes; bool forceCopy = false; KisNodeGraphListener *initialListener = 0; // Qt 4.7 and Qt 5.5 way const KisMimeData *mimedata = qobject_cast(data); if (mimedata) { nodes = mimedata->nodes(); forceCopy = mimedata->m_forceCopy; initialListener = mimedata->m_initialListener; } // Qt 4.8 way if (nodes.isEmpty() && data->hasFormat("application/x-krita-node-internal-pointer")) { QByteArray nodeXml = data->data("application/x-krita-node-internal-pointer"); QDomDocument doc; doc.setContent(nodeXml); QDomElement element = doc.documentElement(); qint64 pid = element.attribute("application_pid").toLongLong(); forceCopy = element.attribute("force_copy").toInt(); qint64 listenerPointerValue = element.attribute("listener_pointer_value").toLongLong(); initialListener = reinterpret_cast(listenerPointerValue); if (pid == QApplication::applicationPid()) { QDomNode n = element.firstChild(); while (!n.isNull()) { QDomElement e = n.toElement(); if (!e.isNull()) { qint64 pointerValue = e.attribute("pointer_value").toLongLong(); if (pointerValue) { nodes << reinterpret_cast(pointerValue); } } n = n.nextSibling(); } } } if (!nodes.isEmpty() && (forceCopy || copyNode || nodes.first()->graphListener() != image.data())) { QList clones; Q_FOREACH (KisNodeSP node, nodes) { node = node->clone(); if ((forceCopy || copyNode) && initialListener == image.data()) { KisLayerUtils::addCopyOfNameTag(node); } initializeExternalNode(node, image, shapeController); clones << node; } nodes = clones; copyNode = true; } return nodes; } QList KisMimeData::loadNodes(const QMimeData *data, const QRect &imageBounds, const QPoint &preferredCenter, bool forceRecenter, KisImageWSP image, KisShapeController *shapeController) { bool alwaysRecenter = false; QList nodes; if (data->hasFormat("application/x-krita-node")) { QByteArray ba = data->data("application/x-krita-node"); KisDocument *tempDoc = KisPart::instance()->createDocument(); bool result = tempDoc->loadNativeFormatFromByteArray(ba); if (result) { KisImageWSP tempImage = tempDoc->image(); Q_FOREACH (KisNodeSP node, tempImage->root()->childNodes(QStringList(), KoProperties())) { nodes << node; tempImage->removeNode(node); initializeExternalNode(node, image, shapeController); } } delete tempDoc; } if (nodes.isEmpty() && data->hasFormat("application/x-krita-node-url")) { QByteArray ba = data->data("application/x-krita-node-url"); QString localFile = QUrl::fromEncoded(ba).toLocalFile(); KisDocument *tempDoc = KisPart::instance()->createDocument(); bool result = tempDoc->loadNativeFormat(localFile); if (result) { KisImageWSP tempImage = tempDoc->image(); Q_FOREACH (KisNodeSP node, tempImage->root()->childNodes(QStringList(), KoProperties())) { nodes << node; tempImage->removeNode(node); initializeExternalNode(node, image, shapeController); } } delete tempDoc; QFile::remove(localFile); } if (nodes.isEmpty() && data->hasImage()) { QImage qimage = qvariant_cast(data->imageData()); KisPaintDeviceSP device = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); device->convertFromQImage(qimage, 0); nodes << new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, device); alwaysRecenter = true; } if (!nodes.isEmpty()) { Q_FOREACH (KisNodeSP node, nodes) { QRect bounds = node->projection()->exactBounds(); if (alwaysRecenter || forceRecenter || (!imageBounds.contains(bounds) && !imageBounds.intersects(bounds))) { QPoint pt = preferredCenter - bounds.center(); node->setX(pt.x()); node->setY(pt.y()); } } } return nodes; } QMimeData* KisMimeData::mimeForLayers(const KisNodeList &nodes, KisNodeSP imageRoot, bool forceCopy) { KisNodeList inputNodes = nodes; KisNodeList sortedNodes; KisLayerUtils::sortMergableNodes(imageRoot, inputNodes, sortedNodes); if (sortedNodes.isEmpty()) return 0; KisMimeData* data = new KisMimeData(sortedNodes, forceCopy); return data; } QMimeData* KisMimeData::mimeForLayersDeepCopy(const KisNodeList &nodes, KisNodeSP imageRoot, bool forceCopy) { KisNodeList inputNodes = nodes; KisNodeList sortedNodes; KisLayerUtils::sortMergableNodes(imageRoot, inputNodes, sortedNodes); if (sortedNodes.isEmpty()) return 0; KisMimeData* data = new KisMimeData(sortedNodes, forceCopy); data->deepCopyNodes(); return data; } bool nodeAllowsAsChild(KisNodeSP parent, KisNodeList nodes) { bool result = true; Q_FOREACH (KisNodeSP node, nodes) { if (!parent->allowAsChild(node)) { result = false; break; } } return result; } bool correctNewNodeLocation(KisNodeList nodes, KisNodeDummy* &parentDummy, KisNodeDummy* &aboveThisDummy) { KisNodeSP parentNode = parentDummy->node(); bool result = true; if(!nodeAllowsAsChild(parentDummy->node(), nodes)) { aboveThisDummy = parentDummy; parentDummy = parentDummy->parent(); result = (!parentDummy) ? false : correctNewNodeLocation(nodes, parentDummy, aboveThisDummy); } return result; } bool KisMimeData::insertMimeLayers(const QMimeData *data, KisImageSP image, KisShapeController *shapeController, KisNodeDummy *parentDummy, KisNodeDummy *aboveThisDummy, bool copyNode, KisNodeInsertionAdapter *nodeInsertionAdapter) { QList nodes = KisMimeData::tryLoadInternalNodes(data, image, shapeController, copyNode /* IN-OUT */); if (nodes.isEmpty()) { QRect imageBounds = image->bounds(); nodes = KisMimeData::loadNodes(data, imageBounds, imageBounds.center(), false, image, shapeController); /** * Don't try to move a node originating from another image, * just copy it. */ copyNode = true; } if (nodes.isEmpty()) return false; bool result = true; if (!correctNewNodeLocation(nodes, parentDummy, aboveThisDummy)) { return false; } KIS_ASSERT_RECOVER(nodeInsertionAdapter) { return false; } Q_ASSERT(parentDummy); KisNodeSP aboveThisNode = aboveThisDummy ? aboveThisDummy->node() : 0; if (copyNode) { nodeInsertionAdapter->addNodes(nodes, parentDummy->node(), aboveThisNode); } else { Q_ASSERT(nodes.first()->graphListener() == image.data()); nodeInsertionAdapter->moveNodes(nodes, parentDummy->node(), aboveThisNode); } return result; } diff --git a/libs/ui/kis_mirror_manager.cpp b/libs/ui/kis_mirror_manager.cpp index f5524f3c53..84fe2b33f7 100644 --- a/libs/ui/kis_mirror_manager.cpp +++ b/libs/ui/kis_mirror_manager.cpp @@ -1,88 +1,89 @@ /* * Copyright (c) 2009 Cyrille Berger * Copyright (c) 2014 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_mirror_manager.h" #include "KisViewManager.h" #include #include #include #include #include #include -#include "canvas/kis_mirror_axis.h" +#include "kis_canvas2.h" +#include "kis_mirror_axis.h" KisMirrorManager::KisMirrorManager(KisViewManager* view) : QObject(view) , m_imageView(0) { } KisMirrorManager::~KisMirrorManager() { } void KisMirrorManager::setup(KActionCollection * collection) { m_mirrorCanvas = new KToggleAction(i18n("Mirror View"), this); m_mirrorCanvas->setChecked(false); //m_mirrorCanvas->setShortcut(QKeySequence(Qt::Key_M)); collection->addAction("mirror_canvas", m_mirrorCanvas); collection->setDefaultShortcut(m_mirrorCanvas, QKeySequence(Qt::Key_M)); updateAction(); } void KisMirrorManager::setView(QPointer imageView) { if (m_imageView) { m_mirrorCanvas->disconnect(); } m_imageView = imageView; if (m_imageView && !decoration()) { m_imageView->canvasBase()->addDecoration(new KisMirrorAxis(m_imageView->viewManager()->resourceProvider(), m_imageView)); } if (m_imageView && decoration()) { connect(m_mirrorCanvas, SIGNAL(toggled(bool)), dynamic_cast(m_imageView->canvasController()), SLOT(mirrorCanvas(bool))); } updateAction(); } void KisMirrorManager::updateAction() { if (decoration()) { m_mirrorCanvas->setChecked(decoration()->visible()); m_mirrorCanvas->setEnabled(true); } else { m_mirrorCanvas->setEnabled(false); } } KisMirrorAxis* KisMirrorManager::decoration() { if (m_imageView && m_imageView->canvasBase()) { - return dynamic_cast(m_imageView->canvasBase()->decoration("mirror_axis")); + return dynamic_cast(m_imageView->canvasBase()->decoration("mirror_axis").data()); } return 0; } diff --git a/libs/ui/kis_multinode_property.cpp b/libs/ui/kis_multinode_property.cpp index 78d62c0501..1bfee50c3a 100644 --- a/libs/ui/kis_multinode_property.cpp +++ b/libs/ui/kis_multinode_property.cpp @@ -1,86 +1,132 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_multinode_property.h" /******************************************************************/ /* MultinodePropertyConnectorInterface */ /******************************************************************/ MultinodePropertyConnectorInterface::~MultinodePropertyConnectorInterface() { } void MultinodePropertyConnectorInterface::connectValueChangedSignal(const QObject *receiver, const char *method, Qt::ConnectionType type) { connect(this, SIGNAL(sigValueChanged()), receiver, method, type); notifyValueChanged(); } void MultinodePropertyConnectorInterface::notifyValueChanged() { emit sigValueChanged(); } +void MultinodePropertyConnectorInterface::connectAutoEnableWidget(QWidget *widget) +{ + Q_UNUSED(widget); +} + /******************************************************************/ /* MultinodePropertyBaseConnector */ /******************************************************************/ MultinodePropertyBaseConnector::MultinodePropertyBaseConnector(KisMultinodePropertyInterface *parent) : m_parent(parent) { } void MultinodePropertyBaseConnector::connectIgnoreCheckBox(QCheckBox *ignoreBox) { m_ignoreBox = ignoreBox; if (!m_parent->isIgnored() && !m_parent->savedValuesDiffer()) { m_ignoreBox->setEnabled(false); m_ignoreBox->setChecked(true); if (m_parent->haveTheOnlyNode()) { m_ignoreBox->setVisible(false); } } else { connect(m_ignoreBox, SIGNAL(stateChanged(int)), SLOT(slotIgnoreCheckBoxChanged(int))); m_ignoreBox->setEnabled(true); m_ignoreBox->setChecked(!m_parent->isIgnored()); } } +#include + +struct AutoEnabler : public QObject { + Q_OBJECT +public: + AutoEnabler(QObject *watched, KisMultinodePropertyInterface *property, QObject *parent) + : QObject(parent), m_watched(watched), m_property(property) + { + watched->installEventFilter(this); + } + + bool eventFilter(QObject *watched, QEvent * event) { + if (watched != m_watched) return false; + if (!m_property->isIgnored()) return false; + + if (event->type() == QEvent::MouseButtonPress || + event->type() == QEvent::TabletPress) { + + emit enableWidget(true); + } + + return false; + } +Q_SIGNALS: + void enableWidget(bool value); + +private: + QObject *m_watched; + KisMultinodePropertyInterface *m_property; +}; + +void MultinodePropertyBaseConnector::connectAutoEnableWidget(QWidget *widget) +{ + KIS_SAFE_ASSERT_RECOVER_RETURN(m_ignoreBox); + + AutoEnabler *enabler = new AutoEnabler(widget, m_parent, this); + connect(enabler, SIGNAL(enableWidget(bool)), m_ignoreBox, SLOT(setChecked(bool))); +} + void MultinodePropertyBaseConnector::slotIgnoreCheckBoxChanged(int state) { m_parent->setIgnored(state != Qt::Checked); } void MultinodePropertyBaseConnector::notifyIgnoreChanged() { if (!m_ignoreBox) return; if (m_ignoreBox->isChecked() != !m_parent->isIgnored()) { m_ignoreBox->setChecked(!m_parent->isIgnored()); } } /******************************************************************/ /* KisMultinodePropertyInterface */ /******************************************************************/ KisMultinodePropertyInterface::KisMultinodePropertyInterface() { } KisMultinodePropertyInterface::~KisMultinodePropertyInterface() { } + +#include "kis_multinode_property.moc" diff --git a/libs/ui/kis_multinode_property.h b/libs/ui/kis_multinode_property.h index 9cd03febea..dc0223c236 100644 --- a/libs/ui/kis_multinode_property.h +++ b/libs/ui/kis_multinode_property.h @@ -1,632 +1,648 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_MULTINODE_PROPERTY_H #define __KIS_MULTINODE_PROPERTY_H #include #include #include #include #include #include #include #include #include "kis_node.h" #include "kis_layer.h" #include "kis_layer_utils.h" #include "kritaui_export.h" class KisMultinodePropertyInterface; class MultinodePropertyBaseConnector; template class MultinodePropertyBoolConnector; template class KisMultinodeProperty; /******************************************************************/ /* Adapters */ /******************************************************************/ struct BaseAdapter { static KisNodeList filterNodes(KisNodeList nodes) { return nodes; } void setNumNodes(int numNodes) { m_numNodes = numNodes; } int m_numNodes = 0; }; struct CompositeOpAdapter : public BaseAdapter { typedef QString ValueType; typedef MultinodePropertyBaseConnector ConnectorType; static const bool forceIgnoreByDefault = false; static ValueType propForNode(KisNodeSP node) { return node->compositeOpId(); } static void setPropForNode(KisNodeSP node, const ValueType &value, int index) { Q_UNUSED(index); node->setCompositeOpId(value); } }; struct NameAdapter : public BaseAdapter { typedef QString ValueType; typedef MultinodePropertyBaseConnector ConnectorType; static const bool forceIgnoreByDefault = true; ValueType propForNode(KisNodeSP node) { return m_numNodes == 1 ? node->name() : stripName(node->name()); } void setPropForNode(KisNodeSP node, const ValueType &value, int index) { QString name; if (index < 0 || m_numNodes == 1) { name = value; } else { name = QString("%1 %2").arg(stripName(value)).arg(index); } node->setName(name); } private: static QString stripName(QString name) { QRegExp rexp("^(.+) (\\d{1,3})$"); int pos = rexp.indexIn(name); if (pos > -1) { name = rexp.cap(1); } return name; } }; struct ColorLabelAdapter : public BaseAdapter { typedef int ValueType; typedef MultinodePropertyBaseConnector ConnectorType; static const bool forceIgnoreByDefault = false; static ValueType propForNode(KisNodeSP node) { return node->colorLabelIndex(); } static void setPropForNode(KisNodeSP node, const ValueType &value, int index) { Q_UNUSED(index); node->setColorLabelIndex(value); } }; struct OpacityAdapter : public BaseAdapter { typedef int ValueType; typedef MultinodePropertyBaseConnector ConnectorType; static const bool forceIgnoreByDefault = false; static ValueType propForNode(KisNodeSP node) { return qRound(node->opacity() / 255.0 * 100); } static void setPropForNode(KisNodeSP node, const ValueType &value, int index) { Q_UNUSED(index); node->setOpacity(qRound(value * 255.0 / 100)); } }; inline uint qHash(const KisBaseNode::Property &prop, uint seed = 0) { return qHash(prop.name, seed); } struct LayerPropertyAdapter : public BaseAdapter { typedef bool ValueType; typedef MultinodePropertyBoolConnector ConnectorType; static const bool forceIgnoreByDefault = false; LayerPropertyAdapter(const QString &propName) : m_propName(propName) {} ValueType propForNode(KisNodeSP node) { KisBaseNode::PropertyList props = node->sectionModelProperties(); Q_FOREACH (const KisBaseNode::Property &prop, props) { if (prop.name == m_propName) { return prop.state.toBool(); } } return false; } void setPropForNode(KisNodeSP node, const ValueType &value, int index) { Q_UNUSED(index); bool stateChanged = false; KisBaseNode::PropertyList props = node->sectionModelProperties(); KisBaseNode::PropertyList::iterator it = props.begin(); KisBaseNode::PropertyList::iterator end = props.end(); for (; it != end; ++it) { if (it->name == m_propName) { it->state = value; stateChanged = true; break; } } if (stateChanged) { node->setSectionModelProperties(props); } } QString name() const { return m_propName; } static KisBaseNode::PropertyList adaptersList(KisNodeList nodes) { QHash adapters; Q_FOREACH (KisNodeSP node, nodes) { int sortingIndex = 0; KisBaseNode::PropertyList props = node->sectionModelProperties(); Q_FOREACH (const KisBaseNode::Property &prop, props) { if (prop.state.type() != QVariant::Bool) continue; if (!adapters.contains(prop)) { adapters.insert(prop, sortingIndex); } else { adapters[prop] = qMin(adapters[prop], sortingIndex); } sortingIndex++; } } QMultiMap sortedAdapters; auto it = adapters.constBegin(); auto end = adapters.constEnd(); for (; it != end; ++it) { sortedAdapters.insert(it.value(), it.key()); } return sortedAdapters.values(); } private: QString m_propName; }; struct ChannelFlagAdapter : public BaseAdapter { typedef bool ValueType; typedef MultinodePropertyBoolConnector ConnectorType; static const bool forceIgnoreByDefault = false; struct Property { Property(QString _name, int _channelIndex) : name(_name), channelIndex(_channelIndex) {} QString name; int channelIndex; }; typedef QList PropertyList; ChannelFlagAdapter(const Property &prop) : m_prop(prop) {} ValueType propForNode(KisNodeSP node) { KisLayerSP layer = toLayer(node); Q_ASSERT(layer); QBitArray flags = layer->channelFlags(); if (flags.isEmpty()) return true; return flags.testBit(m_prop.channelIndex); } void setPropForNode(KisNodeSP node, const ValueType &value, int index) { Q_UNUSED(index); KisLayerSP layer = toLayer(node); Q_ASSERT(layer); QBitArray flags = layer->channelFlags(); if (flags.isEmpty()) { flags = QBitArray(layer->colorSpace()->channelCount(), true); } if (flags.testBit(m_prop.channelIndex) != value) { flags.setBit(m_prop.channelIndex, value); layer->setChannelFlags(flags); } } QString name() const { return m_prop.name;; } static PropertyList adaptersList(KisNodeList nodes) { PropertyList props; { bool nodesDiffer = KisLayerUtils::checkNodesDiffer(nodes, [](KisNodeSP node) { return node->colorSpace(); }); if (nodesDiffer) { return props; } } QList channels = nodes.first()->colorSpace()->channels(); int index = 0; Q_FOREACH (KoChannelInfo *info, channels) { props << Property(info->name(), index); index++; } return props; } static KisNodeList filterNodes(KisNodeList nodes) { KisNodeList filteredNodes; Q_FOREACH (KisNodeSP node, nodes) { if (toLayer(node)) { filteredNodes << node; } } return filteredNodes; } private: static KisLayerSP toLayer(KisNodeSP node) { return dynamic_cast(node.data()); } private: Property m_prop; }; /******************************************************************/ /* MultinodePropertyConnectorInterface */ /******************************************************************/ class KRITAUI_EXPORT MultinodePropertyConnectorInterface : public QObject { Q_OBJECT public: virtual ~MultinodePropertyConnectorInterface(); /** * Public interface */ virtual void connectIgnoreCheckBox(QCheckBox *ignoreBox) = 0; void connectValueChangedSignal(const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection); + /** + * Clicking on this widget will automatically enable it, + * setting "Ignored" property to false. + * + * Default implementation does nothing. + */ + virtual void connectAutoEnableWidget(QWidget *widget); + Q_SIGNALS: void sigValueChanged(); protected Q_SLOTS: virtual void slotIgnoreCheckBoxChanged(int state) = 0; public: /** * Interface for KisMultinodeProperty's notifications */ virtual void notifyValueChanged(); virtual void notifyIgnoreChanged() = 0; }; /******************************************************************/ /* MultinodePropertyBaseConnector */ /******************************************************************/ class KRITAUI_EXPORT MultinodePropertyBaseConnector : public MultinodePropertyConnectorInterface { public: MultinodePropertyBaseConnector(KisMultinodePropertyInterface *parent); void connectIgnoreCheckBox(QCheckBox *ignoreBox); void notifyIgnoreChanged(); + void connectAutoEnableWidget(QWidget *widget); + protected: void slotIgnoreCheckBoxChanged(int state); private: QPointer m_ignoreBox; KisMultinodePropertyInterface *m_parent; }; /******************************************************************/ /* MultinodePropertyBoolConnector */ /******************************************************************/ template class MultinodePropertyBoolConnector : public MultinodePropertyConnectorInterface { typedef KisMultinodeProperty PropertyType; public: MultinodePropertyBoolConnector(PropertyType *parent) : m_parent(parent) { } void connectIgnoreCheckBox(QCheckBox *ignoreBox) { m_ignoreBox = ignoreBox; if ((!m_parent->isIgnored() && !m_parent->savedValuesDiffer()) || m_parent->haveTheOnlyNode()) { m_ignoreBox->setTristate(false); } else { m_ignoreBox->setTristate(true); } connect(m_ignoreBox, SIGNAL(stateChanged(int)), SLOT(slotIgnoreCheckBoxChanged(int))); } void notifyIgnoreChanged() { // noop } void notifyValueChanged() { if (m_ignoreBox) { Qt::CheckState newState = m_parent->isIgnored() ? Qt::PartiallyChecked : bool(m_parent->value()) ? Qt::Checked : Qt::Unchecked; if (m_ignoreBox->checkState() != newState) { m_ignoreBox->setCheckState(newState); } } MultinodePropertyConnectorInterface::notifyValueChanged(); } protected: void slotIgnoreCheckBoxChanged(int state) { if (state == Qt::PartiallyChecked) { m_parent->setIgnored(true); } else { m_parent->setIgnored(false); m_parent->setValue(bool(state == Qt::Checked)); } } private: QPointer m_ignoreBox; PropertyType *m_parent; }; /******************************************************************/ /* MultinodePropertyUndoCommand */ /******************************************************************/ template class MultinodePropertyUndoCommand : public KUndo2Command { public: typedef typename PropertyAdapter::ValueType ValueType; public: MultinodePropertyUndoCommand(PropertyAdapter propAdapter, KisNodeList nodes, const QList &oldValues, ValueType newValue, KUndo2Command *parent = 0) : KUndo2Command(parent), m_propAdapter(propAdapter), m_nodes(nodes), m_oldValues(oldValues), m_newValue(newValue) { } void undo() { int index = 0; Q_FOREACH (KisNodeSP node, m_nodes) { m_propAdapter.setPropForNode(node, m_oldValues[index], -1); index++; } } void redo() { int index = 0; Q_FOREACH (KisNodeSP node, m_nodes) { m_propAdapter.setPropForNode(node, m_newValue, index); index++; } } private: PropertyAdapter m_propAdapter; KisNodeList m_nodes; QList m_oldValues; ValueType m_newValue; }; /******************************************************************/ /* KisMultinodePropertyInterface */ /******************************************************************/ class KRITAUI_EXPORT KisMultinodePropertyInterface { public: KisMultinodePropertyInterface(); virtual ~KisMultinodePropertyInterface(); virtual void rereadCurrentValue() = 0; virtual void setIgnored(bool value) = 0; virtual bool isIgnored() const = 0; virtual bool savedValuesDiffer() const = 0; virtual bool haveTheOnlyNode() const = 0; virtual void connectValueChangedSignal(const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection) = 0; virtual void connectIgnoreCheckBox(QCheckBox *ignoreBox) = 0; + virtual void connectAutoEnableWidget(QWidget *widget) = 0; + virtual KUndo2Command* createPostExecutionUndoCommand() = 0; }; typedef QSharedPointer KisMultinodePropertyInterfaceSP; /******************************************************************/ /* KisMultinodeProperty */ /******************************************************************/ template class KisMultinodeProperty : public KisMultinodePropertyInterface { public: typedef typename PropertyAdapter::ValueType ValueType; typedef typename PropertyAdapter::ConnectorType ConnectorType; public: KisMultinodeProperty(KisNodeList nodes, PropertyAdapter adapter = PropertyAdapter()) : m_nodes(PropertyAdapter::filterNodes(nodes)), m_savedValuesDiffer(false), m_propAdapter(adapter), m_connector(new ConnectorType(this)) { Q_ASSERT(!m_nodes.isEmpty()); m_propAdapter.setNumNodes(m_nodes.size()); ValueType lastValue = m_propAdapter.propForNode(m_nodes.first()); Q_FOREACH (KisNodeSP node, m_nodes) { ValueType value = m_propAdapter.propForNode(node); m_savedValues.append(value); if (value != lastValue) { m_savedValuesDiffer = true; } lastValue = value; } m_isIgnored = m_nodes.size() > 1 && PropertyAdapter::forceIgnoreByDefault ? true : m_savedValuesDiffer; m_currentValue = defaultValue(); } ~KisMultinodeProperty() {} void rereadCurrentValue() { if (m_isIgnored) return; ValueType lastValue = m_propAdapter.propForNode(m_nodes.first()); Q_FOREACH (KisNodeSP node, m_nodes) { ValueType value = m_propAdapter.propForNode(node); if (value != lastValue) { qWarning() << "WARNING: mutiprops: values differ after reread!"; } lastValue = value; } if (lastValue != m_currentValue) { m_currentValue = lastValue; m_connector->notifyValueChanged(); } } void setValue(const ValueType &value) { Q_ASSERT(!m_isIgnored); if (value == m_currentValue) return; int index = 0; Q_FOREACH (KisNodeSP node, m_nodes) { m_propAdapter.setPropForNode(node, value, index); index++; } m_currentValue = value; m_connector->notifyValueChanged(); } ValueType value() const { return m_currentValue; } void setIgnored(bool value) { if (value == m_isIgnored) return; m_isIgnored = value; if (m_isIgnored) { int index = 0; Q_FOREACH (KisNodeSP node, m_nodes) { m_propAdapter.setPropForNode(node, m_savedValues[index], -1); index++; } m_currentValue = defaultValue(); } else { int index = 0; Q_FOREACH (KisNodeSP node, m_nodes) { m_propAdapter.setPropForNode(node, m_currentValue, index); index++; } } m_connector->notifyValueChanged(); m_connector->notifyIgnoreChanged(); } bool isIgnored() const { return m_isIgnored; } KUndo2Command* createPostExecutionUndoCommand() { KIS_ASSERT_RECOVER(!m_isIgnored) { return new KUndo2Command(); } return new MultinodePropertyUndoCommand(m_propAdapter, m_nodes, m_savedValues, m_currentValue); } // TODO: disconnect methods... void connectIgnoreCheckBox(QCheckBox *ignoreBox) { m_connector->connectIgnoreCheckBox(ignoreBox); } void connectValueChangedSignal(const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection) { m_connector->connectValueChangedSignal(receiver, method, type); } + void connectAutoEnableWidget(QWidget *widget) { + m_connector->connectAutoEnableWidget(widget); + } + /** * Interface for the connector */ bool savedValuesDiffer() const { return m_savedValuesDiffer; } bool haveTheOnlyNode() const { return m_nodes.size() == 1; } private: ValueType defaultValue() const { return m_savedValues.first(); } private: bool m_isIgnored; ValueType m_currentValue; KisNodeList m_nodes; QList m_savedValues; bool m_savedValuesDiffer; PropertyAdapter m_propAdapter; QScopedPointer m_connector; }; typedef KisMultinodeProperty KisMultinodeCompositeOpProperty; typedef KisMultinodeProperty KisMultinodeOpacityProperty; typedef KisMultinodeProperty KisMultinodeNameProperty; typedef KisMultinodeProperty KisMultinodeColorLabelProperty; #endif /* __KIS_MULTINODE_PROPERTY_H */ diff --git a/libs/ui/kis_node_manager.cpp b/libs/ui/kis_node_manager.cpp index af163e5028..964c097690 100644 --- a/libs/ui/kis_node_manager.cpp +++ b/libs/ui/kis_node_manager.cpp @@ -1,1300 +1,1300 @@ /* * Copyright (C) 2007 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_node_manager.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 "KisPart.h" #include "canvas/kis_canvas2.h" #include "kis_shape_controller.h" #include "kis_canvas_resource_provider.h" #include "KisViewManager.h" #include "KisDocument.h" #include "kis_mask_manager.h" #include "kis_group_layer.h" #include "kis_layer_manager.h" #include "kis_selection_manager.h" #include "kis_node_commands_adapter.h" #include "kis_action.h" #include "kis_action_manager.h" #include "kis_processing_applicator.h" #include "kis_sequential_iterator.h" #include "kis_transaction.h" #include "kis_node_selection_adapter.h" #include "kis_node_insertion_adapter.h" #include "kis_node_juggler_compressed.h" #include "kis_clipboard.h" #include "kis_node_dummies_graph.h" #include "kis_mimedata.h" #include "kis_layer_utils.h" #include "krita_utils.h" #include "processing/kis_mirror_processing_visitor.h" #include "KisView.h" struct KisNodeManager::Private { Private(KisNodeManager *_q, KisViewManager *v) : q(_q) , view(v) , imageView(0) , layerManager(v) , maskManager(v) , commandsAdapter(v) , nodeSelectionAdapter(new KisNodeSelectionAdapter(q)) , nodeInsertionAdapter(new KisNodeInsertionAdapter(q)) { } KisNodeManager * q; KisViewManager * view; QPointerimageView; KisLayerManager layerManager; KisMaskManager maskManager; KisNodeCommandsAdapter commandsAdapter; QScopedPointer nodeSelectionAdapter; QScopedPointer nodeInsertionAdapter; KisNodeList selectedNodes; QPointer nodeJuggler; bool activateNodeImpl(KisNodeSP node); QSignalMapper nodeCreationSignalMapper; QSignalMapper nodeConversionSignalMapper; void saveDeviceAsImage(KisPaintDeviceSP device, const QString &defaultName, const QRect &bounds, qreal xRes, qreal yRes, quint8 opacity); void mergeTransparencyMaskAsAlpha(bool writeToLayers); KisNodeJugglerCompressed* lazyGetJuggler(const KUndo2MagicString &actionName); }; bool KisNodeManager::Private::activateNodeImpl(KisNodeSP node) { Q_ASSERT(view); Q_ASSERT(view->canvasBase()); Q_ASSERT(view->canvasBase()->globalShapeManager()); Q_ASSERT(imageView); if (node && node == q->activeNode()) { return false; } // Set the selection on the shape manager to the active layer // and set call KoSelection::setActiveLayer( KoShapeLayer* layer ) // with the parent of the active layer. KoSelection *selection = view->canvasBase()->globalShapeManager()->selection(); Q_ASSERT(selection); selection->deselectAll(); if (!node) { selection->setActiveLayer(0); imageView->setCurrentNode(0); maskManager.activateMask(0); layerManager.activateLayer(0); } else { KoShape * shape = view->document()->shapeForNode(node); Q_ASSERT(shape); selection->select(shape); KoShapeLayer * shapeLayer = dynamic_cast(shape); Q_ASSERT(shapeLayer); // shapeLayer->setGeometryProtected(node->userLocked()); // shapeLayer->setVisible(node->visible()); selection->setActiveLayer(shapeLayer); imageView->setCurrentNode(node); if (KisLayerSP layer = dynamic_cast(node.data())) { maskManager.activateMask(0); layerManager.activateLayer(layer); } else if (KisMaskSP mask = dynamic_cast(node.data())) { maskManager.activateMask(mask); // XXX_NODE: for now, masks cannot be nested. layerManager.activateLayer(static_cast(node->parent().data())); } } return true; } KisNodeManager::KisNodeManager(KisViewManager *view) : m_d(new Private(this, view)) { connect(&m_d->layerManager, SIGNAL(sigLayerActivated(KisLayerSP)), SIGNAL(sigLayerActivated(KisLayerSP))); } KisNodeManager::~KisNodeManager() { delete m_d; } void KisNodeManager::setView(QPointerimageView) { m_d->maskManager.setView(imageView); m_d->layerManager.setView(imageView); if (m_d->imageView) { KisShapeController *shapeController = dynamic_cast(m_d->imageView->document()->shapeController()); Q_ASSERT(shapeController); shapeController->disconnect(SIGNAL(sigActivateNode(KisNodeSP)), this); m_d->imageView->image()->disconnect(this); } m_d->imageView = imageView; if (m_d->imageView) { KisShapeController *shapeController = dynamic_cast(m_d->imageView->document()->shapeController()); Q_ASSERT(shapeController); connect(shapeController, SIGNAL(sigActivateNode(KisNodeSP)), SLOT(slotNonUiActivatedNode(KisNodeSP))); connect(m_d->imageView->image(), SIGNAL(sigIsolatedModeChanged()),this, SLOT(slotUpdateIsolateModeAction())); connect(m_d->imageView->image(), SIGNAL(sigRequestNodeReselection(KisNodeSP, const KisNodeList&)),this, SLOT(slotImageRequestNodeReselection(KisNodeSP, const KisNodeList&))); m_d->imageView->resourceProvider()->slotNodeActivated(m_d->imageView->currentNode()); } } #define NEW_LAYER_ACTION(id, layerType) \ { \ action = actionManager->createAction(id); \ m_d->nodeCreationSignalMapper.setMapping(action, layerType); \ connect(action, SIGNAL(triggered()), \ &m_d->nodeCreationSignalMapper, SLOT(map())); \ } #define CONVERT_NODE_ACTION(id, layerType) \ { \ action = actionManager->createAction(id); \ action->setExcludedNodeTypes(QStringList(layerType)); \ actionManager->addAction(id, action); \ m_d->nodeConversionSignalMapper.setMapping(action, layerType); \ connect(action, SIGNAL(triggered()), \ &m_d->nodeConversionSignalMapper, SLOT(map())); \ } void KisNodeManager::setup(KActionCollection * actionCollection, KisActionManager* actionManager) { m_d->layerManager.setup(actionManager); m_d->maskManager.setup(actionCollection, actionManager); KisAction * action = actionManager->createAction("mirrorNodeX"); connect(action, SIGNAL(triggered()), this, SLOT(mirrorNodeX())); action = actionManager->createAction("mirrorNodeY"); connect(action, SIGNAL(triggered()), this, SLOT(mirrorNodeY())); action = actionManager->createAction("activateNextLayer"); connect(action, SIGNAL(triggered()), this, SLOT(activateNextNode())); action = actionManager->createAction("activatePreviousLayer"); connect(action, SIGNAL(triggered()), this, SLOT(activatePreviousNode())); action = actionManager->createAction("save_node_as_image"); connect(action, SIGNAL(triggered()), this, SLOT(saveNodeAsImage())); action = actionManager->createAction("duplicatelayer"); connect(action, SIGNAL(triggered()), this, SLOT(duplicateActiveNode())); action = actionManager->createAction("copy_layer_clipboard"); connect(action, SIGNAL(triggered()), this, SLOT(copyLayersToClipboard())); action = actionManager->createAction("cut_layer_clipboard"); connect(action, SIGNAL(triggered()), this, SLOT(cutLayersToClipboard())); action = actionManager->createAction("paste_layer_from_clipboard"); connect(action, SIGNAL(triggered()), this, SLOT(pasteLayersFromClipboard())); action = actionManager->createAction("create_quick_group"); connect(action, SIGNAL(triggered()), this, SLOT(createQuickGroup())); action = actionManager->createAction("create_quick_clipping_group"); connect(action, SIGNAL(triggered()), this, SLOT(createQuickClippingGroup())); action = actionManager->createAction("quick_ungroup"); connect(action, SIGNAL(triggered()), this, SLOT(quickUngroup())); action = actionManager->createAction("select_all_layers"); connect(action, SIGNAL(triggered()), this, SLOT(selectAllNodes())); action = actionManager->createAction("select_visible_layers"); connect(action, SIGNAL(triggered()), this, SLOT(selectVisibleNodes())); action = actionManager->createAction("select_locked_layers"); connect(action, SIGNAL(triggered()), this, SLOT(selectLockedNodes())); action = actionManager->createAction("select_invisible_layers"); connect(action, SIGNAL(triggered()), this, SLOT(selectInvisibleNodes())); action = actionManager->createAction("select_unlocked_layers"); connect(action, SIGNAL(triggered()), this, SLOT(selectUnlockedNodes())); NEW_LAYER_ACTION("add_new_paint_layer", "KisPaintLayer"); NEW_LAYER_ACTION("add_new_group_layer", "KisGroupLayer"); NEW_LAYER_ACTION("add_new_clone_layer", "KisCloneLayer"); NEW_LAYER_ACTION("add_new_shape_layer", "KisShapeLayer"); NEW_LAYER_ACTION("add_new_adjustment_layer", "KisAdjustmentLayer"); NEW_LAYER_ACTION("add_new_fill_layer", "KisGeneratorLayer"); NEW_LAYER_ACTION("add_new_file_layer", "KisFileLayer"); NEW_LAYER_ACTION("add_new_transparency_mask", "KisTransparencyMask"); NEW_LAYER_ACTION("add_new_filter_mask", "KisFilterMask"); NEW_LAYER_ACTION("add_new_transform_mask", "KisTransformMask"); NEW_LAYER_ACTION("add_new_selection_mask", "KisSelectionMask"); connect(&m_d->nodeCreationSignalMapper, SIGNAL(mapped(const QString &)), this, SLOT(createNode(const QString &))); CONVERT_NODE_ACTION("convert_to_paint_layer", "KisPaintLayer"); CONVERT_NODE_ACTION("convert_to_selection_mask", "KisSelectionMask"); CONVERT_NODE_ACTION("convert_to_filter_mask", "KisFilterMask"); CONVERT_NODE_ACTION("convert_to_transparency_mask", "KisTransparencyMask"); connect(&m_d->nodeConversionSignalMapper, SIGNAL(mapped(const QString &)), this, SLOT(convertNode(const QString &))); action = actionManager->createAction("isolate_layer"); connect(action, SIGNAL(triggered(bool)), this, SLOT(toggleIsolateMode(bool))); action = actionManager->createAction("split_alpha_into_mask"); connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaIntoMask())); action = actionManager->createAction("split_alpha_write"); connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaWrite())); // HINT: we can save even when the nodes are not editable action = actionManager->createAction("split_alpha_save_merged"); connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaSaveMerged())); connect(this, SIGNAL(sigNodeActivated(KisNodeSP)), SLOT(slotUpdateIsolateModeAction())); connect(this, SIGNAL(sigNodeActivated(KisNodeSP)), SLOT(slotTryFinishIsolatedMode())); } void KisNodeManager::updateGUI() { // enable/disable all relevant actions m_d->layerManager.updateGUI(); m_d->maskManager.updateGUI(); } KisNodeSP KisNodeManager::activeNode() { if (m_d->imageView) { return m_d->imageView->currentNode(); } return 0; } KisLayerSP KisNodeManager::activeLayer() { return m_d->layerManager.activeLayer(); } const KoColorSpace* KisNodeManager::activeColorSpace() { if (m_d->maskManager.activeDevice()) { return m_d->maskManager.activeDevice()->colorSpace(); } else { Q_ASSERT(m_d->layerManager.activeLayer()); if (m_d->layerManager.activeLayer()->parentLayer()) return m_d->layerManager.activeLayer()->parentLayer()->colorSpace(); else return m_d->view->image()->colorSpace(); } } void KisNodeManager::moveNodeAt(KisNodeSP node, KisNodeSP parent, int index) { if (parent->allowAsChild(node)) { if (node->inherits("KisSelectionMask") && parent->inherits("KisLayer")) { KisSelectionMask *m = dynamic_cast(node.data()); KisLayer *l = dynamic_cast(parent.data()); KisSelectionMaskSP selMask = l->selectionMask(); if (m && m->active() && l && l->selectionMask()) selMask->setActive(false); } m_d->commandsAdapter.moveNode(node, parent, index); } } void KisNodeManager::moveNodesDirect(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis) { KUndo2MagicString actionName = kundo2_i18n("Move Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->moveNode(nodes, parent, aboveThis); } void KisNodeManager::copyNodesDirect(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis) { KUndo2MagicString actionName = kundo2_i18n("Copy Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->copyNode(nodes, parent, aboveThis); } void KisNodeManager::addNodesDirect(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis) { KUndo2MagicString actionName = kundo2_i18n("Add Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->addNode(nodes, parent, aboveThis); } void KisNodeManager::toggleIsolateActiveNode() { KisImageWSP image = m_d->view->image(); KisNodeSP activeNode = this->activeNode(); KIS_ASSERT_RECOVER_RETURN(activeNode); if (activeNode == image->isolatedModeRoot()) { toggleIsolateMode(false); } else { toggleIsolateMode(true); } } void KisNodeManager::toggleIsolateMode(bool checked) { KisImageWSP image = m_d->view->image(); if (checked) { KisNodeSP activeNode = this->activeNode(); // Transform masks don't have pixel data... if (activeNode->inherits("KisTransformMask")) return; KIS_ASSERT_RECOVER_RETURN(activeNode); if (!image->startIsolatedMode(activeNode)) { KisAction *action = m_d->view->actionManager()->actionByName("isolate_layer"); action->setChecked(false); } } else { image->stopIsolatedMode(); } } void KisNodeManager::slotUpdateIsolateModeAction() { KisAction *action = m_d->view->actionManager()->actionByName("isolate_layer"); Q_ASSERT(action); KisNodeSP activeNode = this->activeNode(); KisNodeSP isolatedRootNode = m_d->view->image()->isolatedModeRoot(); action->setChecked(isolatedRootNode && isolatedRootNode == activeNode); } void KisNodeManager::slotTryFinishIsolatedMode() { KisNodeSP isolatedRootNode = m_d->view->image()->isolatedModeRoot(); if (!isolatedRootNode) return; bool belongsToIsolatedGroup = false; KisNodeSP node = this->activeNode(); while(node) { if (node == isolatedRootNode) { belongsToIsolatedGroup = true; break; } node = node->parent(); } if (!belongsToIsolatedGroup) { m_d->view->image()->stopIsolatedMode(); } } void KisNodeManager::createNode(const QString & nodeType, bool quiet, KisPaintDeviceSP copyFrom) { KisNodeSP activeNode = this->activeNode(); if (!activeNode) { activeNode = m_d->view->image()->root(); } KIS_ASSERT_RECOVER_RETURN(activeNode); if (activeNode->systemLocked()) { return; } // XXX: make factories for this kind of stuff, // with a registry if (nodeType == "KisPaintLayer") { m_d->layerManager.addLayer(activeNode); } else if (nodeType == "KisGroupLayer") { m_d->layerManager.addGroupLayer(activeNode); } else if (nodeType == "KisAdjustmentLayer") { m_d->layerManager.addAdjustmentLayer(activeNode); } else if (nodeType == "KisGeneratorLayer") { m_d->layerManager.addGeneratorLayer(activeNode); } else if (nodeType == "KisShapeLayer") { m_d->layerManager.addShapeLayer(activeNode); } else if (nodeType == "KisCloneLayer") { m_d->layerManager.addCloneLayer(activeNode); } else if (nodeType == "KisTransparencyMask") { m_d->maskManager.createTransparencyMask(activeNode, copyFrom, false); } else if (nodeType == "KisFilterMask") { m_d->maskManager.createFilterMask(activeNode, copyFrom, quiet, false); } else if (nodeType == "KisTransformMask") { m_d->maskManager.createTransformMask(activeNode); } else if (nodeType == "KisSelectionMask") { m_d->maskManager.createSelectionMask(activeNode, copyFrom, false); } else if (nodeType == "KisFileLayer") { m_d->layerManager.addFileLayer(activeNode); } } KisLayerSP KisNodeManager::createPaintLayer() { KisNodeSP activeNode = this->activeNode(); if (!activeNode) { activeNode = m_d->view->image()->root(); } return m_d->layerManager.addLayer(activeNode); } void KisNodeManager::convertNode(const QString &nodeType) { KisNodeSP activeNode = this->activeNode(); if (!activeNode) return; if (nodeType == "KisPaintLayer") { m_d->layerManager.convertNodeToPaintLayer(activeNode); } else if (nodeType == "KisSelectionMask" || nodeType == "KisFilterMask" || nodeType == "KisTransparencyMask") { KisPaintDeviceSP copyFrom = activeNode->paintDevice() ? activeNode->paintDevice() : activeNode->projection(); m_d->commandsAdapter.beginMacro(kundo2_i18n("Convert to a Selection Mask")); if (nodeType == "KisSelectionMask") { m_d->maskManager.createSelectionMask(activeNode, copyFrom, true); } else if (nodeType == "KisFilterMask") { m_d->maskManager.createFilterMask(activeNode, copyFrom, false, true); } else if (nodeType == "KisTransparencyMask") { m_d->maskManager.createTransparencyMask(activeNode, copyFrom, true); } m_d->commandsAdapter.removeNode(activeNode); m_d->commandsAdapter.endMacro(); } else { warnKrita << "Unsupported node conversion type:" << nodeType; } } void KisNodeManager::slotSomethingActivatedNodeImpl(KisNodeSP node) { KIS_ASSERT_RECOVER_RETURN(node != activeNode()); if (m_d->activateNodeImpl(node)) { emit sigUiNeedChangeActiveNode(node); emit sigNodeActivated(node); nodesUpdated(); if (node) { bool toggled = m_d->view->actionCollection()->action("view_show_canvas_only")->isChecked(); if (toggled) { m_d->view->showFloatingMessage( activeLayer()->name(), QIcon(), 1600, KisFloatingMessage::Medium, Qt::TextSingleLine); } } } } void KisNodeManager::slotNonUiActivatedNode(KisNodeSP node) { if (node == activeNode()) return; slotSomethingActivatedNodeImpl(node); if (node) { bool toggled = m_d->view->actionCollection()->action("view_show_canvas_only")->isChecked(); if (toggled) { m_d->view->showFloatingMessage( activeLayer()->name(), QIcon(), 1600, KisFloatingMessage::Medium, Qt::TextSingleLine); } } } void KisNodeManager::slotUiActivatedNode(KisNodeSP node) { if (node == activeNode()) return; slotSomethingActivatedNodeImpl(node); if (node) { QStringList vectorTools = QStringList() << "InteractionTool" << "KarbonPatternTool" << "KarbonGradientTool" << "KarbonCalligraphyTool" << "CreateShapesTool" << "PathTool"; QStringList pixelTools = QStringList() << "KritaShape/KisToolBrush" << "KritaShape/KisToolDyna" << "KritaShape/KisToolMultiBrush" << "KritaFill/KisToolFill" << "KritaFill/KisToolGradient"; if (node->inherits("KisShapeLayer")) { if (pixelTools.contains(KoToolManager::instance()->activeToolId())) { KoToolManager::instance()->switchToolRequested("InteractionTool"); } } else { if (vectorTools.contains(KoToolManager::instance()->activeToolId())) { KoToolManager::instance()->switchToolRequested("KritaShape/KisToolBrush"); } } } } void KisNodeManager::nodesUpdated() { KisNodeSP node = activeNode(); if (!node) return; m_d->layerManager.layersUpdated(); m_d->maskManager.masksUpdated(); m_d->view->updateGUI(); m_d->view->selectionManager()->selectionChanged(); } KisPaintDeviceSP KisNodeManager::activePaintDevice() { return m_d->maskManager.activeMask() ? m_d->maskManager.activeDevice() : m_d->layerManager.activeDevice(); } void KisNodeManager::nodeProperties(KisNodeSP node) { if (selectedNodes().size() > 1 || node->inherits("KisLayer")) { m_d->layerManager.layerProperties(); } else if (node->inherits("KisMask")) { m_d->maskManager.maskProperties(); } } qint32 KisNodeManager::convertOpacityToInt(qreal opacity) { /** * Scales opacity from the range 0...100 * to the integer range 0...255 */ return qMin(255, int(opacity * 2.55 + 0.5)); } void KisNodeManager::setNodeOpacity(KisNodeSP node, qint32 opacity, bool finalChange) { if (!node) return; if (node->opacity() == opacity) return; if (!finalChange) { node->setOpacity(opacity); node->setDirty(); } else { m_d->commandsAdapter.setOpacity(node, opacity); } } void KisNodeManager::setNodeCompositeOp(KisNodeSP node, const KoCompositeOp* compositeOp) { if (!node) return; if (node->compositeOp() == compositeOp) return; m_d->commandsAdapter.setCompositeOp(node, compositeOp); } void KisNodeManager::slotImageRequestNodeReselection(KisNodeSP activeNode, const KisNodeList &selectedNodes) { if (activeNode) { slotNonUiActivatedNode(activeNode); } if (!selectedNodes.isEmpty()) { slotSetSelectedNodes(selectedNodes); } } void KisNodeManager::slotSetSelectedNodes(const KisNodeList &nodes) { m_d->selectedNodes = nodes; emit sigUiNeedChangeSelectedNodes(nodes); } KisNodeList KisNodeManager::selectedNodes() { return m_d->selectedNodes; } KisNodeSelectionAdapter* KisNodeManager::nodeSelectionAdapter() const { return m_d->nodeSelectionAdapter.data(); } KisNodeInsertionAdapter* KisNodeManager::nodeInsertionAdapter() const { return m_d->nodeInsertionAdapter.data(); } void KisNodeManager::nodeOpacityChanged(qreal opacity, bool finalChange) { KisNodeSP node = activeNode(); setNodeOpacity(node, convertOpacityToInt(opacity), finalChange); } void KisNodeManager::nodeCompositeOpChanged(const KoCompositeOp* op) { KisNodeSP node = activeNode(); setNodeCompositeOp(node, op); } void KisNodeManager::duplicateActiveNode() { KUndo2MagicString actionName = kundo2_i18n("Duplicate Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->duplicateNode(selectedNodes()); } KisNodeJugglerCompressed* KisNodeManager::Private::lazyGetJuggler(const KUndo2MagicString &actionName) { KisImageWSP image = view->image(); if (!nodeJuggler || (nodeJuggler && !nodeJuggler->canMergeAction(actionName))) { nodeJuggler = new KisNodeJugglerCompressed(actionName, image, q, 750); nodeJuggler->setAutoDelete(true); } return nodeJuggler; } void KisNodeManager::raiseNode() { KUndo2MagicString actionName = kundo2_i18n("Raise Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->raiseNode(selectedNodes()); } void KisNodeManager::lowerNode() { KUndo2MagicString actionName = kundo2_i18n("Lower Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->lowerNode(selectedNodes()); } void KisNodeManager::removeSingleNode(KisNodeSP node) { if (!node || !node->parent()) { return; } KisNodeList nodes; nodes << node; removeSelectedNodes(nodes); } void KisNodeManager::removeSelectedNodes(KisNodeList nodes) { KUndo2MagicString actionName = kundo2_i18n("Remove Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->removeNode(nodes); } void KisNodeManager::removeNode() { removeSelectedNodes(selectedNodes()); } void KisNodeManager::mirrorNodeX() { KisNodeSP node = activeNode(); KUndo2MagicString commandName; if (node->inherits("KisLayer")) { commandName = kundo2_i18n("Mirror Layer X"); } else if (node->inherits("KisMask")) { commandName = kundo2_i18n("Mirror Mask X"); } mirrorNode(node, commandName, Qt::Horizontal); } void KisNodeManager::mirrorNodeY() { KisNodeSP node = activeNode(); KUndo2MagicString commandName; if (node->inherits("KisLayer")) { commandName = kundo2_i18n("Mirror Layer Y"); } else if (node->inherits("KisMask")) { commandName = kundo2_i18n("Mirror Mask Y"); } mirrorNode(node, commandName, Qt::Vertical); } inline bool checkForGlobalSelection(KisNodeSP node) { return dynamic_cast(node.data()) && node->parent() && !node->parent()->parent(); } void KisNodeManager::activateNextNode() { KisNodeSP activeNode = this->activeNode(); if (!activeNode) return; KisNodeSP node = activeNode->nextSibling(); if (!node && activeNode->parent() && activeNode->parent()->parent()) { node = activeNode->parent(); } while(node && checkForGlobalSelection(node)) { node = node->nextSibling(); } if (node) { slotNonUiActivatedNode(node); } } void KisNodeManager::activatePreviousNode() { KisNodeSP activeNode = this->activeNode(); if (!activeNode) return; KisNodeSP node = activeNode->prevSibling(); if (!node && activeNode->parent()) { node = activeNode->parent()->prevSibling(); } while(node && checkForGlobalSelection(node)) { node = node->prevSibling(); } if (node) { slotNonUiActivatedNode(node); } } void KisNodeManager::mergeLayer() { m_d->layerManager.mergeLayer(); } void KisNodeManager::rotate(double radians) { // XXX: implement rotation for masks as well m_d->layerManager.rotateLayer(radians); } void KisNodeManager::rotate180() { rotate(M_PI); } void KisNodeManager::rotateLeft90() { rotate(-M_PI / 2); } void KisNodeManager::rotateRight90() { rotate(M_PI / 2); } void KisNodeManager::shear(double angleX, double angleY) { // XXX: implement shear for masks as well m_d->layerManager.shearLayer(angleX, angleY); } void KisNodeManager::scale(double sx, double sy, KisFilterStrategy *filterStrategy) { KisNodeSP node = activeNode(); KIS_ASSERT_RECOVER_RETURN(node); m_d->view->image()->scaleNode(node, sx, sy, filterStrategy); nodesUpdated(); } void KisNodeManager::mirrorNode(KisNodeSP node, const KUndo2MagicString& actionName, Qt::Orientation orientation) { KisImageSignalVector emitSignals; emitSignals << ModifiedSignal; KisProcessingApplicator applicator(m_d->view->image(), node, KisProcessingApplicator::RECURSIVE, emitSignals, actionName); KisProcessingVisitorSP visitor = new KisMirrorProcessingVisitor(m_d->view->image()->bounds(), orientation); applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT); applicator.end(); nodesUpdated(); } void KisNodeManager::Private::saveDeviceAsImage(KisPaintDeviceSP device, const QString &defaultName, const QRect &bounds, qreal xRes, qreal yRes, quint8 opacity) { KoFileDialog dialog(view->mainWindow(), KoFileDialog::SaveFile, "savenodeasimage"); dialog.setCaption(i18n("Export \"%1\"", defaultName)); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); - dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Export)); + dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Export)); QString filename = dialog.filename(); if (filename.isEmpty()) return; QUrl url = QUrl::fromLocalFile(filename); if (url.isEmpty()) return; QString mimefilter = KisMimeDatabase::mimeTypeForFile(filename);; QScopedPointer d(KisPart::instance()->createDocument()); d->prepareForImport(); KisImageSP dst = new KisImage(d->createUndoStore(), bounds.width(), bounds.height(), device->compositionSourceColorSpace(), defaultName); dst->setResolution(xRes, yRes); d->setCurrentImage(dst); KisPaintLayer* paintLayer = new KisPaintLayer(dst, "paint device", opacity); paintLayer->paintDevice()->makeCloneFrom(device, bounds); dst->addNode(paintLayer, dst->rootLayer(), KisLayerSP(0)); dst->initialRefreshGraph(); d->setOutputMimeType(mimefilter.toLatin1()); d->exportDocument(url); } void KisNodeManager::saveNodeAsImage() { KisNodeSP node = activeNode(); if (!node) { warnKrita << "BUG: Save Node As Image was called without any node selected"; return; } KisImageWSP image = m_d->view->image(); QRect saveRect = image->bounds() | node->exactBounds(); KisPaintDeviceSP device = node->paintDevice(); if (!device) { device = node->projection(); } m_d->saveDeviceAsImage(device, node->name(), saveRect, image->xRes(), image->yRes(), node->opacity()); } void KisNodeManager::slotSplitAlphaIntoMask() { KisNodeSP node = activeNode(); // guaranteed by KisActionManager KIS_ASSERT_RECOVER_RETURN(node->hasEditablePaintDevice()); KisPaintDeviceSP srcDevice = node->paintDevice(); const KoColorSpace *srcCS = srcDevice->colorSpace(); const QRect processRect = srcDevice->exactBounds() | srcDevice->defaultBounds()->bounds(); KisPaintDeviceSP selectionDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); m_d->commandsAdapter.beginMacro(kundo2_i18n("Split Alpha into a Mask")); KisTransaction transaction(kundo2_noi18n("__split_alpha_channel__"), srcDevice); KisSequentialIterator srcIt(srcDevice, processRect); KisSequentialIterator dstIt(selectionDevice, processRect); do { quint8 *srcPtr = srcIt.rawData(); quint8 *alpha8Ptr = dstIt.rawData(); *alpha8Ptr = srcCS->opacityU8(srcPtr); srcCS->setOpacity(srcPtr, OPACITY_OPAQUE_U8, 1); } while (srcIt.nextPixel() && dstIt.nextPixel()); m_d->commandsAdapter.addExtraCommand(transaction.endAndTake()); createNode("KisTransparencyMask", false, selectionDevice); m_d->commandsAdapter.endMacro(); } void KisNodeManager::Private::mergeTransparencyMaskAsAlpha(bool writeToLayers) { KisNodeSP node = q->activeNode(); KisNodeSP parentNode = node->parent(); // guaranteed by KisActionManager KIS_ASSERT_RECOVER_RETURN(node->inherits("KisTransparencyMask")); if (writeToLayers && !parentNode->hasEditablePaintDevice()) { QMessageBox::information(view->mainWindow(), i18nc("@title:window", "Layer %1 is not editable").arg(parentNode->name()), i18n("Cannot write alpha channel of " "the parent layer \"%1\".\n" "The operation will be cancelled.").arg(parentNode->name())); return; } KisPaintDeviceSP dstDevice; if (writeToLayers) { KIS_ASSERT_RECOVER_RETURN(parentNode->paintDevice()); dstDevice = parentNode->paintDevice(); } else { KisPaintDeviceSP copyDevice = parentNode->paintDevice(); if (!copyDevice) { copyDevice = parentNode->original(); } dstDevice = new KisPaintDevice(*copyDevice); } const KoColorSpace *dstCS = dstDevice->colorSpace(); KisPaintDeviceSP selectionDevice = node->paintDevice(); KIS_ASSERT_RECOVER_RETURN(selectionDevice->colorSpace()->pixelSize() == 1); const QRect processRect = selectionDevice->exactBounds() | dstDevice->exactBounds() | selectionDevice->defaultBounds()->bounds(); QScopedPointer transaction; if (writeToLayers) { commandsAdapter.beginMacro(kundo2_i18n("Write Alpha into a Layer")); transaction.reset(new KisTransaction(kundo2_noi18n("__write_alpha_channel__"), dstDevice)); } KisSequentialIterator srcIt(selectionDevice, processRect); KisSequentialIterator dstIt(dstDevice, processRect); do { quint8 *alpha8Ptr = srcIt.rawData(); quint8 *dstPtr = dstIt.rawData(); dstCS->setOpacity(dstPtr, *alpha8Ptr, 1); } while (srcIt.nextPixel() && dstIt.nextPixel()); if (writeToLayers) { commandsAdapter.addExtraCommand(transaction->endAndTake()); commandsAdapter.removeNode(node); commandsAdapter.endMacro(); } else { KisImageWSP image = view->image(); QRect saveRect = image->bounds(); saveDeviceAsImage(dstDevice, parentNode->name(), saveRect, image->xRes(), image->yRes(), OPACITY_OPAQUE_U8); } } void KisNodeManager::slotSplitAlphaWrite() { m_d->mergeTransparencyMaskAsAlpha(true); } void KisNodeManager::slotSplitAlphaSaveMerged() { m_d->mergeTransparencyMaskAsAlpha(false); } void KisNodeManager::cutLayersToClipboard() { KisNodeList nodes = this->selectedNodes(); KisNodeSP root = m_d->view->image()->root(); if (nodes.isEmpty()) return; KisClipboard::instance()->setLayers(nodes, root, false); KUndo2MagicString actionName = kundo2_i18n("Cut Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->removeNode(nodes); } void KisNodeManager::copyLayersToClipboard() { KisNodeList nodes = this->selectedNodes(); KisNodeSP root = m_d->view->image()->root(); KisClipboard::instance()->setLayers(nodes, root, true); } void KisNodeManager::pasteLayersFromClipboard() { const QMimeData *data = KisClipboard::instance()->layersMimeData(); if (!data) return; KisNodeSP activeNode = this->activeNode(); KisShapeController *shapeController = dynamic_cast(m_d->imageView->document()->shapeController()); Q_ASSERT(shapeController); KisDummiesFacadeBase *dummiesFacade = dynamic_cast(m_d->imageView->document()->shapeController()); Q_ASSERT(dummiesFacade); const bool copyNode = false; KisImageSP image = m_d->view->image(); KisNodeDummy *parentDummy = dummiesFacade->dummyForNode(activeNode); KisNodeDummy *aboveThisDummy = parentDummy ? parentDummy->lastChild() : 0; KisMimeData::insertMimeLayers(data, image, shapeController, parentDummy, aboveThisDummy, copyNode, nodeInsertionAdapter()); } void KisNodeManager::createQuickGroupImpl(KisNodeJugglerCompressed *juggler, const QString &overrideGroupName, KisNodeSP *newGroup, KisNodeSP *newLastChild) { KisNodeSP active = activeNode(); if (!active) return; KisImageSP image = m_d->view->image(); QString groupName = !overrideGroupName.isEmpty() ? overrideGroupName : image->nextLayerName(); KisGroupLayerSP group = new KisGroupLayer(image.data(), groupName, OPACITY_OPAQUE_U8); KisNodeList nodes1; nodes1 << group; KisNodeList nodes2; nodes2 = KisLayerUtils::sortMergableNodes(image->root(), selectedNodes()); KisLayerUtils::filterMergableNodes(nodes2); if (KisLayerUtils::checkIsChildOf(active, nodes2)) { active = nodes2.first(); } KisNodeSP parent = active->parent(); KisNodeSP aboveThis = active; juggler->addNode(nodes1, parent, aboveThis); juggler->moveNode(nodes2, group, 0); *newGroup = group; *newLastChild = nodes2.last(); } void KisNodeManager::createQuickGroup() { KUndo2MagicString actionName = kundo2_i18n("Quick Group"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); KisNodeSP parent; KisNodeSP above; createQuickGroupImpl(juggler, "", &parent, &above); } void KisNodeManager::createQuickClippingGroup() { KUndo2MagicString actionName = kundo2_i18n("Quick Clipping Group"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); KisNodeSP parent; KisNodeSP above; KisImageSP image = m_d->view->image(); createQuickGroupImpl(juggler, image->nextLayerName(i18nc("default name for a clipping group layer", "Clipping Group")), &parent, &above); KisPaintLayerSP maskLayer = new KisPaintLayer(image.data(), i18nc("default name for quick clip group mask layer", "Mask Layer"), OPACITY_OPAQUE_U8, image->colorSpace()); maskLayer->disableAlphaChannel(true); juggler->addNode(KisNodeList() << maskLayer, parent, above); } void KisNodeManager::quickUngroup() { KisNodeSP active = activeNode(); if (!active) return; KisNodeSP parent = active->parent(); KisNodeSP aboveThis = active; KUndo2MagicString actionName = kundo2_i18n("Quick Ungroup"); if (parent && dynamic_cast(active.data())) { KisNodeList nodes = active->childNodes(QStringList(), KoProperties()); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->moveNode(nodes, parent, active); juggler->removeNode(KisNodeList() << active); } else if (parent && parent->parent()) { KisNodeSP grandParent = parent->parent(); KisNodeList allChildNodes = parent->childNodes(QStringList(), KoProperties()); KisNodeList allSelectedNodes = selectedNodes(); const bool removeParent = KritaUtils::compareListsUnordered(allChildNodes, allSelectedNodes); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->moveNode(allSelectedNodes, grandParent, parent); if (removeParent) { juggler->removeNode(KisNodeList() << parent); } } } void KisNodeManager::selectLayersImpl(const KoProperties &props, const KoProperties &invertedProps) { KisImageSP image = m_d->view->image(); KisNodeList nodes = KisLayerUtils::findNodesWithProps(image->root(), props, true); KisNodeList selectedNodes = this->selectedNodes(); if (KritaUtils::compareListsUnordered(nodes, selectedNodes)) { nodes = KisLayerUtils::findNodesWithProps(image->root(), invertedProps, true); } if (!nodes.isEmpty()) { slotImageRequestNodeReselection(nodes.last(), nodes); } } void KisNodeManager::selectAllNodes() { KoProperties props; selectLayersImpl(props, props); } void KisNodeManager::selectVisibleNodes() { KoProperties props; props.setProperty("visible", true); KoProperties invertedProps; invertedProps.setProperty("visible", false); selectLayersImpl(props, invertedProps); } void KisNodeManager::selectLockedNodes() { KoProperties props; props.setProperty("locked", true); KoProperties invertedProps; invertedProps.setProperty("locked", false); selectLayersImpl(props, invertedProps); } void KisNodeManager::selectInvisibleNodes() { KoProperties props; props.setProperty("visible", false); KoProperties invertedProps; invertedProps.setProperty("visible", true); selectLayersImpl(props, invertedProps); } void KisNodeManager::selectUnlockedNodes() { KoProperties props; props.setProperty("locked", false); KoProperties invertedProps; invertedProps.setProperty("locked", true); selectLayersImpl(props, invertedProps); } diff --git a/libs/ui/kis_node_view_color_scheme.cpp b/libs/ui/kis_node_view_color_scheme.cpp index f48582cd5f..11630fef25 100644 --- a/libs/ui/kis_node_view_color_scheme.cpp +++ b/libs/ui/kis_node_view_color_scheme.cpp @@ -1,176 +1,175 @@ /* * 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_node_view_color_scheme.h" #include #include #include Q_GLOBAL_STATIC(KisNodeViewColorScheme, s_instance) struct KisNodeViewColorScheme::Private { Private() { if (colorLabels.isEmpty()) { colorLabels << Qt::transparent; - colorLabels << QColor(252, 235, 87); - colorLabels << QColor(251, 183, 76); - colorLabels << QColor(160, 127, 110); - colorLabels << QColor(151, 202, 63); - colorLabels << QColor(123, 166, 209); - colorLabels << QColor(178, 138, 173); - colorLabels << QColor(238, 50, 51); - colorLabels << QColor(216, 218, 213); - colorLabels << QColor(145, 147, 142); + colorLabels << QColor(91,173,220); + colorLabels << QColor(151,202,63); + colorLabels << QColor(247,229,61); + colorLabels << QColor(255,170,63); + colorLabels << QColor(177,102,63); + colorLabels << QColor(238,50,51); + colorLabels << QColor(191,106,209); + colorLabels << QColor(118,119,114); } } static QVector colorLabels; }; QVector KisNodeViewColorScheme::Private::colorLabels; KisNodeViewColorScheme::KisNodeViewColorScheme() : m_d(new Private) { } KisNodeViewColorScheme::~KisNodeViewColorScheme() { } KisNodeViewColorScheme* KisNodeViewColorScheme::instance() { return s_instance; } QColor KisNodeViewColorScheme::gridColor(const QStyleOptionViewItem &option, QTreeView *view) { const int gridHint = view->style()->styleHint(QStyle::SH_Table_GridLineColor, &option, view); const QColor gridColor = static_cast(gridHint); return gridColor; } int KisNodeViewColorScheme::visibilitySize() const { return 16; } int KisNodeViewColorScheme::visibilityMargin() const { return 2; } int KisNodeViewColorScheme::thumbnailSize() const { return 20; } int KisNodeViewColorScheme::thumbnailMargin() const { return 3; } int KisNodeViewColorScheme::decorationSize() const { return 12; } int KisNodeViewColorScheme::decorationMargin() const { return 1; } int KisNodeViewColorScheme::textMargin() const { return 2; } int KisNodeViewColorScheme::iconSize() const { return 16; } int KisNodeViewColorScheme::iconMargin() const { return 1; } int KisNodeViewColorScheme::border() const { return 1; } int KisNodeViewColorScheme::rowHeight() const { return border() + 2 * thumbnailMargin() + thumbnailSize(); } int KisNodeViewColorScheme::visibilityColumnWidth() const { return border() + 2 * visibilityMargin() + visibilitySize() + border(); } int KisNodeViewColorScheme::indentation() const { return 2 * thumbnailMargin() + thumbnailSize() + border(); } QRect KisNodeViewColorScheme::relThumbnailRect() const { return QRect(-indentation(), border(), thumbnailSize() + 2 * thumbnailMargin(), thumbnailSize() + 2 * thumbnailMargin()); } QRect KisNodeViewColorScheme::relDecorationRect() const { return QRect(border() + decorationMargin(), border() + decorationMargin(), decorationSize(), decorationSize()); } QRect KisNodeViewColorScheme::relExpandButtonRect() const { const int newY = rowHeight() - decorationMargin() - decorationSize(); QRect rc = relDecorationRect(); rc.moveTop(newY); return rc; } QColor KisNodeViewColorScheme::colorLabel(int index) const { return m_d->colorLabels[index % m_d->colorLabels.size()]; } QVector KisNodeViewColorScheme::allColorLabels() const { return m_d->colorLabels; } diff --git a/libs/ui/kis_painting_assistants_decoration.cpp b/libs/ui/kis_painting_assistants_decoration.cpp index 418f2ded3e..2d3d96ab5c 100644 --- a/libs/ui/kis_painting_assistants_decoration.cpp +++ b/libs/ui/kis_painting_assistants_decoration.cpp @@ -1,237 +1,239 @@ /* * Copyright (c) 2009 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_painting_assistants_decoration.h" #include #include #include #include #include #include #include "kis_debug.h" #include "KisDocument.h" #include struct KisPaintingAssistantsDecoration::Private { - Private() : assistantVisible(false), - outlineVisible(false), - snapOnlyOneAssistant(true), - firstAssistant(0), - aFirstStroke(false){} + Private() + : assistantVisible(false) + , outlineVisible(false) + , snapOnlyOneAssistant(true) + , firstAssistant(0) + , aFirstStroke(false) + {} bool assistantVisible; bool outlineVisible; bool snapOnlyOneAssistant; KisPaintingAssistantSP firstAssistant; bool aFirstStroke; }; KisPaintingAssistantsDecoration::KisPaintingAssistantsDecoration(QPointer parent) : - KisCanvasDecoration("paintingAssistantsDecoration", parent), - d(new Private) + KisCanvasDecoration("paintingAssistantsDecoration", parent), + d(new Private) { setAssistantVisible(true); setOutlineVisible(true); d->snapOnlyOneAssistant=true;//turn on by default. } KisPaintingAssistantsDecoration::~KisPaintingAssistantsDecoration() { delete d; } void KisPaintingAssistantsDecoration::addAssistant(KisPaintingAssistantSP assistant) { QList assistants = view()->document()->assistants(); if (assistants.contains(assistant)) return; assistants.append(assistant); view()->document()->setAssistants(assistants); emit assistantChanged(); } void KisPaintingAssistantsDecoration::removeAssistant(KisPaintingAssistantSP assistant) { QList assistants = view()->document()->assistants(); KIS_ASSERT_RECOVER_NOOP(assistants.contains(assistant)); if (assistants.removeAll(assistant)) { view()->document()->setAssistants(assistants); emit assistantChanged(); } } void KisPaintingAssistantsDecoration::removeAll() { QList assistants = view()->document()->assistants(); assistants.clear(); view()->document()->setAssistants(assistants); emit assistantChanged(); } QPointF KisPaintingAssistantsDecoration::adjustPosition(const QPointF& point, const QPointF& strokeBegin) { QList assistants = view()->document()->assistants(); if (assistants.empty()) return point; if (assistants.count() == 1) { if(assistants.first()->snapping()==true){ - QPointF newpoint = assistants.first()->adjustPosition(point, strokeBegin); - // check for NaN - if (newpoint.x() != newpoint.x()) return point; - return newpoint; + QPointF newpoint = assistants.first()->adjustPosition(point, strokeBegin); + // check for NaN + if (newpoint.x() != newpoint.x()) return point; + return newpoint; } } QPointF best = point; double distance = DBL_MAX; //the following tries to find the closest point to stroke-begin. It checks all assistants for the closest point// if(!d->snapOnlyOneAssistant){ Q_FOREACH (KisPaintingAssistantSP assistant, assistants) { if(assistant->snapping()==true){//this checks if the assistant in question has it's snapping boolean turned on// QPointF pt = assistant->adjustPosition(point, strokeBegin); if (pt.x() != pt.x()) continue; double dist = qAbs(pt.x() - point.x()) + qAbs(pt.y() - point.y()); if (dist < distance) { best = pt; distance = dist; } } } } else if (d->aFirstStroke==false) { Q_FOREACH (KisPaintingAssistantSP assistant, assistants) { if(assistant->snapping()==true){//this checks if the assistant in question has it's snapping boolean turned on// QPointF pt = assistant->adjustPosition(point, strokeBegin); if (pt.x() != pt.x()) continue; double dist = qAbs(pt.x() - point.x()) + qAbs(pt.y() - point.y()); if (dist < distance) { best = pt; distance = dist; d->firstAssistant = assistant; } } } } else if(d->firstAssistant) { //make sure there's a first assistant to begin with.// best = d->firstAssistant->adjustPosition(point, strokeBegin); } else { d->aFirstStroke=false; } //this is here to be compatible with the movement in the perspective tool. qreal dx = point.x() - strokeBegin.x(), dy = point.y() - strokeBegin.y(); - if (dx * dx + dy * dy >= 4.0) { - // allow some movement before snapping - d->aFirstStroke=true; - } + if (dx * dx + dy * dy >= 4.0) { + // allow some movement before snapping + d->aFirstStroke=true; + } return best; } void KisPaintingAssistantsDecoration::endStroke() { QList assistants = view()->document()->assistants(); d->aFirstStroke=false; Q_FOREACH (KisPaintingAssistantSP assistant, assistants) { assistant->endStroke(); } } void KisPaintingAssistantsDecoration::drawDecoration(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter,KisCanvas2* canvas) { if (!canvas) { dbgFile<<"canvas does not exist in painting assistant decoration, you may have passed arguments incorrectly:"< assistants = view()->document()->assistants(); Q_FOREACH (KisPaintingAssistantSP assistant, assistants) { - assistant->drawAssistant(gc, updateRect, converter, true, canvas, assistantVisibility(), outlineVisibility()); + assistant->drawAssistant(gc, updateRect, converter, true, canvas, assistantVisibility(), outlineVisibility()); } } //drawPreview// QList KisPaintingAssistantsDecoration::handles() { QList assistants = view()->document()->assistants(); QList hs; Q_FOREACH (KisPaintingAssistantSP assistant, assistants) { Q_FOREACH (const KisPaintingAssistantHandleSP handle, assistant->handles()) { if (!hs.contains(handle)) { hs.push_back(handle); } } Q_FOREACH (const KisPaintingAssistantHandleSP handle, assistant->sideHandles()) { if (!hs.contains(handle)) { hs.push_back(handle); } } } return hs; } QList KisPaintingAssistantsDecoration::assistants() { QList assistants = view()->document()->assistants(); return assistants; } void KisPaintingAssistantsDecoration::setAssistantVisible(bool set) { d->assistantVisible=set; } void KisPaintingAssistantsDecoration::setOutlineVisible(bool set) { d->outlineVisible=set; } void KisPaintingAssistantsDecoration::setOnlyOneAssistantSnap(bool assistant) { d->snapOnlyOneAssistant = assistant; } bool KisPaintingAssistantsDecoration::assistantVisibility() { return d->assistantVisible; } bool KisPaintingAssistantsDecoration::outlineVisibility() { return d->outlineVisible; } void KisPaintingAssistantsDecoration::uncache() { QList assistants = view()->document()->assistants(); Q_FOREACH (KisPaintingAssistantSP assistant, assistants) { - assistant->uncache(); + assistant->uncache(); } } void KisPaintingAssistantsDecoration::toggleAssistantVisible() { setAssistantVisible(!assistantVisibility()); uncache(); } void KisPaintingAssistantsDecoration::toggleOutlineVisible() { setOutlineVisible(!outlineVisibility()); } diff --git a/libs/ui/kis_painting_assistants_decoration.h b/libs/ui/kis_painting_assistants_decoration.h index 58d10e54b3..3db3c77aaa 100644 --- a/libs/ui/kis_painting_assistants_decoration.h +++ b/libs/ui/kis_painting_assistants_decoration.h @@ -1,72 +1,75 @@ /* * Copyright (c) 2009 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_PAINTING_ASSISTANTS_MANAGER_H_ #define _KIS_PAINTING_ASSISTANTS_MANAGER_H_ #include #include "canvas/kis_canvas_decoration.h" #include "kis_painting_assistant.h" #include class KisView; +class KisPaintingAssistantsDecoration; +typedef KisSharedPtr KisPaintingAssistantsDecorationSP; + /** * This class hold a list of painting assistants. */ class KRITAUI_EXPORT KisPaintingAssistantsDecoration : public KisCanvasDecoration { Q_OBJECT public: KisPaintingAssistantsDecoration(QPointer parent); ~KisPaintingAssistantsDecoration(); void addAssistant(KisPaintingAssistantSP assistant); void removeAssistant(KisPaintingAssistantSP assistant); void removeAll(); QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin); void endStroke(); QList handles(); QList assistants(); /*sets whether the main assistant is visible*/ void setAssistantVisible(bool set); /*sets whether the preview is visible*/ void setOutlineVisible(bool set); /*sets whether we snap to only one assistant*/ void setOnlyOneAssistantSnap(bool assistant); /*returns assistant visibility*/ bool assistantVisibility(); /*returns preview visibility*/ bool outlineVisibility(); /*uncache all assistants*/ void uncache(); Q_SIGNALS: void assistantChanged(); public Q_SLOTS: void toggleAssistantVisible(); void toggleOutlineVisible(); protected: void drawDecoration(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter,KisCanvas2* canvas); private: struct Private; Private* const d; }; #endif diff --git a/libs/ui/kis_painting_assistants_manager.cpp b/libs/ui/kis_painting_assistants_manager.cpp index 2a26190284..da0264624d 100644 --- a/libs/ui/kis_painting_assistants_manager.cpp +++ b/libs/ui/kis_painting_assistants_manager.cpp @@ -1,92 +1,93 @@ /* * Copyright (c) 2009 Cyrille Berger * Copyright (c) 2014 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_painting_assistants_manager.h" -#include "kis_painting_assistants_decoration.h" + #include "KisViewManager.h" #include "kis_action_manager.h" #include "kis_action.h" +#include "kis_canvas2.h" #include #include #include #include KisPaintingAssistantsManager::KisPaintingAssistantsManager(KisViewManager* view) : QObject(view) , m_imageView(0) { } KisPaintingAssistantsManager::~KisPaintingAssistantsManager() { } void KisPaintingAssistantsManager::setup(KisActionManager * actionManager) { m_toggleAssistant = actionManager->createAction("view_toggle_painting_assistants"); m_togglePreview = actionManager->createAction("view_toggle_assistant_previews"); updateAction(); } void KisPaintingAssistantsManager::setView(QPointer imageView) { if (m_imageView) { m_toggleAssistant->disconnect(); if (decoration()) { decoration()->disconnect(this); } } m_imageView = imageView; if (m_imageView && !decoration()) { KisPaintingAssistantsDecoration* deco = new KisPaintingAssistantsDecoration(m_imageView); m_imageView->canvasBase()->addDecoration(deco); } if (m_imageView && decoration()) { connect(m_toggleAssistant, SIGNAL(triggered()), decoration(), SLOT(toggleAssistantVisible())); connect(m_togglePreview, SIGNAL(triggered()), decoration(), SLOT(toggleOutlineVisible())); connect(decoration(), SIGNAL(assistantChanged()), SLOT(updateAction())); } updateAction(); } void KisPaintingAssistantsManager::updateAction() { if (decoration()) { bool enabled = !decoration()->assistants().isEmpty(); m_toggleAssistant->setChecked(decoration()->visible()); m_toggleAssistant->setEnabled(enabled); m_togglePreview->setChecked(decoration()->outlineVisibility()); m_togglePreview->setEnabled(enabled); } else { m_toggleAssistant->setEnabled(false); } } -KisPaintingAssistantsDecoration* KisPaintingAssistantsManager::decoration() +KisPaintingAssistantsDecorationSP KisPaintingAssistantsManager::decoration() { if (m_imageView) { return m_imageView->canvasBase()->paintingAssistantsDecoration(); } return 0; } diff --git a/libs/ui/kis_painting_assistants_manager.h b/libs/ui/kis_painting_assistants_manager.h index 14912a722f..8040ba8b60 100644 --- a/libs/ui/kis_painting_assistants_manager.h +++ b/libs/ui/kis_painting_assistants_manager.h @@ -1,57 +1,56 @@ /* * Copyright (c) 2009 Cyrille Berger * Copyright (c) 2014 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. */ #ifndef KIS_PAINTING_ASSISTANTS_MANAGER_H #define KIS_PAINTING_ASSISTANTS_MANAGER_H #include #include #include "KisView.h" +#include "kis_painting_assistants_decoration.h" class KisViewManager; -class KisPaintingAssistantsDecoration; class KisAction; class KisActionManager; - class KisPaintingAssistantsManager : public QObject { Q_OBJECT public: KisPaintingAssistantsManager(KisViewManager* view); virtual ~KisPaintingAssistantsManager(); void setup(KisActionManager* actionManager); void setView(QPointer imageView); private Q_SLOTS: void updateAction(); private: - KisPaintingAssistantsDecoration* decoration(); - + KisPaintingAssistantsDecorationSP decoration(); + QPointer m_imageView; KisAction* m_toggleAssistant; KisAction* m_togglePreview; }; #endif // KIS_PAINTING_ASSISTANTS_MANAGER_H diff --git a/libs/ui/kis_paintop_settings_widget.cpp b/libs/ui/kis_paintop_settings_widget.cpp index 2e56e2a25d..73c94398d3 100644 --- a/libs/ui/kis_paintop_settings_widget.cpp +++ b/libs/ui/kis_paintop_settings_widget.cpp @@ -1,234 +1,236 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2008 * Copyright (C) Silvio Heinrich , (C) 2011 * * 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_paintop_settings_widget.h" #include "kis_paintop_option.h" #include "kis_paintop_options_model.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct KisPaintOpSettingsWidget::Private { QList paintOpOptions; KisCategorizedListView* optionsList; KisPaintOpOptionListModel* model; QStackedWidget* optionsStack; }; KisPaintOpSettingsWidget::KisPaintOpSettingsWidget(QWidget * parent) : KisPaintOpConfigWidget(parent) , m_d(new Private()) { setObjectName("KisPaintOpPresetsWidget"); - + m_d->model = new KisPaintOpOptionListModel(this); m_d->optionsList = new KisCategorizedListView(false, this); m_d->optionsList->setModel(m_d->model); - m_d->optionsList->setItemDelegate(new KisCategorizedItemDelegate(false, m_d->optionsList)); - m_d->optionsList->setFixedWidth(175); - - QSizePolicy policy = QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); - policy.setHorizontalStretch(0); + m_d->optionsList->setItemDelegate(new KisCategorizedItemDelegate(m_d->optionsList)); + m_d->optionsList->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); + m_d->optionsList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + QSizePolicy policy = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); m_d->optionsList->setSizePolicy(policy); m_d->optionsStack = new QStackedWidget(this); policy = QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - policy.setHorizontalStretch(3); m_d->optionsStack->setSizePolicy(policy); - + QHBoxLayout* layout = new QHBoxLayout(this); layout->addWidget(m_d->optionsList); layout->addWidget(m_d->optionsStack); + layout->setStretch(0, 0); + layout->setStretch(1, 1); + m_saveLockedOption = false; connect(m_d->optionsList, SIGNAL(activated(const QModelIndex&)), this, SLOT(changePage(const QModelIndex&))); connect(m_d->optionsList, SIGNAL(clicked(QModelIndex)), this, SLOT(changePage(const QModelIndex&))); connect(m_d->optionsList, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(lockProperties(const QModelIndex&))); connect(m_d->optionsList, SIGNAL(rightClickedMenuDropSettingsTriggered()), this, SLOT(slotLockPropertiesDrop())); connect(m_d->optionsList, SIGNAL(rightClickedMenuSaveSettingsTriggered()), this, SLOT(slotLockPropertiesSave())); connect(m_d->optionsList, SIGNAL(sigEntryChecked(QModelIndex)), this, SLOT(slotEntryChecked(QModelIndex))); } KisPaintOpSettingsWidget::~KisPaintOpSettingsWidget() { qDeleteAll(m_d->paintOpOptions); delete m_d; } void KisPaintOpSettingsWidget::addPaintOpOption(KisPaintOpOption *option, const QString &label) { if (!option->configurationPage()) return; m_d->model->addPaintOpOption(option, m_d->optionsStack->count(), label); connect(option, SIGNAL(sigSettingChanged()), SIGNAL(sigConfigurationItemChanged())); m_d->optionsStack->addWidget(option->configurationPage()); m_d->paintOpOptions << option; } void KisPaintOpSettingsWidget::setConfiguration(const KisPropertiesConfiguration * config) { Q_ASSERT(!config->getString("paintop").isEmpty()); KisLockedPropertiesProxy* propertiesProxy = KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(config); int indexcount = 0; Q_FOREACH (KisPaintOpOption* option, m_d->paintOpOptions) { option->startReadOptionSetting(propertiesProxy); if (KisLockedPropertiesServer::instance()->propertiesFromLocked()) { option->setLocked(true); } else { option->setLocked(false); } KisLockedPropertiesServer::instance()->setPropertiesFromLocked(false); KisOptionInfo info; info.option = option; info.index = indexcount; m_d->model->categoriesMapper()->itemFromRow(m_d->model->indexOf(info).row())->setLocked(option->isLocked()); m_d->model->categoriesMapper()->itemFromRow(m_d->model->indexOf(info).row())->setLockable(true); m_d->model->signalDataChanged(m_d->model->indexOf(info)); indexcount++; } KisPaintOpConfigWidget::setConfiguration(propertiesProxy); delete propertiesProxy; } void KisPaintOpSettingsWidget::writeConfiguration(KisPropertiesConfiguration *config) const { KisLockedPropertiesProxy* propertiesProxy = KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(config); Q_FOREACH (const KisPaintOpOption* option, m_d->paintOpOptions) { option->startWriteOptionSetting(propertiesProxy); } KisPaintOpConfigWidget::writeConfiguration(propertiesProxy); delete propertiesProxy; } KisPaintopLodLimitations KisPaintOpSettingsWidget::lodLimitations() const { KisPaintopLodLimitations l; Q_FOREACH (const KisPaintOpOption* option, m_d->paintOpOptions) { if (option->isCheckable() && !option->isChecked()) continue; option->lodLimitations(&l); } return l; } void KisPaintOpSettingsWidget::setImage(KisImageWSP image) { Q_FOREACH (KisPaintOpOption* option, m_d->paintOpOptions) { option->setImage(image); } } void KisPaintOpSettingsWidget::setNode(KisNodeWSP node) { Q_FOREACH (KisPaintOpOption* option, m_d->paintOpOptions) { option->setNode(node); } } void KisPaintOpSettingsWidget::changePage(const QModelIndex& index) { KisOptionInfo info; QPalette palette; palette.setColor(QPalette::Base, QColor(255,200,200)); palette.setColor(QPalette::Text, Qt::black); - + if(m_d->model->entryAt(info, index)) { m_d->optionsStack->setCurrentIndex(info.index); } notifyPageChanged(); } void KisPaintOpSettingsWidget::notifyPageChanged() { } void KisPaintOpSettingsWidget::lockProperties(const QModelIndex& index) { KisOptionInfo info; if (m_d->model->entryAt(info, index)) { m_d->optionsList->setCurrentIndex(index); KisPropertiesConfiguration* p = new KisPropertiesConfiguration(); info.option->startWriteOptionSetting(p); if (!info.option->isLocked()){ KisLockedPropertiesServer::instance()->addToLockedProperties(p); info.option->setLocked(true); m_d->model->categoriesMapper()->itemFromRow(index.row())->setLocked(true); } else { KisLockedPropertiesServer::instance()->removeFromLockedProperties(p); info.option->setLocked(false); m_d->model->categoriesMapper()->itemFromRow(index.row())->setLocked(false); if (m_saveLockedOption){ emit sigSaveLockedConfig(p); } else { emit sigDropLockedConfig(p); } m_saveLockedOption = false; } m_d->model->signalDataChanged(index); } } void KisPaintOpSettingsWidget::slotLockPropertiesDrop() { m_saveLockedOption = false; lockProperties(m_d->optionsList->currentIndex()); } void KisPaintOpSettingsWidget::slotLockPropertiesSave() { m_saveLockedOption = true; lockProperties(m_d->optionsList->currentIndex()); } void KisPaintOpSettingsWidget::slotEntryChecked(const QModelIndex &index) { Q_UNUSED(index); emit sigConfigurationItemChanged(); } diff --git a/libs/ui/kis_popup_palette.cpp b/libs/ui/kis_popup_palette.cpp index 1833928206..86e0dc9883 100644 --- a/libs/ui/kis_popup_palette.cpp +++ b/libs/ui/kis_popup_palette.cpp @@ -1,578 +1,574 @@ /* This file is part of the KDE project Copyright 2009 Vera Lukman Copyright 2011 Sven Langkamp 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 "kis_config.h" #include "kis_popup_palette.h" #include "kis_paintop_box.h" #include "kis_favorite_resource_manager.h" #include "kis_icon_utils.h" #include #include "kis_resource_server_provider.h" #include #include "KoColorSpaceRegistry.h" #include #include #include #include #include #include #include #include #include "kis_signal_compressor.h" #include #define colorInnerRadius 72.0 #define colorOuterRadius 92.0 #define maxbrushRadius 42.0 #define widgetSize (colorOuterRadius*2+maxbrushRadius*4) class PopupColorTriangle : public KoTriangleColorSelector { public: PopupColorTriangle(const KoColorDisplayRendererInterface *displayRenderer, QWidget* parent) : KoTriangleColorSelector(displayRenderer, parent) , m_dragging(false) { } virtual ~PopupColorTriangle() {} void tabletEvent(QTabletEvent* event) { event->accept(); QMouseEvent* mouseEvent = 0; switch (event->type()) { case QEvent::TabletPress: mouseEvent = new QMouseEvent(QEvent::MouseButtonPress, event->pos(), Qt::LeftButton, Qt::LeftButton, event->modifiers()); m_dragging = true; mousePressEvent(mouseEvent); break; case QEvent::TabletMove: mouseEvent = new QMouseEvent(QEvent::MouseMove, event->pos(), (m_dragging) ? Qt::LeftButton : Qt::NoButton, (m_dragging) ? Qt::LeftButton : Qt::NoButton, event->modifiers()); mouseMoveEvent(mouseEvent); break; case QEvent::TabletRelease: mouseEvent = new QMouseEvent(QEvent::MouseButtonRelease, event->pos(), Qt::LeftButton, Qt::LeftButton, event->modifiers()); m_dragging = false; mouseReleaseEvent(mouseEvent); break; default: break; } delete mouseEvent; } private: bool m_dragging; }; KisPopupPalette::KisPopupPalette(KisFavoriteResourceManager* manager, const KoColorDisplayRendererInterface *displayRenderer, QWidget *parent) : QWidget(parent, Qt::FramelessWindowHint) , m_resourceManager(manager) , m_triangleColorSelector(0) , m_timer(0) , m_displayRenderer(displayRenderer) , m_colorChangeCompressor(new KisSignalCompressor(50, KisSignalCompressor::POSTPONE)) { - + const int borderWidth = 3; m_triangleColorSelector = new PopupColorTriangle(displayRenderer, this); m_triangleColorSelector->move(widgetSize/2-colorInnerRadius+borderWidth, widgetSize/2-colorInnerRadius+borderWidth); m_triangleColorSelector->resize(colorInnerRadius*2-borderWidth*2, colorInnerRadius*2-borderWidth*2); m_triangleColorSelector->setVisible(true); QRegion maskedRegion(0, 0, m_triangleColorSelector->width(), m_triangleColorSelector->height(), QRegion::Ellipse ); m_triangleColorSelector->setMask(maskedRegion); setAttribute(Qt::WA_ContentsPropagated, true); //setAttribute(Qt::WA_TranslucentBackground, true); connect(m_triangleColorSelector, SIGNAL(realColorChanged(KoColor)), m_colorChangeCompressor.data(), SLOT(start())); connect(m_colorChangeCompressor.data(), SIGNAL(timeout()), SLOT(slotEmitColorChanged())); connect(m_resourceManager, SIGNAL(sigChangeFGColorSelector(KoColor)), SLOT(slotExternalFgColorChanged(KoColor))); connect(this, SIGNAL(sigChangefGColor(KoColor)), m_resourceManager, SIGNAL(sigSetFGColor(KoColor))); connect(this, SIGNAL(sigChangeActivePaintop(int)), m_resourceManager, SLOT(slotChangeActivePaintop(int))); connect(this, SIGNAL(sigUpdateRecentColor(int)), m_resourceManager, SLOT(slotUpdateRecentColor(int))); connect(m_resourceManager, SIGNAL(setSelectedColor(int)), SLOT(slotSetSelectedColor(int))); connect(m_resourceManager, SIGNAL(updatePalettes()), SLOT(slotUpdate())); connect(m_resourceManager, SIGNAL(hidePalettes()), SLOT(slotHide())); // This is used to handle a bug: // If pop up palette is visible and a new colour is selected, the new colour // will be added when the user clicks on the canvas to hide the palette // In general, we want to be able to store recent color if the pop up palette // is not visible m_timer = new QTimer(this); m_timer->setSingleShot(true); connect(this, SIGNAL(sigTriggerTimer()), this, SLOT(slotTriggerTimer())); connect(m_timer, SIGNAL(timeout()), this, SLOT(slotEnableChangeFGColor())); connect(this, SIGNAL(sigEnableChangeFGColor(bool)), m_resourceManager, SIGNAL(sigEnableChangeColor(bool))); setCursor(Qt::ArrowCursor); setMouseTracking(true); setHoveredPreset(-1); setHoveredColor(-1); setSelectedColor(-1); setVisible(true); setVisible(false); } void KisPopupPalette::slotExternalFgColorChanged(const KoColor &color) { m_triangleColorSelector->setRealColor(color); } void KisPopupPalette::slotEmitColorChanged() { if (isVisible()) { update(); emit sigChangefGColor(m_triangleColorSelector->realColor()); } } //setting KisPopupPalette properties int KisPopupPalette::hoveredPreset() const { return m_hoveredPreset; } void KisPopupPalette::setHoveredPreset(int x) { m_hoveredPreset = x; } int KisPopupPalette::hoveredColor() const { return m_hoveredColor; } void KisPopupPalette::setHoveredColor(int x) { m_hoveredColor = x; } int KisPopupPalette::selectedColor() const { return m_selectedColor; } void KisPopupPalette::setSelectedColor(int x) { m_selectedColor = x; } void KisPopupPalette::slotTriggerTimer() { m_timer->start(750); } void KisPopupPalette::slotEnableChangeFGColor() { emit sigEnableChangeFGColor(true); } void KisPopupPalette::showPopupPalette(const QPoint &p) { if (!isVisible() && parentWidget()) { QSize parentSize(parentWidget()->size()); QPoint pointPalette(p.x() - width() / 2, p.y() - height() / 2); //setting offset point in case the widget is shown outside of canvas region int offsetX = 0, offsetY = 0; if ((offsetX = pointPalette.x() + width() - parentSize.width()) > 0 || (offsetX = pointPalette.x()) < 0) { pointPalette.setX(pointPalette.x() - offsetX); } if ((offsetY = pointPalette.y() + height() - parentSize.height()) > 0 || (offsetY = pointPalette.y()) < 0) { pointPalette.setY(pointPalette.y() - offsetY); } move(pointPalette); } showPopupPalette(!isVisible()); } void KisPopupPalette::showPopupPalette(bool show) { if (show) { emit sigEnableChangeFGColor(!show); } else { emit sigTriggerTimer(); } setVisible(show); } //redefinition of setVariable function to change the scope to private void KisPopupPalette::setVisible(bool b) { QWidget::setVisible(b); } QSize KisPopupPalette::sizeHint() const { return QSize(widgetSize, widgetSize); } void KisPopupPalette::resizeEvent(QResizeEvent*) { } void KisPopupPalette::paintEvent(QPaintEvent* e) { Q_UNUSED(e) float rotationAngle = 0.0; QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + painter.translate(width() / 2, height() / 2); //painting background color QPainterPath bgColor; bgColor.addEllipse(QPoint(-width() / 2 + 24, -height() / 2 + 60), 20, 20); painter.fillPath(bgColor, m_displayRenderer->toQColor(m_resourceManager->bgColor())); painter.drawPath(bgColor); //painting foreground color QPainterPath fgColor; fgColor.addEllipse(QPoint(-width() / 2 + 50, -height() / 2 + 32), 30, 30); painter.fillPath(fgColor, m_displayRenderer->toQColor(m_triangleColorSelector->realColor())); painter.drawPath(fgColor); - // create an ellipse for the background that is slightly // smaller than the clipping mask. This will prevent aliasing QPainterPath backgroundContainer; backgroundContainer.addEllipse( -colorOuterRadius, -colorOuterRadius, colorOuterRadius*2, colorOuterRadius*2 ); painter.fillPath(backgroundContainer,palette().brush(QPalette::Window)); painter.drawPath(backgroundContainer); - - - - //painting favorite brushes QList images(m_resourceManager->favoritePresetImages()); //painting favorite brushes pixmap/icon QPainterPath path; for (int pos = 0; pos < numSlots(); pos++) { painter.save(); path = pathFromPresetIndex(pos); - - if(pos < images.size()) - { + + if (pos < images.size()) { painter.setClipPath(path); QRect bounds = path.boundingRect().toAlignedRect(); - painter.drawImage(bounds.topLeft() , images.at(pos).scaled(bounds.size() , Qt::KeepAspectRatioByExpanding)); + painter.drawImage(bounds.topLeft() , images.at(pos).scaled(bounds.size() , Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation)); } else { - painter.fillPath(path, palette().brush(QPalette::Window)); + painter.fillPath(path, palette().brush(QPalette::Window)); } QPen pen = painter.pen(); pen.setWidth(3); painter.setPen(pen); painter.drawPath(path); - + painter.restore(); } if (hoveredPreset() > -1) { path = pathFromPresetIndex(hoveredPreset()); QPen pen(palette().color(QPalette::Highlight)); pen.setWidth(3); painter.setPen(pen); painter.drawPath(path); } //painting recent colors painter.setPen(Qt::NoPen); rotationAngle = -360.0 / m_resourceManager->recentColorsTotal(); KoColor kocolor; for (int pos = 0; pos < m_resourceManager->recentColorsTotal(); pos++) { QPainterPath path(drawDonutPathAngle(colorInnerRadius, colorOuterRadius, m_resourceManager->recentColorsTotal())); //accessing recent color of index pos kocolor = m_resourceManager->recentColorAt(pos); painter.fillPath(path, m_displayRenderer->toQColor(kocolor)); painter.drawPath(path); painter.rotate(rotationAngle); } painter.setBrush(Qt::transparent); if (m_resourceManager->recentColorsTotal() == 0) { QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, colorInnerRadius, colorOuterRadius)); painter.setPen(QPen(palette().color(QPalette::Window).darker(130), 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); painter.drawPath(path_ColorDonut); } //painting hovered color if (hoveredColor() > -1) { painter.setPen(QPen(palette().color(QPalette::Highlight), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); if (m_resourceManager->recentColorsTotal() == 1) { QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, colorInnerRadius, colorOuterRadius)); painter.drawPath(path_ColorDonut); } else { painter.rotate((m_resourceManager->recentColorsTotal() + hoveredColor()) *rotationAngle); QPainterPath path(drawDonutPathAngle(colorInnerRadius, colorOuterRadius, m_resourceManager->recentColorsTotal())); painter.drawPath(path); painter.rotate(hoveredColor() * -1 * rotationAngle); } } //painting selected color if (selectedColor() > -1) { painter.setPen(QPen(palette().color(QPalette::Highlight).darker(130), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); if (m_resourceManager->recentColorsTotal() == 1) { QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, colorInnerRadius, colorOuterRadius)); painter.drawPath(path_ColorDonut); } else { painter.rotate((m_resourceManager->recentColorsTotal() + selectedColor()) *rotationAngle); QPainterPath path(drawDonutPathAngle(colorInnerRadius, colorOuterRadius, m_resourceManager->recentColorsTotal())); painter.drawPath(path); painter.rotate(selectedColor() * -1 * rotationAngle); } } //painting configure background, then icon QPainterPath configureContainer; int side = qMin(width(), height()); configureContainer.addEllipse( side / 2 - 38 , side / 2 - 38 , 35 , 35 ); painter.fillPath(configureContainer,palette().brush(QPalette::Window)); painter.drawPath(configureContainer); QPixmap settingIcon = KisIconUtils::loadIcon("configure").pixmap(QSize(22,22)); painter.drawPixmap(side / 2 - 40 + 9, side / 2 - 40 + 9, settingIcon); - + } QPainterPath KisPopupPalette::drawDonutPathFull(int x, int y, int inner_radius, int outer_radius) { QPainterPath path; path.addEllipse(QPointF(x, y), outer_radius, outer_radius); path.addEllipse(QPointF(x, y), inner_radius, inner_radius); path.setFillRule(Qt::OddEvenFill); return path; } QPainterPath KisPopupPalette::drawDonutPathAngle(int inner_radius, int outer_radius, int limit) { QPainterPath path; path.moveTo(-0.999 * outer_radius * sin(M_PI / limit), 0.999 * outer_radius * cos(M_PI / limit)); path.arcTo(-1 * outer_radius, -1 * outer_radius, 2 * outer_radius, 2 * outer_radius, -90.0 - 180.0 / limit, 360.0 / limit); path.arcTo(-1 * inner_radius, -1 * inner_radius, 2 * inner_radius, 2 * inner_radius, -90.0 + 180.0 / limit, - 360.0 / limit); path.closeSubpath(); return path; } void KisPopupPalette::mouseMoveEvent(QMouseEvent* event) { QPointF point = event->posF(); event->accept(); QPainterPath pathColor(drawDonutPathFull(width() / 2, height() / 2, colorInnerRadius, colorOuterRadius)); setToolTip(""); setHoveredPreset(-1); setHoveredColor(-1); { int pos = calculatePresetIndex(point, m_resourceManager->numFavoritePresets()); if (pos >= 0 && pos < m_resourceManager->numFavoritePresets()) { setToolTip(m_resourceManager->favoritePresetList().at(pos).data()->name()); setHoveredPreset(pos); } } if (pathColor.contains(point)) { int pos = calculateIndex(point, m_resourceManager->recentColorsTotal()); if (pos >= 0 && pos < m_resourceManager->recentColorsTotal()) { setHoveredColor(pos); } } update(); } void KisPopupPalette::mousePressEvent(QMouseEvent* event) { QPointF point = event->posF(); event->accept(); if (event->button() == Qt::LeftButton) { //in favorite brushes area int pos = calculateIndex(point, m_resourceManager->numFavoritePresets()); if (pos >= 0 && pos < m_resourceManager->numFavoritePresets() && isPointInPixmap(point, pos)) { //setSelectedBrush(pos); update(); } int side = qMin(width(), height()); QPainterPath settingCircle; settingCircle.addEllipse(width() / 2 + side / 2 - 40, height() / 2 + side / 2 - 40, 40, 40); - if (settingCircle.contains(point)) { + if (settingCircle.contains(point)) { KisPaintOpPresetResourceServer* rServer = KisResourceServerProvider::instance()->paintOpPresetServer(); QStringList tags = rServer->tagNamesList(); qSort(tags); if (!tags.isEmpty()) { QMenu menu; Q_FOREACH (const QString& tag, tags) { menu.addAction(tag); } QAction* action = menu.exec(event->globalPos()); if (action) { m_resourceManager->setCurrentTag(action->text()); } } else { - QWhatsThis::showText(event->globalPos(), + QWhatsThis::showText(event->globalPos(), i18n("There are no tags available to show in this popup. To add presets, you need to tag them and then select the tag here.")); } } } } void KisPopupPalette::tabletEvent(QTabletEvent* /*event*/) { } void KisPopupPalette::mouseReleaseEvent(QMouseEvent * event) { QPointF point = event->posF(); event->accept(); if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) { QPainterPath pathColor(drawDonutPathFull(width() / 2, height() / 2, colorInnerRadius, colorOuterRadius)); //in favorite brushes area if (hoveredPreset() > -1) { //setSelectedBrush(hoveredBrush()); emit sigChangeActivePaintop(hoveredPreset()); } if (pathColor.contains(point)) { int pos = calculateIndex(point, m_resourceManager->recentColorsTotal()); if (pos >= 0 && pos < m_resourceManager->recentColorsTotal()) { emit sigUpdateRecentColor(pos); } } } } int KisPopupPalette::calculateIndex(QPointF point, int n) { calculatePresetIndex(point, n); //translate to (0,0) point.setX(point.x() - width() / 2); point.setY(point.y() - height() / 2); //rotate float smallerAngle = M_PI / 2 + M_PI / n - atan2(point.y(), point.x()); float radius = sqrt((float)point.x() * point.x() + point.y() * point.y()); point.setX(radius * cos(smallerAngle)); point.setY(radius * sin(smallerAngle)); //calculate brush index int pos = floor(acos(point.x() / radius) * n / (2 * M_PI)); if (point.y() < 0) pos = n - pos - 1; return pos; } bool KisPopupPalette::isPointInPixmap(QPointF& point, int pos) { if (pathFromPresetIndex(pos).contains(point + QPointF(-width() / 2, -height() / 2))) { return true; } return false; } KisPopupPalette::~KisPopupPalette() { } QPainterPath KisPopupPalette::pathFromPresetIndex(int index) { qreal angleLength = 360.0 / numSlots() / 180 * M_PI; qreal angle = index * angleLength; qreal r = colorOuterRadius * sin(angleLength/2) / ( 1 - sin(angleLength/2)); - + QPainterPath path; path.addEllipse((colorOuterRadius+r) * cos(angle)-r, -(colorOuterRadius+r) * sin(angle)-r, 2*r, 2*r); path.closeSubpath(); return path; } int KisPopupPalette::calculatePresetIndex(QPointF point, int /*n*/) { for(int i = 0; i < numSlots(); i++) { QPointF adujustedPoint = point - QPointF(width()/2, height()/2); if(pathFromPresetIndex(i).contains(adujustedPoint)) { return i; } } return -1; } int KisPopupPalette::numSlots() { KisConfig config; return qMax(config.favoritePresets(), 10); } diff --git a/libs/ui/kis_selection_manager.cc b/libs/ui/kis_selection_manager.cc index c1b64dd3c0..19a6b73f24 100644 --- a/libs/ui/kis_selection_manager.cc +++ b/libs/ui/kis_selection_manager.cc @@ -1,583 +1,583 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2007 Sven Langkamp * * The outline algorith uses the limn algorithm of fontutils by * Karl Berry and Kathryn Hargreaves * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_selection_manager.h" #include #include #include #include #include #include #include #include #include #include #include "KoCanvasController.h" #include "KoChannelInfo.h" #include "KoIntegerMaths.h" #include #include #include #include #include #include #include #include #include #include #include "kis_adjustment_layer.h" #include "kis_node_manager.h" #include "canvas/kis_canvas2.h" #include "kis_config.h" #include "kis_convolution_painter.h" #include "kis_convolution_kernel.h" #include "kis_debug.h" #include "KisDocument.h" #include "kis_fill_painter.h" #include "kis_group_layer.h" #include "kis_layer.h" #include "kis_statusbar.h" #include "kis_paint_device.h" #include "kis_paint_layer.h" #include "kis_painter.h" #include "kis_transaction.h" #include "kis_selection.h" #include "kis_types.h" #include "kis_canvas_resource_provider.h" #include "kis_undo_adapter.h" #include "kis_pixel_selection.h" #include "flake/kis_shape_selection.h" #include "commands/kis_selection_commands.h" #include "kis_selection_mask.h" #include "flake/kis_shape_layer.h" #include "kis_selection_decoration.h" #include "canvas/kis_canvas_decoration.h" #include "kis_node_commands_adapter.h" #include "kis_iterator_ng.h" #include "kis_clipboard.h" #include "KisViewManager.h" #include "kis_selection_filters.h" #include "kis_figure_painting_tool_helper.h" #include "KisView.h" #include "actions/kis_selection_action_factories.h" #include "kis_action.h" #include "kis_action_manager.h" #include "operations/kis_operation_configuration.h" KisSelectionManager::KisSelectionManager(KisViewManager * view) : m_view(view), m_doc(0), m_imageView(0), m_adapter(new KisNodeCommandsAdapter(view)), m_copy(0), m_copyMerged(0), m_cut(0), m_paste(0), m_pasteNew(0), m_cutToNewLayer(0), m_selectAll(0), m_deselect(0), m_clear(0), m_reselect(0), m_invert(0), m_copyToNewLayer(0), m_fillForegroundColor(0), m_fillBackgroundColor(0), m_fillPattern(0), m_imageResizeToSelection(0), m_selectionDecoration(0) { m_clipboard = KisClipboard::instance(); } KisSelectionManager::~KisSelectionManager() { } void KisSelectionManager::setup(KisActionManager* actionManager) { m_cut = actionManager->createStandardAction(KStandardAction::Cut, this, SLOT(cut())); m_copy = actionManager->createStandardAction(KStandardAction::Copy, this, SLOT(copy())); m_paste = actionManager->createStandardAction(KStandardAction::Paste, this, SLOT(paste())); KisAction *action = actionManager->createAction("copy_sharp"); connect(action, SIGNAL(triggered()), this, SLOT(copySharp())); action = actionManager->createAction("cut_sharp"); connect(action, SIGNAL(triggered()), this, SLOT(cutSharp())); m_pasteNew = actionManager->createAction("paste_new"); connect(m_pasteNew, SIGNAL(triggered()), this, SLOT(pasteNew())); m_pasteAt = actionManager->createAction("paste_at"); connect(m_pasteAt, SIGNAL(triggered()), this, SLOT(pasteAt())); m_copyMerged = actionManager->createAction("copy_merged"); connect(m_copyMerged, SIGNAL(triggered()), this, SLOT(copyMerged())); m_selectAll = actionManager->createAction("select_all"); connect(m_selectAll, SIGNAL(triggered()), this, SLOT(selectAll())); m_deselect = actionManager->createAction("deselect"); connect(m_deselect, SIGNAL(triggered()), this, SLOT(deselect())); m_clear = actionManager->createAction("clear"); connect(m_clear, SIGNAL(triggered()), SLOT(clear())); m_reselect = actionManager->createAction("reselect"); connect(m_reselect, SIGNAL(triggered()), this, SLOT(reselect())); m_invert = actionManager->createAction("invert"); m_invert->setOperationID("invertselection"); actionManager->registerOperation(new KisInvertSelectionOperaton); m_copyToNewLayer = actionManager->createAction("copy_selection_to_new_layer"); connect(m_copyToNewLayer, SIGNAL(triggered()), this, SLOT(copySelectionToNewLayer())); m_cutToNewLayer = actionManager->createAction("cut_selection_to_new_layer"); connect(m_cutToNewLayer, SIGNAL(triggered()), this, SLOT(cutToNewLayer())); m_fillForegroundColor = actionManager->createAction("fill_selection_foreground_color"); connect(m_fillForegroundColor, SIGNAL(triggered()), this, SLOT(fillForegroundColor())); m_fillBackgroundColor = actionManager->createAction("fill_selection_background_color"); connect(m_fillBackgroundColor, SIGNAL(triggered()), this, SLOT(fillBackgroundColor())); m_fillPattern = actionManager->createAction("fill_selection_pattern"); connect(m_fillPattern, SIGNAL(triggered()), this, SLOT(fillPattern())); m_fillForegroundColorOpacity = actionManager->createAction("fill_selection_foreground_color_opacity"); connect(m_fillForegroundColorOpacity, SIGNAL(triggered()), this, SLOT(fillForegroundColorOpacity())); m_fillBackgroundColorOpacity = actionManager->createAction("fill_selection_background_color_opacity"); connect(m_fillBackgroundColorOpacity, SIGNAL(triggered()), this, SLOT(fillBackgroundColorOpacity())); m_fillPatternOpacity = actionManager->createAction("fill_selection_pattern_opacity"); connect(m_fillPatternOpacity, SIGNAL(triggered()), this, SLOT(fillPatternOpacity())); m_strokeShapes = actionManager->createAction("stroke_shapes"); connect(m_strokeShapes, SIGNAL(triggered()), this, SLOT(paintSelectedShapes())); m_toggleDisplaySelection = actionManager->createAction("toggle_display_selection"); connect(m_toggleDisplaySelection, SIGNAL(triggered()), this, SLOT(toggleDisplaySelection())); m_toggleDisplaySelection->setChecked(true); m_imageResizeToSelection = actionManager->createAction("resizeimagetoselection"); connect(m_imageResizeToSelection, SIGNAL(triggered()), this, SLOT(imageResizeToSelection())); action = actionManager->createAction("convert_to_vector_selection"); connect(action, SIGNAL(triggered()), SLOT(convertToVectorSelection())); action = actionManager->createAction("convert_shapes_to_vector_selection"); connect(action, SIGNAL(triggered()), SLOT(convertShapesToVectorSelection())); action = actionManager->createAction("convert_selection_to_shape"); connect(action, SIGNAL(triggered()), SLOT(convertToShape())); m_toggleSelectionOverlayMode = actionManager->createAction("toggle-selection-overlay-mode"); connect(m_toggleSelectionOverlayMode, SIGNAL(triggered()), SLOT(slotToggleSelectionDecoration())); QClipboard *cb = QApplication::clipboard(); connect(cb, SIGNAL(dataChanged()), SLOT(clipboardDataChanged())); } void KisSelectionManager::setView(QPointerimageView) { if (m_imageView && m_imageView->canvasBase()) { disconnect(m_imageView->canvasBase()->toolProxy(), SIGNAL(toolChanged(const QString&)), this, SLOT(clipboardDataChanged())); KoSelection *selection = m_imageView->canvasBase()->globalShapeManager()->selection(); selection->disconnect(this, SLOT(shapeSelectionChanged())); - KisSelectionDecoration *decoration = qobject_cast(m_imageView->canvasBase()->decoration("selection")); + KisSelectionDecoration *decoration = qobject_cast(m_imageView->canvasBase()->decoration("selection").data()); if (decoration) { disconnect(SIGNAL(currentSelectionChanged()), decoration); } m_imageView->image()->undoAdapter()->disconnect(this); m_selectionDecoration = 0; } m_imageView = imageView; if (m_imageView) { KoSelection * selection = m_imageView->canvasBase()->globalShapeManager()->selection(); Q_ASSERT(selection); connect(selection, SIGNAL(selectionChanged()), this, SLOT(shapeSelectionChanged())); - KisSelectionDecoration* decoration = qobject_cast(m_imageView->canvasBase()->decoration("selection")); + KisSelectionDecoration* decoration = qobject_cast(m_imageView->canvasBase()->decoration("selection").data()); if (!decoration) { decoration = new KisSelectionDecoration(m_imageView); decoration->setVisible(true); m_imageView->canvasBase()->addDecoration(decoration); } m_selectionDecoration = decoration; connect(this, SIGNAL(currentSelectionChanged()), decoration, SLOT(selectionChanged())); connect(m_imageView->image()->undoAdapter(), SIGNAL(selectionChanged()), SLOT(selectionChanged())); connect(m_imageView->canvasBase()->toolProxy(), SIGNAL(toolChanged(const QString&)), SLOT(clipboardDataChanged())); } } void KisSelectionManager::clipboardDataChanged() { m_view->updateGUI(); } bool KisSelectionManager::havePixelsSelected() { KisSelectionSP activeSelection = m_view->selection(); return activeSelection && !activeSelection->selectedRect().isEmpty(); } bool KisSelectionManager::havePixelsInClipboard() { return m_clipboard->hasClip(); } bool KisSelectionManager::haveShapesSelected() { if (m_view && m_view->canvasBase() && m_view->canvasBase()->shapeManager() && m_view->canvasBase()->shapeManager()->selection()->count()) { return m_view->canvasBase()->shapeManager()->selection()->count() > 0; } return false; } bool KisSelectionManager::haveShapesInClipboard() { KisShapeLayer *shapeLayer = dynamic_cast(m_view->activeLayer().data()); if (shapeLayer) { const QMimeData* data = QApplication::clipboard()->mimeData(); if (data) { QStringList mimeTypes = m_view->canvasBase()->toolProxy()->supportedPasteMimeTypes(); Q_FOREACH (const QString & mimeType, mimeTypes) { if (data->hasFormat(mimeType)) { return true; } } } } return false; } bool KisSelectionManager::havePixelSelectionWithPixels() { KisSelectionSP selection = m_view->selection(); if (selection && selection->hasPixelSelection()) { return !selection->pixelSelection()->selectedRect().isEmpty(); } return false; } void KisSelectionManager::updateGUI() { Q_ASSERT(m_view); Q_ASSERT(m_clipboard); if (!m_view || !m_clipboard) return; bool havePixelsSelected = this->havePixelsSelected(); bool havePixelsInClipboard = this->havePixelsInClipboard(); bool haveShapesSelected = this->haveShapesSelected(); bool haveShapesInClipboard = this->haveShapesInClipboard(); bool haveDevice = m_view->activeDevice(); KisLayerSP activeLayer = m_view->activeLayer(); KisImageWSP image = activeLayer ? activeLayer->image() : 0; bool canReselect = image && image->canReselectGlobalSelection(); bool canDeselect = image && image->globalSelection(); m_clear->setEnabled(haveDevice || havePixelsSelected || haveShapesSelected); m_cut->setEnabled(havePixelsSelected || haveShapesSelected); m_copy->setEnabled(havePixelsSelected || haveShapesSelected); m_paste->setEnabled(havePixelsInClipboard || haveShapesInClipboard); m_pasteAt->setEnabled(havePixelsInClipboard || haveShapesInClipboard); // FIXME: how about pasting shapes? m_pasteNew->setEnabled(havePixelsInClipboard); m_selectAll->setEnabled(true); m_deselect->setEnabled(canDeselect); m_reselect->setEnabled(canReselect); // m_load->setEnabled(true); // m_save->setEnabled(havePixelsSelected); updateStatusBar(); emit signalUpdateGUI(); } void KisSelectionManager::updateStatusBar() { if (m_view && m_view->statusBar()) { m_view->statusBar()->setSelection(m_view->image()); } } void KisSelectionManager::selectionChanged() { m_view->updateGUI(); emit currentSelectionChanged(); } void KisSelectionManager::cut() { KisCutCopyActionFactory factory; factory.run(true, false, m_view); } void KisSelectionManager::copy() { KisCutCopyActionFactory factory; factory.run(false, false, m_view); } void KisSelectionManager::cutSharp() { KisCutCopyActionFactory factory; factory.run(true, true, m_view); } void KisSelectionManager::copySharp() { KisCutCopyActionFactory factory; factory.run(false, true, m_view); } void KisSelectionManager::copyMerged() { KisCopyMergedActionFactory factory; factory.run(m_view); } void KisSelectionManager::paste() { KisPasteActionFactory factory; factory.run(m_view); } void KisSelectionManager::pasteAt() { //XXX } void KisSelectionManager::pasteNew() { KisPasteNewActionFactory factory; factory.run(m_view); } void KisSelectionManager::selectAll() { KisSelectAllActionFactory factory; factory.run(m_view); } void KisSelectionManager::deselect() { KisDeselectActionFactory factory; factory.run(m_view); } void KisSelectionManager::invert() { if(m_invert) m_invert->trigger(); } void KisSelectionManager::reselect() { KisReselectActionFactory factory; factory.run(m_view); } void KisSelectionManager::convertToVectorSelection() { KisSelectionToVectorActionFactory factory; factory.run(m_view); } void KisSelectionManager::convertShapesToVectorSelection() { KisShapesToVectorSelectionActionFactory factory; factory.run(m_view); } void KisSelectionManager::convertToShape() { KisSelectionToShapeActionFactory factory; factory.run(m_view); } void KisSelectionManager::clear() { KisClearActionFactory factory; factory.run(m_view); } void KisSelectionManager::fillForegroundColor() { KisFillActionFactory factory; factory.run("fg", m_view); } void KisSelectionManager::fillBackgroundColor() { KisFillActionFactory factory; factory.run("bg", m_view); } void KisSelectionManager::fillPattern() { KisFillActionFactory factory; factory.run("pattern", m_view); } void KisSelectionManager::fillForegroundColorOpacity() { KisFillActionFactory factory; factory.run("fg_opacity", m_view); } void KisSelectionManager::fillBackgroundColorOpacity() { KisFillActionFactory factory; factory.run("bg_opacity", m_view); } void KisSelectionManager::fillPatternOpacity() { KisFillActionFactory factory; factory.run("pattern_opacity", m_view); } void KisSelectionManager::copySelectionToNewLayer() { copy(); paste(); } void KisSelectionManager::cutToNewLayer() { cut(); paste(); } void KisSelectionManager::toggleDisplaySelection() { KIS_ASSERT_RECOVER_RETURN(m_selectionDecoration); m_selectionDecoration->toggleVisibility(); m_toggleDisplaySelection->blockSignals(true); m_toggleDisplaySelection->setChecked(m_selectionDecoration->visible()); m_toggleDisplaySelection->blockSignals(false); emit displaySelectionChanged(); } bool KisSelectionManager::displaySelection() { return m_toggleDisplaySelection->isChecked(); } void KisSelectionManager::shapeSelectionChanged() { KoShapeManager* shapeManager = m_view->canvasBase()->globalShapeManager(); KoSelection * selection = shapeManager->selection(); QList selectedShapes = selection->selectedShapes(); KoShapeStroke* border = new KoShapeStroke(0, Qt::lightGray); Q_FOREACH (KoShape* shape, shapeManager->shapes()) { if (dynamic_cast(shape->parent())) { if (selectedShapes.contains(shape)) shape->setStroke(border); else shape->setStroke(0); } } m_view->updateGUI(); } void KisSelectionManager::imageResizeToSelection() { KisImageResizeToSelectionActionFactory factory; factory.run(m_view); } void KisSelectionManager::paintSelectedShapes() { KisImageWSP image = m_view->image(); if (!image) return; KisLayerSP layer = m_view->activeLayer(); if (!layer) return; QList shapes = m_view->canvasBase()->shapeManager()->selection()->selectedShapes(); KisPaintLayerSP paintLayer = new KisPaintLayer(image, i18n("Stroked Shapes"), OPACITY_OPAQUE_U8); KUndo2MagicString actionName = kundo2_i18n("Stroke Shapes"); m_adapter->beginMacro(actionName); m_adapter->addNode(paintLayer.data(), layer->parent().data(), layer.data()); KisFigurePaintingToolHelper helper(actionName, image, paintLayer.data(), m_view->resourceProvider()->resourceManager(), KisPainter::StrokeStyleBrush, KisPainter::FillStyleNone); Q_FOREACH (KoShape* shape, shapes) { QTransform matrix = shape->absoluteTransformation(0) * QTransform::fromScale(image->xRes(), image->yRes()); QPainterPath mapedOutline = matrix.map(shape->outline()); helper.paintPainterPath(mapedOutline); } m_adapter->endMacro(); } void KisSelectionManager::slotToggleSelectionDecoration() { KIS_ASSERT_RECOVER_RETURN(m_selectionDecoration); KisSelectionDecoration::Mode mode = m_selectionDecoration->mode() ? KisSelectionDecoration::Ants : KisSelectionDecoration::Mask; m_selectionDecoration->setMode(mode); emit displaySelectionChanged(); } bool KisSelectionManager::showSelectionAsMask() const { if (m_selectionDecoration) { return m_selectionDecoration->mode() == KisSelectionDecoration::Mask; } return false; } diff --git a/libs/ui/kis_splash_screen.cpp b/libs/ui/kis_splash_screen.cpp index 9712e35fac..7a6299b705 100644 --- a/libs/ui/kis_splash_screen.cpp +++ b/libs/ui/kis_splash_screen.cpp @@ -1,180 +1,182 @@ /* * Copyright (c) 2014 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_splash_screen.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include KisSplashScreen::KisSplashScreen(const QString &version, const QPixmap &pixmap, bool themed, QWidget *parent, Qt::WindowFlags f) : QWidget(parent, Qt::SplashScreen | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | f), m_themed(themed) { setupUi(this); setWindowIcon(KisIconUtils::loadIcon("calligrakrita")); QString color = colorString(); // Maintain the aspect ratio on high DPI screens when scaling #ifdef Q_OS_MAC lblSplash->setPixmap(pixmap); #else - lblSplash->setPixmap(pixmap.scaled(lblSplash->width(),lblSplash->height(),Qt::KeepAspectRatio)); + lblSplash->setPixmap(pixmap.scaled(lblSplash->width(),lblSplash->height(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); #endif bnClose->hide(); connect(bnClose, SIGNAL(clicked()), this, SLOT(close())); chkShowAtStartup->hide(); connect(chkShowAtStartup, SIGNAL(toggled(bool)), this, SLOT(toggleShowAtStartup(bool))); KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen"); bool hideSplash = cfg.readEntry("HideSplashAfterStartup", false); chkShowAtStartup->setChecked(hideSplash); lblLinks->setTextFormat(Qt::RichText); lblLinks->setText(i18n("" "" "" "

Links

" "

Support Krita

" "

Getting Started

" "

Manual

" "

Krita Website

" "

User Community

" "

Source Code

" "

Krita on Steam

" "" "", color)); lblVersion->setText(i18n("Version: %1", version)); lblVersion->setStyleSheet("color:" + color); updateText(); connect(lblRecent, SIGNAL(linkActivated(QString)), SLOT(linkClicked(QString))); connect(&m_timer, SIGNAL(timeout()), SLOT(raise())); m_timer.setSingleShot(true); m_timer.start(10); } void KisSplashScreen::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); updateText(); } void KisSplashScreen::updateText() { QString color = colorString(); KConfigGroup cfg2( KSharedConfig::openConfig(), "RecentFiles"); int i = 1; QString recent = i18n("" "" "" "

Recent Files

", color); QString path; QStringList recentfiles; QFontMetrics metrics(lblRecent->font()); do { path = cfg2.readPathEntry(QString("File%1").arg(i), QString()); if (!path.isEmpty()) { QString name = cfg2.readPathEntry(QString("Name%1").arg(i), QString()); QUrl url(path); if (name.isEmpty()) { name = url.fileName(); } name = metrics.elidedText(name, Qt::ElideMiddle, lblRecent->width()); if (!url.isLocalFile() || QFile::exists(url.toLocalFile())) { recentfiles.insert(0, QString("

%2

").arg(path).arg(name).arg(color)); } } i++; } while (!path.isEmpty() || i <= 8); recent += recentfiles.join("\n"); recent += "" ""; lblRecent->setText(recent); } QString KisSplashScreen::colorString() const { QString color = "#FFFFFF"; if (m_themed && qApp->palette().background().color().value() > 100) { color = "#000000"; } return color; } void KisSplashScreen::repaint() { QWidget::repaint(); QApplication::flush(); } void KisSplashScreen::show() { QRect r(QPoint(), sizeHint()); resize(r.size()); move(QApplication::desktop()->availableGeometry().center() - r.center()); if (isVisible()) { repaint(); } - + m_timer.setSingleShot(true); + m_timer.start(1); + QWidget::show(); } void KisSplashScreen::toggleShowAtStartup(bool toggle) { KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen"); cfg.writeEntry("HideSplashAfterStartup", toggle); } void KisSplashScreen::linkClicked(const QString &link) { KisPart::instance()->openExistingFile(QUrl::fromLocalFile(link)); if (isTopLevel()) { close(); } } diff --git a/libs/ui/kra/kis_kra_loader.cpp b/libs/ui/kra/kis_kra_loader.cpp index e6e2ce89ce..ae95d1ca18 100644 --- a/libs/ui/kra/kis_kra_loader.cpp +++ b/libs/ui/kra/kis_kra_loader.cpp @@ -1,1023 +1,1023 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2007 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kra/kis_kra_loader.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_resource_server_provider.h" #include "kis_keyframe_channel.h" #include "KisDocument.h" #include "kis_config.h" #include "kis_kra_tags.h" #include "kis_kra_utils.h" #include "kis_kra_load_visitor.h" #include "kis_dom_utils.h" #include "kis_image_animation_interface.h" #include "kis_time_range.h" #include "kis_grid_config.h" #include "kis_guides_config.h" /* Color model id comparison through the ages: 2.4 2.5 2.6 ideal ALPHA ALPHA ALPHA ALPHAU8 CMYK CMYK CMYK CMYKAU8 CMYKAF32 CMYKAF32 CMYKA16 CMYKAU16 CMYKAU16 GRAYA GRAYA GRAYA GRAYAU8 GrayF32 GRAYAF32 GRAYAF32 GRAYA16 GRAYAU16 GRAYAU16 LABA LABA LABA LABAU16 LABAF32 LABAF32 LABAU8 LABAU8 RGBA RGBA RGBA RGBAU8 RGBA16 RGBA16 RGBA16 RGBAU16 RgbAF32 RGBAF32 RGBAF32 RgbAF16 RgbAF16 RGBAF16 XYZA16 XYZA16 XYZA16 XYZAU16 XYZA8 XYZA8 XYZAU8 XyzAF16 XyzAF16 XYZAF16 XyzAF32 XYZAF32 XYZAF32 YCbCrA YCBCRA8 YCBCRA8 YCBCRAU8 YCbCrAU16 YCBCRAU16 YCBCRAU16 YCBCRF32 YCBCRF32 */ using namespace KRA; struct KisKraLoader::Private { public: KisDocument* document; QString imageName; // used to be stored in the image, is now in the documentInfo block QString imageComment; // used to be stored in the image, is now in the documentInfo block QMap layerFilenames; // temp storage during loading int syntaxVersion; // version of the fileformat we are loading vKisNodeSP selectedNodes; // the nodes that were active when saving the document. QMap assistantsFilenames; QList assistants; QMap keyframeFilenames; QStringList errorMessages; }; void convertColorSpaceNames(QString &colorspacename, QString &profileProductName) { if (colorspacename == "Grayscale + Alpha") { colorspacename = "GRAYA"; profileProductName.clear(); } else if (colorspacename == "RgbAF32") { colorspacename = "RGBAF32"; profileProductName.clear(); } else if (colorspacename == "RgbAF16") { colorspacename = "RGBAF16"; profileProductName.clear(); } else if (colorspacename == "CMYKA16") { colorspacename = "CMYKAU16"; } else if (colorspacename == "GrayF32") { colorspacename = "GRAYAF32"; profileProductName.clear(); } else if (colorspacename == "GRAYA16") { colorspacename = "GRAYAU16"; } else if (colorspacename == "XyzAF16") { colorspacename = "XYZAF16"; profileProductName.clear(); } else if (colorspacename == "XyzAF32") { colorspacename = "XYZAF32"; profileProductName.clear(); } else if (colorspacename == "YCbCrA") { colorspacename = "YCBCRA8"; } else if (colorspacename == "YCbCrAU16") { colorspacename = "YCBCRAU16"; } } KisKraLoader::KisKraLoader(KisDocument * document, int syntaxVersion) : m_d(new Private()) { m_d->document = document; m_d->syntaxVersion = syntaxVersion; } KisKraLoader::~KisKraLoader() { delete m_d; } KisImageWSP KisKraLoader::loadXML(const KoXmlElement& element) { QString attr; KisImageWSP image = 0; QString name; qint32 width; qint32 height; QString profileProductName; double xres; double yres; QString colorspacename; const KoColorSpace * cs; if ((attr = element.attribute(MIME)) == NATIVE_MIMETYPE) { if ((m_d->imageName = element.attribute(NAME)).isNull()) { m_d->errorMessages << i18n("Image does not have a name."); return KisImageWSP(0); } if ((attr = element.attribute(WIDTH)).isNull()) { m_d->errorMessages << i18n("Image does not specify a width."); return KisImageWSP(0); } width = KisDomUtils::toInt(attr); if ((attr = element.attribute(HEIGHT)).isNull()) { m_d->errorMessages << i18n("Image does not specify a height."); return KisImageWSP(0); } height = KisDomUtils::toInt(attr); m_d->imageComment = element.attribute(DESCRIPTION); xres = 100.0 / 72.0; if (!(attr = element.attribute(X_RESOLUTION)).isNull()) { qreal value = KisDomUtils::toDouble(attr); if (value > 1.0) { xres = value / 72.0; } } yres = 100.0 / 72.0; if (!(attr = element.attribute(Y_RESOLUTION)).isNull()) { qreal value = KisDomUtils::toDouble(attr); if (value > 1.0) { yres = value / 72.0; } } if ((colorspacename = element.attribute(COLORSPACE_NAME)).isNull()) { // An old file: take a reasonable default. // Krita didn't support anything else in those // days anyway. colorspacename = "RGBA"; } profileProductName = element.attribute(PROFILE); // A hack for an old colorspacename convertColorSpaceNames(colorspacename, profileProductName); QString colorspaceModel = KoColorSpaceRegistry::instance()->colorSpaceColorModelId(colorspacename).id(); QString colorspaceDepth = KoColorSpaceRegistry::instance()->colorSpaceColorDepthId(colorspacename).id(); if (profileProductName.isNull()) { // no mention of profile so get default profile"; cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, ""); } else { cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, profileProductName); } if (cs == 0) { // try once more without the profile cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, ""); if (cs == 0) { m_d->errorMessages << i18n("Image specifies an unsupported color model: %1.", colorspacename); return KisImageWSP(0); } } if (m_d->document) { image = new KisImage(m_d->document->createUndoStore(), width, height, cs, name); } else { image = new KisImage(0, width, height, cs, name); } image->setResolution(xres, yres); loadNodes(element, image, const_cast(image->rootLayer().data())); KoXmlNode child; for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) { KoXmlElement e = child.toElement(); if(e.tagName() == "ProjectionBackgroundColor") { if (e.hasAttribute("ColorData")) { QByteArray colorData = QByteArray::fromBase64(e.attribute("ColorData").toLatin1()); KoColor color((const quint8*)colorData.data(), image->colorSpace()); image->setDefaultProjectionColor(color); } } if (e.tagName().toLower() == "animation") { loadAnimationMetadata(e, image); } } for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) { KoXmlElement e = child.toElement(); if(e.tagName() == "compositions") { loadCompositions(e, image); } } } KoXmlNode child; for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) { KoXmlElement e = child.toElement(); if (e.tagName() == "grid") { loadGrid(e); } else if (e.tagName() == "guides") { loadGuides(e); } else if (e.tagName() == "assistants") { loadAssistantsList(e); } } return image; } void KisKraLoader::loadBinaryData(KoStore * store, KisImageWSP image, const QString & uri, bool external) { // icc profile: if present, this overrides the profile product name loaded in loadXML. QString location = external ? QString() : uri; location += m_d->imageName + ICC_PATH; if (store->hasFile(location)) { if (store->open(location)) { QByteArray data; data.resize(store->size()); bool res = (store->read(data.data(), store->size()) > -1); store->close(); if (res) { const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(image->colorSpace()->colorModelId().id(), image->colorSpace()->colorDepthId().id(), data); if (profile && profile->valid()) { res = image->assignImageProfile(profile); } if (!res) { profile = KoColorSpaceRegistry::instance()->profileByName(KoColorSpaceRegistry::instance()->colorSpaceFactory(image->colorSpace()->id())->defaultProfile()); Q_ASSERT(profile && profile->valid()); image->assignImageProfile(profile); } } } } // Load the layers data: if there is a profile associated with a layer it will be set now. KisKraLoadVisitor visitor(image, store, m_d->layerFilenames, m_d->keyframeFilenames, m_d->imageName, m_d->syntaxVersion); if (external) { visitor.setExternalUri(uri); } image->rootLayer()->accept(visitor); if (!visitor.errorMessages().isEmpty()) { m_d->errorMessages.append(visitor.errorMessages()); } // annotations // exif location = external ? QString() : uri; location += m_d->imageName + EXIF_PATH; if (store->hasFile(location)) { QByteArray data; store->open(location); data = store->read(store->size()); store->close(); image->addAnnotation(KisAnnotationSP(new KisAnnotation("exif", "", data))); } // layer styles location = external ? QString() : uri; location += m_d->imageName + LAYER_STYLES_PATH; if (store->hasFile(location)) { KisPSDLayerStyleCollectionResource *collection = new KisPSDLayerStyleCollectionResource("Embedded Styles.asl"); collection->setName(i18nc("Auto-generated layer style collection name for embedded styles (collection)", "<%1> (embedded)", m_d->imageName)); KIS_ASSERT_RECOVER_NOOP(!collection->valid()); store->open(location); { KoStoreDevice device(store); device.open(QIODevice::ReadOnly); /** * ASL loading code cannot work with non-sequential IO devices, * so convert the device beforehand! */ QByteArray buf = device.readAll(); QBuffer raDevice(&buf); raDevice.open(QIODevice::ReadOnly); collection->loadFromDevice(&raDevice); } store->close(); if (collection->valid()) { KoResourceServer *server = KisResourceServerProvider::instance()->layerStyleCollectionServer(); server->addResource(collection, false); collection->assignAllLayerStyles(image->root()); } else { warnKrita << "WARNING: Couldn't load layer styles library from .kra!"; delete collection; } } if (m_d->document && m_d->document->documentInfo()->aboutInfo("title").isNull()) m_d->document->documentInfo()->setAboutInfo("title", m_d->imageName); if (m_d->document && m_d->document->documentInfo()->aboutInfo("comment").isNull()) m_d->document->documentInfo()->setAboutInfo("comment", m_d->imageComment); loadAssistants(store, uri, external); } vKisNodeSP KisKraLoader::selectedNodes() const { return m_d->selectedNodes; } QList KisKraLoader::assistants() const { return m_d->assistants; } QStringList KisKraLoader::errorMessages() const { return m_d->errorMessages; } void KisKraLoader::loadAssistants(KoStore *store, const QString &uri, bool external) { QString file_path; QString location; QMap handleMap; KisPaintingAssistant* assistant = 0; QMap::const_iterator loadedAssistant = m_d->assistantsFilenames.constBegin(); while (loadedAssistant != m_d->assistantsFilenames.constEnd()){ const KisPaintingAssistantFactory* factory = KisPaintingAssistantFactoryRegistry::instance()->get(loadedAssistant.value()); if (factory) { assistant = factory->createPaintingAssistant(); location = external ? QString() : uri; location += m_d->imageName + ASSISTANTS_PATH; file_path = location + loadedAssistant.key(); assistant->loadXml(store, handleMap, file_path); //If an assistant has too few handles than it should according to it's own setup, just don't load it// if (assistant->handles().size()==assistant->numHandles()){ m_d->assistants.append(toQShared(assistant)); } } loadedAssistant++; } } void KisKraLoader::loadAnimationMetadata(const KoXmlElement &element, KisImageWSP image) { QDomDocument qDom; KoXml::asQDomElement(qDom, element); QDomElement qElement = qDom.firstChildElement(); float framerate; KisTimeRange range; int currentTime; KisImageAnimationInterface *animation = image->animationInterface(); if (KisDomUtils::loadValue(qElement, "framerate", &framerate)) { animation->setFramerate(framerate); } if (KisDomUtils::loadValue(qElement, "range", &range)) { animation->setFullClipRange(range); } if (KisDomUtils::loadValue(qElement, "currentTime", ¤tTime)) { animation->switchCurrentTimeAsync(currentTime); } } KisNodeSP KisKraLoader::loadNodes(const KoXmlElement& element, KisImageWSP image, KisNodeSP parent) { KoXmlNode node = element.firstChild(); KoXmlNode child; if (!node.isNull()) { if (node.isElement()) { if (node.nodeName().toUpper() == LAYERS.toUpper() || node.nodeName().toUpper() == MASKS.toUpper()) { for (child = node.lastChild(); !child.isNull(); child = child.previousSibling()) { KisNodeSP node = loadNode(child.toElement(), image, parent); if (node) { image->nextLayerName(); // Make sure the nameserver is current with the number of nodes. image->addNode(node, parent); if (node->inherits("KisLayer") && child.childNodesCount() > 0) { loadNodes(child.toElement(), image, node); } } } } } } return parent; } KisNodeSP KisKraLoader::loadNode(const KoXmlElement& element, KisImageWSP image, KisNodeSP parent) { // Nota bene: If you add new properties to layers, you should // ALWAYS define a default value in case the property is not // present in the layer definition: this helps a LOT with backward // compatibility. QString name = element.attribute(NAME, "No Name"); QUuid id = QUuid(element.attribute(UUID, QUuid().toString())); qint32 x = element.attribute(X, "0").toInt(); qint32 y = element.attribute(Y, "0").toInt(); qint32 opacity = element.attribute(OPACITY, QString::number(OPACITY_OPAQUE_U8)).toInt(); if (opacity < OPACITY_TRANSPARENT_U8) opacity = OPACITY_TRANSPARENT_U8; if (opacity > OPACITY_OPAQUE_U8) opacity = OPACITY_OPAQUE_U8; const KoColorSpace* colorSpace = 0; if ((element.attribute(COLORSPACE_NAME)).isNull()) { dbgFile << "No attribute color space for layer: " << name; colorSpace = image->colorSpace(); } else { QString colorspacename = element.attribute(COLORSPACE_NAME); QString profileProductName; convertColorSpaceNames(colorspacename, profileProductName); QString colorspaceModel = KoColorSpaceRegistry::instance()->colorSpaceColorModelId(colorspacename).id(); QString colorspaceDepth = KoColorSpaceRegistry::instance()->colorSpaceColorDepthId(colorspacename).id(); dbgFile << "Searching color space: " << colorspacename << colorspaceModel << colorspaceDepth << " for layer: " << name; // use default profile - it will be replaced later in completeLoading colorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, ""); dbgFile << "found colorspace" << colorSpace; if (!colorSpace) { m_d->errorMessages << i18n("Layer %1 specifies an unsupported color model: %2.", name, colorspacename); return 0; } } const bool visible = element.attribute(VISIBLE, "1") == "0" ? false : true; const bool locked = element.attribute(LOCKED, "0") == "0" ? false : true; const bool collapsed = element.attribute(COLLAPSED, "0") == "0" ? false : true; const int colorLabelIndex = element.attribute(COLOR_LABEL, "0").toInt(); // Now find out the layer type and do specific handling QString nodeType; if (m_d->syntaxVersion == 1) { nodeType = element.attribute("layertype"); if (nodeType.isEmpty()) { nodeType = PAINT_LAYER; } } else { nodeType = element.attribute(NODE_TYPE); } if (nodeType.isEmpty()) { m_d->errorMessages << i18n("Layer %1 has an unsupported type.", name); return 0; } KisNodeSP node = 0; if (nodeType == PAINT_LAYER) node = loadPaintLayer(element, image, name, colorSpace, opacity); else if (nodeType == GROUP_LAYER) node = loadGroupLayer(element, image, name, colorSpace, opacity); else if (nodeType == ADJUSTMENT_LAYER) node = loadAdjustmentLayer(element, image, name, colorSpace, opacity); else if (nodeType == SHAPE_LAYER) node = loadShapeLayer(element, image, name, colorSpace, opacity); else if (nodeType == GENERATOR_LAYER) node = loadGeneratorLayer(element, image, name, colorSpace, opacity); else if (nodeType == CLONE_LAYER) node = loadCloneLayer(element, image, name, colorSpace, opacity); else if (nodeType == FILTER_MASK) node = loadFilterMask(element, parent); else if (nodeType == TRANSFORM_MASK) node = loadTransformMask(element, parent); else if (nodeType == TRANSPARENCY_MASK) node = loadTransparencyMask(element, parent); else if (nodeType == SELECTION_MASK) node = loadSelectionMask(image, element, parent); else if (nodeType == FILE_LAYER) { node = loadFileLayer(element, image, name, opacity); } else { m_d->errorMessages << i18n("Layer %1 has an unsupported type: %2.", name, nodeType); return 0; } // Loading the node went wrong. Return empty node and leave to // upstream to complain to the user if (!node) { m_d->errorMessages << i18n("Failure loading layer %1 of type: %2.", name, nodeType); return 0; } node->setVisible(visible, true); node->setUserLocked(locked); node->setCollapsed(collapsed); node->setColorLabelIndex(colorLabelIndex); node->setX(x); node->setY(y); node->setName(name); if (! id.isNull()) // if no uuid in file, new one has been generated already node->setUuid(id); if (node->inherits("KisLayer")) { KisLayer* layer = qobject_cast(node.data()); QBitArray channelFlags = stringToFlags(element.attribute(CHANNEL_FLAGS, ""), colorSpace->channelCount()); QString compositeOpName = element.attribute(COMPOSITE_OP, "normal"); layer->setChannelFlags(channelFlags); layer->setCompositeOpId(compositeOpName); if (element.hasAttribute(LAYER_STYLE_UUID)) { QString uuidString = element.attribute(LAYER_STYLE_UUID); QUuid uuid(uuidString); if (!uuid.isNull()) { KisPSDLayerStyleSP dumbLayerStyle(new KisPSDLayerStyle()); dumbLayerStyle->setUuid(uuid); layer->setLayerStyle(dumbLayerStyle); } else { warnKrita << "WARNING: Layer style for layer" << layer->name() << "contains invalid UUID" << uuidString; } } } if (node->inherits("KisGroupLayer")) { if (element.hasAttribute(PASS_THROUGH_MODE)) { bool value = element.attribute(PASS_THROUGH_MODE, "0") != "0"; KisGroupLayer *group = qobject_cast(node.data()); group->setPassThroughMode(value); } } if (node->inherits("KisPaintLayer")) { KisPaintLayer* layer = qobject_cast(node.data()); QBitArray channelLockFlags = stringToFlags(element.attribute(CHANNEL_LOCK_FLAGS, ""), colorSpace->channelCount()); layer->setChannelLockFlags(channelLockFlags); bool onionEnabled = element.attribute(ONION_SKIN_ENABLED, "0") == "0" ? false : true; layer->setOnionSkinEnabled(onionEnabled); bool timelineEnabled = element.attribute(VISIBLE_IN_TIMELINE, "0") == "0" ? false : true; layer->setUseInTimeline(timelineEnabled); } if (element.attribute(FILE_NAME).isNull()) { m_d->layerFilenames[node.data()] = name; } else { m_d->layerFilenames[node.data()] = element.attribute(FILE_NAME); } if (element.hasAttribute("selected") && element.attribute("selected") == "true") { m_d->selectedNodes.append(node); } if (element.hasAttribute(KEYFRAME_FILE)) { m_d->keyframeFilenames.insert(node.data(), element.attribute(KEYFRAME_FILE)); } return node; } KisNodeSP KisKraLoader::loadPaintLayer(const KoXmlElement& element, KisImageWSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { Q_UNUSED(element); KisPaintLayer* layer; layer = new KisPaintLayer(image, name, opacity, cs); Q_CHECK_PTR(layer); // Load exif info /*TODO: write and use the legacy stuff to load that exif tag for( KoXmlNode node = element.firstChild(); !node.isNull(); node = node.nextSibling() ) { KoXmlElement e = node.toElement(); if ( !e.isNull() && e.tagName() == "ExifInfo" ) { layer->paintDevice()->exifInfo()->load(e); } }*/ // TODO load metadata return layer; } KisNodeSP KisKraLoader::loadFileLayer(const KoXmlElement& element, KisImageWSP image, const QString& name, quint32 opacity) { QString filename = element.attribute("source", QString()); if (filename.isNull()) return 0; bool scale = (element.attribute("scale", "true") == "true"); int scalingMethod = element.attribute("scalingmethod", "-1").toInt(); if (scalingMethod < 0) { if (scale) { scalingMethod = KisFileLayer::ToImagePPI; } else { scalingMethod = KisFileLayer::None; } } QString documentPath; if (m_d->document) { documentPath = m_d->document->url().toLocalFile(); } QFileInfo info(documentPath); QString basePath = info.absolutePath(); QString fullPath = basePath + QDir::separator() + filename; // Entering the event loop to show the messagebox will delete the image, so up the ref by one image->ref(); if (!QFileInfo(fullPath).exists()) { qApp->setOverrideCursor(Qt::ArrowCursor); QString msg = i18nc( "@info", "The file associated to a file layer with the name \"%1\" is not found." "Expected path:" "%2" "Do you want to locate it manually?", name, fullPath); int result = QMessageBox::warning(0, i18nc("@title:window", "File not found"), msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if (result == QMessageBox::Yes) { KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument"); - dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Import)); + dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import)); dialog.setDefaultDir(basePath); QString url = dialog.filename(); if (!QFileInfo(basePath).exists()) { filename = url; } else { QDir d(basePath); filename = d.relativeFilePath(url); } } qApp->restoreOverrideCursor(); } KisLayer *layer = new KisFileLayer(image, basePath, filename, (KisFileLayer::ScalingMethod)scalingMethod, name, opacity); Q_CHECK_PTR(layer); return layer; } KisNodeSP KisKraLoader::loadGroupLayer(const KoXmlElement& element, KisImageWSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { Q_UNUSED(element); Q_UNUSED(cs); QString attr; KisGroupLayer* layer; layer = new KisGroupLayer(image, name, opacity); Q_CHECK_PTR(layer); return layer; } KisNodeSP KisKraLoader::loadAdjustmentLayer(const KoXmlElement& element, KisImageWSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { // XXX: do something with filterversion? Q_UNUSED(cs); QString attr; KisAdjustmentLayer* layer; QString filtername; if ((filtername = element.attribute(FILTER_NAME)).isNull()) { // XXX: Invalid adjustmentlayer! We should warn about it! warnFile << "No filter in adjustment layer"; return 0; } KisFilterSP f = KisFilterRegistry::instance()->value(filtername); if (!f) { warnFile << "No filter for filtername" << filtername << ""; return 0; // XXX: We don't have this filter. We should warn about it! } KisFilterConfiguration * kfc = f->defaultConfiguration(0); // We'll load the configuration and the selection later. layer = new KisAdjustmentLayer(image, name, kfc, 0); Q_CHECK_PTR(layer); layer->setOpacity(opacity); return layer; } KisNodeSP KisKraLoader::loadShapeLayer(const KoXmlElement& element, KisImageWSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { Q_UNUSED(element); Q_UNUSED(cs); QString attr; KoShapeBasedDocumentBase * shapeController = 0; if (m_d->document) { shapeController = m_d->document->shapeController(); } KisShapeLayer* layer = new KisShapeLayer(shapeController, image, name, opacity); Q_CHECK_PTR(layer); return layer; } KisNodeSP KisKraLoader::loadGeneratorLayer(const KoXmlElement& element, KisImageWSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { Q_UNUSED(cs); // XXX: do something with generator version? KisGeneratorLayer* layer; QString generatorname = element.attribute(GENERATOR_NAME); if (generatorname.isNull()) { // XXX: Invalid generator layer! We should warn about it! warnFile << "No generator in generator layer"; return 0; } KisGeneratorSP generator = KisGeneratorRegistry::instance()->value(generatorname); if (!generator) { warnFile << "No generator for generatorname" << generatorname << ""; return 0; // XXX: We don't have this generator. We should warn about it! } KisFilterConfiguration * kgc = generator->defaultConfiguration(0); // We'll load the configuration and the selection later. layer = new KisGeneratorLayer(image, name, kgc, 0); Q_CHECK_PTR(layer); layer->setOpacity(opacity); return layer; } KisNodeSP KisKraLoader::loadCloneLayer(const KoXmlElement& element, KisImageWSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { Q_UNUSED(cs); KisCloneLayerSP layer = new KisCloneLayer(0, image, name, opacity); KisCloneInfo info; if (! (element.attribute(CLONE_FROM_UUID)).isNull()) { info = KisCloneInfo(QUuid(element.attribute(CLONE_FROM_UUID))); } else { if ((element.attribute(CLONE_FROM)).isNull()) { return 0; } else { info = KisCloneInfo(element.attribute(CLONE_FROM)); } } layer->setCopyFromInfo(info); if ((element.attribute(CLONE_TYPE)).isNull()) { return 0; } else { layer->setCopyType((CopyLayerType) element.attribute(CLONE_TYPE).toInt()); } return layer; } KisNodeSP KisKraLoader::loadFilterMask(const KoXmlElement& element, KisNodeSP parent) { Q_UNUSED(parent); QString attr; KisFilterMask* mask; QString filtername; // XXX: should we check the version? if ((filtername = element.attribute(FILTER_NAME)).isNull()) { // XXX: Invalid filter layer! We should warn about it! warnFile << "No filter in filter layer"; return 0; } KisFilterSP f = KisFilterRegistry::instance()->value(filtername); if (!f) { warnFile << "No filter for filtername" << filtername << ""; return 0; // XXX: We don't have this filter. We should warn about it! } KisFilterConfiguration * kfc = f->defaultConfiguration(0); // We'll load the configuration and the selection later. mask = new KisFilterMask(); mask->setFilter(kfc); Q_CHECK_PTR(mask); return mask; } KisNodeSP KisKraLoader::loadTransformMask(const KoXmlElement& element, KisNodeSP parent) { Q_UNUSED(element); Q_UNUSED(parent); KisTransformMask* mask; /** * We'll load the transform configuration later on a stage * of binary data loading */ mask = new KisTransformMask(); Q_CHECK_PTR(mask); return mask; } KisNodeSP KisKraLoader::loadTransparencyMask(const KoXmlElement& element, KisNodeSP parent) { Q_UNUSED(element); Q_UNUSED(parent); KisTransparencyMask* mask = new KisTransparencyMask(); Q_CHECK_PTR(mask); return mask; } KisNodeSP KisKraLoader::loadSelectionMask(KisImageWSP image, const KoXmlElement& element, KisNodeSP parent) { Q_UNUSED(parent); KisSelectionMaskSP mask = new KisSelectionMask(image); bool active = element.attribute(ACTIVE, "1") == "0" ? false : true; mask->setActive(active); Q_CHECK_PTR(mask); return mask; } void KisKraLoader::loadCompositions(const KoXmlElement& elem, KisImageWSP image) { KoXmlNode child; for (child = elem.firstChild(); !child.isNull(); child = child.nextSibling()) { KoXmlElement e = child.toElement(); QString name = e.attribute("name"); bool exportEnabled = e.attribute("exportEnabled", "1") == "0" ? false : true; KisLayerComposition* composition = new KisLayerComposition(image, name); composition->setExportEnabled(exportEnabled); KoXmlNode value; for (value = child.lastChild(); !value.isNull(); value = value.previousSibling()) { KoXmlElement e = value.toElement(); QUuid uuid(e.attribute("uuid")); bool visible = e.attribute("visible", "1") == "0" ? false : true; composition->setVisible(uuid, visible); bool collapsed = e.attribute("collapsed", "1") == "0" ? false : true; composition->setCollapsed(uuid, collapsed); } image->addComposition(composition); } } void KisKraLoader::loadAssistantsList(const KoXmlElement &elem) { KoXmlNode child; int count = 0; for (child = elem.firstChild(); !child.isNull(); child = child.nextSibling()) { KoXmlElement e = child.toElement(); QString type = e.attribute("type"); QString file_name = e.attribute("filename"); m_d->assistantsFilenames.insert(file_name,type); count++; } } void KisKraLoader::loadGrid(const KoXmlElement& elem) { QDomDocument dom; KoXml::asQDomElement(dom, elem); QDomElement domElement = dom.firstChildElement(); KisGridConfig config; config.loadDynamicDataFromXml(domElement); config.loadStaticData(); m_d->document->setGridConfig(config); } void KisKraLoader::loadGuides(const KoXmlElement& elem) { QDomDocument dom; KoXml::asQDomElement(dom, elem); QDomElement domElement = dom.firstChildElement(); KisGuidesConfig guides; guides.loadFromXml(domElement); m_d->document->setGuidesConfig(guides); } diff --git a/libs/ui/opengl/kis_opengl.cpp b/libs/ui/opengl/kis_opengl.cpp index 71ef749e5b..72983edb2d 100644 --- a/libs/ui/opengl/kis_opengl.cpp +++ b/libs/ui/opengl/kis_opengl.cpp @@ -1,188 +1,165 @@ /* * 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 "opengl/kis_opengl.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { + bool initialized = false; bool NeedsFenceWorkaround = false; - int glVersion = 0; + int glMajorVersion = 0; + int glMinorVersion = 0; + bool supportsDeprecatedFunctions = false; + QString Renderer; } -class TestWindow : public QWindow { -public: - TestWindow(QScreen *screen = 0) - : QWindow(screen) - { - QOpenGLContext *context = 0; - QSurfaceFormat format; - - // 3.2 Compatibility - format.setMajorVersion( 3 ); - format.setMinorVersion( 2 ); - format.setProfile( QSurfaceFormat::CoreProfile ); - setFormat( format ); - // Create an OpenGL context - context = new QOpenGLContext; - context->setFormat( format ); - context->create(); - context->makeCurrent(this); - QOpenGLFunctions_3_2_Core *f0 = context->versionFunctions(); - version32Core = f0; - delete context; - - // 3.2 Core - format.setProfile( QSurfaceFormat::CompatibilityProfile ); - setFormat( format ); - // Create an OpenGL context - context = new QOpenGLContext; - context->setFormat( format ); - context->create(); - context->makeCurrent(this); - QOpenGLFunctions_3_2_Compatibility *f1 = context->versionFunctions(); - version32Compatibility = f1; - delete context; - - // 2.1 - format.setMajorVersion( 3 ); - format.setMinorVersion( 2 ); - setFormat( format ); - // Create an OpenGL context - context = new QOpenGLContext; - context->setFormat( format ); - context->create(); - context->makeCurrent(this); - QOpenGLFunctions_2_1 *f2 = context->versionFunctions(); - version32Core = f2; - delete context; +void KisOpenGL::initialize() +{ + if (initialized) return; - } + setDefaultFormat(); - bool version32Core; - bool version32Compatibility; - bool version21; -}; + // we need a QSurface active to get our GL functions from the context + QWindow surface; + surface.setSurfaceType( QSurface::OpenGLSurface ); + surface.create(); + QOpenGLContext context; + context.create(); + context.makeCurrent( &surface ); -void KisOpenGL::initialize() -{ - { - TestWindow w; - qDebug() << "3.2 core" << w.version32Core << "3.2 compatibility" << w.version32Compatibility << "2.1" << w.version21; - } + QOpenGLFunctions *funcs = context.functions(); + funcs->initializeOpenGLFunctions(); - QSurfaceFormat format; - format.setProfile(QSurfaceFormat::CompatibilityProfile); - format.setOptions(QSurfaceFormat::DeprecatedFunctions); - format.setDepthBufferSize(24); - format.setStencilBufferSize(8); - format.setVersion(3, 2); - // if (cfg.disableDoubleBuffering()) { - if (false) { - format.setSwapBehavior(QSurfaceFormat::SingleBuffer); - } - else { - format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); - } - format.setSwapInterval(0); // Disable vertical refresh syncing - QSurfaceFormat::setDefaultFormat(format); + qDebug() << "OpenGL Info"; + qDebug() << " Vendor: " << reinterpret_cast(funcs->glGetString(GL_VENDOR)); + qDebug() << " Renderer: " << reinterpret_cast(funcs->glGetString(GL_RENDERER)); + qDebug() << " Version: " << reinterpret_cast(funcs->glGetString(GL_VERSION)); + qDebug() << " Shading language: " << reinterpret_cast(funcs->glGetString(GL_SHADING_LANGUAGE_VERSION)); + qDebug() << " Requested format: " << QSurfaceFormat::defaultFormat(); + qDebug() << " Current format: " << context.format(); + + glMajorVersion = context.format().majorVersion(); + glMinorVersion = context.format().minorVersion(); + supportsDeprecatedFunctions = (context.format().options() & QSurfaceFormat::DeprecatedFunctions); + + initialized = true; } -int KisOpenGL::initializeContext(QOpenGLContext* s) { - KisConfig cfg; +void KisOpenGL::initializeContext(QOpenGLContext *ctx) +{ + initialize(); + dbgUI << "OpenGL: Opening new context"; // Double check we were given the version we requested - QSurfaceFormat format = s->format(); - glVersion = 100 * format.majorVersion() + format.minorVersion(); - qDebug() << "glVersion"; - QOpenGLFunctions *f = s->functions(); + QSurfaceFormat format = ctx->format(); + QOpenGLFunctions *f = ctx->functions(); + f->initializeOpenGLFunctions(); #ifndef GL_RENDERER # define GL_RENDERER 0x1F01 #endif Renderer = QString((const char*)f->glGetString(GL_RENDERER)); QFile log(QDesktopServices::storageLocation(QDesktopServices::TempLocation) + "/krita-opengl.txt"); - dbgUI << "Writing OpenGL log to" << log.fileName(); log.open(QFile::WriteOnly); QString vendor((const char*)f->glGetString(GL_VENDOR)); log.write(vendor.toLatin1()); log.write(", "); log.write(Renderer.toLatin1()); log.write(", "); QString version((const char*)f->glGetString(GL_VERSION)); log.write(version.toLatin1()); + log.close(); // Check if we have a bugged driver that needs fence workaround bool isOnX11 = false; #ifdef HAVE_X11 isOnX11 = true; #endif - + KisConfig cfg; if ((isOnX11 && Renderer.startsWith("AMD")) || cfg.forceOpenGLFenceWorkaround()) { NeedsFenceWorkaround = true; } +} - return glVersion; +bool KisOpenGL::supportsGLSL13() +{ + initialize(); + return glMajorVersion >= 3 && supportsDeprecatedFunctions; } bool KisOpenGL::supportsFenceSync() { - glVersion = 100 * QSurfaceFormat::defaultFormat().majorVersion() + QSurfaceFormat::defaultFormat().minorVersion(); - dbgOpenGL << "GL Version:" << glVersion << QSurfaceFormat::defaultFormat().swapInterval() << QSurfaceFormat::defaultFormat().swapBehavior(); - - return glVersion >= 302; + initialize(); + return glMajorVersion >= 3; } bool KisOpenGL::needsFenceWorkaround() { + initialize(); return NeedsFenceWorkaround; } -QString KisOpenGL::renderer() +void KisOpenGL::setDefaultFormat() { - return Renderer; + QSurfaceFormat format; +#ifdef Q_OS_MAC +// format.setProfile(QSurfaceFormat::CoreProfile); +// format.setOptions(QSurfaceFormat::DeprecatedFunctions); + format.setVersion(2, 1); +#else + format.setProfile(QSurfaceFormat::CompatibilityProfile); + format.setOptions(QSurfaceFormat::DeprecatedFunctions); + format.setVersion(3, 0); +#endif + format.setDepthBufferSize(24); + format.setStencilBufferSize(8); + format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); + KisConfig cfg; + if (cfg.disableVSync()) { + format.setSwapInterval(0); // Disable vertical refresh syncing + } + QSurfaceFormat::setDefaultFormat(format); } bool KisOpenGL::hasOpenGL() { - glVersion = 100 * QSurfaceFormat::defaultFormat().majorVersion() + QSurfaceFormat::defaultFormat().minorVersion(); - qDebug() << "GL Version:" << glVersion << QSurfaceFormat::defaultFormat().swapInterval() << QSurfaceFormat::defaultFormat().swapBehavior(); - - return glVersion >= 302; + return ((glMajorVersion * 100 + glMinorVersion) >= 201); + //return (glMajorVersion >= 3 && supportsDeprecatedFunctions); } diff --git a/libs/ui/opengl/kis_opengl.h b/libs/ui/opengl/kis_opengl.h index 34c4dff610..8f0167c5b8 100644 --- a/libs/ui/opengl/kis_opengl.h +++ b/libs/ui/opengl/kis_opengl.h @@ -1,75 +1,78 @@ /* * 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. */ #ifndef KIS_OPENGL_H_ #define KIS_OPENGL_H_ /** @file */ #include #include class QOpenGLContext; #include "kritaui_export.h" /** * This class manages a shared OpenGL context and provides utility * functions for checking capabilities and error reporting. */ class KRITAUI_EXPORT KisOpenGL { public: enum FilterMode { NearestFilterMode, // nearest BilinearFilterMode, // linear, no mipmap TrilinearFilterMode, // LINEAR_MIPMAP_LINEAR HighQualityFiltering // Mipmaps + custom shader }; public: /// Request OpenGL version 3.2 static void initialize(); /// Initialize shared OpenGL context - static int initializeContext(QOpenGLContext* s); + static void initializeContext(QOpenGLContext *ctx); + + static bool supportsGLSL13(); /// Check for OpenGL static bool hasOpenGL(); /** * @brief supportsFilter * @return True if OpenGL provides fence sync methods. */ static bool supportsFenceSync(); /** * Returns true if we have a driver that has bugged support to sync objects (a fence) * and false otherwise. */ static bool needsFenceWorkaround(); - static QString renderer(); - private: + + static void setDefaultFormat(); + KisOpenGL(); }; #endif // KIS_OPENGL_H_ diff --git a/libs/ui/opengl/kis_opengl_canvas2.cpp b/libs/ui/opengl/kis_opengl_canvas2.cpp index 70a2d67ba8..91149c3adc 100644 --- a/libs/ui/opengl/kis_opengl_canvas2.cpp +++ b/libs/ui/opengl/kis_opengl_canvas2.cpp @@ -1,807 +1,813 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2006-2013 * Copyright (C) 2015 Michael Abrahams * * This program is free software; you can redistribute 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. */ #define GL_GLEXT_PROTOTYPES #include "opengl/kis_opengl_canvas2.h" #include "opengl/kis_opengl_canvas2_p.h" #include "opengl/kis_opengl_canvas_debugger.h" #include "canvas/kis_canvas2.h" #include "canvas/kis_coordinates_converter.h" #include "canvas/kis_display_filter.h" #include "canvas/kis_display_color_converter.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_debug.h" #include #include #include #include #include #include #include #include #include #define NEAR_VAL -1000.0 #define FAR_VAL 1000.0 #ifndef GL_CLAMP_TO_EDGE #define GL_CLAMP_TO_EDGE 0x812F #endif #define PROGRAM_VERTEX_ATTRIBUTE 0 #define PROGRAM_TEXCOORD_ATTRIBUTE 1 static bool OPENGL_SUCCESS = false; typedef void (*kis_glLogicOp)(int); static kis_glLogicOp ptr_glLogicOp = 0; struct KisOpenGLCanvas2::Private { public: ~Private() { delete displayShader; delete checkerShader; delete cursorShader; Sync::deleteSync(glSyncObject); } bool canvasInitialized{false}; QVector3D vertices[6]; QVector2D texCoords[6]; KisOpenGLImageTexturesSP openGLImageTextures; QOpenGLShaderProgram *displayShader{0}; int displayUniformLocationModelViewProjection; int displayUniformLocationTextureMatrix; int displayUniformLocationViewPortScale; int displayUniformLocationTexelSize; int displayUniformLocationTexture0; int displayUniformLocationTexture1; int displayUniformLocationFixedLodLevel; QOpenGLShaderProgram *checkerShader{0}; GLfloat checkSizeScale; bool scrollCheckers; int checkerUniformLocationModelViewProjection; int checkerUniformLocationTextureMatrix; QOpenGLShaderProgram *cursorShader{0}; int cursorShaderModelViewProjectionUniform; KisDisplayFilter* displayFilter; KisOpenGL::FilterMode filterMode; GLsync glSyncObject{0}; bool wrapAroundMode{false}; int xToColWithWrapCompensation(int x, const QRect &imageRect) { int firstImageColumn = openGLImageTextures->xToCol(imageRect.left()); int lastImageColumn = openGLImageTextures->xToCol(imageRect.right()); int colsPerImage = lastImageColumn - firstImageColumn + 1; int numWraps = floor(qreal(x) / imageRect.width()); int remainder = x - imageRect.width() * numWraps; return colsPerImage * numWraps + openGLImageTextures->xToCol(remainder); } int yToRowWithWrapCompensation(int y, const QRect &imageRect) { int firstImageRow = openGLImageTextures->yToRow(imageRect.top()); int lastImageRow = openGLImageTextures->yToRow(imageRect.bottom()); int rowsPerImage = lastImageRow - firstImageRow + 1; int numWraps = floor(qreal(y) / imageRect.height()); int remainder = y - imageRect.height() * numWraps; return rowsPerImage * numWraps + openGLImageTextures->yToRow(remainder); } }; KisOpenGLCanvas2::KisOpenGLCanvas2(KisCanvas2 *canvas, KisCoordinatesConverter *coordinatesConverter, QWidget *parent, KisImageWSP image, KisDisplayColorConverter *colorConverter) : QOpenGLWidget(parent) , KisCanvasWidgetBase(canvas, coordinatesConverter) , d(new Private()) { + + QSurfaceFormat format; format.setDepthBufferSize(24); setFormat(format); KisConfig cfg; cfg.writeEntry("canvasState", "OPENGL_STARTED"); d->openGLImageTextures = - KisOpenGLImageTextures::getImageTextures(image, - colorConverter->monitorProfile(), - colorConverter->renderingIntent(), - colorConverter->conversionFlags()); + KisOpenGLImageTextures::getImageTextures(image, + colorConverter->monitorProfile(), + colorConverter->renderingIntent(), + colorConverter->conversionFlags()); setAcceptDrops(true); - setFocusPolicy(Qt::StrongFocus); - setAttribute(Qt::WA_NoSystemBackground); - setAttribute(Qt::WA_AcceptTouchEvents); setAutoFillBackground(false); + setFocusPolicy(Qt::StrongFocus); + setAttribute(Qt::WA_NoSystemBackground, true); +#ifdef Q_OS_MAC + setAttribute(Qt::WA_AcceptTouchEvents, false); +#else + setAttribute(Qt::WA_AcceptTouchEvents, true); +#endif setAttribute(Qt::WA_InputMethodEnabled, true); setAttribute(Qt::WA_DontCreateNativeAncestors, true); setDisplayFilterImpl(colorConverter->displayFilter(), true); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); slotConfigChanged(); cfg.writeEntry("canvasState", "OPENGL_SUCCESS"); } KisOpenGLCanvas2::~KisOpenGLCanvas2() { delete d; } bool KisOpenGLCanvas2::needsFpsDebugging() const { return KisOpenglCanvasDebugger::instance()->showFpsOnCanvas(); } void KisOpenGLCanvas2::setDisplayFilter(KisDisplayFilter* displayFilter) { setDisplayFilterImpl(displayFilter, false); } void KisOpenGLCanvas2::setDisplayFilterImpl(KisDisplayFilter* displayFilter, bool initializing) { - bool needsInternalColorManagement = - !displayFilter || displayFilter->useInternalColorManagement(); + !displayFilter || displayFilter->useInternalColorManagement(); - bool needsFullRefresh = d->openGLImageTextures-> - setInternalColorManagementActive(needsInternalColorManagement); + bool needsFullRefresh = d->openGLImageTextures->setInternalColorManagementActive(needsInternalColorManagement); d->displayFilter = displayFilter; if (d->canvasInitialized) { d->canvasInitialized = false; initializeDisplayShader(); initializeCheckerShader(); d->canvasInitialized = true; } if (!initializing && needsFullRefresh) { canvas()->startUpdateInPatches(canvas()->image()->bounds()); } else if (!initializing) { canvas()->updateCanvas(); } } void KisOpenGLCanvas2::setWrapAroundViewingMode(bool value) { d->wrapAroundMode = value; update(); } void KisOpenGLCanvas2::initializeGL() { -// KisConfig cfg; -// if (cfg.disableVSync()) { -// if (!VSyncWorkaround::tryDisableVSync(this)) { -// warnUI; -// warnUI << "WARNING: We didn't manage to switch off VSync on your graphics adapter."; -// warnUI << "WARNING: It means either your hardware or driver doesn't support it,"; -// warnUI << "WARNING: or we just don't know about this hardware. Please report us a bug"; -// warnUI << "WARNING: with the output of \'glxinfo\' for your card."; -// warnUI; -// warnUI << "WARNING: Trying to workaround it by disabling Double Buffering."; -// warnUI << "WARNING: You may see some flickering when painting with some tools. It doesn't"; -// warnUI << "WARNING: affect the quality of the final image, though."; -// warnUI; - -// if (cfg.disableDoubleBuffering() && QOpenGLContext::currentContext()->format().swapBehavior() == QSurfaceFormat::DoubleBuffer) { -// errUI << "CRITICAL: Failed to disable Double Buffering. Lines may look \"bended\" on your image."; -// errUI << "CRITICAL: Your graphics card or driver does not fully support Krita's OpenGL canvas."; -// errUI << "CRITICAL: For an optimal experience, please disable OpenGL"; -// errUI; -// } -// } -// } - - KisConfig cfg; - dbgUI << "OpenGL: Preparing to initialize OpenGL for KisCanvas"; - int glVersion = KisOpenGL::initializeContext(context()); - dbgUI << "OpenGL: Version found" << glVersion; + KisOpenGL::initializeContext(context()); initializeOpenGLFunctions(); - VSyncWorkaround::tryDisableVSync(context()); + KisConfig cfg; d->openGLImageTextures->initGL(context()->functions()); d->openGLImageTextures->generateCheckerTexture(createCheckersImage(cfg.checkSize())); initializeCheckerShader(); initializeDisplayShader(); ptr_glLogicOp = (kis_glLogicOp)(context()->getProcAddress("glLogicOp")); Sync::init(context()); d->canvasInitialized = true; } void KisOpenGLCanvas2::resizeGL(int width, int height) { coordinatesConverter()->setCanvasWidgetSize(QSize(width, height)); paintGL(); } void KisOpenGLCanvas2::paintGL() { if (!OPENGL_SUCCESS) { KisConfig cfg; cfg.writeEntry("canvasState", "OPENGL_PAINT_STARTED"); } KisOpenglCanvasDebugger::instance()->nofityPaintRequested(); QPainter gc(this); gc.beginNativePainting(); renderCanvasGL(); if (d->glSyncObject) { Sync::deleteSync(d->glSyncObject); } d->glSyncObject = Sync::getSync(); gc.endNativePainting(); renderDecorations(&gc); gc.end(); if (!OPENGL_SUCCESS) { KisConfig cfg; cfg.writeEntry("canvasState", "OPENGL_SUCCESS"); OPENGL_SUCCESS = true; } } QOpenGLShaderProgram *KisOpenGLCanvas2::getCursorShader() { if (d->cursorShader == 0) { d->cursorShader = new QOpenGLShaderProgram(); d->cursorShader->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/cursor.vert"); d->cursorShader->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/cursor.frag"); d->cursorShader->bindAttributeLocation("a_vertexPosition", PROGRAM_VERTEX_ATTRIBUTE); if (! d->cursorShader->link()) { dbgUI << "OpenGL error" << glGetError(); qFatal("Failed linking cursor shader"); } Q_ASSERT(d->cursorShader->isLinked()); d->cursorShaderModelViewProjectionUniform = d->cursorShader->uniformLocation("modelViewProjection"); } return d->cursorShader; } void KisOpenGLCanvas2::paintToolOutline(const QPainterPath &path) { QOpenGLShaderProgram *cursorShader = getCursorShader(); cursorShader->bind(); // setup the mvp transformation KisCoordinatesConverter *converter = coordinatesConverter(); QMatrix4x4 projectionMatrix; projectionMatrix.setToIdentity(); projectionMatrix.ortho(0, width(), height(), 0, NEAR_VAL, FAR_VAL); // Set view/projection matrices QMatrix4x4 modelMatrix(converter->flakeToWidgetTransform()); modelMatrix.optimize(); modelMatrix = projectionMatrix * modelMatrix; cursorShader->setUniformValue(d->cursorShaderModelViewProjectionUniform, modelMatrix); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); // XXX: glLogicOp not in ES 2.0 -- it would be better to use another method. // It is defined in 3.1 core profile onward. + // Actually, https://www.opengl.org/sdk/docs/man/html/glLogicOp.xhtml says it's in 2.0 onwards, + // only not in ES, but we don't care about ES, so we could use the function directly. glEnable(GL_COLOR_LOGIC_OP); if (ptr_glLogicOp) { ptr_glLogicOp(GL_XOR); } // setup the array of vertices QVector vertices; QList subPathPolygons = path.toSubpathPolygons(); for (int i=0; ienableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE); cursorShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, vertices.constData()); glDrawArrays(GL_LINE_STRIP, 0, vertices.size()); vertices.clear(); } glDisable(GL_COLOR_LOGIC_OP); cursorShader->release(); } bool KisOpenGLCanvas2::isBusy() const { const bool isBusyStatus = Sync::syncStatus(d->glSyncObject) == Sync::Unsignaled; KisOpenglCanvasDebugger::instance()->nofitySyncStatus(isBusyStatus); return isBusyStatus; } inline void rectToVertices(QVector3D* vertices, const QRectF &rc) { - vertices[0] = QVector3D(rc.left(), rc.bottom(), 0.f); - vertices[1] = QVector3D(rc.left(), rc.top(), 0.f); - vertices[2] = QVector3D(rc.right(), rc.bottom(), 0.f); - vertices[3] = QVector3D(rc.left(), rc.top(), 0.f); - vertices[4] = QVector3D(rc.right(), rc.top(), 0.f); - vertices[5] = QVector3D(rc.right(), rc.bottom(), 0.f); + vertices[0] = QVector3D(rc.left(), rc.bottom(), 0.f); + vertices[1] = QVector3D(rc.left(), rc.top(), 0.f); + vertices[2] = QVector3D(rc.right(), rc.bottom(), 0.f); + vertices[3] = QVector3D(rc.left(), rc.top(), 0.f); + vertices[4] = QVector3D(rc.right(), rc.top(), 0.f); + vertices[5] = QVector3D(rc.right(), rc.bottom(), 0.f); } inline void rectToTexCoords(QVector2D* texCoords, const QRectF &rc) { texCoords[0] = QVector2D(rc.left(), rc.bottom()); texCoords[1] = QVector2D(rc.left(), rc.top()); texCoords[2] = QVector2D(rc.right(), rc.bottom()); texCoords[3] = QVector2D(rc.left(), rc.top()); texCoords[4] = QVector2D(rc.right(), rc.top()); texCoords[5] = QVector2D(rc.right(), rc.bottom()); } void KisOpenGLCanvas2::drawCheckers() { if (!d->checkerShader) { return; } KisCoordinatesConverter *converter = coordinatesConverter(); QTransform textureTransform; QTransform modelTransform; QRectF textureRect; QRectF modelRect; QRectF viewportRect = !d->wrapAroundMode ? - converter->imageRectInViewportPixels() : - converter->widgetToViewport(this->rect()); + converter->imageRectInViewportPixels() : + converter->widgetToViewport(this->rect()); converter->getOpenGLCheckersInfo(viewportRect, &textureTransform, &modelTransform, &textureRect, &modelRect, d->scrollCheckers); textureTransform *= QTransform::fromScale(d->checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE, d->checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE); d->checkerShader->bind(); QMatrix4x4 projectionMatrix; projectionMatrix.setToIdentity(); projectionMatrix.ortho(0, width(), height(), 0, NEAR_VAL, FAR_VAL); // Set view/projection matrices QMatrix4x4 modelMatrix(modelTransform); modelMatrix.optimize(); modelMatrix = projectionMatrix * modelMatrix; d->checkerShader->setUniformValue(d->checkerUniformLocationModelViewProjection, modelMatrix); QMatrix4x4 textureMatrix(textureTransform); d->checkerShader->setUniformValue(d->checkerUniformLocationTextureMatrix, textureMatrix); //Setup the geometry for rendering rectToVertices(d->vertices, modelRect); d->checkerShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE); d->checkerShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, d->vertices); rectToTexCoords(d->texCoords, textureRect); d->checkerShader->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE); d->checkerShader->setAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE, d->texCoords); - // render checkers + // render checkers glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, d->openGLImageTextures->checkerTexture()); glDrawArrays(GL_TRIANGLES, 0, 6); glBindTexture(GL_TEXTURE_2D, 0); d->checkerShader->release(); } void KisOpenGLCanvas2::drawImage() { if (!d->displayShader) { return; } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); KisCoordinatesConverter *converter = coordinatesConverter(); d->displayShader->bind(); QMatrix4x4 projectionMatrix; projectionMatrix.setToIdentity(); projectionMatrix.ortho(0, width(), height(), 0, NEAR_VAL, FAR_VAL); // Set view/projection matrices QMatrix4x4 modelMatrix(coordinatesConverter()->imageToWidgetTransform()); modelMatrix.optimize(); modelMatrix = projectionMatrix * modelMatrix; d->displayShader->setUniformValue(d->displayUniformLocationModelViewProjection, modelMatrix); QMatrix4x4 textureMatrix; textureMatrix.setToIdentity(); d->displayShader->setUniformValue(d->displayUniformLocationTextureMatrix, textureMatrix); QRectF widgetRect(0,0, width(), height()); QRectF widgetRectInImagePixels = converter->documentToImage(converter->widgetToDocument(widgetRect)); qreal scaleX, scaleY; converter->imageScale(&scaleX, &scaleY); d->displayShader->setUniformValue(d->displayUniformLocationViewPortScale, (GLfloat) scaleX); d->displayShader->setUniformValue(d->displayUniformLocationTexelSize, (GLfloat) d->openGLImageTextures->texelSize()); QRect ir = d->openGLImageTextures->storedImageBounds(); QRect wr = widgetRectInImagePixels.toAlignedRect(); if (!d->wrapAroundMode) { // if we don't want to paint wrapping images, just limit the // processing area, and the code will handle all the rest wr &= ir; } int firstColumn = d->xToColWithWrapCompensation(wr.left(), ir); int lastColumn = d->xToColWithWrapCompensation(wr.right(), ir); int firstRow = d->yToRowWithWrapCompensation(wr.top(), ir); int lastRow = d->yToRowWithWrapCompensation(wr.bottom(), ir); int minColumn = d->openGLImageTextures->xToCol(ir.left()); int maxColumn = d->openGLImageTextures->xToCol(ir.right()); int minRow = d->openGLImageTextures->yToRow(ir.top()); int maxRow = d->openGLImageTextures->yToRow(ir.bottom()); int imageColumns = maxColumn - minColumn + 1; int imageRows = maxRow - minRow + 1; for (int col = firstColumn; col <= lastColumn; col++) { for (int row = firstRow; row <= lastRow; row++) { int effectiveCol = col; int effectiveRow = row; QPointF tileWrappingTranslation; if (effectiveCol > maxColumn || effectiveCol < minColumn) { int translationStep = floor(qreal(col) / imageColumns); int originCol = translationStep * imageColumns; effectiveCol = col - originCol; tileWrappingTranslation.rx() = translationStep * ir.width(); } if (effectiveRow > maxRow || effectiveRow < minRow) { int translationStep = floor(qreal(row) / imageRows); int originRow = translationStep * imageRows; effectiveRow = row - originRow; tileWrappingTranslation.ry() = translationStep * ir.height(); } KisTextureTile *tile = d->openGLImageTextures->getTextureTileCR(effectiveCol, effectiveRow); if (!tile) { warnUI << "OpenGL: Trying to paint texture tile but it has not been created yet."; continue; } /* * We create a float rect here to workaround Qt's * "history reasons" in calculation of right() * and bottom() coordinates of integer rects. */ QRectF textureRect(tile->tileRectInTexturePixels()); QRectF modelRect(tile->tileRectInImagePixels().translated(tileWrappingTranslation.x(), tileWrappingTranslation.y())); //Setup the geometry for rendering rectToVertices(d->vertices, modelRect); d->displayShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE); d->displayShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, d->vertices); rectToTexCoords(d->texCoords, textureRect); d->displayShader->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE); d->displayShader->setAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE, d->texCoords); if (d->displayFilter) { glActiveTexture(GL_TEXTURE0 + 1); glBindTexture(GL_TEXTURE_3D, d->displayFilter->lutTexture()); d->displayShader->setUniformValue(d->displayUniformLocationTexture1, 1); } int currentLodPlane = tile->currentLodPlane(); if (d->displayUniformLocationFixedLodLevel >= 0) { d->displayShader->setUniformValue(d->displayUniformLocationFixedLodLevel, (GLfloat) currentLodPlane); } glActiveTexture(GL_TEXTURE0); tile->bindToActiveTexture(); if (currentLodPlane) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); } else if (SCALE_MORE_OR_EQUAL_TO(scaleX, scaleY, 2.0)) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); switch(d->filterMode) { case KisOpenGL::NearestFilterMode: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); break; case KisOpenGL::BilinearFilterMode: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); break; case KisOpenGL::TrilinearFilterMode: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); break; case KisOpenGL::HighQualityFiltering: if (SCALE_LESS_THAN(scaleX, scaleY, 0.5)) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } break; } } glDrawArrays(GL_TRIANGLES, 0, 6); } } glBindTexture(GL_TEXTURE_2D, 0); d->displayShader->release(); } void KisOpenGLCanvas2::reportShaderLinkFailedAndExit(bool result, const QString &context, const QString &log) { KisConfig cfg; if (cfg.useVerboseOpenGLDebugOutput()) { dbgUI << "GL-log:" << context << log; } if (result) return; QMessageBox::critical(this, i18nc("@title:window", "Krita"), QString(i18n("Krita could not initialize the OpenGL canvas:\n\n%1\n\n%2\n\n Krita will disable OpenGL and close now.")).arg(context).arg(log), QMessageBox::Close); cfg.setUseOpenGL(false); cfg.setCanvasState("OPENGL_FAILED"); } void KisOpenGLCanvas2::initializeCheckerShader() { if (d->canvasInitialized) return; delete d->checkerShader; d->checkerShader = new QOpenGLShaderProgram(); + QString vertexShaderName; + QString fragmentShaderName; bool result; - result = d->checkerShader->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/matrix_transform.vert"); + if (KisOpenGL::supportsGLSL13()) { + vertexShaderName = ":/matrix_transform.vert"; + fragmentShaderName = ":/simple_texture.frag"; + } + else { + vertexShaderName = ":/matrix_transform_legacy.vert"; + fragmentShaderName = ":/simple_texture_legacy.frag"; + } + + result = d->checkerShader->addShaderFromSourceFile(QOpenGLShader::Vertex, vertexShaderName); reportShaderLinkFailedAndExit(result, "Checker vertex shader", d->checkerShader->log()); - result = d->checkerShader->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/simple_texture.frag"); + result = d->checkerShader->addShaderFromSourceFile(QOpenGLShader::Fragment, fragmentShaderName); reportShaderLinkFailedAndExit(result, "Checker fragment shader", d->checkerShader->log()); + d->checkerShader->bindAttributeLocation("a_vertexPosition", PROGRAM_VERTEX_ATTRIBUTE); d->checkerShader->bindAttributeLocation("a_textureCoordinate", PROGRAM_TEXCOORD_ATTRIBUTE); result = d->checkerShader->link(); reportShaderLinkFailedAndExit(result, "Checker shader (link)", d->checkerShader->log()); Q_ASSERT(d->checkerShader->isLinked()); d->checkerUniformLocationModelViewProjection = d->checkerShader->uniformLocation("modelViewProjection"); d->checkerUniformLocationTextureMatrix = d->checkerShader->uniformLocation("textureMatrix"); } QByteArray KisOpenGLCanvas2::buildFragmentShader() { QByteArray shaderText; bool haveDisplayFilter = d->displayFilter && !d->displayFilter->program().isEmpty(); bool useHiQualityFiltering = d->filterMode == KisOpenGL::HighQualityFiltering; + bool haveGLSL13 = KisOpenGL::supportsGLSL13(); - QString filename = "highq_downscale.frag"; - // FIXME is this necessary? - shaderText.append("#version 130\n"); + QString filename; + + if (haveGLSL13) { + filename = "highq_downscale.frag"; + shaderText.append("#version 130\n"); + } else { + filename = "simple_texture_legacy.frag"; + } if (haveDisplayFilter) { shaderText.append("#define USE_OCIO\n"); shaderText.append(d->displayFilter->program().toLatin1()); } - if (useHiQualityFiltering) { + if (haveGLSL13 && useHiQualityFiltering) { shaderText.append("#define HIGHQ_SCALING\n"); } - shaderText.append("#define DIRECT_LOD_FETCH\n"); + if (haveGLSL13) { + shaderText.append("#define DIRECT_LOD_FETCH\n"); + } { QFile prefaceFile(":/" + filename); prefaceFile.open(QIODevice::ReadOnly); shaderText.append(prefaceFile.readAll()); } return shaderText; } void KisOpenGLCanvas2::initializeDisplayShader() { if (d->canvasInitialized) return; delete d->displayShader; d->displayShader = new QOpenGLShaderProgram(); bool result = d->displayShader->addShaderFromSourceCode(QOpenGLShader::Fragment, buildFragmentShader()); reportShaderLinkFailedAndExit(result, "Display fragment shader", d->displayShader->log()); - result = d->displayShader->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/matrix_transform.vert"); + if (KisOpenGL::supportsGLSL13()) { + result = d->displayShader->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/matrix_transform.vert"); + } + else { + result = d->displayShader->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/matrix_transform_legacy.vert"); + } reportShaderLinkFailedAndExit(result, "Display vertex shader", d->displayShader->log()); d->displayShader->bindAttributeLocation("a_vertexPosition", PROGRAM_VERTEX_ATTRIBUTE); d->displayShader->bindAttributeLocation("a_textureCoordinate", PROGRAM_TEXCOORD_ATTRIBUTE); result = d->displayShader->link(); reportShaderLinkFailedAndExit(result, "Display shader (link)", d->displayShader->log()); Q_ASSERT(d->displayShader->isLinked()); d->displayUniformLocationModelViewProjection = d->displayShader->uniformLocation("modelViewProjection"); d->displayUniformLocationTextureMatrix = d->displayShader->uniformLocation("textureMatrix"); d->displayUniformLocationTexture0 = d->displayShader->uniformLocation("texture0"); // ocio d->displayUniformLocationTexture1 = d->displayShader->uniformLocation("texture1"); // highq || lod d->displayUniformLocationViewPortScale = d->displayShader->uniformLocation("viewportScale"); // highq d->displayUniformLocationTexelSize = d->displayShader->uniformLocation("texelSize"); - // TODO: The trilinear filtering mode is having issues when that is set in the application. It sometimes causes Krita to crash // I cannot tell where that is at in here... Scott P (11/8/2015) // lod - d->displayUniformLocationFixedLodLevel = d->displayShader->uniformLocation("fixedLodLevel"); + d->displayUniformLocationFixedLodLevel = + KisOpenGL::supportsGLSL13() ? + d->displayShader->uniformLocation("fixedLodLevel") : -1; } void KisOpenGLCanvas2::slotConfigChanged() { KisConfig cfg; d->checkSizeScale = KisOpenGLImageTextures::BACKGROUND_TEXTURE_CHECK_SIZE / static_cast(cfg.checkSize()); d->scrollCheckers = cfg.scrollCheckers(); d->openGLImageTextures->generateCheckerTexture(createCheckersImage(cfg.checkSize())); d->openGLImageTextures->updateConfig(cfg.useOpenGLTextureBuffer(), cfg.numMipmapLevels()); d->filterMode = (KisOpenGL::FilterMode) cfg.openGLFilteringMode(); notifyConfigChanged(); } QVariant KisOpenGLCanvas2::inputMethodQuery(Qt::InputMethodQuery query) const { return processInputMethodQuery(query); } void KisOpenGLCanvas2::inputMethodEvent(QInputMethodEvent *event) { processInputMethodEvent(event); } void KisOpenGLCanvas2::renderCanvasGL() { // Draw the border (that is, clear the whole widget to the border color) QColor widgetBackgroundColor = borderColor(); glClearColor(widgetBackgroundColor.redF(), widgetBackgroundColor.greenF(), widgetBackgroundColor.blueF(), 1.0); glClear(GL_COLOR_BUFFER_BIT); if (d->displayFilter) { d->displayFilter->updateShader(); } drawCheckers(); drawImage(); } void KisOpenGLCanvas2::renderDecorations(QPainter *painter) { QRect boundingRect = coordinatesConverter()->imageRectInWidgetPixels().toAlignedRect(); drawDecorations(*painter, boundingRect); } void KisOpenGLCanvas2::setDisplayProfile(KisDisplayColorConverter *colorConverter) { d->openGLImageTextures->setMonitorProfile(colorConverter->monitorProfile(), colorConverter->renderingIntent(), colorConverter->conversionFlags()); } void KisOpenGLCanvas2::channelSelectionChanged(const QBitArray &channelFlags) { d->openGLImageTextures->setChannelFlags(channelFlags); } void KisOpenGLCanvas2::finishResizingImage(qint32 w, qint32 h) { if (d->canvasInitialized) { d->openGLImageTextures->slotImageSizeChanged(w, h); } } KisUpdateInfoSP KisOpenGLCanvas2::startUpdateCanvasProjection(const QRect & rc, const QBitArray &channelFlags) { d->openGLImageTextures->setChannelFlags(channelFlags); return d->openGLImageTextures->updateCache(rc); } QRect KisOpenGLCanvas2::updateCanvasProjection(KisUpdateInfoSP info) { // See KisQPainterCanvas::updateCanvasProjection for more info bool isOpenGLUpdateInfo = dynamic_cast(info.data()); if (isOpenGLUpdateInfo) { d->openGLImageTextures->recalculateCache(info); } return QRect(); // FIXME: Implement dirty rect for OpenGL } bool KisOpenGLCanvas2::callFocusNextPrevChild(bool next) { return focusNextPrevChild(next); } KisOpenGLImageTexturesSP KisOpenGLCanvas2::openGLImageTextures() const { return d->openGLImageTextures; } diff --git a/libs/ui/opengl/kis_opengl_canvas2_p.h b/libs/ui/opengl/kis_opengl_canvas2_p.h index 4b76764dce..62c188a0eb 100644 --- a/libs/ui/opengl/kis_opengl_canvas2_p.h +++ b/libs/ui/opengl/kis_opengl_canvas2_p.h @@ -1,249 +1,122 @@ /* * Copyright (C) Boudewijn Rempt , (C) 2006 * * This program is free software; you can redistribute 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_OPENGL_CANVAS_2_P_H #define KIS_OPENGL_CANVAS_2_P_H #include /** * This is a workaround for a very slow updates in OpenGL canvas (~6ms). * The delay happens because of VSync in the swapBuffers() call. At first * we try to disable VSync. If it fails we just disable Double Buffer * completely. * * This file is effectively a bit of copy-paste from qgl_x11.cpp */ - -#if defined Q_OS_LINUX - -#include -#include -#include -#include -#include -#include - -#ifndef GL_NUM_EXTENSIONS -#define GL_NUM_EXTENSIONS 0x821D -#endif - -QString gl_library_name() { -#if defined (QT_OPENGL_ES_2) - return QLatin1String("GLESv2"); -#else - return QLatin1String("GL"); -#endif -} - -namespace VSyncWorkaround { - - bool tryDisableVSync(QOpenGLContext* ctx) { - bool result = false; - - bool triedDisable = false; - Display *dpy = QX11Info::display(); - dbgUI << "OpenGL architecture is" << gl_library_name(); - - if (ctx->hasExtension("GLX_EXT_swap_control")) { - dbgUI << "Swap control extension found."; - typedef WId (*k_glXGetCurrentDrawable)(void); - typedef void (*kis_glXSwapIntervalEXT)(Display*, WId, int); - k_glXGetCurrentDrawable kis_glXGetCurrentDrawable = (k_glXGetCurrentDrawable)ctx->getProcAddress("glXGetCurrentDrawable"); - kis_glXSwapIntervalEXT glXSwapIntervalEXT = (kis_glXSwapIntervalEXT)ctx->getProcAddress("glXSwapIntervalEXT"); - WId wid = kis_glXGetCurrentDrawable(); - - if (glXSwapIntervalEXT) { - glXSwapIntervalEXT(dpy, wid, 0); - triedDisable = true; - - unsigned int swap = 1; - -#ifdef GLX_SWAP_INTERVAL_EXT - typedef int (*k_glXQueryDrawable)(Display *, WId, int, unsigned int *); - k_glXQueryDrawable kis_glXQueryDrawable = (k_glXQueryDrawable)ctx->getProcAddress("glXQueryDrawable"); - kis_glXQueryDrawable(dpy, wid, GLX_SWAP_INTERVAL_EXT, &swap); -#endif - - result = !swap; - } else { - dbgUI << "Couldn't load glXSwapIntervalEXT extension function"; - } - } else if (ctx->hasExtension("GLX_MESA_swap_control")) { - dbgUI << "MESA swap control extension found."; - typedef int (*kis_glXSwapIntervalMESA)(unsigned int); - typedef int (*kis_glXGetSwapIntervalMESA)(void); - - kis_glXSwapIntervalMESA glXSwapIntervalMESA = (kis_glXSwapIntervalMESA)ctx->getProcAddress("glXSwapIntervalMESA"); - kis_glXGetSwapIntervalMESA glXGetSwapIntervalMESA = (kis_glXGetSwapIntervalMESA)ctx->getProcAddress("glXGetSwapIntervalMESA"); - - if (glXSwapIntervalMESA) { - int retval = glXSwapIntervalMESA(0); - triedDisable = true; - - int swap = 1; - - if (glXGetSwapIntervalMESA) { - swap = glXGetSwapIntervalMESA(); - } else { - dbgUI << "Couldn't load glXGetSwapIntervalMESA extension function"; - } - - result = !retval && !swap; - } else { - dbgUI << "Couldn't load glXSwapIntervalMESA extension function"; - } - } else { - dbgUI << "There is neither GLX_EXT_swap_control or GLX_MESA_swap_control extension supported"; - } - - if (triedDisable && !result) { - errUI; - errUI << "CRITICAL: Your video driver forbids disabling VSync!"; - errUI << "CRITICAL: Try toggling some VSync- or VBlank-related options in your driver configuration dialog."; - errUI << "CRITICAL: NVIDIA users can do:"; - errUI << "CRITICAL: sudo nvidia-settings > (tab) OpenGL settings > Sync to VBlank ( unchecked )"; - errUI; - } - return result; - } -} - -#elif defined Q_OS_WIN -namespace VSyncWorkaround { - bool tryDisableVSync(QOpenGLContext *ctx) { - bool retval = false; - - if (ctx->hasExtension("WGL_EXT_swap_control")) { - typedef void (*wglSwapIntervalEXT)(int); - typedef int (*wglGetSwapIntervalEXT)(void); - ((wglSwapIntervalEXT)ctx->getProcAddress("wglSwapIntervalEXT"))(0); - int interval = ((wglGetSwapIntervalEXT)ctx->getProcAddress("wglGetSwapIntervalEXT"))(); - - if (interval) { - warnUI << "Failed to disable VSync with WGL_EXT_swap_control"; - } - - retval = !interval; - } else { - warnUI << "WGL_EXT_swap_control extension is not available. Found extensions" << ctx->extensions(); - } - return retval; - } -} - -#else // !defined Q_OS_LINUX && !defined Q_OS_WIN - -namespace VSyncWorkaround { - bool tryDisableVSync(QOpenGLContext *) { - return false; - } -} -#endif // defined Q_OS_LINUX - namespace Sync { //For checking sync status enum SyncStatus { Signaled, Unsignaled }; #ifndef GL_SYNC_GPU_COMMANDS_COMPLETE #define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 #endif #ifndef GL_UNSIGNALED #define GL_UNSIGNALED 0x9118 #endif #ifndef GL_SIGNALED #define GL_SIGNALED 0x9119 #endif #ifndef GL_SYNC_STATUS #define GL_SYNC_STATUS 0x9114 #endif //Function pointers for glFenceSync and glGetSynciv typedef GLsync (*kis_glFenceSync)(GLenum, GLbitfield); static kis_glFenceSync k_glFenceSync = 0; typedef void (*kis_glGetSynciv)(GLsync, GLenum, GLsizei, GLsizei*, GLint*); static kis_glGetSynciv k_glGetSynciv = 0; typedef void (*kis_glDeleteSync)(GLsync); static kis_glDeleteSync k_glDeleteSync = 0; typedef GLenum (*kis_glClientWaitSync)(GLsync,GLbitfield,GLuint64); static kis_glClientWaitSync k_glClientWaitSync = 0; //Initialise the function pointers for glFenceSync and glGetSynciv //Note: Assumes a current OpenGL context. void init(QOpenGLContext* ctx) { #if defined Q_OS_WIN if (KisOpenGL::supportsFenceSync()) { #ifdef ENV64BIT k_glFenceSync = (kis_glFenceSync)ctx->getProcAddress("glFenceSync"); k_glGetSynciv = (kis_glGetSynciv)ctx->getProcAddress("glGetSynciv"); k_glDeleteSync = (kis_glDeleteSync)ctx->getProcAddress("glDeleteSync"); #else k_glFenceSync = (kis_glFenceSync)ctx->getProcAddress("glFenceSyncARB"); k_glGetSynciv = (kis_glGetSynciv)ctx->getProcAddress("glGetSyncivARB"); k_glDeleteSync = (kis_glDeleteSync)ctx->getProcAddress("glDeleteSyncARB"); #endif k_glClientWaitSync = (kis_glClientWaitSync)ctx->getProcAddress("glClientWaitSync"); } #elif defined Q_OS_LINUX if (KisOpenGL::supportsFenceSync()) { k_glFenceSync = (kis_glFenceSync)ctx->getProcAddress("glFenceSync"); k_glGetSynciv = (kis_glGetSynciv)ctx->getProcAddress("glGetSynciv"); k_glDeleteSync = (kis_glDeleteSync)ctx->getProcAddress("glDeleteSync"); k_glClientWaitSync = (kis_glClientWaitSync)ctx->getProcAddress("glClientWaitSync"); } #endif if (k_glFenceSync == 0 || k_glGetSynciv == 0 || k_glDeleteSync == 0 || k_glClientWaitSync == 0) { warnUI << "Could not find sync functions, disabling sync notification."; } } //Get a fence sync object from OpenGL GLsync getSync() { if(k_glFenceSync) { GLsync sync = k_glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); if (KisOpenGL::needsFenceWorkaround()) { k_glClientWaitSync(sync, 0, 1); } return sync; } return 0; } //Check the status of a sync object SyncStatus syncStatus(GLsync syncObject) { if(syncObject && k_glGetSynciv) { GLint status = -1; k_glGetSynciv(syncObject, GL_SYNC_STATUS, 1, 0, &status); return status == GL_SIGNALED ? Sync::Signaled : Sync::Unsignaled; } return Sync::Signaled; } void deleteSync(GLsync syncObject) { if(syncObject && k_glDeleteSync) { k_glDeleteSync(syncObject); } } } #endif // KIS_OPENGL_CANVAS_2_P_H diff --git a/libs/ui/opengl/kis_opengl_image_textures.cpp b/libs/ui/opengl/kis_opengl_image_textures.cpp index b442720597..835d41ee84 100644 --- a/libs/ui/opengl/kis_opengl_image_textures.cpp +++ b/libs/ui/opengl/kis_opengl_image_textures.cpp @@ -1,608 +1,613 @@ /* * Copyright (c) 2005-2007 Adrian Page * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "opengl/kis_opengl_image_textures.h" #include #include #include #include #include #include #include #include #include "kis_image.h" #include "kis_config.h" #include "KisPart.h" #ifdef HAVE_OPENEXR #include #endif #ifndef GL_CLAMP_TO_EDGE #define GL_CLAMP_TO_EDGE 0x812F #endif #ifndef GL_BGRA #define GL_BGRA 0x80E1 #endif KisOpenGLImageTextures::ImageTexturesMap KisOpenGLImageTextures::imageTexturesMap; KisOpenGLImageTextures::KisOpenGLImageTextures() : m_image(0) , m_monitorProfile(0) , m_tilesDestinationColorSpace(0) , m_internalColorManagementActive(true) , m_checkerTexture(0) , m_glFuncs(0) , m_allChannelsSelected(true) , m_useOcio(false) , m_initialized(false) { KisConfig cfg; m_renderingIntent = (KoColorConversionTransformation::Intent)cfg.monitorRenderIntent(); m_conversionFlags = KoColorConversionTransformation::HighQuality; if (cfg.useBlackPointCompensation()) m_conversionFlags |= KoColorConversionTransformation::BlackpointCompensation; if (!cfg.allowLCMSOptimization()) m_conversionFlags |= KoColorConversionTransformation::NoOptimization; m_useOcio = cfg.useOcio(); } KisOpenGLImageTextures::KisOpenGLImageTextures(KisImageWSP image, const KoColorProfile *monitorProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) : m_image(image) , m_monitorProfile(monitorProfile) , m_renderingIntent(renderingIntent) , m_conversionFlags(conversionFlags) , m_tilesDestinationColorSpace(0) , m_internalColorManagementActive(true) , m_checkerTexture(0) , m_glFuncs(0) , m_allChannelsSelected(true) , m_useOcio(false) , m_initialized(false) { Q_ASSERT(renderingIntent < 4); } -void KisOpenGLImageTextures::initGL(QOpenGLFunctions *f) { +void KisOpenGLImageTextures::initGL(QOpenGLFunctions *f) +{ if (f) { m_glFuncs = f; } else { errUI << "Tried to create OpenGLImageTextures with uninitialized QOpenGLFunctions"; } getTextureSize(&m_texturesInfo); m_glFuncs->glGenTextures(1, &m_checkerTexture); createImageTextureTiles(); KisOpenGLUpdateInfoSP info = updateCache(m_image->bounds()); recalculateCache(info); } KisOpenGLImageTextures::~KisOpenGLImageTextures() { ImageTexturesMap::iterator it = imageTexturesMap.find(m_image); if (it != imageTexturesMap.end()) { KisOpenGLImageTextures *textures = it.value(); if (textures == this) { dbgUI << "Removing shared image context from map"; imageTexturesMap.erase(it); } } destroyImageTextureTiles(); m_glFuncs->glDeleteTextures(1, &m_checkerTexture); } KisImageSP KisOpenGLImageTextures::image() const { return m_image; } bool KisOpenGLImageTextures::imageCanShareTextures() { KisConfig cfg; if (cfg.useOcio()) return false; if (KisPart::instance()->mainwindowCount() == 1) return true; if (qApp->desktop()->screenCount() == 1) return true; for (int i = 1; i < qApp->desktop()->screenCount(); i++) { if (cfg.displayProfile(i) != cfg.displayProfile(i - 1)) { return false; } } return true; } KisOpenGLImageTexturesSP KisOpenGLImageTextures::getImageTextures(KisImageWSP image, const KoColorProfile *monitorProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { // Disabled until we figure out why we're deleting the shared textures on closing the second view on a single image if (false && imageCanShareTextures()) { ImageTexturesMap::iterator it = imageTexturesMap.find(image); if (it != imageTexturesMap.end()) { KisOpenGLImageTexturesSP textures = it.value(); textures->setMonitorProfile(monitorProfile, renderingIntent, conversionFlags); return textures; } else { KisOpenGLImageTextures *imageTextures = new KisOpenGLImageTextures(image, monitorProfile, renderingIntent, conversionFlags); imageTexturesMap[image] = imageTextures; dbgUI << "Added shareable textures to map"; return imageTextures; } } else { return new KisOpenGLImageTextures(image, monitorProfile, renderingIntent, conversionFlags); } } QRect KisOpenGLImageTextures::calculateTileRect(int col, int row) const { return m_image->bounds() & QRect(col * m_texturesInfo.effectiveWidth, row * m_texturesInfo.effectiveHeight, m_texturesInfo.effectiveWidth, m_texturesInfo.effectiveHeight); } void KisOpenGLImageTextures::createImageTextureTiles() { - KisConfig cfg; destroyImageTextureTiles(); updateTextureFormat(); + if (!m_tilesDestinationColorSpace) { + qDebug() << "No destination colorspace!!!!"; + return; + } + + m_storedImageBounds = m_image->bounds(); const int lastCol = xToCol(m_image->width()); const int lastRow = yToRow(m_image->height()); m_numCols = lastCol + 1; // Default color is transparent black const int pixelSize = m_tilesDestinationColorSpace->pixelSize(); QByteArray emptyTileData((m_texturesInfo.width) * (m_texturesInfo.height) * pixelSize, 0); KisConfig config; KisOpenGL::FilterMode mode = (KisOpenGL::FilterMode)config.openGLFilteringMode(); QOpenGLContext *ctx = QOpenGLContext::currentContext(); if (ctx) { QOpenGLFunctions *f = ctx->functions(); m_initialized = true; dbgUI << "OpenGL: creating texture tiles of size" << m_texturesInfo.height << "x" << m_texturesInfo.width; m_textureTiles.reserve((lastRow+1)*m_numCols); for (int row = 0; row <= lastRow; row++) { for (int col = 0; col <= lastCol; col++) { QRect tileRect = calculateTileRect(col, row); KisTextureTile *tile = new KisTextureTile(tileRect, - &m_texturesInfo, - emptyTileData, - mode, - cfg.useOpenGLTextureBuffer(), - cfg.numMipmapLevels(), - f); + &m_texturesInfo, + emptyTileData, + mode, + config.useOpenGLTextureBuffer(), + config.numMipmapLevels(), + f); m_textureTiles.append(tile); } } } else { dbgUI << "Tried to init texture tiles without a current OpenGL Context."; } } void KisOpenGLImageTextures::destroyImageTextureTiles() { if (m_textureTiles.isEmpty()) return; Q_FOREACH (KisTextureTile *tile, m_textureTiles) { delete tile; } m_textureTiles.clear(); m_storedImageBounds = QRect(); } KisOpenGLUpdateInfoSP KisOpenGLImageTextures::updateCache(const QRect& rect) { return updateCacheImpl(rect, true); } KisOpenGLUpdateInfoSP KisOpenGLImageTextures::updateCacheNoConversion(const QRect& rect) { return updateCacheImpl(rect, false); } KisOpenGLUpdateInfoSP KisOpenGLImageTextures::updateCacheImpl(const QRect& rect, bool convertColorSpace) { const KoColorSpace *dstCS = m_tilesDestinationColorSpace; ConversionOptions options; if (convertColorSpace) { options = ConversionOptions(dstCS, m_renderingIntent, m_conversionFlags); } KisOpenGLUpdateInfoSP info = new KisOpenGLUpdateInfo(options); QRect updateRect = rect & m_image->bounds(); if (updateRect.isEmpty() || !(m_initialized)) return info; /** * Why the rect is artificial? That's easy! * It does not represent any real piece of the image. It is * intentionally stretched to get through the overlappping * stripes of neutrality and poke neighbouring tiles. * Thanks to the rect we get the coordinates of all the tiles * involved into update process */ QRect artificialRect = stretchRect(updateRect, m_texturesInfo.border); artificialRect &= m_image->bounds(); int firstColumn = xToCol(artificialRect.left()); int lastColumn = xToCol(artificialRect.right()); int firstRow = yToRow(artificialRect.top()); int lastRow = yToRow(artificialRect.bottom()); QBitArray channelFlags; // empty by default if (m_channelFlags.size() != m_image->projection()->colorSpace()->channels().size()) { setChannelFlags(QBitArray()); } if (!m_useOcio) { // Ocio does its own channel flipping if (!m_allChannelsSelected) { // and we do it only if necessary channelFlags = m_channelFlags; } } qint32 numItems = (lastColumn - firstColumn + 1) * (lastRow - firstRow + 1); info->tileList.reserve(numItems); const QRect bounds = m_image->bounds(); const int levelOfDetail = m_image->currentLevelOfDetail(); QRect alignedUpdateRect = updateRect; QRect alignedBounds = bounds; if (levelOfDetail) { alignedUpdateRect = KisLodTransform::alignedRect(alignedUpdateRect, levelOfDetail); alignedBounds = KisLodTransform::alignedRect(alignedBounds, levelOfDetail); } for (int col = firstColumn; col <= lastColumn; col++) { for (int row = firstRow; row <= lastRow; row++) { const QRect tileRect = calculateTileRect(col, row); const QRect tileTextureRect = stretchRect(tileRect, m_texturesInfo.border); QRect alignedTileTextureRect = levelOfDetail ? - KisLodTransform::alignedRect(tileTextureRect, levelOfDetail) : - tileTextureRect; + KisLodTransform::alignedRect(tileTextureRect, levelOfDetail) : + tileTextureRect; KisTextureTileUpdateInfoSP tileInfo( - new KisTextureTileUpdateInfo(col, row, - alignedTileTextureRect, - alignedUpdateRect, - alignedBounds, - levelOfDetail)); + new KisTextureTileUpdateInfo(col, row, + alignedTileTextureRect, + alignedUpdateRect, + alignedBounds, + levelOfDetail)); // Don't update empty tiles if (tileInfo->valid()) { tileInfo->retrieveData(m_image, channelFlags, m_onlyOneChannelSelected, m_selectedChannelIndex); if (convertColorSpace) { tileInfo->convertTo(dstCS, m_renderingIntent, m_conversionFlags); } info->tileList.append(tileInfo); } else { dbgUI << "Trying to create an empty tileinfo record" << col << row << tileTextureRect << updateRect << m_image->bounds(); } } } info->assignDirtyImageRect(rect); info->assignLevelOfDetail(levelOfDetail); return info; } void KisOpenGLImageTextures::recalculateCache(KisUpdateInfoSP info) { if (!m_initialized) { dbgUI << "OpenGL: Tried to edit image texture cache before it was initialized."; return; } KisOpenGLUpdateInfoSP glInfo = dynamic_cast(info.data()); if(!glInfo) return; KisTextureTileUpdateInfoSP tileInfo; Q_FOREACH (tileInfo, glInfo->tileList) { KisTextureTile *tile = getTextureTileCR(tileInfo->tileCol(), tileInfo->tileRow()); KIS_ASSERT_RECOVER_RETURN(tile); tile->update(*tileInfo); } } void KisOpenGLImageTextures::generateCheckerTexture(const QImage &checkImage) { QOpenGLContext *ctx = QOpenGLContext::currentContext(); if (ctx) { QOpenGLFunctions *f = ctx->functions(); dbgUI << "Attaching checker texture" << checkerTexture(); f->glBindTexture(GL_TEXTURE_2D, checkerTexture()); f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); f->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); QImage img = checkImage; if (checkImage.width() != BACKGROUND_TEXTURE_SIZE || checkImage.height() != BACKGROUND_TEXTURE_SIZE) { img = checkImage.scaled(BACKGROUND_TEXTURE_SIZE, BACKGROUND_TEXTURE_SIZE); } f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, BACKGROUND_TEXTURE_SIZE, BACKGROUND_TEXTURE_SIZE, - 0, GL_BGRA, GL_UNSIGNED_BYTE, img.constBits()); + 0, GL_BGRA, GL_UNSIGNED_BYTE, img.constBits()); } else { dbgUI << "OpenGL: Tried to generate checker texture before OpenGL was initialized."; } } GLuint KisOpenGLImageTextures::checkerTexture() { if (m_glFuncs) { if (m_checkerTexture == 0) { m_glFuncs->glGenTextures(1, &m_checkerTexture); } return m_checkerTexture; } else { dbgUI << "Tried to access checker texture before OpenGL was initialized"; return 0; } } void KisOpenGLImageTextures::updateConfig(bool useBuffer, int NumMipmapLevels) { if(m_textureTiles.isEmpty()) return; Q_FOREACH (KisTextureTile *tile, m_textureTiles) { tile->setUseBuffer(useBuffer); tile->setNumMipmapLevels(NumMipmapLevels); } } void KisOpenGLImageTextures::slotImageSizeChanged(qint32 /*w*/, qint32 /*h*/) { createImageTextureTiles(); } void KisOpenGLImageTextures::setMonitorProfile(const KoColorProfile *monitorProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { //dbgUI << "Setting monitor profile to" << monitorProfile->name() << renderingIntent << conversionFlags; m_monitorProfile = monitorProfile; m_renderingIntent = renderingIntent; m_conversionFlags = conversionFlags; createImageTextureTiles(); } void KisOpenGLImageTextures::setChannelFlags(const QBitArray &channelFlags) { m_channelFlags = channelFlags; int selectedChannels = 0; const KoColorSpace *projectionCs = m_image->projection()->colorSpace(); QList channelInfo = projectionCs->channels(); if (m_channelFlags.size() != channelInfo.size()) { m_channelFlags = QBitArray(); } for (int i = 0; i < m_channelFlags.size(); ++i) { if (m_channelFlags.testBit(i) && channelInfo[i]->channelType() == KoChannelInfo::COLOR) { selectedChannels++; m_selectedChannelIndex = i; } } m_allChannelsSelected = (selectedChannels == m_channelFlags.size()); m_onlyOneChannelSelected = (selectedChannels == 1); } void KisOpenGLImageTextures::getTextureSize(KisGLTexturesInfo *texturesInfo) { KisConfig cfg; const GLint preferredTextureSize = cfg.openGLTextureSize(); GLint maxTextureSize; if (m_glFuncs) { m_glFuncs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); } else { dbgUI << "OpenGL: Tried to read texture size before OpenGL was initialized."; maxTextureSize = GL_MAX_TEXTURE_SIZE; } texturesInfo->width = qMin(preferredTextureSize, maxTextureSize); texturesInfo->height = qMin(preferredTextureSize, maxTextureSize); texturesInfo->border = cfg.textureOverlapBorder(); texturesInfo->effectiveWidth = texturesInfo->width - 2 * texturesInfo->border; texturesInfo->effectiveHeight = texturesInfo->height - 2 * texturesInfo->border; } bool KisOpenGLImageTextures::internalColorManagementActive() const { return m_internalColorManagementActive; } bool KisOpenGLImageTextures::setInternalColorManagementActive(bool value) { bool needsFinalRegeneration = m_internalColorManagementActive != value; - if (needsFinalRegeneration) { m_internalColorManagementActive = value; createImageTextureTiles(); // at this point the value of m_internalColorManagementActive might // have been forcely reverted to 'false' in case of some problems } return needsFinalRegeneration; } void KisOpenGLImageTextures::updateTextureFormat() { QOpenGLContext *ctx = QOpenGLContext::currentContext(); if (!(m_image && ctx)) return; m_texturesInfo.internalFormat = GL_RGBA8; m_texturesInfo.type = GL_UNSIGNED_BYTE; m_texturesInfo.format = GL_BGRA; KoID colorModelId = m_image->colorSpace()->colorModelId(); KoID colorDepthId = m_image->colorSpace()->colorDepthId(); KoID destinationColorModelId = RGBAColorModelID; KoID destinationColorDepthId = Integer8BitsColorDepthID; dbgUI << "Choosing texture format:"; if (colorModelId == RGBAColorModelID) { if (colorDepthId == Float16BitsColorDepthID) { if (ctx->hasExtension("GL_ARB_texture_float")) { m_texturesInfo.internalFormat = GL_RGBA16F_ARB; dbgUI << "Using ARB half"; } else if (ctx->hasExtension("GL_ATI_texture_float")) { m_texturesInfo.internalFormat = GL_RGBA_FLOAT16_ATI; dbgUI << "Using ATI half"; } bool haveBuiltInOpenExr = false; #ifdef HAVE_OPENEXR haveBuiltInOpenExr = true; #endif if (haveBuiltInOpenExr && ctx->hasExtension("GL_ARB_half_float_pixel")) { m_texturesInfo.type = GL_HALF_FLOAT_ARB; destinationColorDepthId = Float16BitsColorDepthID; dbgUI << "Pixel type half"; } else { m_texturesInfo.type = GL_FLOAT; destinationColorDepthId = Float32BitsColorDepthID; dbgUI << "Pixel type float"; } m_texturesInfo.format = GL_RGBA; } else if (colorDepthId == Float32BitsColorDepthID) { if (ctx->hasExtension("GL_ARB_texture_float")) { m_texturesInfo.internalFormat = GL_RGBA32F_ARB; dbgUI << "Using ARB float"; } else if (ctx->hasExtension("GL_ATI_texture_float")) { m_texturesInfo.internalFormat = GL_RGBA_FLOAT32_ATI; dbgUI << "Using ATI float"; } m_texturesInfo.type = GL_FLOAT; m_texturesInfo.format = GL_RGBA; destinationColorDepthId = Float32BitsColorDepthID; } else if (colorDepthId == Integer16BitsColorDepthID) { m_texturesInfo.internalFormat = GL_RGBA16; m_texturesInfo.type = GL_UNSIGNED_SHORT; m_texturesInfo.format = GL_BGRA; destinationColorDepthId = Integer16BitsColorDepthID; dbgUI << "Using 16 bits rgba"; } } else { // We will convert the colorspace to 16 bits rgba, instead of 8 bits if (colorDepthId == Integer16BitsColorDepthID) { m_texturesInfo.internalFormat = GL_RGBA16; m_texturesInfo.type = GL_UNSIGNED_SHORT; m_texturesInfo.format = GL_BGRA; destinationColorDepthId = Integer16BitsColorDepthID; dbgUI << "Using conversion to 16 bits rgba"; } } if (!m_internalColorManagementActive && - colorModelId != destinationColorModelId) { + colorModelId != destinationColorModelId) { KisConfig cfg; KisConfig::OcioColorManagementMode cm = cfg.ocioColorManagementMode(); if (cm != KisConfig::INTERNAL) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("You enabled OpenColorIO based color management, but your image is not an RGB image.\n" "OpenColorIO-based color management only works with RGB images.\n" "Please check the settings in the LUT docker.\n" "OpenColorIO will now be deactivated.")); } warnUI << "WARNING: Internal color management was forcely enabled"; warnUI << "Color Management Mode: " << cm; warnUI << ppVar(m_image->colorSpace()); warnUI << ppVar(destinationColorModelId); warnUI << ppVar(destinationColorDepthId); cfg.setOcioColorManagementMode(KisConfig::INTERNAL); m_internalColorManagementActive = true; } const KoColorProfile *profile = - m_internalColorManagementActive || - colorModelId != destinationColorModelId ? - m_monitorProfile : m_image->colorSpace()->profile(); + m_internalColorManagementActive || + colorModelId != destinationColorModelId ? + m_monitorProfile : m_image->colorSpace()->profile(); /** * TODO: add an optimization so that the tile->convertTo() method * would not be called when not needed (DK) */ m_tilesDestinationColorSpace = - KoColorSpaceRegistry::instance()->colorSpace(destinationColorModelId.id(), - destinationColorDepthId.id(), - profile); + KoColorSpaceRegistry::instance()->colorSpace(destinationColorModelId.id(), + destinationColorDepthId.id(), + profile); } diff --git a/libs/ui/opengl/kis_opengl_image_textures.h b/libs/ui/opengl/kis_opengl_image_textures.h index e85568105e..d681366ea8 100644 --- a/libs/ui/opengl/kis_opengl_image_textures.h +++ b/libs/ui/opengl/kis_opengl_image_textures.h @@ -1,199 +1,199 @@ /* * Copyright (c) 2005-2007 Adrian Page * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_OPENGL_IMAGE_TEXTURES_H_ #define KIS_OPENGL_IMAGE_TEXTURES_H_ #include #include #include "kritaui_export.h" #include "kis_shared.h" #include "canvas/kis_update_info.h" #include "opengl/kis_texture_tile.h" class KisOpenGLImageTextures; class QOpenGLFunctions; typedef KisSharedPtr KisOpenGLImageTexturesSP; class KoColorProfile; /** * A set of OpenGL textures that contains the projection of a KisImage. */ class KRITAUI_EXPORT KisOpenGLImageTextures : public KisShared { public: /** * Obtain a KisOpenGLImageTextures object for the given image. * @param image The image * @param monitorProfile The profile of the display device */ static KisOpenGLImageTexturesSP getImageTextures(KisImageWSP image, const KoColorProfile *monitorProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags); /** * Default constructor. */ KisOpenGLImageTextures(); /** * Destructor. */ virtual ~KisOpenGLImageTextures(); /** * \return the image associated with the textures */ KisImageSP image() const; /** * Set the color profile of the display device. * @param profile The color profile of the display device */ void setMonitorProfile(const KoColorProfile *monitorProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags); /** * Complete initialization can only happen once an OpenGL context has been created. * @param f Pointer to OpenGL functions. They must already be ininitialized. */ void initGL(QOpenGLFunctions *f); void setChannelFlags(const QBitArray &channelFlags); bool internalColorManagementActive() const; bool setInternalColorManagementActive(bool value); /** * The background checkers texture. */ static const int BACKGROUND_TEXTURE_CHECK_SIZE = 32; static const int BACKGROUND_TEXTURE_SIZE = BACKGROUND_TEXTURE_CHECK_SIZE * 2; /** * Generate a background texture from the given QImage. This is used for the checker * pattern on which the image is rendered. */ void generateCheckerTexture(const QImage & checkImage); GLuint checkerTexture(); void updateConfig(bool useBuffer, int NumMipmapLevels); public: inline QRect storedImageBounds() { return m_storedImageBounds; } inline int xToCol(int x) { return x / m_texturesInfo.effectiveWidth; } inline int yToRow(int y) { return y / m_texturesInfo.effectiveHeight; } inline KisTextureTile* getTextureTileCR(int col, int row) { if (m_initialized) { int tile = row * m_numCols + col; KIS_ASSERT_RECOVER_RETURN_VALUE(m_textureTiles.size() > tile, 0); return m_textureTiles[tile]; } return 0; } inline qreal texelSize() const { Q_ASSERT(m_texturesInfo.width == m_texturesInfo.height); return 1.0 / m_texturesInfo.width; } KisOpenGLUpdateInfoSP updateCache(const QRect& rect); KisOpenGLUpdateInfoSP updateCacheNoConversion(const QRect& rect); void recalculateCache(KisUpdateInfoSP info); void slotImageSizeChanged(qint32 w, qint32 h); protected: KisOpenGLImageTextures(KisImageWSP image, const KoColorProfile *monitorProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags); void createImageTextureTiles(); void destroyImageTextureTiles(); static bool imageCanShareTextures(); private: QRect calculateTileRect(int col, int row) const; void getTextureSize(KisGLTexturesInfo *texturesInfo); void updateTextureFormat(); KisOpenGLUpdateInfoSP updateCacheImpl(const QRect& rect, bool convertColorSpace); private: KisImageWSP m_image; QRect m_storedImageBounds; const KoColorProfile *m_monitorProfile; KoColorConversionTransformation::Intent m_renderingIntent; KoColorConversionTransformation::ConversionFlags m_conversionFlags; /** * If the destination color space coincides with the one of the image, * then effectively, there is no conversion happens. That is used * for working with OCIO. */ - const KoColorSpace* m_tilesDestinationColorSpace; + const KoColorSpace *m_tilesDestinationColorSpace; /** * Shows whether the internal color management should be enabled or not. * Please note that if you disable color management, *but* your image color * space will not be supported (non-RGB), then it will be enabled anyway. * And this valiable will hold the real state of affairs! */ bool m_internalColorManagementActive; GLuint m_checkerTexture; KisGLTexturesInfo m_texturesInfo; int m_numCols; QVector m_textureTiles; QOpenGLFunctions *m_glFuncs; QBitArray m_channelFlags; bool m_allChannelsSelected; bool m_onlyOneChannelSelected; int m_selectedChannelIndex; bool m_useOcio; bool m_initialized; private: typedef QMap ImageTexturesMap; static ImageTexturesMap imageTexturesMap; }; #endif // KIS_OPENGL_IMAGE_TEXTURES_H_ diff --git a/libs/ui/opengl/kis_texture_tile.h b/libs/ui/opengl/kis_texture_tile.h index d4b9c3d1ff..2b6ed9110b 100644 --- a/libs/ui/opengl/kis_texture_tile.h +++ b/libs/ui/opengl/kis_texture_tile.h @@ -1,119 +1,127 @@ /* * Copyright (c) 2010 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_TEXTURE_TILE_H_ #define KIS_TEXTURE_TILE_H_ #include #include // no forward-declaration, used to get GL* primitive types defined #include #include "kis_opengl.h" #if !defined(QT_OPENGL_ES) #define USE_PIXEL_BUFFERS #endif class KisTextureTileUpdateInfo; class QOpenGLBuffer; struct KisGLTexturesInfo { + KisGLTexturesInfo() + : width(0) + , height(0) + , effectiveWidth(1) + , effectiveHeight(1) + , border(0) + {} + // real width and height int width; int height; // width and height minus border padding? int effectiveWidth; int effectiveHeight; // size of the border padding int border; GLint internalFormat; GLint format; GLint type; }; inline QRect stretchRect(const QRect &rc, int delta) { return rc.adjusted(-delta, -delta, delta, delta); } class KisTextureTile { public: KisTextureTile(const QRect &imageRect, const KisGLTexturesInfo *texturesInfo, const QByteArray &fillData, KisOpenGL::FilterMode mode, bool useBuffer, int numMipmapLevels, QOpenGLFunctions *f); ~KisTextureTile(); void setUseBuffer(bool useBuffer) { m_useBuffer = useBuffer; } void setNumMipmapLevels(int num) { m_numMipmapLevels = num; } void update(const KisTextureTileUpdateInfo &updateInfo); inline QRect tileRectInImagePixels() { return m_tileRectInImagePixels; } inline QRect textureRectInImagePixels() { return m_textureRectInImagePixels; } inline QRectF tileRectInTexturePixels() { return m_tileRectInTexturePixels; } void bindToActiveTexture(); int currentLodPlane() const; private: inline void setTextureParameters(); void setNeedsMipmapRegeneration(); void setCurrentLodPlane(int lod); GLuint m_textureId; #ifdef USE_PIXEL_BUFFERS void createTextureBuffer(const char*data, int size); QOpenGLBuffer *m_glBuffer; #endif QRect m_tileRectInImagePixels; QRectF m_tileRectInTexturePixels; QRect m_textureRectInImagePixels; KisOpenGL::FilterMode m_filter; const KisGLTexturesInfo *m_texturesInfo; bool m_needsMipmapRegeneration; int m_currentLodPlane; bool m_useBuffer; int m_numMipmapLevels; QOpenGLFunctions *f; Q_DISABLE_COPY(KisTextureTile) }; #endif /* KIS_TEXTURE_TILE_H_ */ diff --git a/libs/ui/tests/CMakeLists.txt b/libs/ui/tests/CMakeLists.txt index 67951c1e11..7219220ac3 100644 --- a/libs/ui/tests/CMakeLists.txt +++ b/libs/ui/tests/CMakeLists.txt @@ -1,255 +1,261 @@ set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) #add_subdirectory(scratchpad) include_directories(${CMAKE_SOURCE_DIR}/libs/image/metadata ${CMAKE_SOURCE_DIR}/sdk/tests ) macro_add_unittest_definitions() ########### next target ############### set(kis_node_model_test_SRCS kis_node_model_test.cpp modeltest.cpp ) kde4_add_broken_unit_test(kis_node_model_test TESTNAME krita-ui-kis_node_model_test ${kis_node_model_test_SRCS}) target_link_libraries(kis_node_model_test kritaui Qt5::Test) ########### next target ############### set(kis_image_view_converter_test_SRCS kis_image_view_converter_test.cpp ) kde4_add_unit_test(KisImageViewConverterTest TESTNAME krita-ui-KisImageViewConverterTest ${kis_image_view_converter_test_SRCS}) target_link_libraries(KisImageViewConverterTest kritaui Qt5::Test) ########### next target ############### set(kis_shape_controller_test_SRCS kis_shape_controller_test.cpp kis_dummies_facade_base_test.cpp) kde4_add_broken_unit_test(kis_shape_controller_test TESTNAME krita-ui-kis_shape_controller_test ${kis_shape_controller_test_SRCS}) target_link_libraries(kis_shape_controller_test kritaimage kritaui Qt5::Test) ########### next target ############### set(squeezedcombobox_test_SRCS squeezedcombobox_test.cpp ) kde4_add_unit_test(squeezedcombobox_test TESTNAME krita-ui-squeezedcombobox_test ${squeezedcombobox_test_SRCS}) target_link_libraries(squeezedcombobox_test kritaui Qt5::Test) ########### next target ############### set(kis_prescaled_projection_test_SRCS kis_prescaled_projection_test.cpp ) kde4_add_broken_unit_test(KisPrescaledProjectionTest TESTNAME krita-ui-kis_prescaled_projection_test ${kis_prescaled_projection_test_SRCS}) target_link_libraries(KisPrescaledProjectionTest kritaui Qt5::Test) ########### next target ############### set(kis_kra_loader_test_SRCS kis_kra_loader_test.cpp ) kde4_add_broken_unit_test(KisKraLoaderTest TESTNAME krita-ui-KisKraLoaderTest ${kis_kra_loader_test_SRCS}) target_link_libraries(KisKraLoaderTest kritaimage kritaui Qt5::Test) ########### next target ############### set(kis_kra_saver_test_SRCS kis_kra_saver_test.cpp ) kde4_add_broken_unit_test(KisKraSaverTest TESTNAME krita-ui-KisKraSaverTest ${kis_kra_saver_test_SRCS}) target_link_libraries(KisKraSaverTest kritaimage kritaui Qt5::Test) ########### next target ############### set(kis_kra_savexml_visitor_test_SRCS kis_kra_savexml_visitor_test.cpp ) kde4_add_unit_test(KisKraSaveXmlVisitorTest TESTNAME krita-ui-KisKraSaveXmlVisitorTest ${kis_kra_savexml_visitor_test_SRCS}) target_link_libraries(KisKraSaveXmlVisitorTest kritaimage kritaui Qt5::Test) ########### next target ############### set(kis_shape_selection_test_SRCS kis_shape_selection_test.cpp ) kde4_add_unit_test(KisShapeSelectionTest TESTNAME krita-ui-ShapeSelectionTest ${kis_shape_selection_test_SRCS}) target_link_libraries(KisShapeSelectionTest kritaimage kritaui Qt5::Test) ########### next target ############### set(kis_exiv2_test_SRCS kis_exiv2_test.cpp ) kde4_add_broken_unit_test(KisExiv2Test TESTNAME krita-ui-KisExiv2Test ${kis_exiv2_test_SRCS}) target_link_libraries(KisExiv2Test kritaimage kritaui Qt5::Test) ########### next target ############### set(kis_recorded_action_editor_test_SRCS kis_recorded_action_editor_test.cpp ) kde4_add_unit_test(KisRecordedActionEditorTest TESTNAME krita-ui-KisRecordedActionEditor ${kis_recorded_action_editor_test_SRCS}) target_link_libraries(KisRecordedActionEditorTest kritaimage kritaui Qt5::Test) ########### next target ############### set(kis_doc2_test_SRCS kis_doc2_test.cpp ) kde4_add_unit_test(KisDoc2Test TESTNAME krita-ui-KisDoc2Test ${kis_doc2_test_SRCS}) target_link_libraries(KisDoc2Test kritaui kritaimage Qt5::Test) ########### next target ############### set(kis_coordinates_converter_test_SRCS kis_coordinates_converter_test.cpp ) kde4_add_unit_test(KisCoordinatesConverterTest TESTNAME krita-ui-KisCoordinatesConverterTest ${kis_coordinates_converter_test_SRCS}) target_link_libraries(KisCoordinatesConverterTest kritaui kritaimage Qt5::Test) ########### next target ############### set(kis_clipboard_test_SRCS kis_clipboard_test.cpp ) kde4_add_broken_unit_test(KisClipboardTest TESTNAME krita-ui-KisClipboardTest ${kis_clipboard_test_SRCS}) target_link_libraries(KisClipboardTest kritaui kritaimage Qt5::Test) ########### next target ############### set(freehand_stroke_test_SRCS freehand_stroke_test.cpp ${CMAKE_SOURCE_DIR}/sdk/tests/stroke_testing_utils.cpp) kde4_add_broken_unit_test(FreehandStrokeTest TESTNAME krita-ui-FreehandStrokeTest ${freehand_stroke_test_SRCS}) target_link_libraries(FreehandStrokeTest kritaui kritaimage Qt5::Test) ########### next target ############### set(fill_processing_visitor_test_SRCS fill_processing_visitor_test.cpp ${CMAKE_SOURCE_DIR}/sdk/tests/stroke_testing_utils.cpp) kde4_add_broken_unit_test(FillProcessingVisitorTest TESTNAME krita-ui-FillProcessingVisitorTest ${fill_processing_visitor_test_SRCS}) target_link_libraries(FillProcessingVisitorTest kritaui kritaimage Qt5::Test) ########### next target ############### set(filter_stroke_test_SRCS filter_stroke_test.cpp ../../../sdk/tests/stroke_testing_utils.cpp) kde4_add_broken_unit_test(FilterStrokeTest TESTNAME krita-ui-FilterStrokeTest ${filter_stroke_test_SRCS}) target_link_libraries(FilterStrokeTest kritaui kritaimage Qt5::Test) ########### next target ############### set(kis_selection_manager_test_SRCS kis_selection_manager_test.cpp) kde4_add_broken_unit_test(KisSelectionManagerTest TESTNAME krita-ui-KisSelectionManagerTest ${kis_selection_manager_test_SRCS}) target_link_libraries(KisSelectionManagerTest kritaui kritaimage Qt5::Test) #set_tests_properties(krita-ui-KisSelectionManagerTest PROPERTIES TIMEOUT 300) ########### next target ############### set(kis_selection_decoration_test_SRCS kis_selection_decoration_test.cpp ../../../sdk/tests/stroke_testing_utils.cpp) kde4_add_unit_test(KisSelectionDecorationTest TESTNAME krita-ui-KisSelectionDecorationTest ${kis_selection_decoration_test_SRCS}) target_link_libraries(KisSelectionDecorationTest kritaui kritaimage Qt5::Test) ########### next target ############### set(kis_node_manager_test_SRCS kis_node_manager_test.cpp) kde4_add_broken_unit_test(KisNodeManagerTest TESTNAME krita-ui-KisNodeManagerTest ${kis_node_manager_test_SRCS}) target_link_libraries(KisNodeManagerTest kritaui kritaimage Qt5::Test) ########### next target ############### set(kis_node_dummies_graph_test_SRCS kis_node_dummies_graph_test.cpp ../../../sdk/tests/testutil.cpp) kde4_add_unit_test(KisNodeDummiesGraphTest TESTNAME krita-ui-KisNodeDummiesGraphTest ${kis_node_dummies_graph_test_SRCS}) target_link_libraries(KisNodeDummiesGraphTest kritaui kritaimage Qt5::Test) ########### next target ############### set(kis_node_shapes_graph_test_SRCS kis_node_shapes_graph_test.cpp ../../../sdk/tests/testutil.cpp) kde4_add_unit_test(KisNodeShapesGraphTest TESTNAME krita-ui-KisNodeShapesGraphTest ${kis_node_shapes_graph_test_SRCS}) target_link_libraries(KisNodeShapesGraphTest kritaui kritaimage Qt5::Test) ########### next target ############### set(kis_dummies_facade_test_SRCS kis_dummies_facade_test.cpp kis_dummies_facade_base_test.cpp ../../../sdk/tests/testutil.cpp) kde4_add_broken_unit_test(KisDummiesFacadeTest TESTNAME krita-ui-KisDummiesFacadeTest ${kis_dummies_facade_test_SRCS}) target_link_libraries(KisDummiesFacadeTest kritaui kritaimage Qt5::Test) ########### next target ############### set(kis_model_index_converter_test_SRCS kis_model_index_converter_test.cpp ../../../sdk/tests/testutil.cpp) kde4_add_unit_test(KisModelIndexConverterTest TESTNAME krita-ui-KisModelIndexConverterTest ${kis_model_index_converter_test_SRCS}) target_link_libraries(KisModelIndexConverterTest kritaui kritaimage Qt5::Test) ########### next target ############### set(kis_zoom_and_pan_test_SRCS kis_zoom_and_pan_test.cpp ../../../sdk/tests/testutil.cpp) kde4_add_broken_unit_test(KisZoomAndPanTest TESTNAME krita-ui-KisZoomAndPanTest ${kis_zoom_and_pan_test_SRCS}) target_link_libraries(KisZoomAndPanTest kritaui kritaimage Qt5::Test) #set_tests_properties(krita-ui-KisZoomAndPanTest PROPERTIES TIMEOUT 300) ########### next target ############### # set(kis_input_manager_test_SRCS kis_input_manager_test.cpp ../../sdk/tests/testutil.cpp) # kde4_add_unit_test(KisInputManagerTest TESTNAME krita-ui-KisInputManagerTest ${kis_input_manager_test_SRCS}) # target_link_libraries(KisInputManagerTest kritaui kritaimage Qt5::Test) ########### next target ############### set(kis_action_manager_test_SRCS kis_action_manager_test.cpp ) kde4_add_broken_unit_test(KisActionManagerTest TESTNAME krita-ui-KisActionManagerTest ${kis_action_manager_test_SRCS}) target_link_libraries(KisActionManagerTest kritaui kritaimage Qt5::Test) ########### next target ############### set(kis_categories_mapper_test_SRCS kis_categories_mapper_test.cpp testing_categories_mapper.cpp) kde4_add_broken_unit_test(KisCategoriesMapperTest TESTNAME krita-ui-KisCategoriesMapperTest ${kis_categories_mapper_test_SRCS}) target_link_libraries(KisCategoriesMapperTest kritaui kritaimage Qt5::Test) ########### next target ############### set(kis_categorized_list_model_test_SRCS kis_categorized_list_model_test.cpp modeltest.cpp) kde4_add_unit_test(KisCategorizedListModelTest TESTNAME krita-ui-KisCategorizedListModelTest ${kis_categorized_list_model_test_SRCS}) target_link_libraries(KisCategorizedListModelTest kritaui kritaimage Qt5::Test) ########### next target ############### set(kis_resource_server_provider_test_SRCS kis_resource_server_provider_test.cpp modeltest.cpp) kde4_add_unit_test(KisResourceServerProviderTest TESTNAME krita-ui-KisResourceServerProviderTest ${kis_resource_server_provider_test_SRCS}) target_link_libraries(KisResourceServerProviderTest kritaui kritaimage Qt5::Test) ########### next target ############### set(kis_file_layer_test_SRCS kis_file_layer_test.cpp ) kde4_add_unit_test(KisFileLayerTest TESTNAME krita-ui-KisFileLayerTest ${kis_file_layer_test_SRCS}) target_link_libraries(KisFileLayerTest kritaui kritaimage Qt5::Test) ########### next target ############### set(kis_asl_layer_style_serializer_test_SRCS kis_asl_layer_style_serializer_test.cpp ) kde4_add_broken_unit_test(KisAslLayerStyleSerializerTest TESTNAME krita-ui-KisAslLayerStyleSerializerTest ${kis_asl_layer_style_serializer_test_SRCS}) target_link_libraries(KisAslLayerStyleSerializerTest kritaui kritaimage ${QT_QTTEST_LIBRARY}) ########### next target ############### set(kis_node_juggler_compressed_test_SRCS kis_node_juggler_compressed_test.cpp ../../../sdk/tests/testutil.cpp) kde4_add_unit_test(KisNodeJugglerCompressedTest TESTNAME krita-image-BaseNodeTest ${kis_node_juggler_compressed_test_SRCS}) target_link_libraries(KisNodeJugglerCompressedTest kritaimage kritaui Qt5::Test) ########### next target ############### set(kis_node_view_test_SRCS kis_node_view_test.cpp ../../../sdk/tests/testutil.cpp) qt5_add_resources(kis_node_view_test_SRCS ${krita_QRCS}) kde4_add_unit_test(KisNodeViewTest TESTNAME krita-image-BaseNodeTest ${kis_node_view_test_SRCS}) target_link_libraries(KisNodeViewTest kritaimage kritaui Qt5::Test) ########### next target ############### set(kis_multinode_property_test_SRCS kis_multinode_property_test.cpp) kde4_add_unit_test(KisMultinodePropertyTest TESTNAME krita-image-BaseNodeTest ${kis_multinode_property_test_SRCS}) target_link_libraries(KisMultinodePropertyTest kritaimage kritaui Qt5::Test) ########### next target ############### set(kis_grid_config_test_SRCS kis_grid_config_test.cpp) kde4_add_unit_test(KisGridConfigTest TESTNAME krita-image-BaseNodeTest ${kis_grid_config_test_SRCS}) target_link_libraries(KisGridConfigTest kritaui Qt5::Test) ########### next target ############### set(kis_animation_exporter_test_SRCS kis_animation_exporter_test.cpp ) kde4_add_broken_unit_test(KisAnimationExporterTest TESTNAME kritaui-animation_exporter_test ${kis_animation_exporter_test_SRCS}) target_link_libraries(KisAnimationExporterTest kritaui kritaimage ${QT_QTTEST_LIBRARY}) ########### next target ############### set(kis_animation_importer_test_SRCS kis_animation_importer_test.cpp ) kde4_add_broken_unit_test(KisAnimationImporterTest TESTNAME kritaui-animation_importer_test ${kis_animation_importer_test_SRCS}) target_link_libraries(KisAnimationImporterTest kritaui kritaimage ${QT_QTTEST_LIBRARY}) ########### next target ############### set(kis_animation_frame_cache_test_SRCS kis_animation_frame_cache_test.cpp ) kde4_add_broken_unit_test(KisAnimationFrameCacheTest TESTNAME kritaui-animation_frame_cache_test ${kis_animation_frame_cache_test_SRCS}) target_link_libraries(KisAnimationFrameCacheTest kritaui kritaimage ${QT_QTTEST_LIBRARY}) ########### next target ############### set(ResourceBundleTest_SRCS ResourceBundleTest.cpp) kde4_add_broken_unit_test(ResourceBundleTest TESTNAME krita-resourcemanager-ResourceBundleTest ${ResourceBundleTest_SRCS}) target_link_libraries(ResourceBundleTest kritaui kritalibbrush kritalibpaintop Qt5::Test ) + +########### next target ############### + +set(kis_stabilized_events_sampler_test_SRCS kis_stabilized_events_sampler_test.cpp) +kde4_add_unit_test(KisStabilizedEventsSamplerTest TESTNAME krita-ui-StabilizedEventsSamplerTest ${kis_stabilized_events_sampler_test_SRCS}) +target_link_libraries(KisStabilizedEventsSamplerTest kritaui Qt5::Test) diff --git a/libs/ui/tests/kis_stabilized_events_sampler_test.cpp b/libs/ui/tests/kis_stabilized_events_sampler_test.cpp new file mode 100644 index 0000000000..cbd3deb5ac --- /dev/null +++ b/libs/ui/tests/kis_stabilized_events_sampler_test.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2016 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kis_stabilized_events_sampler_test.h" + +#include "kis_stabilized_events_sampler.h" +#include "kis_paint_information.h" + +void KisStabilizedEventsSamplerTest::test() +{ + KisStabilizedEventsSampler sampler(20); + + KisPaintInformation pi1(QPoint(10,10)); + KisPaintInformation pi2(QPoint(20,20)); + + sampler.addEvent(pi1); + + QTest::qSleep(50); + + sampler.addEvent(pi2); + + QTest::qSleep(70); + + KisStabilizedEventsSampler::iterator it; + KisStabilizedEventsSampler::iterator end; + std::tie(it, end) = sampler.range(); + + + int numTotal = 0; + int num1 = 0; + int num2 = 0; + + for (; it != end; ++it) { + numTotal++; + if (it->pos().x() == 10) { + num1++; + } else if (it->pos().x() == 20) { + num2++; + } + + qDebug() << ppVar(it->pos()); + } + + QVERIFY(numTotal >= 6); + QVERIFY(num1 >= 3); + QVERIFY(num2 >= 3); +} + +QTEST_MAIN(KisStabilizedEventsSamplerTest) diff --git a/benchmarks/kis_mask_generator_benchmark.h b/libs/ui/tests/kis_stabilized_events_sampler_test.h similarity index 72% copy from benchmarks/kis_mask_generator_benchmark.h copy to libs/ui/tests/kis_stabilized_events_sampler_test.h index a7fa369551..885ea2b180 100644 --- a/benchmarks/kis_mask_generator_benchmark.h +++ b/libs/ui/tests/kis_stabilized_events_sampler_test.h @@ -1,34 +1,31 @@ /* - * Copyright (c) 2010 Cyrille Berger + * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef KIS_MASK_GENERATOR_BENCHMARK_H -#define KIS_MASK_GENERATOR_BENCHMARK_H +#ifndef __KIS_STABILIZED_EVENTS_SAMPLER_TEST_H +#define __KIS_STABILIZED_EVENTS_SAMPLER_TEST_H -#include +#include -class KisMaskGeneratorBenchmark : public QObject +class KisStabilizedEventsSamplerTest : public QObject { Q_OBJECT private Q_SLOTS: - void benchmarkCircle(); - void benchmarkSIMD(); - void benchmarkSquare(); - + void test(); }; -#endif +#endif /* __KIS_STABILIZED_EVENTS_SAMPLER_TEST_H */ diff --git a/libs/ui/thememanager.cpp b/libs/ui/thememanager.cpp index 53e2d185f2..2917171871 100644 --- a/libs/ui/thememanager.cpp +++ b/libs/ui/thememanager.cpp @@ -1,368 +1,310 @@ /* ============================================================ * * This file is a part of digiKam project * http://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 // KDE includes #include #include #include #include #include #include #include #include #include // Calligra #include #ifdef __APPLE__ #include #endif namespace Digikam { // --------------------------------------------------------------- class ThemeManager::ThemeManagerPriv { public: ThemeManagerPriv() - : defaultThemeName(i18nc("default theme name", "Default")) - , themeMenuActionGroup(0) + : themeMenuActionGroup(0) , themeMenuAction(0) { } - QString defaultThemeName; 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->defaultThemeName = theme; d->currentThemeName = theme; populateThemeMap(); } ThemeManager::~ThemeManager() { delete d; } -QString ThemeManager::defaultThemeName() const -{ - //qDebug() << "defaultThemeName()" << d->defaultThemeName; - return d->defaultThemeName; -} - QString ThemeManager::currentThemeName() const { //qDebug() << "getting current themename"; QString themeName; if (d->themeMenuAction && d->themeMenuActionGroup) { QAction* action = d->themeMenuActionGroup->checkedAction(); - themeName = (!action ? defaultThemeName() : action->text().remove('&')); + 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() << "\tdefault theme name" << d->defaultThemeName; - - themeName = d->defaultThemeName; + //qDebug() << "\tfallback"; + themeName = "Krita dark"; } //qDebug() << "\tresult" << themeName; return themeName; } void ThemeManager::setCurrentTheme(const QString& name) { //qDebug() << "setCurrentTheme();" << d->currentThemeName << "to" << name; - if (d->currentThemeName == name) return; - d->currentThemeName = name; if (d->themeMenuAction && d->themeMenuActionGroup) { QList list = d->themeMenuActionGroup->actions(); - Q_FOREACH (QAction* action, list) - { - if (action->text().remove('&') == name) - { + Q_FOREACH (QAction* action, list) { + if (action->text().remove('&') == name) { action->setChecked(true); } } } slotChangePalette(); } void ThemeManager::slotChangePalette() { //qDebug() << "slotChangePalette" << sender(); - updateCurrentKDEdefaultThemePreview(); QString theme(currentThemeName()); - - // Windows doesn't do well with KDE theming - #if defined (Q_OS_LINUX) || defined (__APPLE__) - if (theme == defaultThemeName() || theme.isEmpty()) { - theme = currentKDEdefaultTheme(); - } - #endif - 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 ) - { + 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; qApp->setPalette(palette); - if (theme == defaultThemeName() || theme.isEmpty()) { -#ifdef __APPLE__ +#ifdef Q_OS_MAC + if (theme == "Krita bright" || theme.isEmpty()) { qApp->setStyle("Macintosh"); qApp->style()->polish(qApp); -#endif } else { -#ifdef __APPLE__ qApp->setStyle("Fusion"); qApp->style()->polish(qApp); -#endif } +#endif 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; - QString theme(currentThemeName()); - 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) - { + 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); + QIcon icon = createSchemePreviewIcon(config); KConfigGroup group(config, "General"); - const QString name = group.readEntry("Name", info.baseName()); - action = new QAction(name, d->themeMenuActionGroup); + const QString name = group.readEntry("Name", info.baseName()); + 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) - { + Q_FOREACH (const QString& name, actionMapKeys) { if ( name == currentThemeName()) { actionMap.value(name)->setChecked(true); } - d->themeMenuAction->addAction(actionMap.value(name)); } - - updateCurrentKDEdefaultThemePreview(); -} - -void ThemeManager::updateCurrentKDEdefaultThemePreview() -{ - if (!d->themeMenuActionGroup) return; - - QList list = d->themeMenuActionGroup->actions(); - Q_FOREACH (QAction* action, list) - { - if (action->text().remove('&') == defaultThemeName()) - { - KSharedConfigPtr config = KSharedConfig::openConfig(d->themeMap.value(currentKDEdefaultTheme())); - QIcon icon = createSchemePreviewIcon(config); - action->setIcon(icon); - } - } } 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; } -QString ThemeManager::currentKDEdefaultTheme() const -{ - KSharedConfigPtr config = KSharedConfig::openConfig("kdeglobals"); - KConfigGroup group(config, "General"); - QString colorScheme = group.readEntry("ColorScheme"); - //qDebug() << "currentKDEdefaultTheme()" << colorScheme; - return colorScheme; -} - void ThemeManager::populateThemeMap() { const QStringList schemeFiles = KoResourcePaths::findAllResources("data", "color-schemes/*.colors"); - for (int i = 0; i < schemeFiles.size(); ++i) - { + 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.baseName()); + const QString name = group.readEntry("Name", info.baseName()); d->themeMap.insert(name, filename); } } } // namespace Digikam diff --git a/libs/ui/thememanager.h b/libs/ui/thememanager.h index cda6fa6266..736b1ca3ed 100644 --- a/libs/ui/thememanager.h +++ b/libs/ui/thememanager.h @@ -1,90 +1,85 @@ /* ============================================================ * * This file is a part of digiKam project * http://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(); QString currentThemeName() const; void setCurrentTheme(const QString& name); - QString defaultThemeName() const; - 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); - QString currentKDEdefaultTheme() const; - void updateCurrentKDEdefaultThemePreview(); private: class ThemeManagerPriv; ThemeManagerPriv* const d; }; } // namespace Digikam #endif /* THEMEMANAGER_H */ diff --git a/libs/ui/tool/kis_stabilized_events_sampler.cpp b/libs/ui/tool/kis_stabilized_events_sampler.cpp new file mode 100644 index 0000000000..2d47f4bcd8 --- /dev/null +++ b/libs/ui/tool/kis_stabilized_events_sampler.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2016 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kis_stabilized_events_sampler.h" + +#include +#include +#include + +#include "kis_paint_information.h" + + +struct KisStabilizedEventsSampler::Private +{ + Private(int _sampleTime) : sampleTime(_sampleTime) {} + + std::function paintLine; + QElapsedTimer lastPaintTime; + QList realEvents; + int sampleTime; + + KisPaintInformation lastPaintInformation; +}; + +KisStabilizedEventsSampler::KisStabilizedEventsSampler(int sampleTime) + : m_d(new Private(sampleTime)) +{ +} + +KisStabilizedEventsSampler::~KisStabilizedEventsSampler() +{ +} + +void KisStabilizedEventsSampler::setLineFunction(std::function func) +{ + m_d->paintLine = func; +} + +void KisStabilizedEventsSampler::clear() +{ + if (!m_d->realEvents.isEmpty()) { + m_d->lastPaintInformation = m_d->realEvents.last(); + } + + m_d->realEvents.clear(); + m_d->lastPaintTime.start(); +} + +void KisStabilizedEventsSampler::addEvent(const KisPaintInformation &pi) +{ + if (!m_d->lastPaintTime.isValid()) { + m_d->lastPaintTime.start(); + } + + m_d->realEvents.append(pi); +} + +void KisStabilizedEventsSampler::processAllEvents() +{ + const int elapsed = m_d->lastPaintTime.restart(); + + const qreal alpha = qreal(m_d->realEvents.size()) / elapsed; + + for (int i = 0; i < elapsed; i += m_d->sampleTime) { + const int k = qFloor(alpha * i); + + m_d->paintLine(m_d->realEvents[k]); + } +} + +const KisPaintInformation& KisStabilizedEventsSampler::iterator::dereference() const +{ + const int k = qFloor(m_alpha * m_index); + return k < m_sampler->m_d->realEvents.size() ? + m_sampler->m_d->realEvents[k] : m_sampler->m_d->lastPaintInformation; +} + +std::pair +KisStabilizedEventsSampler::range() const +{ + const int elapsed = m_d->lastPaintTime.restart() / m_d->sampleTime; + const qreal alpha = qreal(m_d->realEvents.size()) / elapsed; + + return std::make_pair(iterator(this, 0, alpha), + iterator(this, elapsed, alpha)); +} + + diff --git a/libs/ui/tool/kis_stabilized_events_sampler.h b/libs/ui/tool/kis_stabilized_events_sampler.h new file mode 100644 index 0000000000..feb6fc6d6d --- /dev/null +++ b/libs/ui/tool/kis_stabilized_events_sampler.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2016 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __KIS_STABILIZED_EVENTS_SAMPLER_H +#define __KIS_STABILIZED_EVENTS_SAMPLER_H + +#include + +#include +#include + +#include "kritaui_export.h" + +class KisPaintInformation; +#include + + +class KRITAUI_EXPORT KisStabilizedEventsSampler +{ +public: + KisStabilizedEventsSampler(int sampleTime = 1); + ~KisStabilizedEventsSampler(); + + void setLineFunction(std::function func); + + void clear(); + void addEvent(const KisPaintInformation &pi); + void processAllEvents(); + +public: + class iterator : + public boost::iterator_facade + { + public: + iterator() + : m_sampler(0), + m_index(0), + m_alpha(0) {} + + iterator(const KisStabilizedEventsSampler* sampler, int index, qreal alpha) + : m_sampler(sampler), + m_index(index), + m_alpha(alpha) {} + + private: + friend class boost::iterator_core_access; + + void increment() { + m_index++; + } + + bool equal(iterator const& other) const { + return m_index == other.m_index && + m_sampler == other.m_sampler; + } + + const KisPaintInformation& dereference() const; + + private: + const KisStabilizedEventsSampler* m_sampler; + int m_index; + qreal m_alpha; + }; + + std::pair range() const; + +private: + struct Private; + const QScopedPointer m_d; +}; + + +#endif /* __KIS_STABILIZED_EVENTS_SAMPLER_H */ diff --git a/libs/ui/tool/kis_tool_freehand_helper.cpp b/libs/ui/tool/kis_tool_freehand_helper.cpp index 20d30a7967..dde3424b5d 100644 --- a/libs/ui/tool/kis_tool_freehand_helper.cpp +++ b/libs/ui/tool/kis_tool_freehand_helper.cpp @@ -1,845 +1,859 @@ /* * 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 #include "kis_painting_information_builder.h" #include "kis_recording_adapter.h" #include "kis_image.h" #include "kis_painter.h" #include #include #include "kis_update_time_monitor.h" +#include "kis_stabilized_events_sampler.h" +#include "kis_config.h" #include //#define DEBUG_BEZIER_CURVES struct KisToolFreehandHelper::Private { KisPaintingInformationBuilder *infoBuilder; KisRecordingAdapter *recordingAdapter; KisStrokesFacade *strokesFacade; KUndo2MagicString transactionText; bool haveTangent; QPointF previousTangent; bool hasPaintAtLeastOnce; QTime strokeTime; QTimer strokeTimeoutTimer; QVector painterInfos; KisResourcesSnapshotSP resources; KisStrokeId strokeId; KisPaintInformation previousPaintInformation; KisPaintInformation olderPaintInformation; KisSmoothingOptionsSP smoothingOptions; QTimer airbrushingTimer; QList history; QList distanceHistory; KisPaintOpUtils::PositionHistory lastOutlinePos; // Stabilizer data QQueue stabilizerDeque; - KisPaintInformation stabilizerLastPaintInfo; QTimer stabilizerPollTimer; + KisStabilizedEventsSampler stabilizedSampler; int canvasRotation; bool canvasMirroredH; KisPaintInformation getStabilizedPaintInfo(const QQueue &queue, const KisPaintInformation &lastPaintInfo); qreal effectiveSmoothnessDistance() const; }; KisToolFreehandHelper::KisToolFreehandHelper(KisPaintingInformationBuilder *infoBuilder, const KUndo2MagicString &transactionText, KisRecordingAdapter *recordingAdapter) : m_d(new Private()) { m_d->infoBuilder = infoBuilder; m_d->recordingAdapter = recordingAdapter; m_d->transactionText = transactionText; m_d->smoothingOptions = KisSmoothingOptionsSP(new KisSmoothingOptions()); m_d->canvasRotation = 0; 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())); } 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 KisPaintOpSettings *globalSettings, KisPaintOpSettings::OutlineMode mode) const { const KisPaintOpSettings *settings = globalSettings; KisPaintInformation info = m_d->infoBuilder->hover(savedCursorPos, event); info.setCanvasRotation(m_d->canvasRotation); info.setCanvasHorizontalMirrorState( m_d->canvasMirroredH ); KisDistanceInformation distanceInfo(m_d->lastOutlinePos.pushThroughHistory(savedCursorPos), 0); if (!m_d->painterInfos.isEmpty()) { settings = m_d->resources->currentPaintOpPreset()->settings(); 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->painterInfos.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(m_d->lastOutlinePos.pushThroughHistory(savedCursorPos), 0); } else if (m_d->painterInfos.first()->dragDistance->isStarted()) { distanceInfo = *m_d->painterInfos.first()->dragDistance; } } KisPaintInformation::DistanceInformationRegistrar registrar = info.registerDistanceInformation(&distanceInfo); 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::initPaint(KoPointerEvent *event, KoCanvasResourceManager *resourceManager, KisImageWSP image, KisNodeSP currentNode, KisStrokesFacade *strokesFacade, KisPostExecutionUndoAdapter *undoAdapter, KisNodeSP overrideNode, KisDefaultBoundsBaseSP bounds) { KisPaintInformation pi = m_d->infoBuilder->startStroke(event, elapsedStrokeTime()); initPaintImpl(pi, resourceManager, image, currentNode, strokesFacade, undoAdapter, overrideNode, bounds); } bool KisToolFreehandHelper::isRunning() const { return m_d->strokeId; } void KisToolFreehandHelper::initPaintImpl(const KisPaintInformation &previousPaintInformation, KoCanvasResourceManager *resourceManager, KisImageWSP image, KisNodeSP currentNode, KisStrokesFacade *strokesFacade, KisPostExecutionUndoAdapter *undoAdapter, KisNodeSP overrideNode, KisDefaultBoundsBaseSP bounds) { Q_UNUSED(overrideNode); m_d->strokesFacade = strokesFacade; m_d->haveTangent = false; m_d->previousTangent = QPointF(); m_d->hasPaintAtLeastOnce = false; m_d->strokeTime.start(); m_d->previousPaintInformation = previousPaintInformation; createPainters(m_d->painterInfos, m_d->previousPaintInformation.pos(), m_d->previousPaintInformation.currentTime()); m_d->resources = new KisResourcesSnapshot(image, currentNode, undoAdapter, resourceManager, bounds); if(overrideNode) { m_d->resources->setCurrentNode(overrideNode); } if(m_d->recordingAdapter) { m_d->recordingAdapter->startStroke(image, m_d->resources); } KisStrokeStrategy *stroke = new FreehandStrokeStrategy(m_d->resources->needsIndirectPainting(), m_d->resources->indirectPaintingCompositeOp(), m_d->resources, m_d->painterInfos, m_d->transactionText); m_d->strokeId = m_d->strokesFacade->startStroke(stroke); m_d->history.clear(); m_d->distanceHistory.clear(); if(m_d->resources->needsAirbrushing()) { m_d->airbrushingTimer.setInterval(m_d->resources->airbrushingRate()); m_d->airbrushingTimer.start(); } if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) { stabilizerStart(m_d->previousPaintInformation); } } 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 << "WARINING: there is no intersection point " // << "in the basic smoothing algoriths"; } 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::paint(KoPointerEvent *event) { KisPaintInformation info = m_d->infoBuilder->continueStroke(event, elapsedStrokeTime()); info.setCanvasRotation( m_d->canvasRotation ); info.setCanvasHorizontalMirrorState( m_d->canvasMirroredH ); KisUpdateTimeMonitor::instance()->reportMouseMove(info.pos()); /** * 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 * 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 controling 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()); paintBezierSegment(m_d->olderPaintInformation, m_d->previousPaintInformation, m_d->previousTangent, newTangent); m_d->previousTangent = newTangent; } m_d->olderPaintInformation = m_d->previousPaintInformation; 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->stabilizerLastPaintInfo = info; + m_d->stabilizedSampler.addEvent(info); } 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(); } /** * 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->painterInfos.clear(); m_d->strokesFacade->endStroke(m_d->strokeId); m_d->strokeId.clear(); if(m_d->recordingAdapter) { m_d->recordingAdapter->endStroke(); } } void KisToolFreehandHelper::cancelPaint() { if (!m_d->strokeId) return; m_d->strokeTimeoutTimer.stop(); if (m_d->airbrushingTimer.isActive()) { m_d->airbrushingTimer.stop(); } if (m_d->stabilizerPollTimer.isActive()) { m_d->stabilizerPollTimer.stop(); } // see a comment in endPaint() m_d->painterInfos.clear(); m_d->strokesFacade->cancelStroke(m_d->strokeId); m_d->strokeId.clear(); if(m_d->recordingAdapter) { //FIXME: not implemented //m_d->recordingAdapter->cancelStroke(); } } int KisToolFreehandHelper::elapsedStrokeTime() const { return m_d->strokeTime.elapsed(); } void KisToolFreehandHelper::stabilizerStart(KisPaintInformation firstPaintInfo) { // 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); } - m_d->stabilizerLastPaintInfo = firstPaintInfo; - // Poll and draw each millisecond - m_d->stabilizerPollTimer.setInterval(1); + // Poll and draw regularly + KisConfig cfg; + m_d->stabilizerPollTimer.setInterval(cfg.stabilizerSampleSize()); m_d->stabilizerPollTimer.start(); + + m_d->stabilizedSampler.clear(); } KisPaintInformation KisToolFreehandHelper::Private::getStabilizedPaintInfo(const QQueue &queue, const KisPaintInformation &lastPaintInfo) { KisPaintInformation result(lastPaintInfo); 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 (smoothingOptions->stabilizeSensors()) { while (it != end) { qreal k = qreal(i - 1) / i; // coeff for uniform averaging result = KisPaintInformation::mix(k, *it, result); it++; i++; } } else{ while (it != end) { qreal k = qreal(i - 1) / i; // coeff for uniform averaging result = KisPaintInformation::mixOnlyPosition(k, *it, result); it++; i++; } } } return result; } void KisToolFreehandHelper::stabilizerPollAndPaint() { - KisPaintInformation newInfo = - m_d->getStabilizedPaintInfo(m_d->stabilizerDeque, m_d->stabilizerLastPaintInfo); + KisStabilizedEventsSampler::iterator it; + KisStabilizedEventsSampler::iterator end; + std::tie(it, end) = m_d->stabilizedSampler.range(); - bool canPaint = true; + for (; it != end; ++it) { + KisPaintInformation sampledInfo = *it; - if (m_d->smoothingOptions->useDelayDistance()) { - const qreal R = m_d->smoothingOptions->delayDistance() / - m_d->resources->effectiveZoom(); + bool canPaint = true; - QPointF diff = m_d->stabilizerLastPaintInfo.pos() - m_d->previousPaintInformation.pos(); - qreal dx = sqrt(pow2(diff.x()) + pow2(diff.y())); + if (m_d->smoothingOptions->useDelayDistance()) { + const qreal R = m_d->smoothingOptions->delayDistance() / + m_d->resources->effectiveZoom(); - canPaint = dx > R; - } + QPointF diff = sampledInfo.pos() - m_d->previousPaintInformation.pos(); + qreal dx = sqrt(pow2(diff.x()) + pow2(diff.y())); + + canPaint = dx > R; + } + + if (canPaint) { + KisPaintInformation newInfo = + m_d->getStabilizedPaintInfo(m_d->stabilizerDeque, sampledInfo); - if (canPaint) { - paintLine(m_d->previousPaintInformation, newInfo); - m_d->previousPaintInformation = newInfo; + paintLine(m_d->previousPaintInformation, newInfo); + m_d->previousPaintInformation = newInfo; - // Push the new entry through the queue - m_d->stabilizerDeque.dequeue(); - m_d->stabilizerDeque.enqueue(m_d->stabilizerLastPaintInfo); + // Push the new entry through the queue + m_d->stabilizerDeque.dequeue(); + m_d->stabilizerDeque.enqueue(sampledInfo); - emit requestExplicitUpdateOutline(); - } else if (m_d->stabilizerDeque.head().pos() != m_d->previousPaintInformation.pos()) { + emit requestExplicitUpdateOutline(); + } 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(); + QQueue::iterator it = m_d->stabilizerDeque.begin(); + QQueue::iterator end = m_d->stabilizerDeque.end(); - while (it != end) { - *it = m_d->previousPaintInformation; - ++it; + while (it != end) { + *it = m_d->previousPaintInformation; + ++it; + } } } + + m_d->stabilizedSampler.clear(); } void KisToolFreehandHelper::stabilizerEnd() { // FIXME: Ugly hack, this is no a "distance" in any way int sampleSize = m_d->smoothingOptions->smoothnessDistance(); assert(sampleSize > 0); // Stop the timer m_d->stabilizerPollTimer.stop(); // Finish the line for (int i = sampleSize; i > 0; i--) { // In each iteration we add the latest paint info and delete the oldest // After `sampleSize` iterations the deque will be filled with the latest // value and we will have reached the end point. if (m_d->smoothingOptions->finishStabilizedCurve()) { stabilizerPollAndPaint(); } } } const KisPaintOp* KisToolFreehandHelper::currentPaintOp() const { return !m_d->painterInfos.isEmpty() ? m_d->painterInfos.first()->painter->paintOp() : 0; } 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() { if(!m_d->painterInfos.isEmpty()) { paintAt(m_d->previousPaintInformation); } } void KisToolFreehandHelper::paintAt(int painterInfoId, const KisPaintInformation &pi) { m_d->hasPaintAtLeastOnce = true; m_d->strokesFacade->addJob(m_d->strokeId, new FreehandStrokeStrategy::Data(m_d->resources->currentNode(), painterInfoId, pi)); if(m_d->recordingAdapter) { m_d->recordingAdapter->addPoint(pi); } } void KisToolFreehandHelper::paintLine(int painterInfoId, const KisPaintInformation &pi1, const KisPaintInformation &pi2) { m_d->hasPaintAtLeastOnce = true; m_d->strokesFacade->addJob(m_d->strokeId, new FreehandStrokeStrategy::Data(m_d->resources->currentNode(), painterInfoId, pi1, pi2)); if(m_d->recordingAdapter) { m_d->recordingAdapter->addLine(pi1, pi2); } } void KisToolFreehandHelper::paintBezierCurve(int painterInfoId, 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(m_d->resources->currentNode(), painterInfoId, pi1, control1, control2, pi2)); if(m_d->recordingAdapter) { m_d->recordingAdapter->addCurve(pi1, control1, control2, pi2); } } void KisToolFreehandHelper::createPainters(QVector &painterInfos, const QPointF &lastPosition, int lastTime) { painterInfos << new PainterInfo(lastPosition, lastTime); } 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); } int KisToolFreehandHelper::canvasRotation() { return m_d->canvasRotation; } void KisToolFreehandHelper::setCanvasRotation(int rotation) { m_d->canvasRotation = rotation; } bool KisToolFreehandHelper::canvasMirroredH() { return m_d->canvasMirroredH; } void KisToolFreehandHelper::setCanvasHorizontalMirrorState(bool mirrored) { m_d->canvasMirroredH = mirrored; } diff --git a/libs/ui/tool/kis_tool_paint.cc b/libs/ui/tool/kis_tool_paint.cc index b5e12eee44..8f99784fa4 100644 --- a/libs/ui/tool/kis_tool_paint.cc +++ b/libs/ui/tool/kis_tool_paint.cc @@ -1,775 +1,775 @@ /* * Copyright (c) 2003-2009 Boudewijn Rempt * Copyright (c) 2015 Moritz Molch * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tool_paint.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_display_color_converter.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_cursor.h" #include "widgets/kis_cmb_composite.h" #include "widgets/kis_slider_spin_box.h" #include "kis_canvas_resource_provider.h" #include #include "kis_tool_utils.h" #include #include #include #include #include "strokes/kis_color_picker_stroke_strategy.h" KisToolPaint::KisToolPaint(KoCanvasBase * canvas, const QCursor & cursor) : KisTool(canvas, cursor), m_showColorPreview(false), m_colorPreviewShowComparePlate(false), m_colorPickerDelayTimer(), m_isOutlineEnabled(true) { m_specialHoverModifier = false; m_optionsWidgetLayout = 0; m_opacity = OPACITY_OPAQUE_U8; updateTabletPressureSamples(); m_supportOutline = false; { const int maxSize = 1000; int brushSize = 1; do { m_standardBrushSizes.push_back(brushSize); int increment = qMax(1, int(std::ceil(qreal(brushSize) / 15))); brushSize += increment; } while (brushSize < maxSize); m_standardBrushSizes.push_back(maxSize); } KisCanvas2 * kiscanvas = dynamic_cast(canvas); KisActionManager *actionManager = kiscanvas->viewManager()->actionManager(); // XXX: Perhaps a better place for these? if (!actionManager->actionByName("increase_brush_size")) { KisAction *increaseBrushSize = new KisAction(i18n("Increase Brush Size")); increaseBrushSize->setShortcut(Qt::Key_BracketRight); actionManager->addAction("increase_brush_size", increaseBrushSize); } if (!actionManager->actionByName("decrease_brush_size")) { KisAction *decreaseBrushSize = new KisAction(i18n("Decrease Brush Size")); decreaseBrushSize->setShortcut(Qt::Key_BracketLeft); actionManager->addAction("decrease_brush_size", decreaseBrushSize); } addAction("increase_brush_size", dynamic_cast(actionManager->actionByName("increase_brush_size"))); addAction("decrease_brush_size", dynamic_cast(actionManager->actionByName("decrease_brush_size"))); if (kiscanvas && kiscanvas->viewManager()) { connect(this, SIGNAL(sigPaintingFinished()), kiscanvas->viewManager()->resourceProvider(), SLOT(slotPainting())); } m_colorPickerDelayTimer.setSingleShot(true); connect(&m_colorPickerDelayTimer, SIGNAL(timeout()), this, SLOT(activatePickColorDelayed())); using namespace std::placeholders; // For _1 placeholder std::function callback = std::bind(&KisToolPaint::addPickerJob, this, _1); m_colorPickingCompressor.reset( new PickingCompressor(100, callback, KisSignalCompressor::FIRST_ACTIVE)); } KisToolPaint::~KisToolPaint() { } int KisToolPaint::flags() const { return KisTool::FLAG_USES_CUSTOM_COMPOSITEOP; } void KisToolPaint::canvasResourceChanged(int key, const QVariant& v) { KisTool::canvasResourceChanged(key, v); switch(key) { case(KisCanvasResourceProvider::Opacity): setOpacity(v.toDouble()); break; default: //nothing break; } connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(resetCursorStyle()), Qt::UniqueConnection); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(updateTabletPressureSamples()), Qt::UniqueConnection); } void KisToolPaint::activate(ToolActivation toolActivation, const QSet &shapes) { if (currentPaintOpPreset()) emit statusTextChanged(currentPaintOpPreset()->name()); KisTool::activate(toolActivation, shapes); connect(actions().value("increase_brush_size"), SIGNAL(triggered()), SLOT(increaseBrushSize()), Qt::UniqueConnection); connect(actions().value("decrease_brush_size"), SIGNAL(triggered()), SLOT(decreaseBrushSize()), Qt::UniqueConnection); } void KisToolPaint::deactivate() { disconnect(actions().value("increase_brush_size"), 0, this, 0); disconnect(actions().value("decrease_brush_size"), 0, this, 0); KisTool::deactivate(); } QPainterPath KisToolPaint::tryFixBrushOutline(const QPainterPath &originalOutline) { KisConfig cfg; if (cfg.newOutlineStyle() == OUTLINE_NONE) return originalOutline; const qreal minThresholdSize = cfg.outlineSizeMinimum(); /** * If the brush outline is bigger than the canvas itself (which * would make it invisible for a user in most of the cases) just * add a cross in the center of it */ QSize widgetSize = canvas()->canvasWidget()->size(); const int maxThresholdSum = widgetSize.width() + widgetSize.height(); QPainterPath outline = originalOutline; QRectF boundingRect = outline.boundingRect(); const qreal sum = boundingRect.width() + boundingRect.height(); QPointF center = boundingRect.center(); if (sum > maxThresholdSum) { const int hairOffset = 7; outline.moveTo(center.x(), center.y() - hairOffset); outline.lineTo(center.x(), center.y() + hairOffset); outline.moveTo(center.x() - hairOffset, center.y()); outline.lineTo(center.x() + hairOffset, center.y()); } else if (sum < minThresholdSize && !outline.isEmpty()) { outline = QPainterPath(); outline.addEllipse(center, 0.5 * minThresholdSize, 0.5 * minThresholdSize); } return outline; } void KisToolPaint::paint(QPainter &gc, const KoViewConverter &converter) { Q_UNUSED(converter); QPainterPath path = tryFixBrushOutline(pixelToView(m_currentOutline)); paintToolOutline(&gc, path); if (m_showColorPreview) { QRectF viewRect = converter.documentToView(m_oldColorPreviewRect); gc.fillRect(viewRect, m_colorPreviewCurrentColor); if (m_colorPreviewShowComparePlate) { QRectF baseColorRect = viewRect.translated(viewRect.width(), 0); gc.fillRect(baseColorRect, m_colorPreviewBaseColor); } } } void KisToolPaint::setMode(ToolMode mode) { if(this->mode() == KisTool::PAINT_MODE && mode != KisTool::PAINT_MODE) { // Let's add history information about recently used colors emit sigPaintingFinished(); } KisTool::setMode(mode); } void KisToolPaint::activatePickColor(AlternateAction action) { m_showColorPreview = true; requestUpdateOutline(m_outlineDocPoint, 0); int resource = colorPreviewResourceId(action); KoColor color = canvas()->resourceManager()->koColorResource(resource); KisCanvas2 * kisCanvas = dynamic_cast(canvas()); KIS_ASSERT_RECOVER_RETURN(kisCanvas); m_colorPreviewCurrentColor = kisCanvas->displayColorConverter()->toQColor(color); if (!m_colorPreviewBaseColor.isValid()) { m_colorPreviewBaseColor = m_colorPreviewCurrentColor; } } void KisToolPaint::deactivatePickColor(AlternateAction action) { Q_UNUSED(action); m_showColorPreview = false; m_oldColorPreviewRect = QRect(); m_oldColorPreviewUpdateRect = QRect(); m_colorPreviewCurrentColor = QColor(); } void KisToolPaint::pickColorWasOverridden() { m_colorPreviewShowComparePlate = false; m_colorPreviewBaseColor = QColor(); } void KisToolPaint::activateAlternateAction(AlternateAction action) { switch (action) { case PickFgNode: case PickBgNode: case PickFgImage: case PickBgImage: delayedAction = action; m_colorPickerDelayTimer.start(100); default: pickColorWasOverridden(); KisTool::activateAlternateAction(action); }; } void KisToolPaint::activatePickColorDelayed() { switch (delayedAction) { case PickFgNode: useCursor(KisCursor::pickerLayerForegroundCursor()); activatePickColor(delayedAction); break; case PickBgNode: useCursor(KisCursor::pickerLayerBackgroundCursor()); activatePickColor(delayedAction); break; case PickFgImage: useCursor(KisCursor::pickerImageForegroundCursor()); activatePickColor(delayedAction); break; case PickBgImage: useCursor(KisCursor::pickerImageBackgroundCursor()); activatePickColor(delayedAction); break; default: break; }; repaintDecorations(); } bool KisToolPaint::isPickingAction(AlternateAction action) { return action == PickFgNode || action == PickBgNode || action == PickFgImage || action == PickBgImage; } void KisToolPaint::deactivateAlternateAction(AlternateAction action) { if (!isPickingAction(action)) { KisTool::deactivateAlternateAction(action); return; } delayedAction = KisTool::NONE; m_colorPickerDelayTimer.stop(); resetCursorStyle(); deactivatePickColor(action); } void KisToolPaint::addPickerJob(const PickingJob &pickingJob) { /** * The actual picking is delayed by a compressor, so we can get this * event when the stroke is already closed */ if (!m_pickerStrokeId) return; KIS_ASSERT_RECOVER_RETURN(isPickingAction(pickingJob.action)); const QPoint imagePoint = image()->documentToIntPixel(pickingJob.documentPixel); const bool fromCurrentNode = pickingJob.action == PickFgNode || pickingJob.action == PickBgNode; m_pickingResource = colorPreviewResourceId(pickingJob.action); KisPaintDeviceSP device = fromCurrentNode ? currentNode()->projection() : image()->projection(); image()->addJob(m_pickerStrokeId, new KisColorPickerStrokeStrategy::Data(device, imagePoint)); } void KisToolPaint::beginAlternateAction(KoPointerEvent *event, AlternateAction action) { if (isPickingAction(action)) { KIS_ASSERT_RECOVER_RETURN(!m_pickerStrokeId); setMode(SECONDARY_PAINT_MODE); KisColorPickerStrokeStrategy *strategy = new KisColorPickerStrokeStrategy(); connect(strategy, &KisColorPickerStrokeStrategy::sigColorUpdated, this, &KisToolPaint::slotColorPickingFinished); m_pickerStrokeId = image()->startStroke(strategy); m_colorPickingCompressor->start(PickingJob(event->point, action)); requestUpdateOutline(event->point, event); } else { KisTool::beginAlternateAction(event, action); } } void KisToolPaint::continueAlternateAction(KoPointerEvent *event, AlternateAction action) { if (isPickingAction(action)) { KIS_ASSERT_RECOVER_RETURN(m_pickerStrokeId); m_colorPickingCompressor->start(PickingJob(event->point, action)); requestUpdateOutline(event->point, event); } else { KisTool::continueAlternateAction(event, action); } } void KisToolPaint::endAlternateAction(KoPointerEvent *event, AlternateAction action) { if (isPickingAction(action)) { KIS_ASSERT_RECOVER_RETURN(m_pickerStrokeId); image()->endStroke(m_pickerStrokeId); m_pickerStrokeId.clear(); requestUpdateOutline(event->point, event); setMode(HOVER_MODE); } else { KisTool::endAlternateAction(event, action); } } int KisToolPaint::colorPreviewResourceId(AlternateAction action) { bool toForegroundColor = action == PickFgNode || action == PickFgImage; int resource = toForegroundColor ? KoCanvasResourceManager::ForegroundColor : KoCanvasResourceManager::BackgroundColor; return resource; } void KisToolPaint::slotColorPickingFinished(const KoColor &color) { canvas()->resourceManager()->setResource(m_pickingResource, color); if (!m_showColorPreview) return; KisCanvas2 * kisCanvas = dynamic_cast(canvas()); KIS_ASSERT_RECOVER_RETURN(kisCanvas); QColor previewColor = kisCanvas->displayColorConverter()->toQColor(color); m_colorPreviewShowComparePlate = true; m_colorPreviewCurrentColor = previewColor; requestUpdateOutline(m_outlineDocPoint, 0); } void KisToolPaint::mousePressEvent(KoPointerEvent *event) { KisTool::mousePressEvent(event); if (mode() == KisTool::HOVER_MODE) { requestUpdateOutline(event->point, event); } } void KisToolPaint::mouseMoveEvent(KoPointerEvent *event) { KisTool::mouseMoveEvent(event); if (mode() == KisTool::HOVER_MODE) { requestUpdateOutline(event->point, event); } } void KisToolPaint::mouseReleaseEvent(KoPointerEvent *event) { KisTool::mouseReleaseEvent(event); if (mode() == KisTool::HOVER_MODE) { requestUpdateOutline(event->point, event); } } QWidget * KisToolPaint::createOptionWidget() { QWidget * optionWidget = new QWidget(); optionWidget->setObjectName(toolId()); QVBoxLayout* verticalLayout = new QVBoxLayout(optionWidget); verticalLayout->setObjectName("KisToolPaint::OptionWidget::VerticalLayout"); verticalLayout->setContentsMargins(0,0,0,0); verticalLayout->setSpacing(1); // See https://bugs.kde.org/show_bug.cgi?id=316896 QWidget *specialSpacer = new QWidget(optionWidget); specialSpacer->setObjectName("SpecialSpacer"); specialSpacer->setFixedSize(0, 0); verticalLayout->addWidget(specialSpacer); verticalLayout->addWidget(specialSpacer); m_optionsWidgetLayout = new QGridLayout(); m_optionsWidgetLayout->setColumnStretch(1, 1); verticalLayout->addLayout(m_optionsWidgetLayout); m_optionsWidgetLayout->setContentsMargins(0,0,0,0); m_optionsWidgetLayout->setSpacing(1); if (!quickHelp().isEmpty()) { QPushButton* push = new QPushButton(KisIconUtils::loadIcon("help-contents"), QString(), optionWidget); connect(push, SIGNAL(clicked()), this, SLOT(slotPopupQuickHelp())); QHBoxLayout* hLayout = new QHBoxLayout(optionWidget); hLayout->addWidget(push); hLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed)); verticalLayout->addLayout(hLayout); } return optionWidget; } QWidget* findLabelWidget(QGridLayout *layout, QWidget *control) { QWidget *result = 0; int index = layout->indexOf(control); int row, col, rowSpan, colSpan; layout->getItemPosition(index, &row, &col, &rowSpan, &colSpan); if (col > 0) { QLayoutItem *item = layout->itemAtPosition(row, col - 1); if (item) { result = item->widget(); } } else { QLayoutItem *item = layout->itemAtPosition(row, col + 1); if (item) { result = item->widget(); } } return result; } void KisToolPaint::showControl(QWidget *control, bool value) { control->setVisible(value); QWidget *label = findLabelWidget(m_optionsWidgetLayout, control); if (label) { label->setVisible(value); } } void KisToolPaint::enableControl(QWidget *control, bool value) { control->setEnabled(value); QWidget *label = findLabelWidget(m_optionsWidgetLayout, control); if (label) { label->setEnabled(value); } } void KisToolPaint::addOptionWidgetLayout(QLayout *layout) { Q_ASSERT(m_optionsWidgetLayout != 0); int rowCount = m_optionsWidgetLayout->rowCount(); m_optionsWidgetLayout->addLayout(layout, rowCount, 0, 1, 2); } void KisToolPaint::addOptionWidgetOption(QWidget *control, QWidget *label) { Q_ASSERT(m_optionsWidgetLayout != 0); if (label) { m_optionsWidgetLayout->addWidget(label, m_optionsWidgetLayout->rowCount(), 0); m_optionsWidgetLayout->addWidget(control, m_optionsWidgetLayout->rowCount() - 1, 1); } else { m_optionsWidgetLayout->addWidget(control, m_optionsWidgetLayout->rowCount(), 0, 1, 2); } } void KisToolPaint::setOpacity(qreal opacity) { m_opacity = quint8(opacity * OPACITY_OPAQUE_U8); } const KoCompositeOp* KisToolPaint::compositeOp() { if (currentNode()) { KisPaintDeviceSP device = currentNode()->paintDevice(); if (device) { QString op = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentCompositeOp).toString(); return device->colorSpace()->compositeOp(op); } } return 0; } void KisToolPaint::slotPopupQuickHelp() { QWhatsThis::showText(QCursor::pos(), quickHelp()); } void KisToolPaint::updateTabletPressureSamples() { KisConfig cfg; KisCubicCurve curve; curve.fromString(cfg.pressureTabletCurve()); m_pressureSamples = curve.floatTransfer(LEVEL_OF_PRESSURE_RESOLUTION + 1); } void KisToolPaint::setupPaintAction(KisRecordedPaintAction* action) { KisTool::setupPaintAction(action); action->setOpacity(m_opacity / qreal(255.0)); const KoCompositeOp* op = compositeOp(); if (op) { action->setCompositeOp(op->id()); } } KisToolPaint::NodePaintAbility KisToolPaint::nodePaintAbility() { KisNodeSP node = currentNode(); if (!node || node->systemLocked()) { return NONE; } if (node->inherits("KisShapeLayer")) { return VECTOR; } if (node->paintDevice()) { return PAINT; } return NONE; } void KisToolPaint::activatePrimaryAction() { pickColorWasOverridden(); setOutlineEnabled(true); KisTool::activatePrimaryAction(); } void KisToolPaint::deactivatePrimaryAction() { setOutlineEnabled(false); KisTool::deactivatePrimaryAction(); } bool KisToolPaint::isOutlineEnabled() const { return m_isOutlineEnabled; } void KisToolPaint::setOutlineEnabled(bool value) { m_isOutlineEnabled = value; requestUpdateOutline(m_outlineDocPoint, 0); } void KisToolPaint::increaseBrushSize() { qreal paintopSize = currentPaintOpPreset()->settings()->paintOpSize().width(); std::vector::iterator result = std::upper_bound(m_standardBrushSizes.begin(), m_standardBrushSizes.end(), - (int)paintopSize); + qRound(paintopSize)); int newValue = result != m_standardBrushSizes.end() ? *result : m_standardBrushSizes.back(); qreal increment = newValue - paintopSize; currentPaintOpPreset()->settings()->changePaintOpSize(increment, 0); requestUpdateOutline(m_outlineDocPoint, 0); } void KisToolPaint::decreaseBrushSize() { qreal paintopSize = currentPaintOpPreset()->settings()->paintOpSize().width(); std::vector::reverse_iterator result = std::upper_bound(m_standardBrushSizes.rbegin(), m_standardBrushSizes.rend(), (int)paintopSize, std::greater()); int newValue = result != m_standardBrushSizes.rend() ? *result : m_standardBrushSizes.front(); qreal decrement = newValue - paintopSize; currentPaintOpPreset()->settings()->changePaintOpSize(decrement, 0); requestUpdateOutline(m_outlineDocPoint, 0); } QRectF KisToolPaint::colorPreviewDocRect(const QPointF &outlineDocPoint) { if (!m_showColorPreview) return QRect(); KisConfig cfg; const QRectF colorPreviewViewRect = cfg.colorPreviewRect(); const QRectF colorPreviewDocumentRect = canvas()->viewConverter()->viewToDocument(colorPreviewViewRect); return colorPreviewDocumentRect.translated(outlineDocPoint); } void KisToolPaint::requestUpdateOutline(const QPointF &outlineDocPoint, const KoPointerEvent *event) { if (!m_supportOutline) return; KisConfig cfg; KisPaintOpSettings::OutlineMode outlineMode; outlineMode = KisPaintOpSettings::CursorNoOutline; if (isOutlineEnabled() && (mode() == KisTool::GESTURE_MODE || ((cfg.newOutlineStyle() == OUTLINE_FULL || cfg.newOutlineStyle() == OUTLINE_CIRCLE || cfg.newOutlineStyle() == OUTLINE_TILT || cfg.newOutlineStyle() == OUTLINE_COLOR ) && ((mode() == HOVER_MODE) || (mode() == PAINT_MODE && cfg.showOutlineWhilePainting()))))) { // lisp forever! if(cfg.newOutlineStyle() == OUTLINE_CIRCLE) { outlineMode = KisPaintOpSettings::CursorIsCircleOutline; } else if(cfg.newOutlineStyle() == OUTLINE_TILT) { outlineMode = KisPaintOpSettings::CursorTiltOutline; } else if(cfg.newOutlineStyle() == OUTLINE_COLOR) { outlineMode = KisPaintOpSettings::CursorColorOutline; } else { outlineMode = KisPaintOpSettings::CursorIsOutline; } } m_outlineDocPoint = outlineDocPoint; m_currentOutline = getOutlinePath(m_outlineDocPoint, event, outlineMode); QRectF outlinePixelRect = m_currentOutline.boundingRect(); QRectF outlineDocRect = currentImage()->pixelToDocument(outlinePixelRect); // This adjusted call is needed as we paint with a 3 pixel wide brush and the pen is outside the bounds of the path // Pen uses view coordinates so we have to zoom the document value to match 2 pixel in view coordiates // See BUG 275829 qreal zoomX; qreal zoomY; canvas()->viewConverter()->zoom(&zoomX, &zoomY); qreal xoffset = 2.0/zoomX; qreal yoffset = 2.0/zoomY; if (!outlineDocRect.isEmpty()) { outlineDocRect.adjust(-xoffset,-yoffset,xoffset,yoffset); } QRectF colorPreviewDocRect = this->colorPreviewDocRect(m_outlineDocPoint); QRectF colorPreviewDocUpdateRect; if (!colorPreviewDocRect.isEmpty()) { colorPreviewDocUpdateRect.adjust(-xoffset,-yoffset,xoffset,yoffset); } if (!m_oldColorPreviewUpdateRect.isEmpty()) { canvas()->updateCanvas(m_oldColorPreviewUpdateRect); } if (!m_oldOutlineRect.isEmpty()) { canvas()->updateCanvas(m_oldOutlineRect); } if (!outlineDocRect.isEmpty()) { canvas()->updateCanvas(outlineDocRect); } if (!colorPreviewDocUpdateRect.isEmpty()) { canvas()->updateCanvas(colorPreviewDocUpdateRect); } m_oldOutlineRect = outlineDocRect; m_oldColorPreviewRect = colorPreviewDocRect; m_oldColorPreviewUpdateRect = colorPreviewDocUpdateRect; } QPainterPath KisToolPaint::getOutlinePath(const QPointF &documentPos, const KoPointerEvent *event, KisPaintOpSettings::OutlineMode outlineMode) { Q_UNUSED(event); QPointF imagePos = currentImage()->documentToPixel(documentPos); QPainterPath path = currentPaintOpPreset()->settings()-> brushOutline(KisPaintInformation(imagePos), outlineMode); return path; } diff --git a/libs/ui/widgets/kis_categorized_list_view.cpp b/libs/ui/widgets/kis_categorized_list_view.cpp index 9f3aca5d17..65311b4c3a 100644 --- a/libs/ui/widgets/kis_categorized_list_view.cpp +++ b/libs/ui/widgets/kis_categorized_list_view.cpp @@ -1,140 +1,148 @@ /* * 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_categorized_list_view.h" #include "kis_categorized_list_model.h" #include #include #include #include #include #include #include - +#include "kis_debug.h" KisCategorizedListView::KisCategorizedListView(bool useCheckBoxHack, QWidget* parent): QListView(parent), m_useCheckBoxHack(useCheckBoxHack) { connect(this, SIGNAL(clicked(QModelIndex)), this, SLOT(slotIndexChanged(QModelIndex))); } KisCategorizedListView::~KisCategorizedListView() { } void KisCategorizedListView::setModel(QAbstractItemModel* model) { QListView::setModel(model); updateRows(0, model->rowCount()); model->sort(0); } +QSize KisCategorizedListView::sizeHint() const +{ + const QSize sh = QListView::sizeHint(); + const int width = sizeHintForColumn(0); + + return QSize(width, sh.height()); +} + void KisCategorizedListView::updateRows(int begin, int end) { for(; begin!=end; ++begin) { QModelIndex index = model()->index(begin, 0); bool isHeader = model()->data(index, __CategorizedListModelBase::IsHeaderRole).toBool(); bool expanded = model()->data(index, __CategorizedListModelBase::ExpandCategoryRole).toBool(); setRowHidden(begin, !expanded && !isHeader); } } void KisCategorizedListView::slotIndexChanged(const QModelIndex& index) { if(model()->data(index, __CategorizedListModelBase::IsHeaderRole).toBool()) { bool expanded = model()->data(index, __CategorizedListModelBase::ExpandCategoryRole).toBool(); model()->setData(index, !expanded, __CategorizedListModelBase::ExpandCategoryRole); emit sigCategoryToggled(index, !expanded); } } void KisCategorizedListView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector &/*roles*/) { QListView::dataChanged(topLeft, bottomRight); updateRows(topLeft.row(), bottomRight.row()+1); } void KisCategorizedListView::rowsInserted(const QModelIndex& parent, int start, int end) { QListView::rowsInserted(parent, start, end); updateRows(0, model()->rowCount()); model()->sort(0); } void KisCategorizedListView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { QListView::rowsAboutToBeRemoved(parent, start, end); model()->sort(0); } void KisCategorizedListView::mousePressEvent(QMouseEvent* event) { if (m_useCheckBoxHack) { QModelIndex index = QListView::indexAt(event->pos()); if (index.isValid() && (event->pos().x() < 25) && (model()->flags(index) & Qt::ItemIsUserCheckable)) { QListView::mousePressEvent(event); QMouseEvent releaseEvent(QEvent::MouseButtonRelease, event->pos(), event->globalPos(), event->button(), event->button() | event->buttons(), event->modifiers()); QListView::mouseReleaseEvent(&releaseEvent); emit sigEntryChecked(index); return; } } QListView::mousePressEvent(event); if(event->button() == Qt::RightButton){ QModelIndex index = QListView::indexAt(event->pos()); QMenu menu(this); if(index.data(__CategorizedListModelBase::isLockableRole).toBool() && index.isValid()) { bool locked = index.data(__CategorizedListModelBase::isLockedRole).toBool(); QIcon icon = locked ? KisIconUtils::loadIcon("locked") : KisIconUtils::loadIcon("unlocked"); QAction* action1 = menu.addAction(icon, locked ? i18n("Unlock (restore settings from preset)") : i18n("Lock")); connect(action1, SIGNAL(triggered()), this, SIGNAL(rightClickedMenuDropSettingsTriggered())); if (locked){ QAction* action2 = menu.addAction(icon, i18n("Unlock (keep current settings)")); connect(action2, SIGNAL(triggered()), this, SIGNAL(rightClickedMenuSaveSettingsTriggered())); } menu.exec(event->globalPos()); } } } void KisCategorizedListView::mouseReleaseEvent(QMouseEvent* event) { QListView::mouseReleaseEvent(event); QModelIndex index = QListView::indexAt(event->pos()); if(index.data(__CategorizedListModelBase::isToggledRole).toBool() && index.isValid()){ emit sigEntryChecked(index); } } diff --git a/libs/ui/widgets/kis_categorized_list_view.h b/libs/ui/widgets/kis_categorized_list_view.h index 279525c09b..06f2c4910e 100644 --- a/libs/ui/widgets/kis_categorized_list_view.h +++ b/libs/ui/widgets/kis_categorized_list_view.h @@ -1,63 +1,63 @@ /* * 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. */ #ifndef KIS_CATEGORIZED_LIST_VIEW_H_ #define KIS_CATEGORIZED_LIST_VIEW_H_ #include #include #include class KRITAUI_EXPORT KisCategorizedListView: public QListView { Q_OBJECT public: KisCategorizedListView(bool useCheckBoxHack=false, QWidget* parent=0); virtual ~KisCategorizedListView(); virtual void setModel(QAbstractItemModel* model); - + QSize sizeHint() const; Q_SIGNALS: void sigCategoryToggled(const QModelIndex& index, bool toggled); void sigEntryChecked(const QModelIndex& index); void rightClickedMenuDropSettingsTriggered(); void rightClickedMenuSaveSettingsTriggered(); protected Q_SLOTS: void slotIndexChanged(const QModelIndex& index); virtual void dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector &roles = QVector()); virtual void rowsInserted(const QModelIndex& parent, int start, int end); virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); virtual void mousePressEvent(QMouseEvent* event); virtual void mouseReleaseEvent(QMouseEvent* event); private: void updateRows(int begin, int end); private: bool m_useCheckBoxHack; }; #endif // KIS_CATEGORIZED_LIST_VIEW_H_ diff --git a/libs/ui/widgets/kis_cmb_composite.cc b/libs/ui/widgets/kis_cmb_composite.cc index 3f87820189..a467a6e0b6 100644 --- a/libs/ui/widgets/kis_cmb_composite.cc +++ b/libs/ui/widgets/kis_cmb_composite.cc @@ -1,469 +1,469 @@ /* * kis_cmb_composite.cc - part of KImageShop/Krayon/Krita * * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org) * 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_cmb_composite.h" #include #include #include "kis_composite_ops_model.h" #include "kis_categorized_item_delegate.h" #include ////////////////////////////////////////////////////////////////////////////////////////// // ---- KisCompositeOpListWidget ------------------------------------------------------ // KisCompositeOpListWidget::KisCompositeOpListWidget(QWidget* parent): KisCategorizedListView(false, parent), m_model(new KisSortedCompositeOpListModel(this)) { setModel(m_model); - setItemDelegate(new KisCategorizedItemDelegate(true, this)); + setItemDelegate(new KisCategorizedItemDelegate(this)); } KisCompositeOpListWidget::~KisCompositeOpListWidget() { } KoID KisCompositeOpListWidget::selectedCompositeOp() const { KoID op; if (m_model->entryAt(op, currentIndex())) { return op; } return KoCompositeOpRegistry::instance().getDefaultCompositeOp(); } ////////////////////////////////////////////////////////////////////////////////////////// // ---- KisCompositeOpComboBox -------------------------------------------------------- // KisCompositeOpComboBox::KisCompositeOpComboBox(QWidget* parent): QComboBox(parent), m_model(new KisSortedCompositeOpListModel(this)), m_allowToHidePopup(true) { m_view = new KisCategorizedListView(true); setMaxVisibleItems(100); setSizeAdjustPolicy(AdjustToContents); m_view->setResizeMode(QListView::Adjust); setToolTip(i18n("Blending Mode")); setModel(m_model); setView(m_view); - setItemDelegate(new KisCategorizedItemDelegate(true, this)); + setItemDelegate(new KisCategorizedItemDelegate(this)); connect(m_view, SIGNAL(sigCategoryToggled(const QModelIndex&, bool)), SLOT(slotCategoryToggled(const QModelIndex&, bool))); connect(m_view, SIGNAL(sigEntryChecked(const QModelIndex&)), SLOT(slotEntryChecked(const QModelIndex&))); selectCompositeOp(KoCompositeOpRegistry::instance().getDefaultCompositeOp()); KisAction *action = 0; // // Cycle through blending modes // // Shift + + (plus) or – (minus) // KisAction *action = new KisAction(i18n("Next Blending Mode"), this); // action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_Plus)); // connect(action, SIGNAL(triggered()), SLOT(slotNextBlendingMode())); // m_actions << action; // action = new KisAction(i18n("Previous Blending Mode"), this); // action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_Minus)); // connect(action, SIGNAL(triggered()), SLOT(slotPreviousBlendingMode())); // m_actions << action; // Normal // Shift + Alt + N action = new KisAction(i18n("Select Normal Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_N)); connect(action, SIGNAL(triggered()), SLOT(slotNormal())); m_actions << action; // Dissolve // Shift + Alt + I action = new KisAction(i18n("Select Dissolve Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_I)); connect(action, SIGNAL(triggered()), SLOT(slotDissolve())); m_actions << action; // Behind (Brush tool only) // Shift + Alt + Q action = new KisAction(i18n("Select Behind Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_Q)); connect(action, SIGNAL(triggered()), SLOT(slotBehind())); m_actions << action; // Clear (Brush tool only) // Shift + Alt + R action = new KisAction(i18n("Select Clear Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_R)); connect(action, SIGNAL(triggered()), SLOT(slotClear())); m_actions << action; // Darken // Shift + Alt + K action = new KisAction(i18n("Select Darken Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_K)); connect(action, SIGNAL(triggered()), SLOT(slotDarken())); m_actions << action; // Multiply // Shift + Alt + M action = new KisAction(i18n("Select Multiply Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_M)); connect(action, SIGNAL(triggered()), SLOT(slotMultiply())); m_actions << action; // Color Burn // Shift + Alt + B action = new KisAction(i18n("Select Color Burn Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_B)); connect(action, SIGNAL(triggered()), SLOT(slotColorBurn())); m_actions << action; // Linear Burn // Shift + Alt + A action = new KisAction(i18n("Select Linear Burn Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_A)); connect(action, SIGNAL(triggered()), SLOT(slotLinearBurn())); m_actions << action; // Lighten // Shift + Alt + G action = new KisAction(i18n("Select Lighten Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_G)); connect(action, SIGNAL(triggered()), SLOT(slotLighten())); m_actions << action; // Screen // Shift + Alt + S action = new KisAction(i18n("Select Screen Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_S)); connect(action, SIGNAL(triggered()), SLOT(slotScreen())); m_actions << action; // Color Dodge // Shift + Alt + D action = new KisAction(i18n("Select Color Dodge Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_D)); connect(action, SIGNAL(triggered()), SLOT(slotColorDodge())); m_actions << action; // Linear Dodge // Shift + Alt + W action = new KisAction(i18n("Select Linear Dodge Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_W)); connect(action, SIGNAL(triggered()), SLOT(slotLinearDodge())); m_actions << action; // Overlay // Shift + Alt + O action = new KisAction(i18n("Select Overlay Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_O)); connect(action, SIGNAL(triggered()), SLOT(slotOverlay())); m_actions << action; // Soft Light // Shift + Alt + F action = new KisAction(i18n("Select Soft Light Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_F)); connect(action, SIGNAL(triggered()), SLOT(slotSoftLight())); m_actions << action; // Hard Light // Shift + Alt + H action = new KisAction(i18n("Select Hard Light Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_H)); connect(action, SIGNAL(triggered()), SLOT(slotHardLight())); m_actions << action; // Vivid Light // Shift + Alt + V action = new KisAction(i18n("Select Vivid Light Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_V)); connect(action, SIGNAL(triggered()), SLOT(slotVividLight())); m_actions << action; // Linear Light // Shift + Alt + J action = new KisAction(i18n("Select Linear Light Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_J)); connect(action, SIGNAL(triggered()), SLOT(slotLinearLight())); m_actions << action; // Pin Light // Shift + Alt + Z action = new KisAction(i18n("Select Pin Light Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_Z)); connect(action, SIGNAL(triggered()), SLOT(slotPinLight())); m_actions << action; // Hard Mix // Shift + Alt + L action = new KisAction(i18n("Select Hard Mix Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_L)); connect(action, SIGNAL(triggered()), SLOT(slotHardMix())); m_actions << action; // Difference // Shift + Alt + E action = new KisAction(i18n("Select Difference Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_E)); connect(action, SIGNAL(triggered()), SLOT(slotDifference())); m_actions << action; // Exclusion // Shift + Alt + X action = new KisAction(i18n("Select Exclusion Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_X)); connect(action, SIGNAL(triggered()), SLOT(slotExclusion())); m_actions << action; // Hue // Shift + Alt + U action = new KisAction(i18n("Select Hue Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_U)); connect(action, SIGNAL(triggered()), SLOT(slotHue())); m_actions << action; // Saturation // Shift + Alt + T action = new KisAction(i18n("Select Saturation Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_T)); connect(action, SIGNAL(triggered()), SLOT(slotSaturation())); m_actions << action; // Color // Shift + Alt + C action = new KisAction(i18n("Select Color Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_C)); connect(action, SIGNAL(triggered()), SLOT(slotColor())); m_actions << action; // Luminosity // Shift + Alt + Y action = new KisAction(i18n("Select Luminosity Blending Mode"), this); action->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_Y)); connect(action, SIGNAL(triggered()), SLOT(slotLuminosity())); m_actions << action; } KisCompositeOpComboBox::~KisCompositeOpComboBox() { delete m_view; } void KisCompositeOpComboBox::validate(const KoColorSpace *cs) { m_model->validate(cs); } void KisCompositeOpComboBox::selectCompositeOp(const KoID &op) { QModelIndex index = m_model->indexOf(op); setCurrentIndex(index.row()); } KoID KisCompositeOpComboBox::selectedCompositeOp() const { KoID op; if (m_model->entryAt(op, m_model->index(currentIndex(), 0))) { return op; } return KoCompositeOpRegistry::instance().getDefaultCompositeOp(); } QList KisCompositeOpComboBox::blendmodeActions() const { return m_actions; } void KisCompositeOpComboBox::slotCategoryToggled(const QModelIndex& index, bool toggled) { Q_UNUSED(index); Q_UNUSED(toggled); //NOTE: this will (should) fit the size of the // popup widget to the view // don't know if this is expected behaviour // on all supported platforms. // Thre is nothing written about this in the docs. showPopup(); } void KisCompositeOpComboBox::slotEntryChecked(const QModelIndex& index) { Q_UNUSED(index); m_allowToHidePopup = false; } void KisCompositeOpComboBox::hidePopup() { if (m_allowToHidePopup) { QComboBox::hidePopup(); } else { QComboBox::showPopup(); } m_allowToHidePopup = true; } void KisCompositeOpComboBox::slotNextBlendingMode() { if (currentIndex() < count()) { setCurrentIndex(currentIndex() + 1); } } void KisCompositeOpComboBox::slotPreviousBlendingMode() { if (currentIndex() > 0) { setCurrentIndex(currentIndex() - 1); } } void KisCompositeOpComboBox::slotNormal() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_OVER)); } void KisCompositeOpComboBox::slotDissolve() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_DISSOLVE)); } void KisCompositeOpComboBox::slotBehind() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_BEHIND)); } void KisCompositeOpComboBox::slotClear() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_CLEAR)); } void KisCompositeOpComboBox::slotDarken() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_DARKEN)); } void KisCompositeOpComboBox::slotMultiply() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_MULT)); } void KisCompositeOpComboBox::slotColorBurn() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_BURN)); } void KisCompositeOpComboBox::slotLinearBurn() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_LINEAR_BURN)); } void KisCompositeOpComboBox::slotLighten() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_LIGHTEN)); } void KisCompositeOpComboBox::slotScreen() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_SCREEN)); } void KisCompositeOpComboBox::slotColorDodge() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_DODGE)); } void KisCompositeOpComboBox::slotLinearDodge() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_LINEAR_DODGE)); } void KisCompositeOpComboBox::slotOverlay() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_OVERLAY)); } void KisCompositeOpComboBox::slotSoftLight() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_SOFT_LIGHT_PHOTOSHOP)); } void KisCompositeOpComboBox::slotHardLight() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_HARD_LIGHT)); } void KisCompositeOpComboBox::slotVividLight() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_VIVID_LIGHT)); } void KisCompositeOpComboBox::slotLinearLight() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_LINEAR_LIGHT)); } void KisCompositeOpComboBox::slotPinLight() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_PIN_LIGHT)); } void KisCompositeOpComboBox::slotHardMix() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_HARD_MIX)); } void KisCompositeOpComboBox::slotDifference() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_DIFF)); } void KisCompositeOpComboBox::slotExclusion() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_EXCLUSION)); } void KisCompositeOpComboBox::slotHue() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_HUE)); } void KisCompositeOpComboBox::slotSaturation() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_SATURATION)); } void KisCompositeOpComboBox::slotColor() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_COLOR)); } void KisCompositeOpComboBox::slotLuminosity() { selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_LUMINIZE)); } diff --git a/libs/ui/widgets/kis_color_space_selector.cc b/libs/ui/widgets/kis_color_space_selector.cc index 271d60e1fd..7f0067fb49 100644 --- a/libs/ui/widgets/kis_color_space_selector.cc +++ b/libs/ui/widgets/kis_color_space_selector.cc @@ -1,227 +1,231 @@ /* * Copyright (C) 2007 Cyrille Berger * Copyright (C) 2011 Boudewijn Rempt * Copyright (C) 2011 Srikanth Tiyyagura * * This program is free software; you can redistribute 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_space_selector.h" #include "kis_advanced_color_space_selector.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "ui_wdgcolorspaceselector.h" struct KisColorSpaceSelector::Private { Ui_WdgColorSpaceSelector* colorSpaceSelector; QString knsrcFile; bool profileValid; QString defaultsuffix; }; KisColorSpaceSelector::KisColorSpaceSelector(QWidget* parent) : QWidget(parent), m_advancedSelector(0), d(new Private) { setObjectName("KisColorSpaceSelector"); d->colorSpaceSelector = new Ui_WdgColorSpaceSelector; d->colorSpaceSelector->setupUi(this); d->colorSpaceSelector->cmbColorModels->setIDList(KoColorSpaceRegistry::instance()->colorModelsList(KoColorSpaceRegistry::OnlyUserVisible)); fillCmbDepths(d->colorSpaceSelector->cmbColorModels->currentItem()); d->colorSpaceSelector->bnInstallProfile->setIcon(KisIconUtils::loadIcon("document-open")); d->colorSpaceSelector->bnInstallProfile->setToolTip( i18n("Open Color Profile") ); connect(d->colorSpaceSelector->cmbColorModels, SIGNAL(activated(const KoID &)), this, SLOT(fillCmbDepths(const KoID &))); connect(d->colorSpaceSelector->cmbColorDepth, SIGNAL(activated(const KoID &)), this, SLOT(fillCmbProfiles())); connect(d->colorSpaceSelector->cmbColorModels, SIGNAL(activated(const KoID &)), this, SLOT(fillCmbProfiles())); connect(d->colorSpaceSelector->cmbProfile, SIGNAL(activated(const QString &)), this, SLOT(colorSpaceChanged())); connect(d->colorSpaceSelector->bnInstallProfile, SIGNAL(clicked()), this, SLOT(installProfile())); d->defaultsuffix = " "+i18nc("This is appended to the color profile which is the default for the given colorspace and bit-depth","(Default)"); connect(d->colorSpaceSelector->bnAdvanced, SIGNAL(clicked()), this, SLOT(slotOpenAdvancedSelector())); fillCmbProfiles(); } KisColorSpaceSelector::~KisColorSpaceSelector() { delete d->colorSpaceSelector; delete d; } void KisColorSpaceSelector::fillCmbProfiles() { QString s = KoColorSpaceRegistry::instance()->colorSpaceId(d->colorSpaceSelector->cmbColorModels->currentItem(), d->colorSpaceSelector->cmbColorDepth->currentItem()); d->colorSpaceSelector->cmbProfile->clear(); const KoColorSpaceFactory * csf = KoColorSpaceRegistry::instance()->colorSpaceFactory(s); if (csf == 0) return; QList profileList = KoColorSpaceRegistry::instance()->profilesFor(csf); QStringList profileNames; Q_FOREACH (const KoColorProfile *profile, profileList) { profileNames.append(profile->name()); } qSort(profileNames); Q_FOREACH (QString stringName, profileNames) { if (stringName==csf->defaultProfile()) { d->colorSpaceSelector->cmbProfile->addSqueezedItem(stringName+d->defaultsuffix); } else { d->colorSpaceSelector->cmbProfile->addSqueezedItem(stringName); } } d->colorSpaceSelector->cmbProfile->setCurrent(csf->defaultProfile()+d->defaultsuffix); colorSpaceChanged(); } void KisColorSpaceSelector::fillCmbDepths(const KoID& id) { KoID activeDepth = d->colorSpaceSelector->cmbColorDepth->currentItem(); d->colorSpaceSelector->cmbColorDepth->clear(); QList depths = KoColorSpaceRegistry::instance()->colorDepthList(id, KoColorSpaceRegistry::OnlyUserVisible); d->colorSpaceSelector->cmbColorDepth->setIDList(depths); if (depths.contains(activeDepth)) { d->colorSpaceSelector->cmbColorDepth->setCurrent(activeDepth); } } const KoColorSpace* KisColorSpaceSelector::currentColorSpace() { QString profilenamestring = d->colorSpaceSelector->cmbProfile->itemHighlighted(); if (profilenamestring.contains(d->defaultsuffix)) { profilenamestring.remove(d->defaultsuffix); return KoColorSpaceRegistry::instance()->colorSpace( d->colorSpaceSelector->cmbColorModels->currentItem().id(), d->colorSpaceSelector->cmbColorDepth->currentItem().id(), profilenamestring); } else { return KoColorSpaceRegistry::instance()->colorSpace( d->colorSpaceSelector->cmbColorModels->currentItem().id(), d->colorSpaceSelector->cmbColorDepth->currentItem().id(), profilenamestring); } } void KisColorSpaceSelector::setCurrentColorModel(const KoID& id) { d->colorSpaceSelector->cmbColorModels->setCurrent(id); fillCmbDepths(id); } void KisColorSpaceSelector::setCurrentColorDepth(const KoID& id) { d->colorSpaceSelector->cmbColorDepth->setCurrent(id); fillCmbProfiles(); } void KisColorSpaceSelector::setCurrentProfile(const QString& name) { d->colorSpaceSelector->cmbProfile->setCurrent(name); } void KisColorSpaceSelector::setCurrentColorSpace(const KoColorSpace* colorSpace) { setCurrentColorModel(colorSpace->colorModelId()); setCurrentColorDepth(colorSpace->colorDepthId()); setCurrentProfile(colorSpace->profile()->name()); } +void KisColorSpaceSelector::showColorBrowserButton(bool showButton) { + d->colorSpaceSelector->bnAdvanced->setVisible(showButton); +} + void KisColorSpaceSelector::colorSpaceChanged() { bool valid = d->colorSpaceSelector->cmbProfile->count() != 0; d->profileValid = valid; emit(selectionChanged(valid)); if(valid) { emit colorSpaceChanged(currentColorSpace()); QString text = currentColorSpace()->profile()->name(); } } void KisColorSpaceSelector::installProfile() { QStringList mime; KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocumentICC"); dialog.setCaption(i18n("Install Color Profiles")); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::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()); } fillCmbProfiles(); } void KisColorSpaceSelector::slotOpenAdvancedSelector() { if (!m_advancedSelector) { m_advancedSelector = new KisAdvancedColorSpaceSelector(this, "Select a Colorspace"); m_advancedSelector->setModal(true); m_advancedSelector->setCurrentColorSpace(currentColorSpace()); connect(m_advancedSelector, SIGNAL(selectionChanged(bool)), this, SLOT(slotProfileValid(bool)) ); } QDialog::DialogCode result = (QDialog::DialogCode)m_advancedSelector->exec(); if (result) { if (d->profileValid==true) { setCurrentColorSpace(m_advancedSelector->currentColorSpace()); } } } void KisColorSpaceSelector::slotProfileValid(bool valid) { d->profileValid = valid; } diff --git a/libs/ui/widgets/kis_color_space_selector.h b/libs/ui/widgets/kis_color_space_selector.h index aac1050dcc..28a8f59e71 100644 --- a/libs/ui/widgets/kis_color_space_selector.h +++ b/libs/ui/widgets/kis_color_space_selector.h @@ -1,63 +1,64 @@ /* * Copyright (C) 2007 Cyrille Berger * Copyright (C) 2011 Srikanth Tiyyagura * * This program is free software; you can redistribute 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_COLOR_SPACE_SELECTOR_H_ #define _KIS_COLOR_SPACE_SELECTOR_H_ #include #include class KoID; class KoColorSpace; class KisAdvancedColorSpaceSelector; class KRITAUI_EXPORT KisColorSpaceSelector : public QWidget { Q_OBJECT public: KisColorSpaceSelector(QWidget* parent); ~KisColorSpaceSelector(); const KoColorSpace* currentColorSpace(); void setCurrentColorModel(const KoID& id); void setCurrentColorDepth(const KoID& id); void setCurrentProfile(const QString& name); void setCurrentColorSpace(const KoColorSpace* colorSpace); + void showColorBrowserButton(bool showButton); Q_SIGNALS: /** * This signal is emitted when a new color space is selected. * @param valid indicates if the color space can be used */ void selectionChanged(bool valid); /// This signal is emitted, when a new color space is selected, that can be used (eg is valid) void colorSpaceChanged(const KoColorSpace*); private Q_SLOTS: void fillCmbDepths(const KoID& idd); void fillCmbProfiles(); void colorSpaceChanged(); void installProfile(); void slotOpenAdvancedSelector(); void slotProfileValid(bool valid); private: struct Private; KisAdvancedColorSpaceSelector *m_advancedSelector; Private * const d; }; #endif diff --git a/libs/ui/widgets/kis_paintop_list_widget.cpp b/libs/ui/widgets/kis_paintop_list_widget.cpp index bab66a075e..6b3ef970f8 100644 --- a/libs/ui/widgets/kis_paintop_list_widget.cpp +++ b/libs/ui/widgets/kis_paintop_list_widget.cpp @@ -1,80 +1,80 @@ /* * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org) * Copyright (c) 2010 Lukáš Tvrdý * 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_paintop_list_widget.h" #include #include #include #include "../kis_paint_ops_model.h" #include "../kis_categorized_item_delegate.h" #include KisPaintOpListWidget::KisPaintOpListWidget(QWidget* parent, const char* name): KisCategorizedListView(false, parent), m_model(new KisSortedPaintOpListModel(this)) { setObjectName(name); connect(this, SIGNAL(clicked(QModelIndex)), this, SLOT(slotOpActivated(QModelIndex))); setModel(m_model); - setItemDelegate(new KisCategorizedItemDelegate(false, this)); + setItemDelegate(new KisCategorizedItemDelegate(this)); } KisPaintOpListWidget::~KisPaintOpListWidget() { } void KisPaintOpListWidget::setPaintOpList(const QList& list) { m_model->fill(list); } QString KisPaintOpListWidget::itemAt(int idx) const { KisPaintOpInfo info; if(m_model->entryAt(info, m_model->index(idx, 0))) return info.id; return ""; } QString KisPaintOpListWidget::currentItem() const { return itemAt(currentIndex().row()); } void KisPaintOpListWidget::setCurrent(const KisPaintOpFactory* op) { setCurrentIndex(m_model->indexOf(KisPaintOpInfo(op->id()))); } void KisPaintOpListWidget::setCurrent(const QString& paintOpId) { setCurrentIndex(m_model->indexOf(KisPaintOpInfo(paintOpId))); } void KisPaintOpListWidget::slotOpActivated(const QModelIndex& index) { emit activated(itemAt(index.row())); } diff --git a/libs/ui/widgets/kis_paintop_presets_popup.cpp b/libs/ui/widgets/kis_paintop_presets_popup.cpp index 539aafa3d8..566507c82a 100644 --- a/libs/ui/widgets/kis_paintop_presets_popup.cpp +++ b/libs/ui/widgets/kis_paintop_presets_popup.cpp @@ -1,407 +1,409 @@ /* This file is part of the KDE project * Copyright (C) 2008 Boudewijn Rempt * Copyright (C) 2010 Lukáš Tvrdý * Copyright (C) 2011 Silvio Heinrich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "widgets/kis_paintop_presets_popup.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_config.h" #include "kis_resource_server_provider.h" #include "kis_lod_availability_widget.h" #include "kis_signal_auto_connection.h" struct KisPaintOpPresetsPopup::Private { public: Ui_WdgPaintOpSettings uiWdgPaintOpPresetSettings; QGridLayout *layout; KisPaintOpConfigWidget *settingsWidget; QFont smallFont; KisCanvasResourceProvider *resourceProvider; bool detached; bool ignoreHideEvents; QSize minimumSettingsWidgetSize; QRect detachedGeometry; KisSignalAutoConnectionsStore widgetConnections; }; KisPaintOpPresetsPopup::KisPaintOpPresetsPopup(KisCanvasResourceProvider * resourceProvider, QWidget * parent) : QWidget(parent) , m_d(new Private()) { setObjectName("KisPaintOpPresetsPopup"); setFont(KoDockRegistry::dockFont()); m_d->resourceProvider = resourceProvider; m_d->uiWdgPaintOpPresetSettings.setupUi(this); m_d->layout = new QGridLayout(m_d->uiWdgPaintOpPresetSettings.frmOptionWidgetContainer); m_d->layout->setSizeConstraint(QLayout::SetFixedSize); m_d->uiWdgPaintOpPresetSettings.scratchPad->setupScratchPad(resourceProvider, Qt::white); m_d->uiWdgPaintOpPresetSettings.scratchPad->setCutoutOverlayRect(QRect(25, 25, 200, 200)); m_d->uiWdgPaintOpPresetSettings.fillLayer->setIcon(KisIconUtils::loadIcon("document-new")); m_d->uiWdgPaintOpPresetSettings.fillLayer->hide(); m_d->uiWdgPaintOpPresetSettings.fillGradient->setIcon(KisIconUtils::loadIcon("krita_tool_gradient")); m_d->uiWdgPaintOpPresetSettings.fillSolid->setIcon(KisIconUtils::loadIcon("krita_tool_color_fill")); m_d->uiWdgPaintOpPresetSettings.eraseScratchPad->setIcon(KisIconUtils::loadIcon("edit-delete")); m_d->uiWdgPaintOpPresetSettings.paintPresetIcon->setIcon(KisIconUtils::loadIcon("krita_tool_freehand")); connect(m_d->uiWdgPaintOpPresetSettings.eraseScratchPad, SIGNAL(clicked()), m_d->uiWdgPaintOpPresetSettings.scratchPad, SLOT(fillDefault())); connect(m_d->uiWdgPaintOpPresetSettings.fillLayer, SIGNAL(clicked()), m_d->uiWdgPaintOpPresetSettings.scratchPad, SLOT(fillLayer())); connect(m_d->uiWdgPaintOpPresetSettings.fillGradient, SIGNAL(clicked()), m_d->uiWdgPaintOpPresetSettings.scratchPad, SLOT(fillGradient())); connect(m_d->uiWdgPaintOpPresetSettings.fillSolid, SIGNAL(clicked()), m_d->uiWdgPaintOpPresetSettings.scratchPad, SLOT(fillBackground())); connect(m_d->uiWdgPaintOpPresetSettings.paintPresetIcon, SIGNAL(clicked()), m_d->uiWdgPaintOpPresetSettings.scratchPad, SLOT(paintPresetImage())); m_d->settingsWidget = 0; setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); connect(m_d->uiWdgPaintOpPresetSettings.bnSave, SIGNAL(clicked()), this, SIGNAL(savePresetClicked())); connect(m_d->uiWdgPaintOpPresetSettings.reload, SIGNAL(clicked()), this, SIGNAL(reloadPresetClicked())); connect(m_d->uiWdgPaintOpPresetSettings.bnDefaultPreset, SIGNAL(clicked()), this, SIGNAL(defaultPresetClicked())); connect(m_d->uiWdgPaintOpPresetSettings.dirtyPresetCheckBox, SIGNAL(toggled(bool)), this, SIGNAL(dirtyPresetToggled(bool))); connect(m_d->uiWdgPaintOpPresetSettings.eraserBrushSizeCheckBox, SIGNAL(toggled(bool)), this, SIGNAL(eraserBrushSizeToggled(bool))); connect(m_d->uiWdgPaintOpPresetSettings.bnDefaultPreset, SIGNAL(clicked()), m_d->uiWdgPaintOpPresetSettings.txtPreset, SLOT(clear())); connect(m_d->uiWdgPaintOpPresetSettings.txtPreset, SIGNAL(textChanged(QString)), SLOT(slotWatchPresetNameLineEdit())); connect(m_d->uiWdgPaintOpPresetSettings.paintopList, SIGNAL(activated(QString)), this, SIGNAL(paintopActivated(QString))); connect(m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser, SIGNAL(resourceSelected(KoResource*)), this, SIGNAL(signalResourceSelected(KoResource*))); connect(m_d->uiWdgPaintOpPresetSettings.bnSave, SIGNAL(clicked()), m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser, SLOT(updateViewSettings())); connect(m_d->uiWdgPaintOpPresetSettings.reload, SIGNAL(clicked()), m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser, SLOT(updateViewSettings())); KisConfig cfg; m_d->detached = !cfg.paintopPopupDetached(); m_d->ignoreHideEvents = false; m_d->minimumSettingsWidgetSize = QSize(0, 0); m_d->uiWdgPaintOpPresetSettings.presetWidget->setVisible(cfg.presetStripVisible()); m_d->uiWdgPaintOpPresetSettings.scratchpadControls->setVisible(cfg.scratchpadVisible()); m_d->detachedGeometry = QRect(100, 100, 0, 0); m_d->uiWdgPaintOpPresetSettings.dirtyPresetCheckBox->setChecked(cfg.useDirtyPresets()); m_d->uiWdgPaintOpPresetSettings.eraserBrushSizeCheckBox->setChecked(cfg.useEraserBrushSize()); m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability->setCanvasResourceManager(resourceProvider->resourceManager()); } KisPaintOpPresetsPopup::~KisPaintOpPresetsPopup() { if (m_d->settingsWidget) { m_d->layout->removeWidget(m_d->settingsWidget); m_d->settingsWidget->hide(); m_d->settingsWidget->setParent(0); m_d->settingsWidget = 0; } delete m_d; } void KisPaintOpPresetsPopup::setPaintOpSettingsWidget(QWidget * widget) { if (m_d->settingsWidget) { m_d->layout->removeWidget(m_d->settingsWidget); m_d->uiWdgPaintOpPresetSettings.frmOptionWidgetContainer->updateGeometry(); } m_d->layout->update(); updateGeometry(); m_d->widgetConnections.clear(); m_d->settingsWidget = 0; if (widget) { m_d->settingsWidget = dynamic_cast(widget); KIS_ASSERT_RECOVER_RETURN(m_d->settingsWidget); if (m_d->settingsWidget->supportScratchBox()) { showScratchPad(); } else { hideScratchPad(); } m_d->widgetConnections.addConnection(m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability, SIGNAL(sigUserChangedLodAvailability(bool)), m_d->settingsWidget, SLOT(slotUserChangedLodAvailability(bool))); m_d->widgetConnections.addConnection(m_d->settingsWidget, SIGNAL(sigUserChangedLodAvailability(bool)), m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability, SLOT(slotUserChangedLodAvailability(bool))); m_d->widgetConnections.addConnection(m_d->settingsWidget, SIGNAL(sigConfigurationItemChanged()), this, SLOT(slotUpdateLodAvailability())); m_d->settingsWidget->coldInitExternalLodAvailabilityWidget(); widget->setFont(m_d->smallFont); QSize hint = widget->sizeHint(); m_d->minimumSettingsWidgetSize = QSize(qMax(hint.width(), m_d->minimumSettingsWidgetSize.width()), qMax(hint.height(), m_d->minimumSettingsWidgetSize.height())); widget->setMinimumSize(m_d->minimumSettingsWidgetSize); m_d->layout->addWidget(widget); m_d->layout->update(); widget->show(); } } void KisPaintOpPresetsPopup::slotUpdateLodAvailability() { if (!m_d->settingsWidget) return; KisPaintopLodLimitations l = m_d->settingsWidget->lodLimitations(); m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability->setLimitations(l); } void KisPaintOpPresetsPopup::slotWatchPresetNameLineEdit() { QString text = m_d->uiWdgPaintOpPresetSettings.txtPreset->text(); KisPaintOpPresetResourceServer * rServer = KisResourceServerProvider::instance()->paintOpPresetServer(); bool overwrite = rServer->resourceByName(text) != 0; KisPaintOpPresetSP preset = m_d->resourceProvider->currentPreset(); bool btnSaveAvailable = preset->valid() && (preset->isPresetDirty() | !overwrite); QString btnText = overwrite ? i18n("Overwrite Preset") : i18n("Save to Presets"); m_d->uiWdgPaintOpPresetSettings.bnSave->setText(btnText); m_d->uiWdgPaintOpPresetSettings.bnSave->setEnabled(btnSaveAvailable); m_d->uiWdgPaintOpPresetSettings.reload->setVisible(true); m_d->uiWdgPaintOpPresetSettings.reload->setEnabled(btnSaveAvailable && overwrite); QFont font = m_d->uiWdgPaintOpPresetSettings.txtPreset->font(); font.setItalic(btnSaveAvailable); m_d->uiWdgPaintOpPresetSettings.txtPreset->setFont(font); } QString KisPaintOpPresetsPopup::getPresetName() const { return m_d->uiWdgPaintOpPresetSettings.txtPreset->text(); } QImage KisPaintOpPresetsPopup::cutOutOverlay() { return m_d->uiWdgPaintOpPresetSettings.scratchPad->cutoutOverlay(); } -void KisPaintOpPresetsPopup::contextMenuEvent(QContextMenuEvent *e) { - +void KisPaintOpPresetsPopup::contextMenuEvent(QContextMenuEvent *e) +{ +#if 0 QMenu menu(this); QAction* action = menu.addAction(m_d->detached ? i18n("Attach to Toolbar") : i18n("Detach from Toolbar")); connect(action, SIGNAL(triggered()), this, SLOT(switchDetached())); QAction* showPresetStrip = menu.addAction(i18n("Show Preset Strip")); showPresetStrip->setCheckable(true); showPresetStrip->setChecked(m_d->uiWdgPaintOpPresetSettings.presetWidget->isVisible()); connect(showPresetStrip, SIGNAL(triggered(bool)), this, SLOT(slotSwitchPresetStrip(bool))); QAction* showScratchPad = menu.addAction(i18n("Show Scratchpad")); showScratchPad->setCheckable(true); showScratchPad->setChecked(m_d->uiWdgPaintOpPresetSettings.scratchPad->isVisible()); connect(showScratchPad, SIGNAL(triggered(bool)), this, SLOT(slotSwitchScratchpad(bool))); menu.exec(e->globalPos()); +#endif } void KisPaintOpPresetsPopup::switchDetached(bool show) { if (parentWidget()) { m_d->detached = !m_d->detached; if (m_d->detached) { m_d->ignoreHideEvents = true; parentWidget()->setWindowFlags(Qt::Tool); m_d->uiWdgPaintOpPresetSettings.scratchpadControls->setVisible(false); if (show) { parentWidget()->show(); } m_d->ignoreHideEvents = false; } else { parentWidget()->setWindowFlags(Qt::Popup); KisConfig cfg; m_d->uiWdgPaintOpPresetSettings.scratchpadControls->setVisible(cfg.scratchpadVisible()); parentWidget()->hide(); } KisConfig cfg; cfg.setPaintopPopupDetached(m_d->detached); } } void KisPaintOpPresetsPopup::hideScratchPad() { m_d->uiWdgPaintOpPresetSettings.scratchPad->setEnabled(false); m_d->uiWdgPaintOpPresetSettings.fillGradient->setEnabled(false); m_d->uiWdgPaintOpPresetSettings.fillSolid->setEnabled(false); m_d->uiWdgPaintOpPresetSettings.eraseScratchPad->setEnabled(false); } void KisPaintOpPresetsPopup::showScratchPad() { m_d->uiWdgPaintOpPresetSettings.scratchPad->setEnabled(true); m_d->uiWdgPaintOpPresetSettings.fillGradient->setEnabled(true); m_d->uiWdgPaintOpPresetSettings.fillSolid->setEnabled(true); m_d->uiWdgPaintOpPresetSettings.eraseScratchPad->setEnabled(true); } void KisPaintOpPresetsPopup::resourceSelected(KoResource* resource) { m_d->uiWdgPaintOpPresetSettings.txtPreset->setText(resource->name()); slotWatchPresetNameLineEdit(); } void KisPaintOpPresetsPopup::setPaintOpList(const QList< KisPaintOpFactory* >& list) { m_d->uiWdgPaintOpPresetSettings.paintopList->setPaintOpList(list); } void KisPaintOpPresetsPopup::setCurrentPaintOp(const QString& paintOpId) { m_d->uiWdgPaintOpPresetSettings.paintopList->setCurrent(paintOpId); m_d->uiWdgPaintOpPresetSettings.presetWidget->setPresetFilter(paintOpId); } QString KisPaintOpPresetsPopup::currentPaintOp() { return m_d->uiWdgPaintOpPresetSettings.paintopList->currentItem(); } void KisPaintOpPresetsPopup::setPresetImage(const QImage& image) { m_d->uiWdgPaintOpPresetSettings.scratchPad->setPresetImage(image); } void KisPaintOpPresetsPopup::hideEvent(QHideEvent *event) { if (m_d->ignoreHideEvents) { return; } if (m_d->detached) { m_d->detachedGeometry = window()->geometry(); } QWidget::hideEvent(event); } void KisPaintOpPresetsPopup::showEvent(QShowEvent *) { if (m_d->detached) { window()->setGeometry(m_d->detachedGeometry); } } void KisPaintOpPresetsPopup::resizeEvent(QResizeEvent* event) { QWidget::resizeEvent(event); emit sizeChanged(); } bool KisPaintOpPresetsPopup::detached() const { return m_d->detached; } void KisPaintOpPresetsPopup::slotSwitchPresetStrip(bool visible) { m_d->uiWdgPaintOpPresetSettings.presetWidget->setVisible(visible); KisConfig cfg; cfg.setPresetStripVisible(visible); } void KisPaintOpPresetsPopup::slotSwitchScratchpad(bool visible) { m_d->uiWdgPaintOpPresetSettings.scratchpadControls->setVisible(visible); KisConfig cfg; cfg.setScratchpadVisible(visible); } void KisPaintOpPresetsPopup::updateViewSettings() { m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser->updateViewSettings(); } void KisPaintOpPresetsPopup::updateThemedIcons() { m_d->uiWdgPaintOpPresetSettings.fillLayer->setIcon(KisIconUtils::loadIcon("document-new")); m_d->uiWdgPaintOpPresetSettings.fillLayer->hide(); m_d->uiWdgPaintOpPresetSettings.fillGradient->setIcon(KisIconUtils::loadIcon("krita_tool_gradient")); m_d->uiWdgPaintOpPresetSettings.fillSolid->setIcon(KisIconUtils::loadIcon("krita_tool_color_fill")); m_d->uiWdgPaintOpPresetSettings.eraseScratchPad->setIcon(KisIconUtils::loadIcon("edit-delete")); m_d->uiWdgPaintOpPresetSettings.paintPresetIcon->setIcon(KisIconUtils::loadIcon("krita_tool_freehand")); } diff --git a/libs/vectorimage/libemf/demo/EmfViewer.cpp b/libs/vectorimage/libemf/demo/EmfViewer.cpp index b0c65ffc98..4730e1a506 100644 --- a/libs/vectorimage/libemf/demo/EmfViewer.cpp +++ b/libs/vectorimage/libemf/demo/EmfViewer.cpp @@ -1,100 +1,99 @@ /* Copyright 2008 Brad Hards Copyright 2008 Inge Wallin 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 + 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 + You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "EmfViewer.h" #include #include #include #include "../EmfOutputPainterStrategy.h" EmfViewer::EmfViewer( QSize &size ) : QMainWindow() { m_size = size; setWindowTitle( "EMF Demo Viewer" ); QMenu *fileMenu = menuBar()->addMenu( "&File" ); // The "Open" action m_fileOpenAction->setShortcut( Qt::CTRL + Qt::Key_O ); m_fileOpenAction = fileMenu->addAction( "&Open", this, SLOT( slotOpenFile() ) ); fileMenu->addSeparator(); // The "Quit" action m_fileQuitAction->setShortcut( Qt::CTRL + Qt::Key_Q ); m_fileQuitAction = fileMenu->addAction( "&Quit", qApp, SLOT( closeAllWindows() ) ); // Set a suitably large size. resize( m_size + QSize( 50, 50 ) ); // ...and finably create the label that will show everything. m_label = new QLabel(this); setCentralWidget( m_label ); } EmfViewer::~EmfViewer() { } void EmfViewer::loadFile( const QString &fileName ) { Parser parser; // The image that the EMF parser should paint on. QImage image( m_size, QImage::Format_ARGB32_Premultiplied ); QPainter painter( &image ); OutputPainterStrategy output( painter, m_size ); parser.setOutput( &output ); parser.load( QString( fileName ) ); QPixmap pixmap = QPixmap::fromImage( image ); - m_label->setPixmap( pixmap.scaled( m_size, Qt::KeepAspectRatio, - Qt::SmoothTransformation ) ); - + m_label->setPixmap( pixmap.scaled(m_size, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + m_label->show(); } // ---------------------------------------------------------------- // Slots void EmfViewer::slotOpenFile() { QString fileName = QFileDialog::getOpenFileName(this, "Open EMF document", QDir::homePath(), "EMF Documents (*.emf)" ); if (fileName.isEmpty()) { return; } loadFile( fileName ); } #include diff --git a/libs/widgets/KoResourceItemView.cpp b/libs/widgets/KoResourceItemView.cpp index e2d21f3a04..60364d3b3b 100644 --- a/libs/widgets/KoResourceItemView.cpp +++ b/libs/widgets/KoResourceItemView.cpp @@ -1,58 +1,62 @@ /* This file is part of the KDE project * Copyright (C) 2008 Jan Hambrecht * Copyright (c) 2011 José Luis Vergara #include #include KoResourceItemView::KoResourceItemView(QWidget *parent) : KoTableView(parent) { } bool KoResourceItemView::viewportEvent(QEvent *event) { if (event->type() == QEvent::ToolTip && model()) { QHelpEvent *he = static_cast(event); QStyleOptionViewItem option = viewOptions(); QModelIndex index = model()->buddy(indexAt(he->pos())); if (index.isValid()) { option.rect = visualRect(index); m_tip.showTip(this, he->pos(), option, index); return true; } } return QTableView::viewportEvent(event); } void KoResourceItemView::selectionChanged(const QItemSelection &selected, const QItemSelection &/*deselected*/) { - emit currentResourceChanged(selected.indexes().first()); + if (selected.isEmpty()) { + emit currentResourceChanged(QModelIndex()); + } else { + emit currentResourceChanged(selected.indexes().first()); + } } void KoResourceItemView::contextMenuEvent(QContextMenuEvent *event) { QTableView::contextMenuEvent(event); emit contextMenuRequested(event->globalPos()); } diff --git a/libs/widgets/KoResourceModel.cpp b/libs/widgets/KoResourceModel.cpp index 2094f2f098..55515cf57a 100644 --- a/libs/widgets/KoResourceModel.cpp +++ b/libs/widgets/KoResourceModel.cpp @@ -1,320 +1,320 @@ /* This file is part of the KDE project * Copyright (C) 2008-2009 Jan Hambrecht * Copyright (c) 2013 Sascha Suelzer * * 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 "KoResourceModel.h" #include #include #include #include #include KoResourceModel::KoResourceModel(QSharedPointer resourceAdapter, QObject * parent) : KoResourceModelBase(parent) , m_resourceAdapter(resourceAdapter) , m_columnCount(4) { Q_ASSERT(m_resourceAdapter); m_resourceAdapter->connectToResourceServer(); connect(m_resourceAdapter.data(), SIGNAL(resourceAdded(KoResource*)), this, SLOT(resourceAdded(KoResource*))); connect(m_resourceAdapter.data(), SIGNAL(removingResource(KoResource*)), this, SLOT(resourceRemoved(KoResource*))); connect(m_resourceAdapter.data(), SIGNAL(resourceChanged(KoResource*)), this, SLOT(resourceChanged(KoResource*))); connect(m_resourceAdapter.data(), SIGNAL(tagsWereChanged()), this, SLOT(tagBoxEntryWasModified())); connect(m_resourceAdapter.data(), SIGNAL(tagCategoryWasAdded(QString)), this, SLOT(tagBoxEntryWasAdded(QString))); connect(m_resourceAdapter.data(), SIGNAL(tagCategoryWasRemoved(QString)), this, SLOT(tagBoxEntryWasRemoved(QString))); } KoResourceModel::~KoResourceModel() { if (!m_currentTag.isEmpty()) { KConfigGroup group = KSharedConfig::openConfig()->group("SelectedTags"); group.writeEntry(serverType(), m_currentTag); } } int KoResourceModel::rowCount( const QModelIndex &/*parent*/ ) const { int resourceCount = m_resourceAdapter->resources().count(); if (!resourceCount) return 0; return static_cast(ceil(static_cast(resourceCount) / m_columnCount)); } int KoResourceModel::columnCount ( const QModelIndex & ) const { return m_columnCount; } QVariant KoResourceModel::data( const QModelIndex &index, int role ) const { if( ! index.isValid() ) return QVariant(); switch( role ) { case Qt::DisplayRole: { KoResource * resource = static_cast(index.internalPointer()); if( ! resource ) return QVariant(); QString resName = i18n( resource->name().toUtf8().data()); if (m_resourceAdapter->assignedTagsList(resource).count()) { QString taglist = m_resourceAdapter->assignedTagsList(resource).join("] , ["); QString tagListToolTip = QString(" - %1: [%2]").arg(i18n("Tags"), taglist); return QVariant( resName + tagListToolTip ); } return QVariant( resName ); } case Qt::DecorationRole: { KoResource * resource = static_cast(index.internalPointer()); if( ! resource ) return QVariant(); return QVariant( resource->image() ); } case KoResourceModel::LargeThumbnailRole: { KoResource * resource = static_cast(index.internalPointer()); if( ! resource ) return QVariant(); QSize imageSize = resource->image().size(); QSize thumbSize( 100, 100 ); if(imageSize.height() > thumbSize.height() || imageSize.width() > thumbSize.width()) { qreal scaleW = static_cast( thumbSize.width() ) / static_cast( imageSize.width() ); qreal scaleH = static_cast( thumbSize.height() ) / static_cast( imageSize.height() ); qreal scale = qMin( scaleW, scaleH ); int thumbW = static_cast( imageSize.width() * scale ); int thumbH = static_cast( imageSize.height() * scale ); - return QVariant(resource->image().scaled( thumbW, thumbH, Qt::IgnoreAspectRatio )); + return QVariant(resource->image().scaled( thumbW, thumbH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); } else return QVariant(resource->image()); } default: return QVariant(); } } QModelIndex KoResourceModel::index ( int row, int column, const QModelIndex & ) const { int index = row * m_columnCount + column; const QList resources = m_resourceAdapter->resources(); if( index >= resources.count() || index < 0) return QModelIndex(); return createIndex( row, column, resources[index] ); } void KoResourceModel::doSafeLayoutReset(KoResource *activateAfterReformat) { emit beforeResourcesLayoutReset(activateAfterReformat); reset(); emit afterResourcesLayoutReset(); } void KoResourceModel::setColumnCount( int columnCount ) { if (columnCount != m_columnCount) { emit beforeResourcesLayoutReset(0); m_columnCount = columnCount; reset(); emit afterResourcesLayoutReset(); } } void KoResourceModel::resourceAdded(KoResource *resource) { int newIndex = m_resourceAdapter->resources().indexOf(resource); if (newIndex >= 0) { doSafeLayoutReset(0); } } void KoResourceModel::resourceRemoved(KoResource *resource) { Q_UNUSED(resource); KoResource *first = !m_resourceAdapter->resources().isEmpty() ? m_resourceAdapter->resources().first() : 0; doSafeLayoutReset(first); } void KoResourceModel::resourceChanged(KoResource* resource) { int resourceIndex = m_resourceAdapter->resources().indexOf(resource); int row = resourceIndex / columnCount(); int column = resourceIndex % columnCount(); QModelIndex modelIndex = index(row, column); if (!modelIndex.isValid()) { return; } emit dataChanged(modelIndex, modelIndex); } void KoResourceModel::tagBoxEntryWasModified() { m_resourceAdapter->updateServer(); emit tagBoxEntryModified(); } void KoResourceModel::tagBoxEntryWasAdded(const QString& tag) { emit tagBoxEntryAdded(tag); } void KoResourceModel::tagBoxEntryWasRemoved(const QString& tag) { emit tagBoxEntryRemoved(tag); } QModelIndex KoResourceModel::indexFromResource(KoResource* resource) const { int resourceIndex = m_resourceAdapter->resources().indexOf(resource); if (columnCount() > 0) { int row = resourceIndex / columnCount(); int column = resourceIndex % columnCount(); return index(row, column); } return QModelIndex(); } QString KoResourceModel::extensions() const { return m_resourceAdapter->extensions(); } void KoResourceModel::importResourceFile(const QString &filename) { m_resourceAdapter->importResourceFile(filename); } void KoResourceModel::importResourceFile(const QString & filename, bool fileCreation) { m_resourceAdapter->importResourceFile(filename, fileCreation); } bool KoResourceModel::removeResource(KoResource* resource) { return m_resourceAdapter->removeResource(resource); } void KoResourceModel::removeResourceFile(const QString &filename) { m_resourceAdapter->removeResourceFile(filename); } QStringList KoResourceModel::assignedTagsList(KoResource *resource) const { return m_resourceAdapter->assignedTagsList(resource); } void KoResourceModel::addTag(KoResource* resource, const QString& tag) { m_resourceAdapter->addTag(resource, tag); emit tagBoxEntryAdded(tag); } void KoResourceModel::deleteTag(KoResource *resource, const QString &tag) { m_resourceAdapter->deleteTag(resource, tag); } QStringList KoResourceModel::tagNamesList() const { return m_resourceAdapter->tagNamesList(); } QStringList KoResourceModel::searchTag(const QString& lineEditText) { return m_resourceAdapter->searchTag(lineEditText); } void KoResourceModel::searchTextChanged(const QString& searchString) { m_resourceAdapter->searchTextChanged(searchString); } void KoResourceModel::enableResourceFiltering(bool enable) { m_resourceAdapter->enableResourceFiltering(enable); } void KoResourceModel::setCurrentTag(const QString& currentTag) { m_currentTag = currentTag; m_resourceAdapter->setCurrentTag(currentTag); } void KoResourceModel::updateServer() { m_resourceAdapter->updateServer(); } int KoResourceModel::resourcesCount() const { return m_resourceAdapter->resources().count(); } QList KoResourceModel::currentlyVisibleResources() const { return m_resourceAdapter->resources(); } void KoResourceModel::tagCategoryMembersChanged() { m_resourceAdapter->tagCategoryMembersChanged(); } void KoResourceModel::tagCategoryAdded(const QString& tag) { m_resourceAdapter->tagCategoryAdded(tag); } void KoResourceModel::tagCategoryRemoved(const QString& tag) { m_resourceAdapter->tagCategoryRemoved(tag); } QString KoResourceModel::serverType() const { return m_resourceAdapter->serverType(); } QList< KoResource* > KoResourceModel::serverResources() const { return m_resourceAdapter->serverResources(); } diff --git a/libs/widgets/KoResourceTaggingManager.cpp b/libs/widgets/KoResourceTaggingManager.cpp index 2386145920..9a86efa9c4 100644 --- a/libs/widgets/KoResourceTaggingManager.cpp +++ b/libs/widgets/KoResourceTaggingManager.cpp @@ -1,387 +1,388 @@ /* * This file is part of the KDE project * Copyright (c) 2002 Patrick Julien * Copyright (c) 2007 Jan Hambrecht * Copyright (c) 2007 Sven Langkamp * Copyright (C) 2011 Srikanth Tiyyagura * Copyright (c) 2011 José Luis Vergara * Copyright (c) 2013 Sascha Suelzer * * 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 "KoResourceTaggingManager.h" #include #include #include #include #include #include #include "KoTagFilterWidget.h" #include "KoTagChooserWidget.h" #include "KoResourceModel.h" #include #include "KoResourceItemChooserContextMenu.h" #include class TaggedResourceSet { public: TaggedResourceSet() {} TaggedResourceSet(const QString& tagName, const QList& resources) : tagName(tagName) , resources(resources) {} QString tagName; QList resources; }; class KoResourceTaggingManager::Private { public: QString currentTag; QList originalResources; TaggedResourceSet lastDeletedTag; KoTagChooserWidget* tagChooser; KoTagFilterWidget* tagFilter; QCompleter* tagCompleter; QPointer model; }; KoResourceTaggingManager::KoResourceTaggingManager(KoResourceModel *model, QWidget* parent) : QObject(parent) , d(new Private()) { d->model = model; d->tagChooser = new KoTagChooserWidget(parent); d->tagChooser->addReadOnlyItem("All"); // not translatable until other tags made translatable! d->tagChooser->addItems(d->model->tagNamesList()); d->tagFilter = new KoTagFilterWidget(parent); connect(d->tagChooser, SIGNAL(tagChosen(QString)), this, SLOT(tagChooserIndexChanged(QString))); connect(d->tagChooser, SIGNAL(newTagRequested(QString)), this, SLOT(contextCreateNewTag(QString))); connect(d->tagChooser, SIGNAL(tagDeletionRequested(QString)), this, SLOT(removeTagFromComboBox(QString))); connect(d->tagChooser, SIGNAL(tagRenamingRequested(QString, QString)), this, SLOT(renameTag(QString, QString))); connect(d->tagChooser, SIGNAL(tagUndeletionRequested(QString)), this, SLOT(undeleteTag(QString))); connect(d->tagChooser, SIGNAL(tagUndeletionListPurgeRequested()), this, SLOT(purgeTagUndeleteList())); connect(d->tagFilter, SIGNAL(saveButtonClicked()), this, SLOT(tagSaveButtonPressed())); connect(d->tagFilter, SIGNAL(filterTextChanged(QString)), this, SLOT(tagSearchLineEditTextChanged(QString))); connect(d->model, SIGNAL(tagBoxEntryAdded(QString)), this, SLOT(syncTagBoxEntryAddition(QString))); connect(d->model, SIGNAL(tagBoxEntryRemoved(QString)), this, SLOT(syncTagBoxEntryRemoval(QString))); connect(d->model, SIGNAL(tagBoxEntryModified()), this, SLOT(syncTagBoxEntries())); // FIXME: fix tag completer // d->tagCompleter = new QCompleter(this); // d->tagSearchLineEdit->setCompleter(d->tagCompleter); syncTagBoxEntries(); } KoResourceTaggingManager::~KoResourceTaggingManager() { delete d; } void KoResourceTaggingManager::showTaggingBar(bool show) { show ? d->tagFilter->show() : d->tagFilter->hide(); show ? d->tagChooser->show() : d->tagChooser->hide(); blockSignals(!show); QString tag("All"); if (show) { KConfigGroup group = KSharedConfig::openConfig()->group("SelectedTags"); tag = group.readEntry(d->model->serverType(), "All"); - } - - d->tagChooser->setCurrentIndex(d->tagChooser->findIndexOf(tag)); + } + int idx = d->tagChooser->findIndexOf(tag); + if (idx < 0) idx = 0; + d->tagChooser->setCurrentIndex(idx); } void KoResourceTaggingManager::purgeTagUndeleteList() { d->lastDeletedTag = TaggedResourceSet(); d->tagChooser->setUndeletionCandidate(QString()); } void KoResourceTaggingManager::undeleteTag(const QString & tagToUndelete) { QString tagName = tagToUndelete; QStringList allTags = availableTags(); if (allTags.contains(tagName)) { bool ok; tagName = QInputDialog::getText( - d->tagChooser, i18n("Unable to undelete tag"), - i18n("The tag you are trying to undelete already exists in tag list.
Please enter a new, unique name for it.
"), - QLineEdit::Normal, - tagName, &ok); + d->tagChooser, i18n("Unable to undelete tag"), + i18n("The tag you are trying to undelete already exists in tag list.
Please enter a new, unique name for it.
"), + QLineEdit::Normal, + tagName, &ok); if (!ok || allTags.contains(tagName) || tagName.isEmpty()) { QMessageBox msgBox; msgBox.setIcon(QMessageBox::Warning); msgBox.setText(i18n("Tag was not undeleted.")); msgBox.exec(); return; } } QList serverResources = d->model->serverResources(); Q_FOREACH (KoResource * resource, d->lastDeletedTag.resources) { if (serverResources.contains(resource)) { addResourceTag(resource, tagName); } } d->model->tagCategoryAdded(tagName); d->tagChooser->setCurrentIndex(d->tagChooser->findIndexOf(tagName)); d->tagChooser->setUndeletionCandidate(QString()); d->lastDeletedTag = TaggedResourceSet(); } QStringList KoResourceTaggingManager::availableTags() const { return d->tagChooser->allTags(); } void KoResourceTaggingManager::addResourceTag(KoResource* resource, const QString& tagName) { QStringList tagsList = d->model->assignedTagsList(resource); if (tagsList.isEmpty()) { d->model->addTag(resource, tagName); } else { Q_FOREACH (const QString & tag, tagsList) { if (tag.compare(tagName)) { d->model->addTag(resource, tagName); } } } } void KoResourceTaggingManager::syncTagBoxEntryAddition(const QString& tag) { d->tagChooser->insertItem(tag); } void KoResourceTaggingManager::contextCreateNewTag(const QString& tag) { if (!tag.isEmpty()) { d->model->addTag(0, tag); d->model->tagCategoryAdded(tag); d->tagChooser->setCurrentIndex(d->tagChooser->findIndexOf(tag)); updateTaggedResourceView(); } } void KoResourceTaggingManager::contextCreateNewTag(KoResource* resource , const QString& tag) { if (!tag.isEmpty()) { d->model->tagCategoryAdded(tag); if (resource) { addResourceTag(resource, tag); } } } void KoResourceTaggingManager::syncTagBoxEntryRemoval(const QString& tag) { d->tagChooser->removeItem(tag); } void KoResourceTaggingManager::syncTagBoxEntries() { QStringList tags = d->model->tagNamesList(); foreach (const QString &tag, tags) { d->tagChooser->insertItem(tag); } } void KoResourceTaggingManager::contextAddTagToResource(KoResource* resource, const QString& tag) { addResourceTag(resource, tag); d->model->tagCategoryMembersChanged(); updateTaggedResourceView(); } void KoResourceTaggingManager::contextRemoveTagFromResource(KoResource* resource, const QString& tag) { removeResourceTag(resource, tag); d->model->tagCategoryMembersChanged(); updateTaggedResourceView(); } void KoResourceTaggingManager::removeTagFromComboBox(const QString &tag) { QList resources = d->model->currentlyVisibleResources(); Q_FOREACH (KoResource * resource, resources) { removeResourceTag(resource, tag); } d->model->tagCategoryRemoved(tag); d->lastDeletedTag = TaggedResourceSet(tag, resources); d->tagChooser->setUndeletionCandidate(tag); } void KoResourceTaggingManager::removeResourceTag(KoResource* resource, const QString& tagName) { QStringList tagsList = d->model->assignedTagsList(resource); Q_FOREACH (const QString & oldName, tagsList) { if (!oldName.compare(tagName)) { d->model->deleteTag(resource, oldName); } } } void KoResourceTaggingManager::renameTag(const QString &oldName, const QString& newName) { if (!d->model->tagNamesList().contains(newName)) { QList resources = d->model->currentlyVisibleResources(); Q_FOREACH (KoResource * resource, resources) { removeResourceTag(resource, oldName); addResourceTag(resource, newName); } contextCreateNewTag(newName); d->model->tagCategoryRemoved(oldName); d->model->tagCategoryAdded(newName); } } void KoResourceTaggingManager::updateTaggedResourceView() { d->model->setCurrentTag(d->currentTag); d->model->updateServer(); d->originalResources = d->model->currentlyVisibleResources(); } void KoResourceTaggingManager::tagChooserIndexChanged(const QString& lineEditText) { if (!d->tagChooser->selectedTagIsReadOnly()) { d->currentTag = lineEditText; d->tagFilter->allowSave(true); d->model->enableResourceFiltering(true); } else { d->model->enableResourceFiltering(false); d->tagFilter->allowSave(false); d->currentTag.clear(); } d->tagFilter->clear(); updateTaggedResourceView(); } void KoResourceTaggingManager::tagSearchLineEditTextChanged(const QString& lineEditText) { if (d->tagChooser->selectedTagIsReadOnly()) { d->model->enableResourceFiltering(!lineEditText.isEmpty()); } else { d->model->enableResourceFiltering(true); } d->model->searchTextChanged(lineEditText); d->model->updateServer(); ///FIXME: fix completer // d->tagCompleter = new QCompleter(tagNamesList(lineEditText),this); // d->tagSearchLineEdit->setCompleter(d->tagCompleter); } void KoResourceTaggingManager::tagSaveButtonPressed() { if (!d->tagChooser->selectedTagIsReadOnly()) { QList newResources = d->model->currentlyVisibleResources(); Q_FOREACH (KoResource * oldRes, d->originalResources) { if (!newResources.contains(oldRes)) removeResourceTag(oldRes, d->currentTag); } Q_FOREACH (KoResource * newRes, newResources) { if (!d->originalResources.contains(newRes)) addResourceTag(newRes, d->currentTag); } d->model->tagCategoryMembersChanged(); } updateTaggedResourceView(); } void KoResourceTaggingManager::contextMenuRequested(KoResource* resource, const QStringList& resourceTags, const QPoint& pos) { /* no visible tag chooser usually means no intended tag interaction, * context menu makes no sense then either */ if (!resource || !d->tagChooser->isVisible()) return; KoResourceItemChooserContextMenu menu(resource, resourceTags, d->tagChooser->currentlySelectedTag(), d->tagChooser->allTags()); connect(&menu, SIGNAL(resourceTagAdditionRequested(KoResource*, QString)), this, SLOT(contextAddTagToResource(KoResource*, QString))); connect(&menu, SIGNAL(resourceTagRemovalRequested(KoResource*, QString)), this, SLOT(contextRemoveTagFromResource(KoResource*, QString))); connect(&menu, SIGNAL(resourceAssignmentToNewTagRequested(KoResource*, QString)), this, SLOT(contextCreateNewTag(KoResource*, QString))); menu.exec(pos); } void KoResourceTaggingManager::contextMenuRequested(KoResource* currentResource, QPoint pos) { if (currentResource) { contextMenuRequested(currentResource, d->model->assignedTagsList(currentResource), pos); } } KoTagChooserWidget* KoResourceTaggingManager::tagChooserWidget() { return d->tagChooser; } KoTagFilterWidget* KoResourceTaggingManager::tagFilterWidget() { return d->tagFilter; } diff --git a/libs/widgets/KoToolBox.cpp b/libs/widgets/KoToolBox.cpp index 5926869784..b614edc21c 100644 --- a/libs/widgets/KoToolBox.cpp +++ b/libs/widgets/KoToolBox.cpp @@ -1,358 +1,372 @@ /* * Copyright (c) 2005-2009 Thomas Zander * Copyright (c) 2009 Peter Simonsson * Copyright (c) 2010 Cyrille Berger * * 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 "KoToolBox_p.h" #include "KoToolBoxLayout_p.h" #include "KoToolBoxButton_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BUTTON_MARGIN 10 static int buttonSize(int screen) { QRect rc = qApp->desktop()->screenGeometry(screen); if (rc.width() <= 1024) { return 12; } else if (rc.width() <= 1377) { return 14; } else if (rc.width() <= 1920 ) { return 16; } else { return 22; } } class KoToolBox::Private { public: Private() : layout(0) , buttonGroup(0) , floating(false) , contextSize(0) { } void addSection(Section *section, const QString &name); QList buttons; QMap sections; KoToolBoxLayout *layout; QButtonGroup *buttonGroup; QHash visibilityCodes; bool floating; QMap contextIconSizes; QMenu* contextSize; + Qt::Orientation orientation; }; void KoToolBox::Private::addSection(Section *section, const QString &name) { section->setName(name); layout->addSection(section); sections.insert(name, section); } KoToolBox::KoToolBox() : d(new Private) { d->layout = new KoToolBoxLayout(this); // add defaults d->addSection(new Section(this), "main"); d->addSection(new Section(this), "dynamic"); d->buttonGroup = new QButtonGroup(this); setLayout(d->layout); Q_FOREACH (KoToolAction *toolAction, KoToolManager::instance()->toolActionList()) { addButton(toolAction); } // Update visibility of buttons setButtonsVisible(QList()); connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*, int)), this, SLOT(setActiveTool(KoCanvasController*, int))); connect(KoToolManager::instance(), SIGNAL(currentLayerChanged(const KoCanvasController*,const KoShapeLayer*)), this, SLOT(setCurrentLayer(const KoCanvasController*,const KoShapeLayer*))); connect(KoToolManager::instance(), SIGNAL(toolCodesSelected(QList)), this, SLOT(setButtonsVisible(QList))); connect(KoToolManager::instance(), SIGNAL(addedTool(KoToolAction*,KoCanvasController*)), this, SLOT(toolAdded(KoToolAction*,KoCanvasController*))); QTimer::singleShot(0, this, SLOT(adjustToFit())); } KoToolBox::~KoToolBox() { delete d; } void KoToolBox::addButton(KoToolAction *toolAction) { KoToolBoxButton *button = new KoToolBoxButton(toolAction, this); d->buttons << button; int toolbuttonSize = buttonSize(qApp->desktop()->screenNumber(this)); KConfigGroup cfg = KSharedConfig::openConfig()->group("KoToolBox"); int iconSize = cfg.readEntry("iconSize", toolbuttonSize); - button->setIconSize(QSize(iconSize, iconSize)); + + button->setIconSize(QSize(iconSize, iconSize)); foreach (Section *section, d->sections.values()) { section->setButtonSize(QSize(iconSize + BUTTON_MARGIN, iconSize + BUTTON_MARGIN)); } QString sectionToBeAddedTo; const QString section = toolAction->section(); if (section.contains(qApp->applicationName())) { sectionToBeAddedTo = "main"; } else if (section.contains("main")) { sectionToBeAddedTo = "main"; } else if (section.contains("dynamic")) { sectionToBeAddedTo = "dynamic"; } else { sectionToBeAddedTo = section; } Section *sectionWidget = d->sections.value(sectionToBeAddedTo); if (sectionWidget == 0) { sectionWidget = new Section(this); d->addSection(sectionWidget, sectionToBeAddedTo); } sectionWidget->addButton(button, toolAction->priority()); d->buttonGroup->addButton(button, toolAction->buttonGroupId()); d->visibilityCodes.insert(button, toolAction->visibilityCode()); } void KoToolBox::setActiveTool(KoCanvasController *canvas, int id) { Q_UNUSED(canvas); QAbstractButton *button = d->buttonGroup->button(id); if (button) { button->setChecked(true); (qobject_cast(button))->setHighlightColor(); } else { warnWidgets << "KoToolBox::setActiveTool(" << id << "): no such button found"; } } void KoToolBox::setButtonsVisible(const QList &codes) { Q_FOREACH (QToolButton *button, d->visibilityCodes.keys()) { QString code = d->visibilityCodes.value(button); if (code.startsWith(QLatin1String("flake/"))) { continue; } if (code.endsWith( QLatin1String( "/always"))) { button->setVisible(true); button->setEnabled( true ); } else if (code.isEmpty()) { button->setVisible(true); button->setEnabled( codes.count() != 0 ); } else { button->setVisible( codes.contains(code) ); } } layout()->invalidate(); update(); } void KoToolBox::setCurrentLayer(const KoCanvasController *canvas, const KoShapeLayer *layer) { Q_UNUSED(canvas); const bool enabled = layer == 0 || (layer->isEditable() && layer->isVisible()); foreach (QToolButton *button, d->visibilityCodes.keys()) { if (d->visibilityCodes[button].endsWith( QLatin1String( "/always") ) ) { continue; } button->setEnabled(enabled); } } void KoToolBox::paintEvent(QPaintEvent *) { QPainter painter(this); const QList sections = d->sections.values(); QList::const_iterator iterator = sections.begin(); int halfSpacing = layout()->spacing(); if (halfSpacing > 0) { halfSpacing /= 2; } while(iterator != sections.end()) { Section *section = *iterator; QStyleOption styleoption; styleoption.palette = palette(); if (section->separators() & Section::SeparatorTop) { int y = section->y() - halfSpacing; styleoption.state = QStyle::State_None; styleoption.rect = QRect(section->x(), y-1, section->width(), 2); style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &styleoption, &painter); } if (section->separators() & Section::SeparatorLeft) { int x = section->x() - halfSpacing; styleoption.state = QStyle::State_Horizontal; styleoption.rect = QRect(x-1, section->y(), 2, section->height()); style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &styleoption, &painter); } ++iterator; } painter.end(); } void KoToolBox::resizeEvent(QResizeEvent* event) { + QRect availableRc = qApp->desktop()->availableGeometry(this); + QSize layoutSize = layout()->minimumSize(); + QWidget::resizeEvent(event); - if (!d->floating) { - setMinimumSize(layout()->minimumSize()); // This enforces the minimum size on the widget + + if (layoutSize.height() > availableRc.height()) { + setMinimumWidth(layout()->minimumSize().width() * 2); // This enforces the minimum size on the widget + adjustToFit(); } + else if (layoutSize.width() > availableRc.width()) { + setMinimumHeight(layout()->minimumSize().height() * 2); // This enforces the minimum size on the widget + adjustToFit(); + } + } void KoToolBox::setOrientation(Qt::Orientation orientation) { + d->orientation = orientation; d->layout->setOrientation(orientation); QTimer::singleShot(0, this, SLOT(update())); Q_FOREACH (Section* section, d->sections) { section->setOrientation(orientation); } } void KoToolBox::setFloating(bool v) { setMinimumSize(QSize(1,1)); d->floating = v; } void KoToolBox::toolAdded(KoToolAction *toolAction, KoCanvasController *canvas) { Q_UNUSED(canvas); addButton(toolAction); setButtonsVisible(QList()); } void KoToolBox::adjustToFit() { int newWidth = width() - (width() % layout()->minimumSize().width()); + if (newWidth != width() && newWidth >= layout()->minimumSize().width()) { setMaximumWidth(newWidth); QTimer::singleShot(0, this, SLOT(resizeUnlock())); } } void KoToolBox::resizeUnlock() { setMaximumWidth(QWIDGETSIZE_MAX); } void KoToolBox::slotContextIconSize() { QAction* action = qobject_cast(sender()); if (action && d->contextIconSizes.contains(action)) { const int iconSize = d->contextIconSizes.value(action); KConfigGroup cfg = KSharedConfig::openConfig()->group("KoToolBox"); cfg.writeEntry("iconSize", iconSize); Q_FOREACH (QToolButton *button, d->buttons) { button->setIconSize(QSize(iconSize, iconSize)); } Q_FOREACH (Section *section, d->sections.values()) { section->setButtonSize(QSize(iconSize + BUTTON_MARGIN, iconSize + BUTTON_MARGIN)); } } adjustToFit(); } void KoToolBox::contextMenuEvent(QContextMenuEvent *event) { int toolbuttonSize = buttonSize(qApp->desktop()->screenNumber(this)); if (!d->contextSize) { d->contextSize = new QMenu(i18n("Icon Size"), this); d->contextIconSizes.insert(d->contextSize->addAction(i18nc("@item:inmenu Icon size", "Default"), this, SLOT(slotContextIconSize())), toolbuttonSize); QList sizes; sizes << 12 << 14 << 16 << 22 << 32 << 48 << 64; //<< 96 << 128 << 192 << 256; Q_FOREACH (int i, sizes) { d->contextIconSizes.insert(d->contextSize->addAction(i18n("%1x%2", i, i), this, SLOT(slotContextIconSize())), i); } QActionGroup *sizeGroup = new QActionGroup(d->contextSize); foreach (QAction *action, d->contextSize->actions()) { action->setActionGroup(sizeGroup); action->setCheckable(true); } } KConfigGroup cfg = KSharedConfig::openConfig()->group("KoToolBox"); toolbuttonSize = cfg.readEntry("iconSize", toolbuttonSize); QMapIterator< QAction*, int > it = d->contextIconSizes; while (it.hasNext()) { it.next(); if (it.value() == toolbuttonSize) { it.key()->setChecked(true); break; } } d->contextSize->exec(event->globalPos()); } diff --git a/libs/widgets/KoToolBoxDocker.cpp b/libs/widgets/KoToolBoxDocker.cpp index 97fafbd041..0494c2c37a 100644 --- a/libs/widgets/KoToolBoxDocker.cpp +++ b/libs/widgets/KoToolBoxDocker.cpp @@ -1,68 +1,67 @@ /* * Copyright (c) 2005-2009 Thomas Zander * Copyright (c) 2009 Peter Simonsson * Copyright (c) 2010 Cyrille Berger * * 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 "KoToolBoxDocker_p.h" #include "KoToolBox_p.h" #include #include KoToolBoxDocker::KoToolBoxDocker(KoToolBox *toolBox) : QDockWidget(i18n("Toolbox")) , m_toolBox(toolBox) { setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); setWidget(toolBox); connect(this, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(updateToolBoxOrientation(Qt::DockWidgetArea))); connect(this, SIGNAL(topLevelChanged(bool)), this, SLOT(updateFloating(bool))); KoDockWidgetTitleBar* titleBar = new KoDockWidgetTitleBar(this); titleBar->setTextVisibilityMode(KoDockWidgetTitleBar::TextCanBeInvisible); titleBar->setToolTip(i18n("Tools")); setTitleBarWidget(titleBar); } void KoToolBoxDocker::setCanvas(KoCanvasBase *canvas) { - setEnabled(canvas != 0); + Q_UNUSED(canvas); } void KoToolBoxDocker::unsetCanvas() { - setEnabled(false); } void KoToolBoxDocker::updateToolBoxOrientation(Qt::DockWidgetArea area) { if (area == Qt::TopDockWidgetArea || area == Qt::BottomDockWidgetArea) { m_toolBox->setOrientation(Qt::Horizontal); } else { m_toolBox->setOrientation(Qt::Vertical); } m_toolBox->setFloating(area == Qt::NoDockWidgetArea); } void KoToolBoxDocker::updateFloating(bool v) { m_toolBox->setFloating(v); } diff --git a/libs/widgets/KoToolBoxLayout_p.h b/libs/widgets/KoToolBoxLayout_p.h index 974181ac76..1e76e7c89f 100644 --- a/libs/widgets/KoToolBoxLayout_p.h +++ b/libs/widgets/KoToolBoxLayout_p.h @@ -1,351 +1,352 @@ /* * Copyright (c) 2005-2009 Thomas Zander * Copyright (c) 2009 Peter Simonsson * Copyright (c) 2010 Cyrille Berger * * 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_TOOLBOX_LAYOUT_H_ #define _KO_TOOLBOX_LAYOUT_H_ #include #include #include #include #include #include #include class SectionLayout : public QLayout { public: explicit SectionLayout(QWidget *parent) : QLayout(parent), m_orientation(Qt::Vertical) { } ~SectionLayout() { qDeleteAll( m_items ); m_items.clear(); } void addButton(QAbstractButton *button, int priority) { addChildWidget(button); m_priorities.insert(button, priority); int index = 1; Q_FOREACH (QWidgetItem *item, m_items) { if (m_priorities.value(static_cast(item->widget())) > priority) break; index++; } m_items.insert(index-1, new QWidgetItem(button)); } QSize sizeHint() const { Q_ASSERT(0); return QSize(); } void addItem(QLayoutItem*) { Q_ASSERT(0); } QLayoutItem* itemAt(int i) const { if (m_items.count() <= i) return 0; return m_items.at(i); } QLayoutItem* takeAt(int i) { return m_items.takeAt(i); } int count() const { return m_items.count(); } void setGeometry (const QRect &rect) { int x = 0; int y = 0; const QSize &size = buttonSize(); if (m_orientation == Qt::Vertical) { foreach (QWidgetItem* w, m_items) { if (w->isEmpty()) continue; w->widget()->setGeometry(QRect(x, y, size.width(), size.height())); x += size.width(); if (x + size.width() > rect.width()) { x = 0; y += size.height(); } } } else { foreach (QWidgetItem* w, m_items) { if (w->isEmpty()) continue; w->widget()->setGeometry(QRect(x, y, size.width(), size.height())); y += size.height(); if (y + size.height() > rect.height()) { x += size.width(); y = 0; } } } } void setButtonSize(const QSize size) { m_buttonSize = size; } const QSize &buttonSize() const { return m_buttonSize; } void setOrientation (Qt::Orientation orientation) { m_orientation = orientation; } private: QSize m_buttonSize; QMap m_priorities; QList m_items; Qt::Orientation m_orientation; }; class Section : public QWidget { public: enum SeparatorFlag { SeparatorTop = 0x0001,/* SeparatorBottom = 0x0002, SeparatorRight = 0x0004,*/ SeparatorLeft = 0x0008 }; Q_DECLARE_FLAGS(Separators, SeparatorFlag) explicit Section(QWidget *parent = 0) : QWidget(parent), m_layout(new SectionLayout(this)) { setLayout(m_layout); } void addButton(QAbstractButton *button, int priority) { m_layout->addButton(button, priority); } void setName(const QString &name) { m_name = name; } QString name() const { return m_name; } void setButtonSize(QSize size) { m_layout->setButtonSize(size); } QSize iconSize() const { return m_layout->buttonSize(); } int visibleButtonCount() const { int count = 0; for(int i = m_layout->count()-1; i >= 0; --i) { if (! static_cast (m_layout->itemAt(i))->isEmpty()) ++count; } return count; } void setSeparator(Separators separators) { m_separators = separators; } Separators separators() const { return m_separators; } void setOrientation (Qt::Orientation orientation) { m_layout->setOrientation(orientation); } private: SectionLayout *m_layout; QString m_name; Separators m_separators; }; Q_DECLARE_OPERATORS_FOR_FLAGS(Section::Separators) class KoToolBoxLayout : public QLayout { public: explicit KoToolBoxLayout(QWidget *parent) : QLayout(parent), m_orientation(Qt::Vertical), m_currentHeight(0) { setSpacing(6); } ~KoToolBoxLayout() { qDeleteAll( m_sections ); m_sections.clear(); } QSize sizeHint() const { if (m_sections.isEmpty()) return QSize(); QSize oneIcon = static_cast (m_sections[0]->widget())->iconSize(); return oneIcon; } + QSize minimumSize() const { QSize s = sizeHint(); if (m_orientation == Qt::Vertical) { s.setHeight(m_currentHeight); } else { s.setWidth(m_currentHeight); } return s; } void addSection(Section *section) { addChildWidget(section); QList::iterator iterator = m_sections.begin(); int defaults = 2; // skip the first two as they are the 'main' and 'dynamic' sections. while (iterator != m_sections.end()) { if (--defaults < 0 && static_cast ((*iterator)->widget())->name() > section->name()) break; ++iterator; } m_sections.insert(iterator, new QWidgetItem(section)); } void addItem(QLayoutItem*) { Q_ASSERT(0); // don't let anything else be added. (code depends on this!) } QLayoutItem* itemAt(int i) const { if (m_sections.count() >= i) return 0; return m_sections.at(i); } QLayoutItem* takeAt(int i) { return m_sections.takeAt(i); } int count() const { return m_sections.count(); } void setGeometry (const QRect &rect) { // nothing to do? if (m_sections.isEmpty()) { m_currentHeight = 0; return; } // the names of the variables assume a vertical orientation, // but all calculations are done based on the real orientation const QSize iconSize = static_cast (m_sections.first()->widget())->iconSize(); const int maxWidth = (m_orientation == Qt::Vertical) ? rect.width() : rect.height(); // using min 1 as width to e.g. protect against div by 0 below const int iconWidth = qMax(1, (m_orientation == Qt::Vertical) ? iconSize.width() : iconSize.height()); const int iconHeight = qMax(1, (m_orientation == Qt::Vertical) ? iconSize.height() : iconSize.width()); const int maxColumns = qMax(1, (maxWidth / iconWidth)); int x = 0; int y = 0; bool firstSection = true; foreach (QWidgetItem *wi, m_sections) { Section *section = static_cast (wi->widget()); const int buttonCount = section->visibleButtonCount(); if (buttonCount == 0) { // move out of view, not perfect TODO: better solution section->setGeometry(1000, 1000, 0, 0); continue; } // rows needed for the buttons (calculation gets the ceiling value of the plain div) const int neededRowCount = ((buttonCount-1) / maxColumns) + 1; const int availableButtonCount = (maxWidth - x + 1) / iconWidth; if (firstSection) { firstSection = false; } else if (buttonCount > availableButtonCount) { // start on a new row, set separator x = 0; y += iconHeight + spacing(); const Section::Separators separator = (m_orientation == Qt::Vertical) ? Section::SeparatorTop : Section::SeparatorLeft; section->setSeparator( separator ); } else { // append to last row, set separators (on first row only to the left side) const bool isFirstRow = (y == 0); const Section::Separators separators = isFirstRow ? ((m_orientation == Qt::Vertical) ? Section::SeparatorLeft : Section::SeparatorTop) : (Section::SeparatorTop | Section::SeparatorLeft); section->setSeparator( separators ); } const int usedColumns = qMin(buttonCount, maxColumns); if (m_orientation == Qt::Vertical) { section->setGeometry(x, y, usedColumns * iconWidth, neededRowCount * iconHeight); } else { section->setGeometry(y, x, neededRowCount * iconHeight, usedColumns * iconWidth); } // advance by the icons in the last row const int lastRowColumnCount = buttonCount - ((neededRowCount-1) * maxColumns); x += (lastRowColumnCount * iconWidth) + spacing(); // advance by all but the last used row y += (neededRowCount - 1) * iconHeight; } // cache total height (or width), adding the iconHeight for the current row m_currentHeight = y + iconHeight; } void setOrientation (Qt::Orientation orientation) { m_orientation = orientation; invalidate(); } private: QList m_sections; Qt::Orientation m_orientation; mutable int m_currentHeight; }; #endif diff --git a/libs/widgetutils/KoFileDialog.cpp b/libs/widgetutils/KoFileDialog.cpp index 9d2b2527d8..5a4b336f18 100644 --- a/libs/widgetutils/KoFileDialog.cpp +++ b/libs/widgetutils/KoFileDialog.cpp @@ -1,504 +1,401 @@ /* This file is part of the KDE project Copyright (C) 2013 - 2014 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 "KoFileDialog.h" #include #include #include #include #include #include #include #include #include #include class Q_DECL_HIDDEN KoFileDialog::Private { public: Private(QWidget *parent_, KoFileDialog::DialogType dialogType_, const QString caption_, const QString defaultDir_, const QString dialogName_) : parent(parent_) , type(dialogType_) , dialogName(dialogName_) , caption(caption_) , defaultDirectory(defaultDir_) , filterList(QStringList()) , defaultFilter(QString()) - , useStaticForNative(false) - , hideDetails(false) , swapExtensionOrder(false) { - // Force the native file dialogs on Windows. Except for KDE, the native file dialogs are only possible - // using the static methods. The Qt documentation is wrong here, if it means what it says " By default, - // the native file dialog is used unless you use a subclass of QFileDialog that contains the Q_OBJECT - // macro." -#ifdef Q_OS_WIN - useStaticForNative = true; -#endif - // Non-static KDE file is broken when called with QFileDialog::AcceptSave: - // then the directory above defaultdir is opened, and defaultdir is given as the default file name... - // - // So: in X11, use static methods inside KDE, which give working native dialogs, but non-static outside - // KDE, which gives working Qt dialogs. - // - // Only show the GTK dialog in Gnome, where people deserve it -#ifdef HAVE_X11 - if (qgetenv("KDE_FULL_SESSION").size() > 0) { - useStaticForNative = true; - } - if (qgetenv("XDG_CURRENT_DESKTOP") == "GNOME") { - useStaticForNative = true; - // The GTK file dialog interferes with the Qt clipboard; so disable that - QClipboard *cb = QApplication::clipboard(); - cb->blockSignals(true); - swapExtensionOrder = true; - } - -#endif - // And OSX? That is apparently the only OS where creating a QFileDialog object, instead of using the - // static functions does call the native dialog... } ~Private() { - if (qgetenv("XDG_CURRENT_DESKTOP") == "GNOME") { - // And re-enable the clipboard. - QClipboard *cb = QApplication::clipboard(); - cb->blockSignals(false); - } } QWidget *parent; KoFileDialog::DialogType type; QString dialogName; QString caption; QString defaultDirectory; QStringList filterList; QString defaultFilter; QScopedPointer fileDialog; QString mimeType; - bool useStaticForNative; - bool hideDetails; bool swapExtensionOrder; }; KoFileDialog::KoFileDialog(QWidget *parent, KoFileDialog::DialogType type, const QString &dialogName) : d(new Private(parent, type, "", getUsedDir(dialogName), dialogName)) { + if (qgetenv("XDG_CURRENT_DESKTOP") == "GNOME") { + // The GTK file dialog interferes with the Qt clipboard; so disable that + QClipboard *cb = QApplication::clipboard(); + cb->blockSignals(true); + } } KoFileDialog::~KoFileDialog() { + if (qgetenv("XDG_CURRENT_DESKTOP") == "GNOME") { + // And re-enable the clipboard. + QClipboard *cb = QApplication::clipboard(); + cb->blockSignals(false); + } delete d; } void KoFileDialog::setCaption(const QString &caption) { d->caption = caption; } void KoFileDialog::setDefaultDir(const QString &defaultDir, bool override) { if (override || d->defaultDirectory.isEmpty() || !QFile(d->defaultDirectory).exists()) { QFileInfo f(defaultDir); d->defaultDirectory = f.absoluteFilePath(); } } void KoFileDialog::setOverrideDir(const QString &overrideDir) { d->defaultDirectory = overrideDir; } void KoFileDialog::setImageFilters() { QStringList imageFilters; // add filters for all formats supported by QImage Q_FOREACH (const QByteArray &format, QImageReader::supportedImageFormats()) { imageFilters << QLatin1String("image/") + format; } setMimeTypeFilters(imageFilters); } -void KoFileDialog::setMimeTypeFilters(const QStringList &filterList, - QString defaultFilter) +void KoFileDialog::setMimeTypeFilters(const QStringList &filterList, QString defaultFilter) { d->filterList = getFilterStringListFromMime(filterList, true); if (!defaultFilter.isEmpty()) { QStringList defaultFilters = getFilterStringListFromMime(QStringList() << defaultFilter, false); if (defaultFilters.size() > 0) { defaultFilter = defaultFilters.first(); } } d->defaultFilter = defaultFilter; } -void KoFileDialog::setHideNameFilterDetailsOption() -{ - d->hideDetails = true; -} - QString KoFileDialog::selectedNameFilter() const { - if (!d->useStaticForNative) { - return d->fileDialog->selectedNameFilter(); - } - else { - return d->defaultFilter; - } + return d->fileDialog->selectedNameFilter(); } QString KoFileDialog::selectedMimeType() const { return d->mimeType; } void KoFileDialog::createFileDialog() { + //qDebug() << "createFIleDialog. Parent:" << d->parent << "Caption:" << d->caption << "Default directory:" << d->defaultDirectory << "Default iflter:" << d->defaultFilter; + d->fileDialog.reset(new QFileDialog(d->parent, d->caption, d->defaultDirectory)); + d->fileDialog->setOption(QFileDialog::DontUseNativeDialog, false); + d->fileDialog->setOption(QFileDialog::DontConfirmOverwrite, false); + d->fileDialog->setOption(QFileDialog::HideNameFilterDetails, true); if (d->type == SaveFile) { d->fileDialog->setAcceptMode(QFileDialog::AcceptSave); d->fileDialog->setFileMode(QFileDialog::AnyFile); } else { // open / import d->fileDialog->setAcceptMode(QFileDialog::AcceptOpen); if (d->type == ImportDirectory || d->type == OpenDirectory){ d->fileDialog->setFileMode(QFileDialog::Directory); d->fileDialog->setOption(QFileDialog::ShowDirsOnly, true); } else { // open / import file(s) if (d->type == OpenFile || d->type == ImportFile) { d->fileDialog->setFileMode(QFileDialog::ExistingFile); } else { // files d->fileDialog->setFileMode(QFileDialog::ExistingFiles); } } } d->fileDialog->setNameFilters(d->filterList); - if (!d->defaultFilter.isEmpty()) { + + if (!QFileInfo(d->defaultDirectory).isDir()) { + //qDebug() << "Finding the right mimetype for the given file" << d->defaultDirectory; + QString mime = KisMimeDatabase::mimeTypeForFile(d->defaultDirectory); + QString description = KisMimeDatabase::descriptionForMimeType(mime); + Q_FOREACH(const QString &filter, d->filterList) { + //qDebug() << "\tConsidering" << filter; + if (filter.startsWith(description)) { + d->fileDialog->selectNameFilter(filter); + break; + } + } + } + else if (!d->defaultFilter.isEmpty()) { d->fileDialog->selectNameFilter(d->defaultFilter); } if (d->type == ImportDirectory || d->type == ImportFile || d->type == ImportFiles || d->type == SaveFile) { d->fileDialog->setWindowModality(Qt::WindowModal); } - - if (d->hideDetails) { - d->fileDialog->setOption(QFileDialog::HideNameFilterDetails); - } - - connect(d->fileDialog.data(), SIGNAL(filterSelected(QString)), this, SLOT(filterSelected(QString))); } QString KoFileDialog::filename() { - //qDebug() << "static:" << d->useStaticForNative; - QString url; - if (!d->useStaticForNative) { - createFileDialog(); - if (d->fileDialog->exec() == QDialog::Accepted) { - url = d->fileDialog->selectedFiles().first(); - } - } - else { - switch (d->type) { - case OpenFile: - { - url = QFileDialog::getOpenFileName(d->parent, - d->caption, - d->defaultDirectory, - d->filterList.join(";;"), - &d->defaultFilter); - break; - } - case OpenDirectory: - { - url = QFileDialog::getExistingDirectory(d->parent, - d->caption, - d->defaultDirectory, - QFileDialog::ShowDirsOnly); - break; - } - case ImportFile: - { - url = QFileDialog::getOpenFileName(d->parent, - d->caption, - d->defaultDirectory, - d->filterList.join(";;"), - &d->defaultFilter); - break; - } - case ImportDirectory: - { - url = QFileDialog::getExistingDirectory(d->parent, - d->caption, - d->defaultDirectory, - QFileDialog::ShowDirsOnly); - break; - } - case SaveFile: - { - url = QFileDialog::getSaveFileName(d->parent, - d->caption, - d->defaultDirectory, - d->filterList.join(";;"), - &d->defaultFilter); - break; - } - default: - ; - } + createFileDialog(); + if (d->fileDialog->exec() == QDialog::Accepted) { + url = d->fileDialog->selectedFiles().first(); } if (!url.isEmpty()) { - if (d->type == SaveFile && QFileInfo(url).suffix().isEmpty()) { - int start = d->defaultFilter.lastIndexOf("*.") + 1; - int end = d->defaultFilter.lastIndexOf(" )"); + QString selectedFilter; + // index 0 is all supported; if that is chosen, saveDocument will automatically make it .kra + for (int i = 1; i < d->filterList.size(); ++i) { + if (d->filterList[i].startsWith(d->fileDialog->selectedNameFilter())) { + selectedFilter = d->filterList[i]; + break; + } + } + int start = selectedFilter.indexOf("*.") + 1; + int end = selectedFilter.indexOf(" ", start); int n = end - start; - QString extension = d->defaultFilter.mid(start, n); - url.append(extension); + QString extension = selectedFilter.mid(start, n); + url = url + "." + extension; } d->mimeType = KisMimeDatabase::mimeTypeForFile(url); saveUsedDir(url, d->dialogName); } return url; } QStringList KoFileDialog::filenames() { - //qDebug() << "static:" << d->useStaticForNative; - QStringList urls; - if (!d->useStaticForNative) { - createFileDialog(); - if (d->fileDialog->exec() == QDialog::Accepted) { - urls = d->fileDialog->selectedFiles(); - } - } - else { - switch (d->type) { - case OpenFiles: - case ImportFiles: - { - urls = QFileDialog::getOpenFileNames(d->parent, - d->caption, - d->defaultDirectory, - d->filterList.join(";;"), - &d->defaultFilter); - break; - } - default: - ; - } + createFileDialog(); + if (d->fileDialog->exec() == QDialog::Accepted) { + urls = d->fileDialog->selectedFiles(); } if (urls.size() > 0) { saveUsedDir(urls.first(), d->dialogName); } return urls; } -void KoFileDialog::filterSelected(const QString &filter) -{ - // "Windows BMP image ( *.bmp )"; - int start = filter.lastIndexOf("*.") + 2; - int end = filter.lastIndexOf(" )"); - int n = end - start; - QString extension = filter.mid(start, n); - d->defaultFilter = filter; - d->fileDialog->setDefaultSuffix(extension); -} - QStringList KoFileDialog::splitNameFilter(const QString &nameFilter, QStringList *mimeList) { Q_ASSERT(mimeList); QStringList filters; QString description; if (nameFilter.contains("(")) { description = nameFilter.left(nameFilter.indexOf("(") -1).trimmed(); } QStringList entries = nameFilter.mid(nameFilter.indexOf("(") + 1).split(" ",QString::SkipEmptyParts ); - + entries.sort(); Q_FOREACH (QString entry, entries) { entry = entry.remove("*"); entry = entry.remove(")"); QString mimeType = KisMimeDatabase::mimeTypeForSuffix(entry); if (mimeType != "application/octet-stream") { if (!mimeList->contains(mimeType)) { mimeList->append(mimeType); filters.append(KisMimeDatabase::descriptionForMimeType(mimeType) + " ( *" + entry + " )"); } } else { filters.append(entry.remove(".").toUpper() + " " + description + " ( *." + entry + " )"); } } return filters; } -const QStringList KoFileDialog::getFilterStringListFromMime(const QStringList &mimeList, +const QStringList KoFileDialog::getFilterStringListFromMime(const QStringList &_mimeList, bool withAllSupportedEntry) { QStringList mimeSeen; // 1 QString allSupported; // 2 QString kritaNative; // 3 QString ora; QStringList ret; - + QStringList mimeList = _mimeList; + mimeList.sort(); Q_FOREACH(const QString &mimeType, mimeList) { if (!mimeSeen.contains(mimeType)) { QString description = KisMimeDatabase::descriptionForMimeType(mimeType); if (description.isEmpty() && !mimeType.isEmpty()) { description = mimeType.split("/")[1]; if (description.startsWith("x-")) { description = description.remove(0, 2); } } QString oneFilter; QStringList patterns = KisMimeDatabase::suffixesForMimeType(mimeType); QStringList globPatterns; Q_FOREACH(const QString &pattern, patterns) { if (pattern.startsWith(".")) { globPatterns << "*" + pattern; } else if (pattern.startsWith("*.")) { globPatterns << pattern; } else { globPatterns << "*." + pattern; } } Q_FOREACH(const QString &glob, globPatterns) { if (d->swapExtensionOrder) { oneFilter.prepend(glob + " "); if (withAllSupportedEntry) { allSupported.prepend(glob + " "); } #ifdef Q_OS_LINUX - if (qgetenv("XDG_CURRENT_DESKTOP") == "GNOME") { - oneFilter.prepend(glob.toUpper() + " "); + if (qgetenv("XDG_CURRENT_DESKTOP") == "GNOME") { + oneFilter.prepend(glob.toUpper() + " "); if (withAllSupportedEntry) { allSupported.prepend(glob.toUpper() + " "); } - } + } #endif } else { oneFilter.append(glob + " "); if (withAllSupportedEntry) { allSupported.append(glob + " "); } #ifdef Q_OS_LINUX if (qgetenv("XDG_CURRENT_DESKTOP") == "GNOME") { oneFilter.append(glob.toUpper() + " "); if (withAllSupportedEntry) { allSupported.append(glob.toUpper() + " "); } } #endif } } Q_ASSERT(!description.isEmpty()); oneFilter = description + " ( " + oneFilter + ")"; if (mimeType == "application/x-krita") { kritaNative = oneFilter; continue; } if (mimeType == "image/openraster") { ora = oneFilter; continue; } else { ret << oneFilter; } mimeSeen << mimeType; } } ret.sort(); ret.removeDuplicates(); if (!ora.isEmpty()) ret.prepend(ora); if (!kritaNative.isEmpty()) ret.prepend(kritaNative); if (!allSupported.isEmpty()) ret.prepend(i18n("All supported formats") + " ( " + allSupported + (")")); return ret; } QString KoFileDialog::getUsedDir(const QString &dialogName) { if (dialogName.isEmpty()) return ""; KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); QString dir = group.readEntry(dialogName); return dir; } void KoFileDialog::saveUsedDir(const QString &fileName, const QString &dialogName) { if (dialogName.isEmpty()) return; QFileInfo fileInfo(fileName); KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); group.writeEntry(dialogName, fileInfo.absolutePath()); } diff --git a/libs/widgetutils/KoFileDialog.h b/libs/widgetutils/KoFileDialog.h index 6ed4902901..3e9a5e0369 100644 --- a/libs/widgetutils/KoFileDialog.h +++ b/libs/widgetutils/KoFileDialog.h @@ -1,138 +1,132 @@ /* This file is part of the KDE project Copyright (C) 2013 - 2014 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. */ #ifndef KOFILEDIALOG_H #define KOFILEDIALOG_H #include "kritawidgetutils_export.h" #include #include #include #include /** * Wrapper around QFileDialog providing native file dialogs * on KDE/Gnome/Windows/OSX/etc. */ class KRITAWIDGETUTILS_EXPORT KoFileDialog : public QObject { Q_OBJECT public: enum DialogType { OpenFile, OpenFiles, OpenDirectory, ImportFile, ImportFiles, ImportDirectory, SaveFile }; /** * @brief constructor * @param parent The parent of the file dialog * @param dialogType usage of the file dialog * @param dialogName the name for the file dialog. This will be used to open * the filedialog in the last open location, instead the specified directory. * * @return The name of the entry user selected in the file dialog * */ KoFileDialog(QWidget *parent, KoFileDialog::DialogType type, const QString &dialogName); ~KoFileDialog(); void setCaption(const QString &caption); /** * @brief setDefaultDir set the default directory to defaultDir * * @param defaultDir a path to a file or directory */ void setDefaultDir(const QString &defaultDir, bool override = false); /** * @brief setOverrideDir override both the default dir and the saved dir found by dialogName * @param overrideDir a path to a file or directory */ void setOverrideDir(const QString &overrideDir); /** * @brief setImageFilters sets the name filters for the file dialog to all * image formats Qt's QImageReader supports. */ void setImageFilters(); void setMimeTypeFilters(const QStringList &filterList, QString defaultFilter = QString()); - void setHideNameFilterDetailsOption(); - /// Get the file names the user selected in the file dialog QStringList filenames(); /// Get the file name the user selected in the file dialog QString filename(); /** * @brief selectedNameFilter returns the name filter the user selected, either * directory or by clicking on it. * @return */ QString selectedNameFilter() const; QString selectedMimeType() const; -private Q_SLOTS: - - void filterSelected(const QString &filter); - private: /** * @brief splitNameFilter take a single line of a QDialog name filter and split it * into several lines. This is needed because a single line name filter can contain * more than one mimetype, making it impossible to figure out the correct extension. * * The methods takes care of some duplicated extensions, like jpeg and jpg. * @param nameFilter the namefilter to be split * @param mimeList a pointer to the list with mimes that shouldn't be added. * @return a stringlist of all name filters. */ static QStringList splitNameFilter(const QString &nameFilter, QStringList *mimeList); void createFileDialog(); QString getUsedDir(const QString &dialogName); void saveUsedDir(const QString &fileName, const QString &dialogName); const QStringList getFilterStringListFromMime(const QStringList &mimeList, bool withAllSupportedEntry = false); class Private; Private * const d; }; #endif /* KOFILEDIALOG_H */ diff --git a/libs/widgetutils/KoResourcePaths.cpp b/libs/widgetutils/KoResourcePaths.cpp index 606bff8ac3..3b7bb1bcca 100644 --- a/libs/widgetutils/KoResourcePaths.cpp +++ b/libs/widgetutils/KoResourcePaths.cpp @@ -1,532 +1,540 @@ /* * Copyright (c) 2015 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "KoResourcePaths.h" #include #include #include #include #include #include #include #include #include #include #include "kis_debug.h" #include "WidgetUtilsDebug.h" Q_GLOBAL_STATIC(KoResourcePaths, s_instance); static QString cleanup(const QString &path) { return QDir::cleanPath(path); } static QStringList cleanup(const QStringList &pathList) { QStringList cleanedPathList; Q_FOREACH(const QString &path, pathList) { cleanedPathList << cleanup(path); } return cleanedPathList; } static QString cleanupDirs(const QString &path) { return QDir::cleanPath(path) + QDir::separator(); } static QStringList cleanupDirs(const QStringList &pathList) { QStringList cleanedPathList; Q_FOREACH(const QString &path, pathList) { cleanedPathList << cleanupDirs(path); } return cleanedPathList; } void appendResources(QStringList *dst, const QStringList &src, bool eliminateDuplicates) { Q_FOREACH (const QString &resource, src) { QString realPath = QDir::cleanPath(resource); if (!eliminateDuplicates || !dst->contains(realPath)) { *dst << realPath; } } } #ifdef Q_OS_WIN static const Qt::CaseSensitivity cs = Qt::CaseInsensitive; #else static const Qt::CaseSensitivity cs = Qt::CaseSensitive; #endif #ifdef Q_OS_MAC #include #include #include #endif QString getInstallationPrefix() { #ifdef Q_OS_MAC - CFURLRef appUrlRef = CFBundleCopyBundleURL(CFBundleGetMainBundle()); - CFStringRef macPath = CFURLCopyFileSystemPath(appUrlRef, - kCFURLPOSIXPathStyle); - const char *pathPtr = CFStringGetCStringPtr(macPath, - CFStringGetSystemEncoding()); - QString bundlePath = QString::fromLatin1(pathPtr); - - debugWidgetUtils << "1" << bundlePath << (bundlePath + QString::fromLatin1("/Contents/MacOS/share")); - if (QFile(bundlePath + QString::fromLatin1("/Contents/share")).exists()) { - debugWidgetUtils << "running from a deployed bundle"; - bundlePath += QString::fromLatin1("/Contents/"); + QString appPath = qApp->applicationDirPath(); + + debugWidgetUtils << "1" << appPath; + appPath.chop(QString("MacOS/").length()); + debugWidgetUtils << "2" << appPath; + + bool makeInstall = QDir(appPath + "/../../../share/kritaplugins").exists(); + bool inBundle = QDir(appPath + "/Resources/kritaplugins").exists(); + + debugWidgetUtils << "3. After make install" << makeInstall; + debugWidgetUtils << "4. In Bundle" << inBundle; + + QString bundlePath; + + if (inBundle) { + bundlePath = appPath + "/"; + } + else if (makeInstall) { + appPath.chop(QString("Contents/").length()); + bundlePath = appPath + "/../../"; } else { - debugWidgetUtils << "running from make install"; - bundlePath += "/../../"; + qFatal("Cannot calculate the bundle path from the app path"); } - CFRelease(appUrlRef); - CFRelease(macPath); debugWidgetUtils << ">>>>>>>>>>>" << bundlePath; return bundlePath; #else return qApp->applicationDirPath() + "/../"; #endif } class Q_DECL_HIDDEN KoResourcePaths::Private { public: QMap absolutes; // For each resource type, the list of absolute paths, from most local (most priority) to most global QMap relatives; // Same with relative paths QMutex relativesMutex; QMutex absolutesMutex; QStringList aliases(const QString &type) { QStringList r; QStringList a; relativesMutex.lock(); if (relatives.contains(type)) { r += relatives[type]; } relativesMutex.unlock(); debugWidgetUtils << "\trelatives" << r; absolutesMutex.lock(); if (absolutes.contains(type)) { a += absolutes[type]; } debugWidgetUtils << "\tabsolutes" << a; absolutesMutex.unlock(); return r + a; } QStandardPaths::StandardLocation mapTypeToQStandardPaths(const QString &type) { if (type == "tmp") { return QStandardPaths::TempLocation; } else if (type == "appdata") { return QStandardPaths::AppDataLocation; } else if (type == "data") { return QStandardPaths::AppDataLocation; } else if (type == "cache") { return QStandardPaths::CacheLocation; } else if (type == "locale") { return QStandardPaths::AppDataLocation; } else { return QStandardPaths::AppDataLocation; } } }; KoResourcePaths::KoResourcePaths() : d(new Private) { } KoResourcePaths::~KoResourcePaths() { } QString KoResourcePaths::getApplicationRoot() { return getInstallationPrefix(); } void KoResourcePaths::addResourceType(const char *type, const char *basetype, const QString &relativeName, bool priority) { s_instance->addResourceTypeInternal(QString::fromLatin1(type), QString::fromLatin1(basetype), relativeName, priority); } void KoResourcePaths::addResourceDir(const char *type, const QString &dir, bool priority) { s_instance->addResourceDirInternal(QString::fromLatin1(type), dir, priority); } QString KoResourcePaths::findResource(const char *type, const QString &fileName) { return cleanup(s_instance->findResourceInternal(QString::fromLatin1(type), fileName)); } QStringList KoResourcePaths::findDirs(const char *type, const QString &reldir) { return cleanupDirs(s_instance->findDirsInternal(QString::fromLatin1(type), reldir)); } QStringList KoResourcePaths::findAllResources(const char *type, const QString &filter, SearchOptions options) { return cleanup(s_instance->findAllResourcesInternal(QString::fromLatin1(type), filter, options)); } QStringList KoResourcePaths::resourceDirs(const char *type) { return cleanupDirs(s_instance->resourceDirsInternal(QString::fromLatin1(type))); } QString KoResourcePaths::saveLocation(const char *type, const QString &suffix, bool create) { return cleanupDirs(s_instance->saveLocationInternal(QString::fromLatin1(type), suffix, create)); } QString KoResourcePaths::locate(const char *type, const QString &filename) { return cleanup(s_instance->locateInternal(QString::fromLatin1(type), filename)); } QString KoResourcePaths::locateLocal(const char *type, const QString &filename, bool createDir) { return cleanup(s_instance->locateLocalInternal(QString::fromLatin1(type), filename, createDir)); } void KoResourcePaths::addResourceTypeInternal(const QString &type, const QString &basetype, const QString &relativename, bool priority) { Q_UNUSED(basetype); if (relativename.isEmpty()) return; QString copy = relativename; Q_ASSERT(basetype == "data"); if (!copy.endsWith(QLatin1Char('/'))) { copy += QLatin1Char('/'); } d->relativesMutex.lock(); QStringList &rels = d->relatives[type]; // find or insert if (!rels.contains(copy, cs)) { if (priority) { rels.prepend(copy); } else { rels.append(copy); } } d->relativesMutex.unlock(); debugWidgetUtils << "addResourceType: type" << type << "basetype" << basetype << "relativename" << relativename << "priority" << priority << d->relatives[type]; } void KoResourcePaths::addResourceDirInternal(const QString &type, const QString &absdir, bool priority) { if (absdir.isEmpty() || type.isEmpty()) return; // find or insert entry in the map QString copy = absdir; if (copy.at(copy.length() - 1) != QLatin1Char('/')) { copy += QLatin1Char('/'); } d->absolutesMutex.lock(); QStringList &paths = d->absolutes[type]; if (!paths.contains(copy, cs)) { if (priority) { paths.prepend(copy); } else { paths.append(copy); } } d->absolutesMutex.unlock(); debugWidgetUtils << "addResourceDir: type" << type << "absdir" << absdir << "priority" << priority << d->absolutes[type]; } QString KoResourcePaths::findResourceInternal(const QString &type, const QString &fileName) { QStringList aliases = d->aliases(type); debugWidgetUtils << "aliases" << aliases << getApplicationRoot(); QString resource = QStandardPaths::locate(QStandardPaths::AppDataLocation, fileName, QStandardPaths::LocateFile); if (resource.isEmpty()) { Q_FOREACH (const QString &alias, aliases) { resource = QStandardPaths::locate(d->mapTypeToQStandardPaths(type), alias + '/' + fileName, QStandardPaths::LocateFile); debugWidgetUtils << "\t1" << resource; if (QFile::exists(resource)) { continue; } } } if (resource.isEmpty() || !QFile::exists(resource)) { QString approot = getApplicationRoot(); Q_FOREACH (const QString &alias, aliases) { resource = approot + "/share/" + alias + '/' + fileName; debugWidgetUtils << "\t1" << resource; if (QFile::exists(resource)) { continue; } } } if (resource.isEmpty() || !QFile::exists(resource)) { QString approot = getApplicationRoot(); Q_FOREACH (const QString &alias, aliases) { resource = approot + "/share/krita/" + alias + '/' + fileName; debugWidgetUtils << "\t1" << resource; if (QFile::exists(resource)) { continue; } } } debugWidgetUtils << "findResource: type" << type << "filename" << fileName << "resource" << resource; Q_ASSERT(!resource.isEmpty()); return resource; } QStringList KoResourcePaths::findDirsInternal(const QString &type, const QString &relDir) { QStringList aliases = d->aliases(type); debugWidgetUtils << type << relDir << aliases << d->mapTypeToQStandardPaths(type); QStringList dirs; { QStringList standardDirs = QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), relDir, QStandardPaths::LocateDirectory); appendResources(&dirs, standardDirs, true); } Q_FOREACH (const QString &alias, aliases) { QStringList aliasDirs = QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias + '/' + relDir, QStandardPaths::LocateDirectory); appendResources(&dirs, aliasDirs, true); } #ifdef Q_OS_MAC - { + debugWidgetUtils << "MAC:" << getApplicationRoot(); QStringList bundlePaths; - bundlePaths << getApplicationRoot() + "/share/" + relDir; - bundlePaths << getApplicationRoot() + "/../share/" + relDir; + bundlePaths << getApplicationRoot() + "/share/krita/" + relDir; + bundlePaths << getApplicationRoot() + "/../share/krita/" + relDir; + debugWidgetUtils << "bundlePaths" << bundlePaths; appendResources(&dirs, bundlePaths, true); + Q_ASSERT(!dirs.isEmpty()); } #endif if (dirs.isEmpty()) { QStringList fallbackPaths; fallbackPaths << getApplicationRoot() + "/share/" + relDir; fallbackPaths << getApplicationRoot() + "/share/krita/" + relDir; appendResources(&dirs, fallbackPaths, true); } debugWidgetUtils << "findDirs: type" << type << "relDir" << relDir<< "resource" << dirs; return dirs; } QStringList filesInDir(const QString &startdir, const QString & filter, bool recursive) { debugWidgetUtils << "filesInDir: startdir" << startdir << "filter" << filter << "recursive" << recursive; QStringList result; // First the entries in this path QStringList nameFilters; nameFilters << filter; const QStringList fileNames = QDir(startdir).entryList(nameFilters, QDir::Files | QDir::CaseSensitive, QDir::Name); debugWidgetUtils << "\tFound:" << fileNames.size() << ":" << fileNames; Q_FOREACH (const QString &fileName, fileNames) { QString file = startdir + '/' + fileName; result << file; } // And then everything underneath, if recursive is specified if (recursive) { const QStringList entries = QDir(startdir).entryList(QDir::Dirs | QDir::NoDotAndDotDot); Q_FOREACH (const QString &subdir, entries) { debugWidgetUtils << "\tGoing to look in subdir" << subdir << "of" << startdir; result << filesInDir(startdir + '/' + subdir, filter, recursive); } } return result; } QStringList KoResourcePaths::findAllResourcesInternal(const QString &type, const QString &_filter, SearchOptions options) const { debugWidgetUtils << "====================================================="; debugWidgetUtils << type << _filter << QStandardPaths::standardLocations(d->mapTypeToQStandardPaths(type)); bool recursive = options & KoResourcePaths::Recursive; debugWidgetUtils << "findAllResources: type" << type << "filter" << _filter << "recursive" << recursive; QStringList aliases = d->aliases(type); QString filter = _filter; // In cases where the filter is like "color-schemes/*.colors" instead of "*.kpp", used with unregistered resource types if (filter.indexOf('*') > 0) { aliases << filter.split('*').first(); filter = '*' + filter.split('*')[1]; debugWidgetUtils << "Split up alias" << aliases << "filter" << filter; } QStringList resources; if (aliases.isEmpty()) { QStringList standardResources = QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), filter, QStandardPaths::LocateFile); appendResources(&resources, standardResources, true); } debugWidgetUtils << "\tresources from qstandardpaths:" << resources.size(); Q_FOREACH (const QString &alias, aliases) { debugWidgetUtils << "\t\talias:" << alias; QStringList dirs; dirs << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory) << getInstallationPrefix() + "share/" + alias + "/" << getInstallationPrefix() + "share/krita/" + alias + "/"; Q_FOREACH (const QString &dir, dirs) { appendResources(&resources, filesInDir(dir, filter, recursive), true); } } debugWidgetUtils << "\tresources also from aliases:" << resources.size(); if (resources.isEmpty()) { QFileInfo fi(filter); QStringList prefixResources; prefixResources << filesInDir(getInstallationPrefix() + "share/" + fi.path(), fi.fileName(), false); prefixResources << filesInDir(getInstallationPrefix() + "share/krita/" + fi.path(), fi.fileName(), false); appendResources(&resources, prefixResources, true); } debugWidgetUtils << "\tresources from installation:" << resources.size(); debugWidgetUtils << "====================================================="; return resources; } QStringList KoResourcePaths::resourceDirsInternal(const QString &type) { QStringList resourceDirs; QStringList aliases = d->aliases(type); Q_FOREACH (const QString &alias, aliases) { QStringList aliasDirs; aliasDirs << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory); aliasDirs << getInstallationPrefix() + "share/" + alias + "/" << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory); aliasDirs << getInstallationPrefix() + "share/krita/" + alias + "/" << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory); appendResources(&resourceDirs, aliasDirs, true); } debugWidgetUtils << "resourceDirs: type" << type << resourceDirs; return resourceDirs; } QString KoResourcePaths::saveLocationInternal(const QString &type, const QString &suffix, bool create) { QStringList aliases = d->aliases(type); QString path; if (aliases.size() > 0) { path = QStandardPaths::writableLocation(d->mapTypeToQStandardPaths(type)) + '/' + aliases.first(); } else { path = QStandardPaths::writableLocation(d->mapTypeToQStandardPaths(type)); if (!path.endsWith("krita")) { path += "/krita"; } if (!suffix.isEmpty()) { path += "/" + suffix; } } QDir d(path); if (!d.exists() && create) { d.mkpath(path); } debugWidgetUtils << "saveLocation: type" << type << "suffix" << suffix << "create" << create << "path" << path; return path; } QString KoResourcePaths::locateInternal(const QString &type, const QString &filename) { QStringList aliases = d->aliases(type); QStringList locations; if (aliases.isEmpty()) { locations << QStandardPaths::locate(d->mapTypeToQStandardPaths(type), filename, QStandardPaths::LocateFile); } Q_FOREACH (const QString &alias, aliases) { locations << QStandardPaths::locate(d->mapTypeToQStandardPaths(type), (alias.endsWith('/') ? alias : alias + '/') + filename, QStandardPaths::LocateFile); } debugWidgetUtils << "locate: type" << type << "filename" << filename << "locations" << locations; if (locations.size() > 0) { return locations.first(); } else { return ""; } } QString KoResourcePaths::locateLocalInternal(const QString &type, const QString &filename, bool createDir) { QString path = saveLocationInternal(type, "", createDir); debugWidgetUtils << "locateLocal: type" << type << "filename" << filename << "CreateDir" << createDir << "path" << path; return path + '/' + filename; } diff --git a/packaging/linux/build-deps.sh b/packaging/linux/build-deps.sh deleted file mode 100644 index 493fd59e56..0000000000 --- a/packaging/linux/build-deps.sh +++ /dev/null @@ -1,128 +0,0 @@ -#!/bin/bash - -# Enter a CentOS 6 chroot (you could use other methods) -# git clone https://github.com/probonopd/AppImageKit.git -# ./AppImageKit/build.sh -# sudo ./AppImageKit/AppImageAssistant.AppDir/testappimage /isodevice/boot/iso/CentOS-6.5-x86_64-LiveCD.iso bash - -# Halt on errors -set -e - -# Be verbose -set -x - -# Now we are inside CentOS 6 -grep -r "CentOS release 6" /etc/redhat-release || exit 1 - -# qjsonparser, used to add metadata to the plugins needs to work in a en_US.UTF-8 environment. That's -# not always set correctly in CentOS 6.7 -export LC_ALL=en_US.UTF-8 -export LANG=en_us.UTF-8 - -# Determine which architecture should be built -if [[ "$(arch)" = "i686" || "$(arch)" = "x86_64" ]] ; then - ARCH=$(arch) -else - echo "Architecture could not be determined" - exit 1 -fi - -# if the library path doesn't point to our usr/lib, linking will be broken and we won't find all deps either -export LD_LIBRARY_PATH=/usr/lib64/:/usr/lib:/krita.appdir/usr/lib - -git_pull_rebase_helper() -{ - git reset --hard HEAD - git pull -} - -yum -y install epel-release -# we need to be up to date in order to install the xcb-keysyms dependency -yum -y update -# base dependencies and Qt5. -yum -y install wget tar bzip2 git libtool which fuse fuse-devel libpng-devel automake libtool mesa-libEGL cppunit-devel cmake glibc-headers libstdc++-devel gcc-c++ freetype-devel fontconfig-devel libxml2-devel libstdc++-devel libXrender-devel patch xcb-util-keysyms-devel libXi-devel mesa-libGL-devel libxcb libxcb-devel xcb-util xcb-util-devel - -# Newer compiler than what comes with CentOS 6 -wget http://people.centos.org/tru/devtools-2/devtools-2.repo -O /etc/yum.repos.d/devtools-2.repo -yum -y install devtoolset-2-gcc devtoolset-2-gcc-c++ devtoolset-2-binutils - -# Make sure we build from the /, parts of this script depends on that. We also need to run as root... -cd / - -# Build AppImageKit -if [ ! -d AppImageKit ] ; then - git clone --depth 1 https://github.com/probonopd/AppImageKit.git /AppImageKit -fi - -cd /AppImageKit/ -git_pull_rebase_helper -./build.sh -cd / - - -# Workaround for: -# /usr/lib/librevenge-stream-0.0.so: undefined reference to `std::__detail::_List_node_base::_M_hook(std::__detail::_List_node_base*)' -# Use the new compiler -. /opt/rh/devtoolset-2/enable - - -# Workaround for: On CentOS 6, .pc files in /usr/lib/pkgconfig are not recognized -# However, this is where .pc files get installed when bulding libraries... (FIXME) -# I found this by comparing the output of librevenge's "make install" command -# between Ubuntu and CentOS 6 -ln -sf /usr/share/pkgconfig /usr/lib/pkgconfig - - -# A krita build layout looks like this: -# krita/ -- the source directory -# krita/3rdparty -- the cmake definitions for the dependencies -# d -- downloads of the dependencies from files.kde.org -# b -- build directory for the dependencies -# krita_build -- build directory for krita itself -# krita.appdir -- install directory for krita and the dependencies - -# Get Krita -if [ ! -d /krita ] ; then - git clone --depth 1 https://github.com/KDE/krita.git /krita -fi - -cd /krita/ -git_pull_rebase_helper - -# Create the build dir for the 3rdparty deps -if [ ! -d /b ] ; then - mkdir /b -fi -if [ ! -d /d ] ; then - mkdir /d -fi - -# start building the deps -cd /b - -rm -rf /b/* || true - -cmake /krita/3rdparty \ - -DCMAKE_INSTALL_PREFIX:PATH=/usr \ - -DINSTALL_ROOT=/usr \ - -DEXTERNALS_DOWNLOAD_DIR=/d - -cmake --build . --config RelWithDebInfo --target ext_qt -cmake --build . --config RelWithDebInfo --target ext_boost -cmake --build . --config RelWithDebInfo --target ext_eigen3 -cmake --build . --config RelWithDebInfo --target ext_exiv2 -cmake --build . --config RelWithDebInfo --target ext_fftw3 -cmake --build . --config RelWithDebInfo --target ext_lcms2 -cmake --build . --config RelWithDebInfo --target ext_ocio -cmake --build . --config RelWithDebInfo --target ext_openexr -cmake --build . --config RelWithDebInfo --target ext_vc -#cmake --build . --config RelWithDebInfo --target ext_png -cmake --build . --config RelWithDebInfo --target ext_tiff -cmake --build . --config RelWithDebInfo --target ext_jpeg -cmake --build . --config RelWithDebInfo --target ext_libraw -# XXX: this builds, but cmake never manages to find the library -#cmake --build . --config RelWithDebInfo --target ext_openjpeg -cmake --build . --config RelWithDebInfo --target ext_kcrash -cmake --build . --config RelWithDebInfo --target ext_poppler -cmake --build . --config RelWithDebInfo --target ext_gsl - diff --git a/packaging/linux/build-image.sh b/packaging/linux/build-image.sh deleted file mode 100644 index 39622a9b17..0000000000 --- a/packaging/linux/build-image.sh +++ /dev/null @@ -1,245 +0,0 @@ -#!/bin/bash - -# Enter a CentOS 6 chroot (you could use other methods) -# git clone https://github.com/probonopd/AppImageKit.git -# ./AppImageKit/build.sh -# sudo ./AppImageKit/AppImageAssistant.AppDir/testappimage /isodevice/boot/iso/CentOS-6.5-x86_64-LiveCD.iso bash - -# Halt on errors -set -e - -# Be verbose -set -x - -# Now we are inside CentOS 6 -grep -r "CentOS release 6" /etc/redhat-release || exit 1 - -# If we are running inside Travis CI, then we want to build Krita -# and we remove the $DO_NOT_BUILD_KRITA environment variable -# that was used in the process of generating the Docker image. -# Also we do not want to download and build the dependencies every -# time we build Krita on travis in the docker image. In order to -# use newer dependencies, we re-build the docker image instead. -#unset DO_NOT_BUILD_KRITA -#NO_DOWNLOAD=1 - -# clean up -rm -f krita-3.0-l10n-win-current.tar.gz -rm -rf /out/* -rm -rf /krita.appdir - -# qjsonparser, used to add metadata to the plugins needs to work in a en_US.UTF-8 environment. That's -# not always set correctly in CentOS 6.7 -export LC_ALL=en_US.UTF-8 -export LANG=en_us.UTF-8 - -# Determine which architecture should be built -if [[ "$(arch)" = "i686" || "$(arch)" = "x86_64" ]] ; then - ARCH=$(arch) -else - echo "Architecture could not be determined" - exit 1 -fi - -# if the library path doesn't point to our usr/lib, linking will be broken and we won't find all deps either -export LD_LIBRARY_PATH=/usr/lib64/:/usr/lib:/krita.appdir/usr/lib - -cd / - -# Prepare the install location -rm -rf /krita.appdir/ || true -mkdir -p /krita.appdir/usr/bin - -# make sure lib and lib64 are the same thing -mkdir -p /krita.appdir/usr/lib -cd /krita.appdir/usr -ln -s lib lib64 - -cd /krita_build -make -j4 install - -cd /krita.appdir - -# FIXME: How to find out which subset of plugins is really needed? I used strace when running the binary -cp -r /usr/plugins ./usr/bin/ -# copy the Qt translation -cp -r /usr/translations ./usr - -cp $(ldconfig -p | grep libsasl2.so.2 | cut -d ">" -f 2 | xargs) ./usr/lib/ -cp $(ldconfig -p | grep libGL.so.1 | cut -d ">" -f 2 | xargs) ./usr/lib/ # otherwise segfaults!? -cp $(ldconfig -p | grep libGLU.so.1 | cut -d ">" -f 2 | xargs) ./usr/lib/ # otherwise segfaults!? -# Fedora 23 seemed to be missing SOMETHING from the Centos 6.7. The only message was: -# This application failed to start because it could not find or load the Qt platform plugin "xcb". -# Setting export QT_DEBUG_PLUGINS=1 revealed the cause. -# QLibraryPrivate::loadPlugin failed on "/usr/lib64/qt5/plugins/platforms/libqxcb.so" : -# "Cannot load library /usr/lib64/qt5/plugins/platforms/libqxcb.so: (/lib64/libEGL.so.1: undefined symbol: drmGetNodeTypeFromFd)" -# Which means that we have to copy libEGL.so.1 in too -cp $(ldconfig -p | grep libEGL.so.1 | cut -d ">" -f 2 | xargs) ./usr/lib/ # Otherwise F23 cannot load the Qt platform plugin "xcb" -# let's not copy xcb itself, that breaks on dri3 systems https://bugs.kde.org/show_bug.cgi?id=360552 -#cp $(ldconfig -p | grep libxcb.so.1 | cut -d ">" -f 2 | xargs) ./usr/lib/ -cp $(ldconfig -p | grep libfreetype.so.6 | cut -d ">" -f 2 | xargs) ./usr/lib/ # For Fedora 20 - -ldd usr/bin/krita | grep "=>" | awk '{print $3}' | xargs -I '{}' cp -v '{}' ./usr/lib || true -#ldd usr/lib64/krita/*.so | grep "=>" | awk '{print $3}' | xargs -I '{}' cp -v '{}' ./usr/lib || true -#ldd usr/lib64/plugins/imageformats/*.so | grep "=>" | awk '{print $3}' | xargs -I '{}' cp -v '{}' ./usr/lib || true - -ldd usr/bin/plugins/platforms/libqxcb.so | grep "=>" | awk '{print $3}' | xargs -I '{}' cp -v '{}' ./usr/lib || true - -# Copy in the indirect dependencies -FILES=$(find . -type f -executable) - -for FILE in $FILES ; do - ldd "${FILE}" | grep "=>" | awk '{print $3}' | xargs -I '{}' cp -v '{}' usr/lib || true -done - -#DEPS="" -#for FILE in $FILES ; do -# ldd "${FILE}" | grep "=>" | awk '{print $3}' | xargs -I '{}' echo '{}' > DEPSFILE -#done -#DEPS=$(cat DEPSFILE |sort | uniq) -#for FILE in $DEPS ; do -# if [ -f $FILE ] ; then -# echo $FILE -# cp --parents -rfL $FILE ./ -# fi -#done -#rm -f DEPSFILE - - -# The following are assumed to be part of the base system -rm -f usr/lib/libcom_err.so.2 || true -rm -f usr/lib/libcrypt.so.1 || true -rm -f usr/lib/libdl.so.2 || true -rm -f usr/lib/libexpat.so.1 || true -#rm -f usr/lib/libfontconfig.so.1 || true -rm -f usr/lib/libgcc_s.so.1 || true -rm -f usr/lib/libglib-2.0.so.0 || true -rm -f usr/lib/libgpg-error.so.0 || true -rm -f usr/lib/libgssapi_krb5.so.2 || true -rm -f usr/lib/libgssapi.so.3 || true -rm -f usr/lib/libhcrypto.so.4 || true -rm -f usr/lib/libheimbase.so.1 || true -rm -f usr/lib/libheimntlm.so.0 || true -rm -f usr/lib/libhx509.so.5 || true -rm -f usr/lib/libICE.so.6 || true -rm -f usr/lib/libidn.so.11 || true -rm -f usr/lib/libk5crypto.so.3 || true -rm -f usr/lib/libkeyutils.so.1 || true -rm -f usr/lib/libkrb5.so.26 || true -rm -f usr/lib/libkrb5.so.3 || true -rm -f usr/lib/libkrb5support.so.0 || true -# rm -f usr/lib/liblber-2.4.so.2 || true # needed for debian wheezy -# rm -f usr/lib/libldap_r-2.4.so.2 || true # needed for debian wheezy -rm -f usr/lib/libm.so.6 || true -rm -f usr/lib/libp11-kit.so.0 || true -rm -f usr/lib/libpcre.so.3 || true -rm -f usr/lib/libpthread.so.0 || true -rm -f usr/lib/libresolv.so.2 || true -rm -f usr/lib/libroken.so.18 || true -rm -f usr/lib/librt.so.1 || true -rm -f usr/lib/libsasl2.so.2 || true -rm -f usr/lib/libSM.so.6 || true -rm -f usr/lib/libusb-1.0.so.0 || true -rm -f usr/lib/libuuid.so.1 || true -rm -f usr/lib/libwind.so.0 || true -rm -f usr/lib/libfontconfig.so.* || true - -# Remove these libraries, we need to use the system versions; this means 11.04 is not supported (12.04 is our baseline) -rm -f usr/lib/libGL.so.* || true -rm -f usr/lib/libdrm.so.* || true -rm -f usr/lib/libX11.so.* || true -#rm -f usr/lib/libz.so.1 || true - -# These seem to be available on most systems but not Ubuntu 11.04 -# rm -f usr/lib/libffi.so.6 usr/lib/libGL.so.1 usr/lib/libglapi.so.0 usr/lib/libxcb.so.1 usr/lib/libxcb-glx.so.0 || true - -# Delete potentially dangerous libraries -rm -f usr/lib/libstdc* usr/lib/libgobject* usr/lib/libc.so.* || true -rm -f usr/lib/libxcb.so.1 - -# Do NOT delete libX* because otherwise on Ubuntu 11.04: -# loaded library "Xcursor" malloc.c:3096: sYSMALLOc: Assertion (...) Aborted - -# We don't bundle the developer stuff -rm -rf usr/include || true -rm -rf usr/lib/cmake || true -rm -rf usr/lib/pkgconfig || true -rm -rf usr/share/ECM/ || true -rm -rf usr/share/gettext || true -rm -rf usr/share/pkgconfig || true - -strip usr/lib/kritaplugins/* usr/bin/* usr/lib/* || true - -# Since we set /krita.appdir as the prefix, we need to patch it away too (FIXME) -# Probably it would be better to use /app as a prefix because it has the same length for all apps -cd usr/ ; find . -type f -exec sed -i -e 's|/krita.appdir/usr/|./././././././././|g' {} \; ; cd .. - -# On openSUSE Qt is picking up the wrong libqxcb.so -# (the one from the system when in fact it should use the bundled one) - is this a Qt bug? -# Also, Krita has a hardcoded /usr which we patch away -cd usr/ ; find . -type f -exec sed -i -e 's|/usr|././|g' {} \; ; cd .. - -# We do not bundle this, so let's not search that inside the AppImage. -# Fixes "Qt: Failed to create XKB context!" and lets us enter text -sed -i -e 's|././/share/X11/|/usr/share/X11/|g' ./usr/bin/plugins/platforminputcontexts/libcomposeplatforminputcontextplugin.so -sed -i -e 's|././/share/X11/|/usr/share/X11/|g' ./usr/lib/libQt5XcbQpa.so.5 - -# Workaround for: -# D-Bus library appears to be incorrectly set up; -# failed to read machine uuid: Failed to open -# The file is more commonly in /etc/machine-id -# sed -i -e 's|/var/lib/dbus/machine-id|//././././etc/machine-id|g' ./usr/lib/libdbus-1.so.3 -# or -rm -f ./usr/lib/libdbus-1.so.3 || true - -cp ../AppImageKit/AppRun . -cp ./usr/share/applications/krita.desktop krita.desktop -cp /krita/krita/pics/app/64-apps-calligrakrita.png calligrakrita.png - -# -# Fetch and install the translations -# -cd / -rm -f krita-3.0-l10-win-current.tar.gz || true -wget http://files.kde.org/krita/build/krita-3.0-l10n-win-current.tar.gz -tar -xf krita-3.0-l10n-win-current.tar.gz -cd /krita.appdir/usr/share -tar -xf /krita-3.0-l10n-win-current.tar.gz - -# replace krita with the lib-checking startup script. -#cd /krita.appdir/usr/bin -#mv krita krita.real -#wget https://raw.githubusercontent.com/boudewijnrempt/AppImages/master/recipes/krita/krita -#chmod a+rx krita -cd / - -APP=krita - -VER=$(grep "#define KRITA_VERSION_STRING" krita_build/libs/version/kritaversion.h | cut -d '"' -f 2) -cd krita -BRANCH=$( git branch | cut -d ' ' -f 2) -REVISION=$(git rev-parse --short HEAD) -cd .. -VERSION=$VER-$BRANCH-$REVISION -VERSION="$(sed s/\ /-/g <<<$VERSION)" -echo $VERSION - -if [[ "$ARCH" = "x86_64" ]] ; then - APPIMAGE=$APP"-"$VERSION"-x86_64.appimage" -fi -if [[ "$ARCH" = "i686" ]] ; then - APPIMAGE=$APP"-"$VERSION"-i386.appimage" -fi -echo $APPIMAGE - -mkdir -p /out - -rm -f /out/*.AppImage || true -AppImageKit/AppImageAssistant.AppDir/package /krita.appdir/ /out/$APPIMAGE - -chmod a+rwx /out/$APPIMAGE # So that we can edit the AppImage outside of the Docker container - -cd /krita.appdir -mv AppRun krita -cd / -mv krita.appdir $APP"-"$VERSION"-x86_64 diff --git a/packaging/linux/build-krita.sh b/packaging/linux/build-krita.sh deleted file mode 100644 index 42f210d84b..0000000000 --- a/packaging/linux/build-krita.sh +++ /dev/null @@ -1,88 +0,0 @@ -#!/bin/bash - -# Enter a CentOS 6 chroot (you could use other methods) -# git clone https://github.com/probonopd/AppImageKit.git -# ./AppImageKit/build.sh -# sudo ./AppImageKit/AppImageAssistant.AppDir/testappimage /isodevice/boot/iso/CentOS-6.5-x86_64-LiveCD.iso bash - -# Halt on errors -set -e - -# Be verbose -set -x - -# Now we are inside CentOS 6 -grep -r "CentOS release 6" /etc/redhat-release || exit 1 - -# qjsonparser, used to add metadata to the plugins needs to work in a en_US.UTF-8 environment. That's -# not always set correctly in CentOS 6.7 -export LC_ALL=en_US.UTF-8 -export LANG=en_us.UTF-8 - -# Determine which architecture should be built -if [[ "$(arch)" = "i686" || "$(arch)" = "x86_64" ]] ; then - ARCH=$(arch) -else - echo "Architecture could not be determined" - exit 1 -fi - -# if the library path doesn't point to our usr/lib, linking will be broken and we won't find all deps either -export LD_LIBRARY_PATH=/usr/lib64/:/usr/lib:/krita.appdir/usr/lib - -git_pull_rebase_helper() -{ - git reset --hard HEAD - git pull -} - -# Use the new compiler -. /opt/rh/devtoolset-2/enable - - -# Workaround for: On CentOS 6, .pc files in /usr/lib/pkgconfig are not recognized -# However, this is where .pc files get installed when bulding libraries... (FIXME) -# I found this by comparing the output of librevenge's "make install" command -# between Ubuntu and CentOS 6 -ln -sf /usr/share/pkgconfig /usr/lib/pkgconfig - - -# A krita build layout looks like this: -# krita/ -- the source directory -# krita/3rdparty -- the cmake definitions for the dependencies -# d -- downloads of the dependencies from files.kde.org -# b -- build directory for the dependencies -# krita_build -- build directory for krita itself -# krita.appdir -- install directory for krita and the dependencies - -# Get Krita -if [ ! -d /krita ] ; then - git clone --depth 1 https://github.com/KDE/krita.git /krita -fi - -cd /krita/ -git_pull_rebase_helper - -cd / - -# If the environment variable DO_NOT_BUILD_KRITA is set to something, -# then stop here. This is for docker hub which has a timeout that -# prevents us from building in one go. -# if [ ! -z "$DO_NOT_BUILD_KRITA" ] ; then -# exit 0 -# fi - -mkdir -p /krita_build -cd /krita_build -cmake ../krita \ - -DCMAKE_INSTALL_PREFIX:PATH=/krita.appdir/usr \ - -DDEFINE_NO_DEPRECATED=1 \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DPACKAGERS_BUILD=1 \ - -DBUILD_TESTING=FALSE \ - -DKDE4_BUILD_TESTS=FALSE \ - -DHAVE_MEMORY_LEAK_TRACKER=FALSE - -# build -make -j4 - diff --git a/packaging/linux/snap/qt5-launch b/packaging/linux/snap/qt5-launch new file mode 100755 index 0000000000..389e499f7f --- /dev/null +++ b/packaging/linux/snap/qt5-launch @@ -0,0 +1,77 @@ +#!/bin/bash + +if [ "$SNAP_ARCH" == "amd64" ]; then + ARCH='x86_64-linux-gnu' +elif [ "$SNAP_ARCH" == "armhf" ]; then + ARCH="arm-linux-gnueabihf" +else + ARCH="$SNAP_ARCH-linux-gnu" +fi + +export LD_LIBRARY_PATH=$SNAP/usr/lib/$ARCH:$LD_LIBRARY_PATH + +# XKB config +export XKB_CONFIG_ROOT=$SNAP/usr/share/X11/xkb + +# Mesa Libs +export LD_LIBRARY_PATH=$SNAP/usr/lib/$ARCH/mesa:$LD_LIBRARY_PATH +export LD_LIBRARY_PATH=$SNAP/usr/lib/$ARCH/mesa-egl:$LD_LIBRARY_PATH + +# XDG Config +export XDG_CONFIG_DIRS=$SNAP/etc:$XDG_CONFIG_DIRS + +# Note: this doesn't seem to work, QML's LocalStorage either ignores +# or fails to use $SNAP_USER_DATA if defined here +export XDG_DATA_DIRS=$SNAP/usr/share:$XDG_DATA_DIRS + +# Font Config +export FONTCONFIG_PATH=$SNAP/etc/fonts/config.d +export FONTCONFIG_FILE=$SNAP/etc/fonts/fonts.conf + +# Tell libGL where to find the drivers +export LIBGL_DRIVERS_PATH=$SNAP/usr/lib/$ARCH/dri + +# Necessary for the SDK to find the translations directory +export APP_DIR=$SNAP + +# Set XDG_DATA_HOME to local path, dependent on snap version +export XDG_DATA_HOME=$SNAP_USER_DATA/.local-$SNAP_VERSION/share +export XDG_DATA_DIRS=$XDG_DATA_HOME:$XDG_DATA_DIRS +mkdir -p $XDG_DATA_HOME + +# Set cache folder to local path, dependent on snap version +export XDG_CACHE_HOME=$SNAP_USER_DATA/.cache-$SNAP_VERSION +mkdir -p $XDG_CACHE_HOME + +# Not good, needed for fontconfig and themes +ln -sf $SNAP/usr/share/{fontconfig,fonts,fonts-*,themes} $XDG_DATA_HOME + +# Qt Platform to Mir +export QTCHOOSER_NO_GLOBAL_DIR=1 +export QT_SELECT=snappy-qt5 + +# Qt Libs +export LD_LIBRARY_PATH=$SNAP/usr/lib/$ARCH/qt5/libs:$LD_LIBRARY_PATH +export LD_LIBRARY_PATH=$SNAP/usr/lib/$ARCH/pulseaudio:$LD_LIBRARY_PATH + +# Qt Modules +export QT_PLUGIN_PATH=$SNAP/usr/lib/$ARCH/qt5/plugins +export QML2_IMPORT_PATH=$QML2_IMPORT_PATH:$SNAP/usr/lib/$ARCH/qt5/qml +export QML2_IMPORT_PATH=$QML2_IMPORT_PATH:$SNAP/lib/$ARCH + +# Removes Qt warning: Could not find a location +# of the system Compose files +export QTCOMPOSE=$SNAP/usr/share/X11/locale + +export DESKTOP_SESSION=ubuntu +export XDG_SESSION_DESKTOP=ubuntu +export XDG_CURRENT_DESKTOP=kde +export QT_QPA_PLATFORMTHEME=appmenu-qt5 +export QT_XKB_CONFIG_ROOT=/usr/share/X11/xkb + +# ensure that our HW/opengl libs are before the snap specific libs +export LD_LIBRARY_PATH=$SNAP_LIBRARY_PATH:$LD_LIBRARY_PATH + +cd $SNAP +exec "$@" + diff --git a/packaging/linux/snap/setup/gui/calligrakrita.png b/packaging/linux/snap/setup/gui/calligrakrita.png new file mode 100644 index 0000000000..c91c0a0e23 Binary files /dev/null and b/packaging/linux/snap/setup/gui/calligrakrita.png differ diff --git a/packaging/linux/snap/setup/gui/krita.desktop b/packaging/linux/snap/setup/gui/krita.desktop new file mode 100755 index 0000000000..e40e8d6ec7 --- /dev/null +++ b/packaging/linux/snap/setup/gui/krita.desktop @@ -0,0 +1,115 @@ +[Desktop Entry] +Name=Krita +Name[af]=Krita +Name[bg]=Krita +Name[br]=Krita +Name[bs]=Krita +Name[ca]=Krita +Name[ca@valencia]=Krita +Name[cs]=Krita +Name[cy]=Krita +Name[da]=Krita +Name[de]=Krita +Name[el]=Krita +Name[en_GB]=Krita +Name[eo]=Krita +Name[es]=Krita +Name[et]=Krita +Name[eu]=Krita +Name[fi]=Krita +Name[fr]=Krita +Name[fy]=Krita +Name[ga]=Krita +Name[gl]=Krita +Name[he]=Krita +Name[hi]=केरिता +Name[hne]=केरिता +Name[hr]=Krita +Name[hu]=Krita +Name[ia]=Krita +Name[is]=Krita +Name[it]=Krita +Name[kk]=Krita +Name[ko]=Krita +Name[lt]=Krita +Name[lv]=Krita +Name[mr]=क्रिटा +Name[ms]=Krita +Name[nb]=Krita +Name[nds]=Krita +Name[ne]=क्रिता +Name[nl]=Krita +Name[pl]=Krita +Name[pt]=Krita +Name[pt_BR]=Krita +Name[ro]=Krita +Name[ru]=Krita +Name[se]=Krita +Name[sk]=Krita +Name[sl]=Krita +Name[sv]=Krita +Name[ta]=கிரிட்டா +Name[tg]=Krita +Name[tr]=Krita +Name[ug]=Krita +Name[uk]=Krita +Name[uz]=Krita +Name[uz@cyrillic]=Krita +Name[wa]=Krita +Name[xh]=Krita +Name[x-test]=xxKritaxx +Name[zh_CN]=Krita +Name[zh_TW]=繪圖_Krita +Exec=krita %U +GenericName=Digital Painting +GenericName[bs]=Digitalno Bojenje +GenericName[ca]=Dibuix digital +GenericName[ca@valencia]=Dibuix digital +GenericName[da]=Digital tegning +GenericName[de]=Digitales Malen +GenericName[el]=Ψηφιακή ζωγραφική +GenericName[en_GB]=Digital Painting +GenericName[es]=Pintura digital +GenericName[et]=Digitaalne joonistamine +GenericName[eu]=Pintura digitala +GenericName[fi]=Digitaalimaalaus +GenericName[fr]=Peinture numérique +GenericName[gl]=Debuxo dixital +GenericName[hu]=Digitális festészet +GenericName[ia]=Pintura Digital +GenericName[it]=Pittura digitale +GenericName[kk]=Цифрлық сурет салу +GenericName[lt]=Skaitmeninis piešimas +GenericName[mr]=डिजिटल पेंटिंग +GenericName[nb]=Digital maling +GenericName[nl]=Digitaal schilderen +GenericName[pl]=Cyfrowe malowanie +GenericName[pt]=Pintura Digital +GenericName[pt_BR]=Pintura digital +GenericName[ru]=Цифровая живопись +GenericName[sk]=Digitálne maľovanie +GenericName[sl]=Digitalno slikanje +GenericName[sv]=Digital målning +GenericName[tr]=Sayısal Boyama +GenericName[ug]=سىفىرلىق رەسىم سىزغۇ +GenericName[uk]=Цифрове малювання +GenericName[x-test]=xxDigital Paintingxx +MimeType=application/x-krita;image/openraster;application/x-krita-paintoppreset; +Comment=Pixel-based image manipulation program for the Calligra Suite +Comment[ca]=Programa de manipulació d'imatges basades en píxels per a la Suite Calligra +Comment[ca@valencia]=Programa de manipulació d'imatges basades en píxels per a la Suite Calligra +Comment[de]=Pixelbasiertes Bildbearbeitungsprogramm für die Calligra-Suite +Comment[es]=Programa de manipulación de imágenes basado en píxeles para la suite Calligra +Comment[nl]=Afbeeldingsbewerkingsprogramma gebaseerd op pixels voor de Calligra Suite +Comment[pl]=Program do obróbki obrazów na poziomie pikseli dla Pakietu Calligra +Comment[pt]='Plugin' de manipulação de imagens em pixels para o Calligra Stage +Comment[sv]=Bildpunktsbaserat bildbehandlingsprogram för Calligra-sviten +Comment[uk]=Програма для роботи із растровими зображеннями для комплексу програм Calligra +Comment[x-test]=xxPixel-based image manipulation program for the Calligra Suitexx +Type=Application +Icon=${SNAP}/meta/gui/calligrakrita.png +Categories=Qt;KDE;Graphics; +X-KDE-NativeMimeType=application/x-krita +X-KDE-ExtraNativeMimeTypes= +StartupNotify=true +X-Krita-Version=28 diff --git a/packaging/linux/snap/snapcraft.yaml b/packaging/linux/snap/snapcraft.yaml new file mode 100644 index 0000000000..222b0ac2db --- /dev/null +++ b/packaging/linux/snap/snapcraft.yaml @@ -0,0 +1,143 @@ +name: krita +version: 3.0-snap11 +summary: Pixel-based image manipulation program for the Calligra Suite +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. + + +apps: + krita: + command: qt5-launch usr/bin/krita + plugs: [x11, unity7, home, opengl, network, network-bind] + +parts: + qt: + plugin: nil + stage-packages: + - libqt5concurrent5 + - libqt5core5a + - libqt5dbus5 + - libqt5gui5 + - libqt5network5 + - libqt5printsupport5 + - libqt5svg5 + - libqt5widgets5 + - libqt5x11extras5 + - libqt5xml5 + + kdeframeworks: + plugin: nil + stage-packages: + - libkf5archive5 + - libkf5completion5 + - libkf5configcore5 + - libkf5configgui5 + - libkf5coreaddons5 + - libkf5guiaddons5 + - libkf5i18n5 + - libkf5itemviews5 + - libkf5widgetsaddons5 + - libkf5windowsystem5 + after: [qt] + + krita: + plugin: cmake +# Using -DKDE_NO_DEBUG_OUTPUT was causing compilation failure for some reason +# configflags: [-DCMAKE_INSTALL_PREFIX=/usr, -DQT_NO_DEBUG=1, -DCMAKE_CXX_FLAGS="-DKDE_NO_DEBUG_OUTPUT"] + configflags: [-DCMAKE_INSTALL_PREFIX=/usr, -DQT_NO_DEBUG=1] + source: ../../../ +# Use these instead to build from the git source +# source: git://anongit.kde.org/krita.git +# source-type: git +# source-branch: krita/3.0 + build-packages: + - 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 + - libopenjpeg-dev + - libpng12-dev + - libpoppler-qt4-dev + - libtiff5-dev + - libvc-dev + - libopencolorio-dev + - libx11-dev + - libxml2-dev + - libxslt1-dev + - libxi-dev + - pkg-config + - pkg-kde-tools + - vc-dev + - zlib1g-dev + - libkdcraw-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 + stage-packages: + - libboost-system1.58.0 + - libexiv2-14 + - libfftw3-double3 + - libgomp1 + - libgsl2 + - libilmbase12 + - libjpeg8 + - liblcms2-2 + - libopencolorio1v5 + - libopenexr22 + - libopenjpeg5 + - libpng12-0 + - libstdc++6 + - libtiff5 + - libx11-6 + - libxcb1 + - libxi6 + - zlib1g + - libraw15 + - libkf5crash5 + - libpoppler-qt5-1 + - curl + after: [qt, kdeframeworks] + + integration: + plugin: nil + stage-packages: + - ttf-ubuntu-font-family + snap: + - usr/share + - -usr/share/doc + + launcher: + plugin: copy + files: + qt5-launch: bin/qt5-launch + diff --git a/packaging/windows/makepkg-32.sh b/packaging/windows/makepkg-32.sh index 81c34d081d..ac4c1deb35 100644 --- a/packaging/windows/makepkg-32.sh +++ b/packaging/windows/makepkg-32.sh @@ -1,90 +1,90 @@ #!/bin/bash # Halt on errors set -e # Be verbose set -x BUILDROOT=/data2/cross MXEROOT=/data2/cross/mxe/usr/i686-w64-mingw32.shared APP=krita cd $BUILDROOT VER=$(grep "#define KRITA_VERSION_STRING" $BUILDROOT/build/libs/version/kritaversion.h | cut -d '"' -f 2) cd $BUILDROOT/krita BRANCH=$( git branch | cut -d ' ' -f 2) +BRANCH="$(sed s/\ /-/g <<<$VERSION)" REVISION=$(git rev-parse --short HEAD) cd .. VERSION=$VER$BRANCH-$REVISION VERSION="$(sed s/\ /-/g <<<$VERSION)" echo $VERSION PACKAGENAME=$APP"-"$VERSION"-x86" mkdir -p $BUILDROOT/out/$PACKAGENAME mkdir -p $BUILDROOT/out/$PACKAGENAME/bin/data mkdir -p $BUILDROOT/out/$PACKAGENAME/lib mkdir -p $BUILDROOT/out/$PACKAGENAME/share -cp $BUILDROOT/krita/windows/krita.lnk $BUILDROOT/out/$PACKAGENAME -cp $BUILDROOT/krita/windows/qt.conf $BUILDROOT/out/$PACKAGENAME/bin +cp $BUILDROOT/krita/packaging/windows/krita.lnk $BUILDROOT/out/$PACKAGENAME +cp $BUILDROOT/krita/packaging/windows/qt.conf $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/bin/krita.exe $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/bin/*.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/bin/*.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/lib/libOpenColorIO.dll $BUILDROOT/out/$PACKAGENAME/bin cp -r $MXEROOT/lib/kritaplugins $BUILDROOT/out/$PACKAGENAME/lib cp $MXEROOT/qt5/bin/Qt5Concurrent.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5Core.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5Gui.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5Network.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5OpenGL.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5PrintSupport.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5Qml.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5Quick.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5Script.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5ScriptTools.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5Svg.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5SystemInfo.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5Widgets.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5WinExtras.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5Xml.dll $BUILDROOT/out/$PACKAGENAME/bin cp -r $MXEROOT/qt5/plugins/iconengines $BUILDROOT/out/$PACKAGENAME/bin/ cp -r $MXEROOT/qt5/plugins/imageformats $BUILDROOT/out/$PACKAGENAME/bin/ cp -r $MXEROOT/qt5/plugins/printsupport $BUILDROOT/out/$PACKAGENAME/bin/ cp -r $MXEROOT/qt5/plugins/platforms $BUILDROOT/out/$PACKAGENAME/bin/ cp -r $MXEROOT/lib/plugins/imageformats/* $BUILDROOT/out/$PACKAGENAME/bin/imageformats/ mkdir $BUILDROOT/out/$PACKAGENAME/bin/translations -cp -r $MXEROOT/qt5/translations/qt_* $BUILDROOT/out/$PACKAGENAME/bin/translations -cp -r $MXEROOT/qt5/translations/qtbase* $BUILDROOT/out/$PACKAGENAME/bin/translations +cp -r $BUILDROOT/qt-translations/qt_* $BUILDROOT/out/$PACKAGENAME/bin/translations cp -r $MXEROOT/share/color $BUILDROOT/out/$PACKAGENAME/share cp -r $MXEROOT/share/color-schemes $BUILDROOT/out/$PACKAGENAME/share cp -r $MXEROOT/share/kf5 $BUILDROOT/out/$PACKAGENAME/share cp -r $MXEROOT/share/krita $BUILDROOT/out/$PACKAGENAME/share cp -r $MXEROOT/share/locale $BUILDROOT/out/$PACKAGENAME/bin/data cp -r $MXEROOT/share/ocio $BUILDROOT/out/$PACKAGENAME/share cd $BUILDROOT rm krita-3.0-l10n-win-current.tar.gz || true rm -rf locale wget http://files.kde.org/krita/build/krita-3.0-l10n-win-current.tar.gz tar -xf krita-3.0-l10n-win-current.tar.gz cp -r $BUILDROOT/locale $BUILDROOT/out/$PACKAGENAME/bin/data cd $BUILDROOT/out/ zip -r $PACKAGENAME-dbg.zip $PACKAGENAME find $BUILDROOT/out/$PACKAGENAME/bin -name \*exe | xargs $BUILDROOT/mxe/usr/bin/i686-w64-mingw32.shared-strip find $BUILDROOT/out/$PACKAGENAME/bin -name \*dll | xargs $BUILDROOT/mxe/usr/bin/i686-w64-mingw32.shared-strip find $BUILDROOT/out/$PACKAGENAME/lib -name \*dll | xargs $BUILDROOT/mxe/usr/bin/i686-w64-mingw32.shared-strip zip -r $PACKAGENAME.zip $PACKAGENAME diff --git a/packaging/windows/makepkg-64.sh b/packaging/windows/makepkg-64.sh index ef08020be9..aa2a3f09f1 100644 --- a/packaging/windows/makepkg-64.sh +++ b/packaging/windows/makepkg-64.sh @@ -1,90 +1,90 @@ #!/bin/bash # Halt on errors set -e # Be verbose set -x BUILDROOT=/data2/cross MXEROOT=/data2/cross/mxe/usr/x86_64-w64-mingw32.shared APP=krita cd $BUILDROOT VER=$(grep "#define KRITA_VERSION_STRING" $BUILDROOT/build/libs/version/kritaversion.h | cut -d '"' -f 2) cd $BUILDROOT/krita BRANCH=$( git branch | cut -d ' ' -f 2) +BRANCH="$(sed s/\ /-/g <<<$VERSION)" REVISION=$(git rev-parse --short HEAD) cd .. VERSION=$VER$BRANCH-$REVISION VERSION="$(sed s/\ /-/g <<<$VERSION)" echo $VERSION PACKAGENAME=$APP"-"$VERSION"-x64" mkdir -p $BUILDROOT/out/$PACKAGENAME mkdir -p $BUILDROOT/out/$PACKAGENAME/bin/data mkdir -p $BUILDROOT/out/$PACKAGENAME/lib mkdir -p $BUILDROOT/out/$PACKAGENAME/share -cp $BUILDROOT/krita/windows/krita.lnk $BUILDROOT/out/$PACKAGENAME -cp $BUILDROOT/krita/windows/qt.conf $BUILDROOT/out/$PACKAGENAME/bin +cp $BUILDROOT/krita/packaging/windows/krita.lnk $BUILDROOT/out/$PACKAGENAME +cp $BUILDROOT/krita/packaging/windows/qt.conf $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/bin/krita.exe $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/bin/*.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/bin/*.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/lib/libOpenColorIO.dll $BUILDROOT/out/$PACKAGENAME/bin cp -r $MXEROOT/lib/kritaplugins $BUILDROOT/out/$PACKAGENAME/lib cp $MXEROOT/qt5/bin/Qt5Concurrent.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5Core.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5Gui.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5Network.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5OpenGL.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5PrintSupport.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5Qml.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5Quick.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5Script.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5ScriptTools.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5Svg.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5SystemInfo.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5Widgets.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5WinExtras.dll $BUILDROOT/out/$PACKAGENAME/bin cp $MXEROOT/qt5/bin/Qt5Xml.dll $BUILDROOT/out/$PACKAGENAME/bin cp -r $MXEROOT/qt5/plugins/iconengines $BUILDROOT/out/$PACKAGENAME/bin/ cp -r $MXEROOT/qt5/plugins/imageformats $BUILDROOT/out/$PACKAGENAME/bin/ cp -r $MXEROOT/qt5/plugins/printsupport $BUILDROOT/out/$PACKAGENAME/bin/ cp -r $MXEROOT/qt5/plugins/platforms $BUILDROOT/out/$PACKAGENAME/bin/ cp -r $MXEROOT/lib/plugins/imageformats/* $BUILDROOT/out/$PACKAGENAME/bin/imageformats/ mkdir $BUILDROOT/out/$PACKAGENAME/bin/translations -cp -r $MXEROOT/qt5/translations/qt_* $BUILDROOT/out/$PACKAGENAME/bin/translations -cp -r $MXEROOT/qt5/translations/qtbase* $BUILDROOT/out/$PACKAGENAME/bin/translations +cp -r $BUILDROOT/qt-translations/qt_* $BUILDROOT/out/$PACKAGENAME/bin/translations cp -r $MXEROOT/share/color $BUILDROOT/out/$PACKAGENAME/share cp -r $MXEROOT/share/color-schemes $BUILDROOT/out/$PACKAGENAME/share cp -r $MXEROOT/share/kf5 $BUILDROOT/out/$PACKAGENAME/share cp -r $MXEROOT/share/krita $BUILDROOT/out/$PACKAGENAME/share cp -r $MXEROOT/share/locale $BUILDROOT/out/$PACKAGENAME/bin/data cp -r $MXEROOT/share/ocio $BUILDROOT/out/$PACKAGENAME/share cd $BUILDROOT rm krita-3.0-l10n-win-current.tar.gz || true rm -rf locale wget http://files.kde.org/krita/build/krita-3.0-l10n-win-current.tar.gz tar -xf krita-3.0-l10n-win-current.tar.gz cp -r $BUILDROOT/locale $BUILDROOT/out/$PACKAGENAME/bin/data cd $BUILDROOT/out/ zip -r $PACKAGENAME-dbg.zip $PACKAGENAME find $BUILDROOT/out/$PACKAGENAME/bin -name \*exe | xargs $BUILDROOT/mxe/usr/bin/x86_64-w64-mingw32.shared-strip find $BUILDROOT/out/$PACKAGENAME/bin -name \*dll | xargs $BUILDROOT/mxe/usr/bin/x86_64-w64-mingw32.shared-strip find $BUILDROOT/out/$PACKAGENAME/lib -name \*dll | xargs $BUILDROOT/mxe/usr/bin/x86_64-w64-mingw32.shared-strip zip -r $PACKAGENAME.zip $PACKAGENAME diff --git a/plugins/assistants/RulerAssistant/CMakeLists.txt b/plugins/assistants/RulerAssistant/CMakeLists.txt index 4fbe5e068f..9d7c35b1aa 100644 --- a/plugins/assistants/RulerAssistant/CMakeLists.txt +++ b/plugins/assistants/RulerAssistant/CMakeLists.txt @@ -1,26 +1,24 @@ -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../RulerAssistantCommon) - set(kritarulerassistanttool_SOURCES RulerAssistant.cc EllipseAssistant.cc Ellipse.cc SplineAssistant.cc PerspectiveAssistant.cc VanishingPointAssistant.cc InfiniteRulerAssistant.cc ParallelRulerAssistant.cc ConcentricEllipseAssistant.cc ruler_assistant_tool.cc kis_ruler_assistant_tool.cc FisheyePointAssistant.cc ) ki18n_wrap_ui(kritarulerassistanttool_SOURCES AssistantsToolOptions.ui ) add_library(kritarulerassistanttool MODULE ${kritarulerassistanttool_SOURCES}) target_link_libraries(kritarulerassistanttool kritaui kritaflake ) install(TARGETS kritarulerassistanttool DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) install( FILES krita_tool_ruler_assistant.png dark_krita_tool_ruler_assistant.png light_krita_tool_ruler_assistant.png DESTINATION ${DATA_INSTALL_DIR}/krita/pics) diff --git a/plugins/color/lcms2engine/colorprofiles/LcmsColorProfileContainer.cpp b/plugins/color/lcms2engine/colorprofiles/LcmsColorProfileContainer.cpp index 0f2320718d..39f956bb46 100644 --- a/plugins/color/lcms2engine/colorprofiles/LcmsColorProfileContainer.cpp +++ b/plugins/color/lcms2engine/colorprofiles/LcmsColorProfileContainer.cpp @@ -1,563 +1,562 @@ /* * This file is part of the KDE project * Copyright (c) 2000 Matthias Elter * 2001 John Califf * 2004 Boudewijn Rempt * Copyright (c) 2007 Thomas Zander * Copyright (c) 2007 Adrian Page * * 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 "LcmsColorProfileContainer.h" #include #include #include #include #include class LcmsColorProfileContainer::Private { public: Private() : valid(false) , suitableForOutput(false) { } cmsHPROFILE profile; cmsColorSpaceSignature colorSpaceSignature; cmsProfileClassSignature deviceClass; QString productDescription; QString manufacturer; QString copyright; QString name; float version; IccColorProfile::Data *data; bool valid; bool suitableForOutput; bool hasColorants; bool hasTRC; bool adaptedFromD50; - cmsNAMEDCOLORLIST *namedColorList; cmsCIEXYZ mediaWhitePoint; cmsCIExyY whitePoint; cmsCIEXYZTRIPLE colorants; cmsToneCurve *redTRC; cmsToneCurve *greenTRC; cmsToneCurve *blueTRC; cmsToneCurve *grayTRC; cmsToneCurve *redTRCReverse; cmsToneCurve *greenTRCReverse; cmsToneCurve *blueTRCReverse; cmsToneCurve *grayTRCReverse; cmsUInt32Number defaultIntent; bool isPerceptualCLUT; bool isRelativeCLUT; bool isAbsoluteCLUT; bool isSaturationCLUT; bool isMatrixShaper; }; LcmsColorProfileContainer::LcmsColorProfileContainer() : d(new Private()) { d->profile = 0; } LcmsColorProfileContainer::LcmsColorProfileContainer(IccColorProfile::Data *data) : d(new Private()) { d->data = data; d->profile = 0; init(); } QByteArray LcmsColorProfileContainer::lcmsProfileToByteArray(const cmsHPROFILE profile) { cmsUInt32Number bytesNeeded = 0; // Make a raw data image ready for saving cmsSaveProfileToMem(profile, 0, &bytesNeeded); // calc size QByteArray rawData; rawData.resize(bytesNeeded); if (rawData.size() >= (int)bytesNeeded) { cmsSaveProfileToMem(profile, rawData.data(), &bytesNeeded); // fill buffer } else { qWarning() << "Couldn't resize the profile buffer, system is probably running out of memory."; rawData.resize(0); } return rawData; } IccColorProfile *LcmsColorProfileContainer::createFromLcmsProfile(const cmsHPROFILE profile) { IccColorProfile *iccprofile = new IccColorProfile(lcmsProfileToByteArray(profile)); cmsCloseProfile(profile); return iccprofile; } LcmsColorProfileContainer::~LcmsColorProfileContainer() { cmsCloseProfile(d->profile); delete d; } #define _BUFFER_SIZE_ 1000 bool LcmsColorProfileContainer::init() { if (d->profile) { cmsCloseProfile(d->profile); } d->profile = cmsOpenProfileFromMem((void *)d->data->rawData().constData(), d->data->rawData().size()); #ifndef NDEBUG if (d->data->rawData().size() == 4096) { qWarning() << "Profile has a size of 4096, which is suspicious and indicates a possible misuse of QIODevice::read(int), check your code."; } #endif if (d->profile) { wchar_t buffer[_BUFFER_SIZE_]; d->colorSpaceSignature = cmsGetColorSpace(d->profile); d->deviceClass = cmsGetDeviceClass(d->profile); cmsGetProfileInfo(d->profile, cmsInfoDescription, cmsNoLanguage, cmsNoCountry, buffer, _BUFFER_SIZE_); d->name = QString::fromWCharArray(buffer); //apparantly this should give us a localised string??? Not sure about this. cmsGetProfileInfo(d->profile, cmsInfoModel, cmsNoLanguage, cmsNoCountry, buffer, _BUFFER_SIZE_); d->productDescription = QString::fromWCharArray(buffer); cmsGetProfileInfo(d->profile, cmsInfoManufacturer, cmsNoLanguage, cmsNoCountry, buffer, _BUFFER_SIZE_); d->manufacturer = QString::fromWCharArray(buffer); cmsGetProfileInfo(d->profile, cmsInfoCopyright, cmsNoLanguage, cmsNoCountry, buffer, _BUFFER_SIZE_); d->copyright = QString::fromWCharArray(buffer); cmsProfileClassSignature profile_class; profile_class = cmsGetDeviceClass(d->profile); d->valid = (profile_class != cmsSigNamedColorClass); //This is where obtain the whitepoint, and convert it to the actual white point of the profile in the case a Chromatic adaption tag is //present. This is necessary for profiles following the v4 spec. cmsCIEXYZ baseMediaWhitePoint;//dummy to hold copy of mediawhitepoint if this is modified by chromatic adaption. if (cmsIsTag(d->profile, cmsSigMediaWhitePointTag)) { d->mediaWhitePoint = *((cmsCIEXYZ *)cmsReadTag(d->profile, cmsSigMediaWhitePointTag)); baseMediaWhitePoint = d->mediaWhitePoint; cmsXYZ2xyY(&d->whitePoint, &d->mediaWhitePoint); if (cmsIsTag(d->profile, cmsSigChromaticAdaptationTag)) { //the chromatic adaption tag represent a matrix from the actual white point of the profile to D50. cmsCIEXYZ *CAM1 = (cmsCIEXYZ *)cmsReadTag(d->profile, cmsSigChromaticAdaptationTag); //We first put all our data into structures we can manipulate. double d3dummy [3] = {d->mediaWhitePoint.X, d->mediaWhitePoint.Y, d->mediaWhitePoint.Z}; QGenericMatrix<1, 3, double> whitePointMatrix(d3dummy); QTransform invertDummy(CAM1[0].X, CAM1[0].Y, CAM1[0].Z, CAM1[1].X, CAM1[1].Y, CAM1[1].Z, CAM1[2].X, CAM1[2].Y, CAM1[2].Z); //we then abuse QTransform's invert function because it probably does matrix invertion 20 times better than I can program. //if the matrix is uninvertable, invertedDummy will be an identity matrix, which for us means that it won't give any noticeble //effect when we start multiplying. QTransform invertedDummy = invertDummy.inverted(); //we then put the QTransform into a generic 3x3 matrix. double d9dummy [9] = {invertedDummy.m11(), invertedDummy.m12(), invertedDummy.m13(), invertedDummy.m21(), invertedDummy.m22(), invertedDummy.m23(), invertedDummy.m31(), invertedDummy.m32(), invertedDummy.m33() }; QGenericMatrix<3, 3, double> chromaticAdaptionMatrix(d9dummy); //multiplying our inverted adaption matrix with the whitepoint gives us the right whitepoint. QGenericMatrix<1, 3, double> result = chromaticAdaptionMatrix * whitePointMatrix; //and then we pour the matrix into the whitepoint variable. Generic matrix does row/column for indices even though it //uses column/row for initialising. d->mediaWhitePoint.X = result(0, 0); d->mediaWhitePoint.Y = result(1, 0); d->mediaWhitePoint.Z = result(2, 0); cmsXYZ2xyY(&d->whitePoint, &d->mediaWhitePoint); } } //This is for RGB profiles, but it only works for matrix profiles. Need to design it to work with non-matrix profiles. if (cmsIsTag(d->profile, cmsSigRedColorantTag)) { cmsCIEXYZTRIPLE tempColorants; tempColorants.Red = *((cmsCIEXYZ *)cmsReadTag(d->profile, cmsSigRedColorantTag)); tempColorants.Green = *((cmsCIEXYZ *)cmsReadTag(d->profile, cmsSigGreenColorantTag)); tempColorants.Blue = *((cmsCIEXYZ *)cmsReadTag(d->profile, cmsSigBlueColorantTag)); //convert to d65, this is useless. cmsAdaptToIlluminant(&d->colorants.Red, &baseMediaWhitePoint, &d->mediaWhitePoint, &tempColorants.Red); cmsAdaptToIlluminant(&d->colorants.Green, &baseMediaWhitePoint, &d->mediaWhitePoint, &tempColorants.Green); cmsAdaptToIlluminant(&d->colorants.Blue, &baseMediaWhitePoint, &d->mediaWhitePoint, &tempColorants.Blue); //d->colorants = tempColorants; d->hasColorants = true; } else { //qDebug()<name<<": has no colorants"; d->hasColorants = false; } //retrieve TRC. if (cmsIsTag(d->profile, cmsSigRedTRCTag) && cmsIsTag(d->profile, cmsSigBlueTRCTag) && cmsIsTag(d->profile, cmsSigGreenTRCTag)) { d->redTRC = ((cmsToneCurve *)cmsReadTag (d->profile, cmsSigRedTRCTag)); d->greenTRC = ((cmsToneCurve *)cmsReadTag (d->profile, cmsSigGreenTRCTag)); d->blueTRC = ((cmsToneCurve *)cmsReadTag (d->profile, cmsSigBlueTRCTag)); d->redTRCReverse = cmsReverseToneCurve(d->redTRC); d->greenTRCReverse = cmsReverseToneCurve(d->greenTRC); d->blueTRCReverse = cmsReverseToneCurve(d->blueTRC); d->hasTRC = true; } else if (cmsIsTag(d->profile, cmsSigGrayTRCTag)) { d->grayTRC = ((cmsToneCurve *)cmsReadTag (d->profile, cmsSigGrayTRCTag)); d->grayTRCReverse = cmsReverseToneCurve(d->grayTRC); d->hasTRC = true; } else { d->hasTRC = false; } // Check if the profile can convert (something->this) d->suitableForOutput = cmsIsMatrixShaper(d->profile) || (cmsIsCLUT(d->profile, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT) && cmsIsCLUT(d->profile, INTENT_PERCEPTUAL, LCMS_USED_AS_OUTPUT)); d->version = cmsGetProfileVersion(d->profile); d->defaultIntent = cmsGetHeaderRenderingIntent(d->profile); d->isMatrixShaper = cmsIsMatrixShaper(d->profile); d->isPerceptualCLUT = cmsIsCLUT(d->profile, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT); d->isSaturationCLUT = cmsIsCLUT(d->profile, INTENT_SATURATION, LCMS_USED_AS_INPUT); d->isAbsoluteCLUT = cmsIsCLUT(d->profile, INTENT_SATURATION, LCMS_USED_AS_INPUT); d->isRelativeCLUT = cmsIsCLUT(d->profile, INTENT_RELATIVE_COLORIMETRIC, LCMS_USED_AS_INPUT); return true; } return false; } cmsHPROFILE LcmsColorProfileContainer::lcmsProfile() const { return d->profile; } cmsColorSpaceSignature LcmsColorProfileContainer::colorSpaceSignature() const { return d->colorSpaceSignature; } cmsProfileClassSignature LcmsColorProfileContainer::deviceClass() const { return d->deviceClass; } QString LcmsColorProfileContainer::manufacturer() const { return d->manufacturer; } QString LcmsColorProfileContainer::copyright() const { return d->copyright; } bool LcmsColorProfileContainer::valid() const { return d->valid; } float LcmsColorProfileContainer::version() const { return d->version; } bool LcmsColorProfileContainer::isSuitableForOutput() const { return d->suitableForOutput; } bool LcmsColorProfileContainer::isSuitableForPrinting() const { return deviceClass() == cmsSigOutputClass; } bool LcmsColorProfileContainer::isSuitableForDisplay() const { return deviceClass() == cmsSigDisplayClass; } bool LcmsColorProfileContainer::supportsPerceptual() const { return d->isPerceptualCLUT; } bool LcmsColorProfileContainer::supportsSaturation() const { return d->isSaturationCLUT; } bool LcmsColorProfileContainer::supportsAbsolute() const { return d->isAbsoluteCLUT;//LCMS2 doesn't convert matrix shapers via absolute intent, because of V4 workflow. } bool LcmsColorProfileContainer::supportsRelative() const { if (d->isRelativeCLUT || d->isMatrixShaper){ return true; } return false; } bool LcmsColorProfileContainer::hasColorants() const { return d->hasColorants; } bool LcmsColorProfileContainer::hasTRC() const { return d->hasTRC; } QVector LcmsColorProfileContainer::getColorantsXYZ() const { QVector colorants(9); colorants[0] = d->colorants.Red.X; colorants[1] = d->colorants.Red.Y; colorants[2] = d->colorants.Red.Z; colorants[3] = d->colorants.Green.X; colorants[4] = d->colorants.Green.Y; colorants[5] = d->colorants.Green.Z; colorants[6] = d->colorants.Blue.X; colorants[7] = d->colorants.Blue.Y; colorants[8] = d->colorants.Blue.Z; return colorants; } QVector LcmsColorProfileContainer::getColorantsxyY() const { cmsCIEXYZ temp1; cmsCIExyY temp2; QVector colorants(9); temp1.X = d->colorants.Red.X; temp1.Y = d->colorants.Red.Y; temp1.Z = d->colorants.Red.Z; cmsXYZ2xyY(&temp2, &temp1); colorants[0] = temp2.x; colorants[1] = temp2.y; colorants[2] = temp2.Y; temp1.X = d->colorants.Green.X; temp1.Y = d->colorants.Green.Y; temp1.Z = d->colorants.Green.Z; cmsXYZ2xyY(&temp2, &temp1); colorants[3] = temp2.x; colorants[4] = temp2.y; colorants[5] = temp2.Y; temp1.X = d->colorants.Blue.X; temp1.Y = d->colorants.Blue.Y; temp1.Z = d->colorants.Blue.Z; cmsXYZ2xyY(&temp2, &temp1); colorants[6] = temp2.x; colorants[7] = temp2.y; colorants[8] = temp2.Y; return colorants; } QVector LcmsColorProfileContainer::getWhitePointXYZ() const { QVector tempWhitePoint(3); tempWhitePoint[0] = d->mediaWhitePoint.X; tempWhitePoint[1] = d->mediaWhitePoint.Y; tempWhitePoint[2] = d->mediaWhitePoint.Z; return tempWhitePoint; } QVector LcmsColorProfileContainer::getWhitePointxyY() const { QVector tempWhitePoint(3); tempWhitePoint[0] = d->whitePoint.x; tempWhitePoint[1] = d->whitePoint.y; tempWhitePoint[2] = d->whitePoint.Y; return tempWhitePoint; } QVector LcmsColorProfileContainer::getEstimatedTRC() const { QVector TRCtriplet(3); if (d->hasColorants) { if (cmsIsToneCurveLinear(d->redTRC)) { TRCtriplet[0] = 1.0; } else { TRCtriplet[0] = cmsEstimateGamma(d->redTRC, 0.01); } if (cmsIsToneCurveLinear(d->greenTRC)) { TRCtriplet[1] = 1.0; } else { TRCtriplet[1] = cmsEstimateGamma(d->greenTRC, 0.01); } if (cmsIsToneCurveLinear(d->blueTRC)) { TRCtriplet[2] = 1.0; } else { TRCtriplet[2] = cmsEstimateGamma(d->blueTRC, 0.01); } } else { if (cmsIsTag(d->profile, cmsSigGrayTRCTag)) { if (cmsIsToneCurveLinear(d->grayTRC)) { TRCtriplet.fill(1.0); } else { TRCtriplet.fill(cmsEstimateGamma(d->grayTRC, 0.01)); } } else { TRCtriplet.fill(1.0); } } return TRCtriplet; } void LcmsColorProfileContainer::LinearizeFloatValue(QVector & Value) const { QVector TRCtriplet(3); TRCtriplet[0] = Value[0]; TRCtriplet[1] = Value[1]; TRCtriplet[2] = Value[2]; if (d->hasColorants) { if (cmsIsToneCurveLinear(d->redTRC)) { TRCtriplet[0] = Value[0]; } else { TRCtriplet[0] = cmsEvalToneCurveFloat(d->redTRC, Value[0]); } if (cmsIsToneCurveLinear(d->greenTRC)) { TRCtriplet[1] = Value[1]; } else { TRCtriplet[1] = cmsEvalToneCurveFloat(d->greenTRC, Value[1]); } if (cmsIsToneCurveLinear(d->blueTRC)) { TRCtriplet[2] = Value[2]; } else { TRCtriplet[2] = cmsEvalToneCurveFloat(d->blueTRC, Value[2]); } } else { if (cmsIsTag(d->profile, cmsSigGrayTRCTag)) { TRCtriplet.fill(cmsEvalToneCurveFloat(d->grayTRC, Value[0])); } } Value[0] = TRCtriplet[0]; Value[1] = TRCtriplet[1]; Value[2] = TRCtriplet[2]; } void LcmsColorProfileContainer::DelinearizeFloatValue(QVector & Value) const { QVector TRCtriplet(3); TRCtriplet[0] = Value[0]; TRCtriplet[1] = Value[1]; TRCtriplet[2] = Value[2]; if (cmsIsTag(d->profile, cmsSigRedTRCTag)) { if (cmsIsToneCurveLinear(d->redTRC)) { TRCtriplet[0] = Value[0]; } else { TRCtriplet[0] = cmsEvalToneCurveFloat(d->redTRCReverse, Value[0]); } if (cmsIsToneCurveLinear(d->greenTRC)) { TRCtriplet[1] = Value[1]; } else { TRCtriplet[1] = cmsEvalToneCurveFloat(d->greenTRCReverse, Value[1]); } if (cmsIsToneCurveLinear(d->blueTRC)) { TRCtriplet[2] = Value[2]; } else { TRCtriplet[2] = cmsEvalToneCurveFloat(d->blueTRCReverse, Value[2]); } } else { if (cmsIsTag(d->profile, cmsSigGrayTRCTag)) { TRCtriplet.fill(cmsEvalToneCurveFloat(d->grayTRCReverse, Value[0])); } } Value[0] = TRCtriplet[0]; Value[1] = TRCtriplet[1]; Value[2] = TRCtriplet[2]; } void LcmsColorProfileContainer::LinearizeFloatValueFast(QVector & Value) const { //we can only reliably delinearise in the 0-1.0 range, outside of that leave the value alone. QVector TRCtriplet(3); TRCtriplet[0] = Value[0]*65535; TRCtriplet[1] = Value[1]*65535; TRCtriplet[2] = Value[2]*65535; if (d->hasColorants) { if (!cmsIsToneCurveLinear(d->redTRC) && Value[0]<1.0) { TRCtriplet[0] = cmsEvalToneCurve16(d->redTRC, TRCtriplet[0]); Value[0] = TRCtriplet[0]/65535.0; } if (!cmsIsToneCurveLinear(d->greenTRC) && Value[1]<1.0) { TRCtriplet[1] = cmsEvalToneCurve16(d->greenTRC, TRCtriplet[1]); Value[1] = TRCtriplet[1]/65535.0; } if (!cmsIsToneCurveLinear(d->blueTRC) && Value[2]<1.0) { TRCtriplet[2] = cmsEvalToneCurve16(d->blueTRC, TRCtriplet[2]); Value[2] = TRCtriplet[2]/65535.0; } } else { if (cmsIsTag(d->profile, cmsSigGrayTRCTag) && Value[0]<1.0) { TRCtriplet[0] = (cmsEvalToneCurve16(d->grayTRC, Value[0]*65535)); Value.fill(TRCtriplet[0]/65535.0); } } } void LcmsColorProfileContainer::DelinearizeFloatValueFast(QVector & Value) const { //we can only reliably delinearise in the 0-1.0 range, outside of that leave the value alone. QVector TRCtriplet(3); TRCtriplet[0] = Value[0]*65535; TRCtriplet[1] = Value[1]*65535; TRCtriplet[2] = Value[2]*65535; if (d->hasColorants) { if (!cmsIsToneCurveLinear(d->redTRC) && Value[0]<1.0) { TRCtriplet[0] = cmsEvalToneCurve16(d->redTRCReverse, TRCtriplet[0]); Value[0] = TRCtriplet[0]/65535.0; } if (!cmsIsToneCurveLinear(d->greenTRC) && Value[1]<1.0) { TRCtriplet[1] = cmsEvalToneCurve16(d->greenTRCReverse, TRCtriplet[1]); Value[1] = TRCtriplet[1]/65535.0; } if (!cmsIsToneCurveLinear(d->blueTRC) && Value[2]<1.0) { TRCtriplet[2] = cmsEvalToneCurve16(d->blueTRCReverse, TRCtriplet[2]); Value[2] = TRCtriplet[2]/65535.0; } } else { if (cmsIsTag(d->profile, cmsSigGrayTRCTag) && Value[0]<1.0) { TRCtriplet[0] = (cmsEvalToneCurve16(d->grayTRCReverse, Value[0]*65535)); Value.fill(TRCtriplet[0]/65535.0); } } } QString LcmsColorProfileContainer::name() const { return d->name; } QString LcmsColorProfileContainer::info() const { return d->productDescription; } diff --git a/plugins/color/lcms2engine/colorspaces/cmyk_f32/CmykF32ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/cmyk_f32/CmykF32ColorSpace.cpp index b9fd5603c6..9516a313ed 100644 --- a/plugins/color/lcms2engine/colorspaces/cmyk_f32/CmykF32ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/cmyk_f32/CmykF32ColorSpace.cpp @@ -1,125 +1,134 @@ /* * Copyright (c) 2006 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 * Library 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 "CmykF32ColorSpace.h" #include #include #include #include "compositeops/KoCompositeOps.h" #include +#include CmykF32ColorSpace::CmykF32ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_CMYKA_FLT, cmsSigCmykData, p) { const IccColorProfile *icc_p = dynamic_cast(p); Q_ASSERT(icc_p); QVector uiRanges(icc_p->getFloatUIMinMax()); Q_ASSERT(uiRanges.size() == 4); addChannel(new KoChannelInfo(i18n("Cyan"), 0 * sizeof(float), 0, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, sizeof(float), Qt::cyan, uiRanges[0])); addChannel(new KoChannelInfo(i18n("Magenta"), 1 * sizeof(float), 1, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, sizeof(float), Qt::magenta, uiRanges[1])); addChannel(new KoChannelInfo(i18n("Yellow"), 2 * sizeof(float), 2, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, sizeof(float), Qt::yellow, uiRanges[2])); addChannel(new KoChannelInfo(i18n("Black"), 3 * sizeof(float), 3, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, sizeof(float), Qt::black, uiRanges[3])); addChannel(new KoChannelInfo(i18n("Alpha"), 4 * sizeof(float), 4, KoChannelInfo::ALPHA, KoChannelInfo::FLOAT32, sizeof(float))); init(); addStandardCompositeOps(this); } bool CmykF32ColorSpace::willDegrade(ColorSpaceIndependence independence) const { if (independence == TO_RGBA16) { return true; } else { return false; } } KoColorSpace *CmykF32ColorSpace::clone() const { return new CmykF32ColorSpace(name(), profile()->clone()); } void CmykF32ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoCmykF32Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("CMYK"); - labElt.setAttribute("c", KoColorSpaceMaths< KoCmykF32Traits::channels_type, qreal>::scaleToA(p->cyan)); - labElt.setAttribute("m", KoColorSpaceMaths< KoCmykF32Traits::channels_type, qreal>::scaleToA(p->magenta)); - labElt.setAttribute("y", KoColorSpaceMaths< KoCmykF32Traits::channels_type, qreal>::scaleToA(p->yellow)); - labElt.setAttribute("k", KoColorSpaceMaths< KoCmykF32Traits::channels_type, qreal>::scaleToA(p->black)); + labElt.setAttribute("c", KisDomUtils::toString(KoColorSpaceMaths< KoCmykF32Traits::channels_type, qreal>::scaleToA(p->cyan))); + labElt.setAttribute("m", KisDomUtils::toString(KoColorSpaceMaths< KoCmykF32Traits::channels_type, qreal>::scaleToA(p->magenta))); + labElt.setAttribute("y", KisDomUtils::toString(KoColorSpaceMaths< KoCmykF32Traits::channels_type, qreal>::scaleToA(p->yellow))); + labElt.setAttribute("k", KisDomUtils::toString(KoColorSpaceMaths< KoCmykF32Traits::channels_type, qreal>::scaleToA(p->black))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void CmykF32ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoCmykF32Traits::Pixel *p = reinterpret_cast(pixel); - p->cyan = KoColorSpaceMaths< qreal, KoCmykF32Traits::channels_type >::scaleToA(elt.attribute("c").toDouble()); - p->magenta = KoColorSpaceMaths< qreal, KoCmykF32Traits::channels_type >::scaleToA(elt.attribute("m").toDouble()); - p->yellow = KoColorSpaceMaths< qreal, KoCmykF32Traits::channels_type >::scaleToA(elt.attribute("y").toDouble()); - p->black = KoColorSpaceMaths< qreal, KoCmykF32Traits::channels_type >::scaleToA(elt.attribute("k").toDouble()); + p->cyan = KoColorSpaceMaths< qreal, KoCmykF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("c"))); + p->magenta = KoColorSpaceMaths< qreal, KoCmykF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("m"))); + p->yellow = KoColorSpaceMaths< qreal, KoCmykF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("y"))); + p->black = KoColorSpaceMaths< qreal, KoCmykF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("k"))); p->alpha = 1.0; } -void CmykF32ColorSpace::toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const +void CmykF32ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { + qreal c0 = channelValues[0]; + qreal c1 = channelValues[1]; + qreal c2 = channelValues[2]; + qreal c3 = channelValues[3]; //we use HSI here because we can't linearise CMYK, and HSY doesn't work right with... - CMYKToCMY(&channelValues[0],&channelValues[1],&channelValues[2],&channelValues[3]); - channelValues[0] = 1.0-channelValues[0]; - channelValues[1] = 1.0-channelValues[1]; - channelValues[2] = 1.0-channelValues[2]; - RGBToHSI(channelValues[0],channelValues[1],channelValues[2], hue, sat, luma); + CMYKToCMY(&c0, &c1, &c2, &c3); + c0 = 1.0 - c0; + c1 = 1.0 - c1; + c2 = 1.0 - c2; + RGBToHSI(c0, c1, c2, hue, sat, luma); } QVector CmykF32ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(5); channelValues.fill(1.0); HSIToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[0] = qBound(0.0,1.0-channelValues[0],1.0); channelValues[1] = qBound(0.0,1.0-channelValues[1],1.0); channelValues[2] = qBound(0.0,1.0-channelValues[2],1.0); CMYToCMYK(&channelValues[0],&channelValues[1],&channelValues[2],&channelValues[3]); return channelValues; } -void CmykF32ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const +void CmykF32ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { - CMYKToCMY(&channelValues[0],&channelValues[1],&channelValues[2],&channelValues[3]); - channelValues[0] = 1.0-channelValues[0]; - channelValues[1] = 1.0-channelValues[1]; - channelValues[2] = 1.0-channelValues[2]; - RGBToYUV(channelValues[0],channelValues[1],channelValues[2], y, u, v, (1.0 - 0.299),(1.0 - 0.587), (1.0 - 0.114)); + qreal c0 = channelValues[0]; + qreal c1 = channelValues[1]; + qreal c2 = channelValues[2]; + qreal c3 = channelValues[3]; + CMYKToCMY(&c0, &c1, &c2, &c3); + c0 = 1.0 - c0; + c1 = 1.0 - c1; + c2 = 1.0 - c2; + RGBToYUV(c0, c1, c2, y, u, v, (1.0 - 0.299),(1.0 - 0.587), (1.0 - 0.114)); } QVector CmykF32ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(5); channelValues.fill(1.0); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2], 0.33, 0.33, 0.33); channelValues[0] = qBound(0.0,1.0-channelValues[0],1.0); channelValues[1] = qBound(0.0,1.0-channelValues[1],1.0); channelValues[2] = qBound(0.0,1.0-channelValues[2],1.0); CMYToCMYK(&channelValues[0],&channelValues[1],&channelValues[2],&channelValues[3]); return channelValues; } diff --git a/plugins/color/lcms2engine/colorspaces/cmyk_f32/CmykF32ColorSpace.h b/plugins/color/lcms2engine/colorspaces/cmyk_f32/CmykF32ColorSpace.h index 04e54477da..8f36cf61f4 100644 --- a/plugins/color/lcms2engine/colorspaces/cmyk_f32/CmykF32ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/cmyk_f32/CmykF32ColorSpace.h @@ -1,125 +1,125 @@ /* * Copyright (c) 2006 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 * Library 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 KIS_COLORSPACE_CMYK_F32_H_ #define KIS_COLORSPACE_CMYK_F32_H_ #include #include "KoColorModelStandardIds.h" struct KoCmykF32Traits; #define TYPE_CMYKA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|EXTRA_SH(1)|CHANNELS_SH(4)|BYTES_SH(4)) class CmykF32ColorSpace : public LcmsColorSpace { public: CmykF32ColorSpace(const QString &name, KoColorProfile *p); virtual bool willDegrade(ColorSpaceIndependence independence) const; virtual KoID colorModelId() const { return CMYKAColorModelID; } virtual KoID colorDepthId() const { return Float32BitsColorDepthID; } virtual KoColorSpace *clone() const; virtual void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const; virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY( qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; static QString colorSpaceId() { return "CMYKAF32"; } virtual bool hasHighDynamicRange() const { return true; } }; class CmykF32ColorSpaceFactory : public LcmsColorSpaceFactory { public: CmykF32ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_CMYKA_FLT, cmsSigCmykData) { } virtual bool userVisible() const { return true; } virtual QString id() const { return CmykF32ColorSpace::colorSpaceId(); } virtual QString name() const { return i18n("CMYK (32 bits floating/channel)"); } virtual KoID colorModelId() const { return CMYKAColorModelID; } virtual KoID colorDepthId() const { return Float32BitsColorDepthID; } virtual int referenceDepth() const { return 32; } virtual KoColorSpace *createColorSpace(const KoColorProfile *p) const { return new CmykF32ColorSpace(name(), p->clone()); } virtual QString defaultProfile() const { return "Chemical proof"; } virtual bool isHdr() const { return true; } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/cmyk_u16/CmykU16ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/cmyk_u16/CmykU16ColorSpace.cpp index 6f4673324e..e006979c80 100644 --- a/plugins/color/lcms2engine/colorspaces/cmyk_u16/CmykU16ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/cmyk_u16/CmykU16ColorSpace.cpp @@ -1,120 +1,129 @@ /* * Copyright (c) 2006 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 * Library 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 "CmykU16ColorSpace.h" #include #include #include #include "compositeops/KoCompositeOps.h" #include +#include CmykU16ColorSpace::CmykU16ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_CMYKA_16, cmsSigCmykData, p) { addChannel(new KoChannelInfo(i18n("Cyan"), 0 * sizeof(quint16), 0, KoChannelInfo::COLOR, KoChannelInfo::UINT16, sizeof(quint16), Qt::cyan)); addChannel(new KoChannelInfo(i18n("Magenta"), 1 * sizeof(quint16), 1, KoChannelInfo::COLOR, KoChannelInfo::UINT16, sizeof(quint16), Qt::magenta)); addChannel(new KoChannelInfo(i18n("Yellow"), 2 * sizeof(quint16), 2, KoChannelInfo::COLOR, KoChannelInfo::UINT16, sizeof(quint16), Qt::yellow)); addChannel(new KoChannelInfo(i18n("Black"), 3 * sizeof(quint16), 3, KoChannelInfo::COLOR, KoChannelInfo::UINT16, sizeof(quint16), Qt::black)); addChannel(new KoChannelInfo(i18n("Alpha"), 4 * sizeof(quint16), 4, KoChannelInfo::ALPHA, KoChannelInfo::UINT16, sizeof(quint16))); init(); addStandardCompositeOps(this); } bool CmykU16ColorSpace::willDegrade(ColorSpaceIndependence independence) const { if (independence == TO_RGBA8) { return true; } else { return false; } } KoColorSpace *CmykU16ColorSpace::clone() const { return new CmykU16ColorSpace(name(), profile()->clone()); } void CmykU16ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const CmykU16Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("CMYK"); - labElt.setAttribute("c", KoColorSpaceMaths< CmykU16Traits::channels_type, qreal>::scaleToA(p->cyan)); - labElt.setAttribute("m", KoColorSpaceMaths< CmykU16Traits::channels_type, qreal>::scaleToA(p->magenta)); - labElt.setAttribute("y", KoColorSpaceMaths< CmykU16Traits::channels_type, qreal>::scaleToA(p->yellow)); - labElt.setAttribute("k", KoColorSpaceMaths< CmykU16Traits::channels_type, qreal>::scaleToA(p->black)); + labElt.setAttribute("c", KisDomUtils::toString(KoColorSpaceMaths< CmykU16Traits::channels_type, qreal>::scaleToA(p->cyan))); + labElt.setAttribute("m", KisDomUtils::toString(KoColorSpaceMaths< CmykU16Traits::channels_type, qreal>::scaleToA(p->magenta))); + labElt.setAttribute("y", KisDomUtils::toString(KoColorSpaceMaths< CmykU16Traits::channels_type, qreal>::scaleToA(p->yellow))); + labElt.setAttribute("k", KisDomUtils::toString(KoColorSpaceMaths< CmykU16Traits::channels_type, qreal>::scaleToA(p->black))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void CmykU16ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { CmykU16Traits::Pixel *p = reinterpret_cast(pixel); - p->cyan = KoColorSpaceMaths< qreal, CmykU16Traits::channels_type >::scaleToA(elt.attribute("c").toDouble()); - p->magenta = KoColorSpaceMaths< qreal, CmykU16Traits::channels_type >::scaleToA(elt.attribute("m").toDouble()); - p->yellow = KoColorSpaceMaths< qreal, CmykU16Traits::channels_type >::scaleToA(elt.attribute("y").toDouble()); - p->black = KoColorSpaceMaths< qreal, CmykU16Traits::channels_type >::scaleToA(elt.attribute("k").toDouble()); + p->cyan = KoColorSpaceMaths< qreal, CmykU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("c"))); + p->magenta = KoColorSpaceMaths< qreal, CmykU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("m"))); + p->yellow = KoColorSpaceMaths< qreal, CmykU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("y"))); + p->black = KoColorSpaceMaths< qreal, CmykU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("k"))); p->alpha = KoColorSpaceMathsTraits::max; } -void CmykU16ColorSpace::toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const +void CmykU16ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { + qreal c0 = channelValues[0]; + qreal c1 = channelValues[1]; + qreal c2 = channelValues[2]; + qreal c3 = channelValues[3]; //we use HSI here because we can't linearise CMYK, and HSY doesn't work right with... - CMYKToCMY(&channelValues[0],&channelValues[1],&channelValues[2],&channelValues[3]); - channelValues[0] = 1.0-channelValues[0]; - channelValues[1] = 1.0-channelValues[1]; - channelValues[2] = 1.0-channelValues[2]; - RGBToHSI(channelValues[0],channelValues[1],channelValues[2], hue, sat, luma); + CMYKToCMY(&c0, &c1, &c2, &c3); + c0 = 1.0 - c0; + c1 = 1.0 - c1; + c2 = 1.0 - c2; + RGBToHSI(c0, c1, c2, hue, sat, luma); } QVector CmykU16ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(5); channelValues.fill(1.0); HSIToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[0] = qBound(0.0,1.0-channelValues[0],1.0); channelValues[1] = qBound(0.0,1.0-channelValues[1],1.0); channelValues[2] = qBound(0.0,1.0-channelValues[2],1.0); CMYToCMYK(&channelValues[0],&channelValues[1],&channelValues[2],&channelValues[3]); return channelValues; } -void CmykU16ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const +void CmykU16ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { - CMYKToCMY(&channelValues[0],&channelValues[1],&channelValues[2],&channelValues[3]); - channelValues[0] = 1.0-channelValues[0]; - channelValues[1] = 1.0-channelValues[1]; - channelValues[2] = 1.0-channelValues[2]; - RGBToYUV(channelValues[0],channelValues[1],channelValues[2], y, u, v, 0.33, 0.33, 0.33); + qreal c0 = channelValues[0]; + qreal c1 = channelValues[1]; + qreal c2 = channelValues[2]; + qreal c3 = channelValues[3]; + CMYKToCMY(&c0, &c1, &c2, &c3); + c0 = 1.0 - c0; + c1 = 1.0 - c1; + c2 = 1.0 - c2; + RGBToYUV(c0, c1, c2, y, u, v, 0.33, 0.33, 0.33); } QVector CmykU16ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(5); channelValues.fill(1.0); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2], 0.33, 0.33, 0.33); channelValues[0] = qBound(0.0,1.0-channelValues[0],1.0); channelValues[1] = qBound(0.0,1.0-channelValues[1],1.0); channelValues[2] = qBound(0.0,1.0-channelValues[2],1.0); CMYToCMYK(&channelValues[0],&channelValues[1],&channelValues[2],&channelValues[3]); return channelValues; } diff --git a/plugins/color/lcms2engine/colorspaces/cmyk_u16/CmykU16ColorSpace.h b/plugins/color/lcms2engine/colorspaces/cmyk_u16/CmykU16ColorSpace.h index c1dec56221..3af4c1d524 100644 --- a/plugins/color/lcms2engine/colorspaces/cmyk_u16/CmykU16ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/cmyk_u16/CmykU16ColorSpace.h @@ -1,116 +1,116 @@ /* * Copyright (c) 2006 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 * Library 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 KIS_STRATEGY_COLORSPACE_CMYK_U16_H_ #define KIS_STRATEGY_COLORSPACE_CMYK_U16_H_ #include #include #include "KoColorModelStandardIds.h" typedef KoCmykTraits CmykU16Traits; #define TYPE_CMYKA_16 (COLORSPACE_SH(PT_CMYK)|EXTRA_SH(1)|CHANNELS_SH(4)|BYTES_SH(2)) class CmykU16ColorSpace : public LcmsColorSpace { public: CmykU16ColorSpace(const QString &name, KoColorProfile *p); virtual bool willDegrade(ColorSpaceIndependence independence) const; virtual KoID colorModelId() const { return CMYKAColorModelID; } virtual KoID colorDepthId() const { return Integer16BitsColorDepthID; } virtual KoColorSpace *clone() const; virtual void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const; virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; static QString colorSpaceId() { return "CMYKAU16"; } }; class CmykU16ColorSpaceFactory : public LcmsColorSpaceFactory { public: CmykU16ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_CMYKA_16, cmsSigCmykData) { } virtual bool userVisible() const { return true; } virtual QString id() const { return CmykU16ColorSpace::colorSpaceId(); } virtual QString name() const { return i18n("CMYK (16-bit integer/channel)"); } virtual KoID colorModelId() const { return CMYKAColorModelID; } virtual KoID colorDepthId() const { return Integer16BitsColorDepthID; } virtual int referenceDepth() const { return 16; } virtual KoColorSpace *createColorSpace(const KoColorProfile *p) const { return new CmykU16ColorSpace(name(), p->clone()); } virtual QString defaultProfile() const { return "Chemical proof"; } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/cmyk_u8/CmykU8ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/cmyk_u8/CmykU8ColorSpace.cpp index b4bd724494..0d906fb54b 100644 --- a/plugins/color/lcms2engine/colorspaces/cmyk_u8/CmykU8ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/cmyk_u8/CmykU8ColorSpace.cpp @@ -1,121 +1,130 @@ /* * 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 * Library 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 "CmykU8ColorSpace.h" #include #include #include #include "compositeops/KoCompositeOps.h" #include +#include CmykU8ColorSpace::CmykU8ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_CMYKA_8, cmsSigCmykData, p) { addChannel(new KoChannelInfo(i18n("Cyan"), 0 * sizeof(quint8), 0, KoChannelInfo::COLOR, KoChannelInfo::UINT8, sizeof(quint8), Qt::cyan)); addChannel(new KoChannelInfo(i18n("Magenta"), 1 * sizeof(quint8), 1, KoChannelInfo::COLOR, KoChannelInfo::UINT8, sizeof(quint8), Qt::magenta)); addChannel(new KoChannelInfo(i18n("Yellow"), 2 * sizeof(quint8), 2, KoChannelInfo::COLOR, KoChannelInfo::UINT8, sizeof(quint8), Qt::yellow)); addChannel(new KoChannelInfo(i18n("Black"), 3 * sizeof(quint8), 3, KoChannelInfo::COLOR, KoChannelInfo::UINT8, sizeof(quint8), Qt::black)); addChannel(new KoChannelInfo(i18n("Alpha"), 4 * sizeof(quint8), 4, KoChannelInfo::ALPHA, KoChannelInfo::UINT8, sizeof(quint8))); init(); addStandardCompositeOps(this); } bool CmykU8ColorSpace::willDegrade(ColorSpaceIndependence independence) const { if (independence == TO_RGBA8) { return true; } else { return false; } } KoColorSpace *CmykU8ColorSpace::clone() const { return new CmykU8ColorSpace(name(), profile()->clone()); } void CmykU8ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const CmykU8Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("CMYK"); - labElt.setAttribute("c", KoColorSpaceMaths< CmykU8Traits::channels_type, qreal>::scaleToA(p->cyan)); - labElt.setAttribute("m", KoColorSpaceMaths< CmykU8Traits::channels_type, qreal>::scaleToA(p->magenta)); - labElt.setAttribute("y", KoColorSpaceMaths< CmykU8Traits::channels_type, qreal>::scaleToA(p->yellow)); - labElt.setAttribute("k", KoColorSpaceMaths< CmykU8Traits::channels_type, qreal>::scaleToA(p->black)); + labElt.setAttribute("c", KisDomUtils::toString(KoColorSpaceMaths< CmykU8Traits::channels_type, qreal>::scaleToA(p->cyan))); + labElt.setAttribute("m", KisDomUtils::toString(KoColorSpaceMaths< CmykU8Traits::channels_type, qreal>::scaleToA(p->magenta))); + labElt.setAttribute("y", KisDomUtils::toString(KoColorSpaceMaths< CmykU8Traits::channels_type, qreal>::scaleToA(p->yellow))); + labElt.setAttribute("k", KisDomUtils::toString(KoColorSpaceMaths< CmykU8Traits::channels_type, qreal>::scaleToA(p->black))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void CmykU8ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { CmykU8Traits::Pixel *p = reinterpret_cast(pixel); - p->cyan = KoColorSpaceMaths< qreal, CmykU8Traits::channels_type >::scaleToA(elt.attribute("c").toDouble()); - p->magenta = KoColorSpaceMaths< qreal, CmykU8Traits::channels_type >::scaleToA(elt.attribute("m").toDouble()); - p->yellow = KoColorSpaceMaths< qreal, CmykU8Traits::channels_type >::scaleToA(elt.attribute("y").toDouble()); - p->black = KoColorSpaceMaths< qreal, CmykU8Traits::channels_type >::scaleToA(elt.attribute("k").toDouble()); + p->cyan = KoColorSpaceMaths< qreal, CmykU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("c"))); + p->magenta = KoColorSpaceMaths< qreal, CmykU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("m"))); + p->yellow = KoColorSpaceMaths< qreal, CmykU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("y"))); + p->black = KoColorSpaceMaths< qreal, CmykU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("k"))); p->alpha = KoColorSpaceMathsTraits::max; } -void CmykU8ColorSpace::toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const +void CmykU8ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { + qreal c0 = channelValues[0]; + qreal c1 = channelValues[1]; + qreal c2 = channelValues[2]; + qreal c3 = channelValues[3]; //we use HSI here because we can't linearise CMYK, and HSY doesn't work right with... - CMYKToCMY(&channelValues[0],&channelValues[1],&channelValues[2],&channelValues[3]); - channelValues[0] = 1.0-channelValues[0]; - channelValues[1] = 1.0-channelValues[1]; - channelValues[2] = 1.0-channelValues[2]; - RGBToHSI(channelValues[0],channelValues[1],channelValues[2], hue, sat, luma); + CMYKToCMY(&c0, &c1, &c2, &c3); + c0 = 1.0 - c0; + c1 = 1.0 - c1; + c2 = 1.0 - c2; + RGBToHSI(c0, c1, c2, hue, sat, luma); } QVector CmykU8ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(5); channelValues.fill(1.0); HSIToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[0] = qBound(0.0,1.0-channelValues[0],1.0); channelValues[1] = qBound(0.0,1.0-channelValues[1],1.0); channelValues[2] = qBound(0.0,1.0-channelValues[2],1.0); CMYToCMYK(&channelValues[0],&channelValues[1],&channelValues[2],&channelValues[3]); return channelValues; } -void CmykU8ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const +void CmykU8ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { - CMYKToCMY(&channelValues[0],&channelValues[1],&channelValues[2],&channelValues[3]); - channelValues[0] = 1.0-channelValues[0]; - channelValues[1] = 1.0-channelValues[1]; - channelValues[2] = 1.0-channelValues[2]; - RGBToYUV(channelValues[0],channelValues[1],channelValues[2], y, u, v, 0.33, 0.33, 0.33); + qreal c0 = channelValues[0]; + qreal c1 = channelValues[1]; + qreal c2 = channelValues[2]; + qreal c3 = channelValues[3]; + CMYKToCMY(&c0, &c1, &c2, &c3); + c0 = 1.0 - c0; + c1 = 1.0 - c1; + c2 = 1.0 - c2; + RGBToYUV(c0, c1, c2, y, u, v, 0.33, 0.33, 0.33); } QVector CmykU8ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(5); channelValues.fill(1.0); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2], 0.33, 0.33, 0.33); channelValues[0] = qBound(0.0,1.0-channelValues[0],1.0); channelValues[1] = qBound(0.0,1.0-channelValues[1],1.0); channelValues[2] = qBound(0.0,1.0-channelValues[2],1.0); CMYToCMYK(&channelValues[0],&channelValues[1],&channelValues[2],&channelValues[3]); return channelValues; } diff --git a/plugins/color/lcms2engine/colorspaces/cmyk_u8/CmykU8ColorSpace.h b/plugins/color/lcms2engine/colorspaces/cmyk_u8/CmykU8ColorSpace.h index e4c8801c67..d70e09528b 100644 --- a/plugins/color/lcms2engine/colorspaces/cmyk_u8/CmykU8ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/cmyk_u8/CmykU8ColorSpace.h @@ -1,114 +1,114 @@ /* * Copyright (c) 2006 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 * Library 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 KIS_STRATEGY_COLORSPACE_CMYK_U8_H_ #define KIS_STRATEGY_COLORSPACE_CMYK_U8_H_ #include #include #include "KoColorModelStandardIds.h" typedef KoCmykTraits CmykU8Traits; class CmykU8ColorSpace : public LcmsColorSpace { public: CmykU8ColorSpace(const QString &name, KoColorProfile *p); virtual bool willDegrade(ColorSpaceIndependence independence) const; virtual KoID colorModelId() const { return CMYKAColorModelID; } virtual KoID colorDepthId() const { return Integer8BitsColorDepthID; } virtual KoColorSpace *clone() const; virtual void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const; virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; static QString colorSpaceId() { return "CMYK"; } }; class CmykU8ColorSpaceFactory : public LcmsColorSpaceFactory { public: CmykU8ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_CMYK5_8, cmsSigCmykData) { } virtual bool userVisible() const { return true; } virtual QString id() const { return CmykU8ColorSpace::colorSpaceId(); } virtual QString name() const { return i18n("CMYK (8-bit integer/channel)"); } virtual KoID colorModelId() const { return CMYKAColorModelID; } virtual KoID colorDepthId() const { return Integer8BitsColorDepthID; } virtual int referenceDepth() const { return 8; } virtual KoColorSpace *createColorSpace(const KoColorProfile *p) const { return new CmykU8ColorSpace(name(), p->clone()); } virtual QString defaultProfile() const { return "Chemical proof"; } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/gray_f16/GrayF16ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/gray_f16/GrayF16ColorSpace.cpp index 8a06debcdd..bf415932ca 100644 --- a/plugins/color/lcms2engine/colorspaces/gray_f16/GrayF16ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/gray_f16/GrayF16ColorSpace.cpp @@ -1,89 +1,90 @@ /* * Copyright (c) 2006 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 "GrayF16ColorSpace.h" #include #include #include #include #include "compositeops/KoCompositeOps.h" +#include GrayF16ColorSpace::GrayF16ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_GRAYA_HALF_FLT, cmsSigGrayData, p) { const IccColorProfile *icc_p = dynamic_cast(p); Q_ASSERT(icc_p); Q_UNUSED(icc_p); addChannel(new KoChannelInfo(i18n("Gray"), 0 * sizeof(half), 0, KoChannelInfo::COLOR, KoChannelInfo::FLOAT16, 2, Qt::gray)); addChannel(new KoChannelInfo(i18n("Alpha"), 1 * sizeof(half), 1, KoChannelInfo::ALPHA, KoChannelInfo::FLOAT16, 2)); init(); addStandardCompositeOps(this); } KoColorSpace *GrayF16ColorSpace::clone() const { return new GrayF16ColorSpace(name(), profile()->clone()); } void GrayF16ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoGrayF16Traits::channels_type *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("Gray"); - labElt.setAttribute("g", KoColorSpaceMaths< KoGrayF16Traits::channels_type, qreal>::scaleToA(p[0])); + labElt.setAttribute("g", KisDomUtils::toString(KoColorSpaceMaths< KoGrayF16Traits::channels_type, qreal>::scaleToA(p[0]))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void GrayF16ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoGrayF16Traits::channels_type *p = reinterpret_cast(pixel); - p[0] = KoColorSpaceMaths< qreal, KoGrayF16Traits::channels_type >::scaleToA(elt.attribute("g").toDouble()); + p[0] = KoColorSpaceMaths< qreal, KoGrayF16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("g"))); p[1] = 1.0; } -void GrayF16ColorSpace::toHSY(QVector channelValues, qreal *, qreal *, qreal *luma) const +void GrayF16ColorSpace::toHSY(const QVector &channelValues, qreal *, qreal *, qreal *luma) const { *luma = channelValues[0]; } QVector GrayF16ColorSpace::fromHSY(qreal *, qreal *, qreal *luma) const { QVector channelValues(2); channelValues.fill(*luma); channelValues[1]=1.0; return channelValues; } -void GrayF16ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *, qreal *) const +void GrayF16ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *, qreal *) const { *y = channelValues[0]; } QVector GrayF16ColorSpace::fromYUV(qreal *y, qreal *, qreal *) const { QVector channelValues(2); channelValues.fill(*y); channelValues[1]=1.0; return channelValues; } diff --git a/plugins/color/lcms2engine/colorspaces/gray_f16/GrayF16ColorSpace.h b/plugins/color/lcms2engine/colorspaces/gray_f16/GrayF16ColorSpace.h index 01ad527e30..e99cf7afc2 100644 --- a/plugins/color/lcms2engine/colorspaces/gray_f16/GrayF16ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/gray_f16/GrayF16ColorSpace.h @@ -1,125 +1,125 @@ /* * Copyright (c) 2004-2006 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 COLORSPACE_GRAYSCALE_F16_H_ #define COLORSPACE_GRAYSCALE_F16_H_ #include #include #include "LcmsColorSpace.h" #define TYPE_GRAYA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)) struct KoGrayF16Traits; class GrayF16ColorSpace : public LcmsColorSpace { public: GrayF16ColorSpace(const QString &name, KoColorProfile *p); virtual bool willDegrade(ColorSpaceIndependence) const { return false; } virtual KoID colorModelId() const { return GrayAColorModelID; } virtual KoID colorDepthId() const { return Float16BitsColorDepthID; } virtual KoColorSpace *clone() const; virtual void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const; virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; static QString colorSpaceId() { return "GRAYAF16"; } virtual bool hasHighDynamicRange() const { return true; } }; class GrayF16ColorSpaceFactory : public LcmsColorSpaceFactory { public: GrayF16ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_GRAYA_HALF_FLT, cmsSigGrayData) { } virtual QString id() const { return GrayF16ColorSpace::colorSpaceId(); } virtual QString name() const { return i18n("Grayscale/Alpha (16-bit float/channel)"); } virtual KoID colorModelId() const { return GrayAColorModelID; } virtual KoID colorDepthId() const { return Float16BitsColorDepthID; } virtual int referenceDepth() const { return 16; } virtual bool userVisible() const { return true; } virtual KoColorSpace *createColorSpace(const KoColorProfile *p) const { return new GrayF16ColorSpace(name(), p->clone()); } virtual QString defaultProfile() const { return "gray built-in"; } virtual bool isHdr() const { return true; } }; #endif // KIS_STRATEGY_COLORSPACE_GRAYSCALE_H_ diff --git a/plugins/color/lcms2engine/colorspaces/gray_f32/GrayF32ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/gray_f32/GrayF32ColorSpace.cpp index 41ab59332d..66aedf2d8d 100644 --- a/plugins/color/lcms2engine/colorspaces/gray_f32/GrayF32ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/gray_f32/GrayF32ColorSpace.cpp @@ -1,92 +1,93 @@ /* * Copyright (c) 2006 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 "GrayF32ColorSpace.h" #include #include #include #include #include #include "compositeops/KoCompositeOps.h" +#include GrayF32ColorSpace::GrayF32ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_GRAYA_FLT, cmsSigGrayData, p) { const IccColorProfile *icc_p = dynamic_cast(p); Q_ASSERT(icc_p); QVector uiRanges(icc_p->getFloatUIMinMax()); Q_ASSERT(uiRanges.size() == 1); addChannel(new KoChannelInfo(i18n("Gray"), 0 * sizeof(float), 0, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, sizeof(float), Qt::gray, uiRanges[0])); addChannel(new KoChannelInfo(i18n("Alpha"), 1 * sizeof(float), 1, KoChannelInfo::ALPHA, KoChannelInfo::FLOAT32, sizeof(float))); init(); addStandardCompositeOps(this); } KoColorSpace *GrayF32ColorSpace::clone() const { return new GrayF32ColorSpace(name(), profile()->clone()); } void GrayF32ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoGrayF32Traits::channels_type *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("Gray"); - labElt.setAttribute("g", KoColorSpaceMaths< KoGrayF32Traits::channels_type, qreal>::scaleToA(p[0])); + labElt.setAttribute("g", KisDomUtils::toString(KoColorSpaceMaths< KoGrayF32Traits::channels_type, qreal>::scaleToA(p[0]))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void GrayF32ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoGrayF32Traits::channels_type *p = reinterpret_cast(pixel); - p[0] = KoColorSpaceMaths< qreal, KoGrayF32Traits::channels_type >::scaleToA(elt.attribute("g").toDouble()); + p[0] = KoColorSpaceMaths< qreal, KoGrayF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("g"))); p[1] = 1.0; } -void GrayF32ColorSpace::toHSY(QVector channelValues, qreal *, qreal *, qreal *luma) const +void GrayF32ColorSpace::toHSY(const QVector &channelValues, qreal *, qreal *, qreal *luma) const { *luma = channelValues[0]; } QVector GrayF32ColorSpace::fromHSY(qreal *, qreal *, qreal *luma) const { QVector channelValues(2); channelValues.fill(*luma); channelValues[1]=1.0; return channelValues; } -void GrayF32ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *, qreal *) const +void GrayF32ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *, qreal *) const { *y = channelValues[0]; } QVector GrayF32ColorSpace::fromYUV(qreal *y, qreal *, qreal *) const { QVector channelValues(2); channelValues.fill(*y); channelValues[1]=1.0; return channelValues; -} \ No newline at end of file +} diff --git a/plugins/color/lcms2engine/colorspaces/gray_f32/GrayF32ColorSpace.h b/plugins/color/lcms2engine/colorspaces/gray_f32/GrayF32ColorSpace.h index c28b327120..f5c07f2f8d 100644 --- a/plugins/color/lcms2engine/colorspaces/gray_f32/GrayF32ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/gray_f32/GrayF32ColorSpace.h @@ -1,125 +1,125 @@ /* * Copyright (c) 2004-2006 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 COLORSPACE_GRAYSCALE_F32_H_ #define COLORSPACE_GRAYSCALE_F32_H_ #include #include #include "LcmsColorSpace.h" #define TYPE_GRAYA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(4)) struct KoGrayF32Traits; class GrayF32ColorSpace : public LcmsColorSpace { public: GrayF32ColorSpace(const QString &name, KoColorProfile *p); virtual bool willDegrade(ColorSpaceIndependence) const { return false; } virtual KoID colorModelId() const { return GrayAColorModelID; } virtual KoID colorDepthId() const { return Float32BitsColorDepthID; } virtual KoColorSpace *clone() const; virtual void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const; virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; static QString colorSpaceId() { return "GRAYAF32"; } virtual bool hasHighDynamicRange() const { return true; } }; class GrayF32ColorSpaceFactory : public LcmsColorSpaceFactory { public: GrayF32ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_GRAYA_FLT, cmsSigGrayData) { } virtual QString id() const { return GrayF32ColorSpace::colorSpaceId(); } virtual QString name() const { return i18n("Grayscale/Alpha (32-bit float/channel)"); } virtual KoID colorModelId() const { return GrayAColorModelID; } virtual KoID colorDepthId() const { return Float32BitsColorDepthID; } virtual int referenceDepth() const { return 32; } virtual bool userVisible() const { return true; } virtual KoColorSpace *createColorSpace(const KoColorProfile *p) const { return new GrayF32ColorSpace(name(), p->clone()); } virtual QString defaultProfile() const { return "gray built-in"; } virtual bool isHdr() const { return true; } }; #endif // KIS_STRATEGY_COLORSPACE_GRAYSCALE_H_ diff --git a/plugins/color/lcms2engine/colorspaces/gray_u16/GrayU16ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/gray_u16/GrayU16ColorSpace.cpp index fd3519d79b..c0a7132b0e 100644 --- a/plugins/color/lcms2engine/colorspaces/gray_u16/GrayU16ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/gray_u16/GrayU16ColorSpace.cpp @@ -1,87 +1,88 @@ /* * Copyright (c) 2006 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 "GrayU16ColorSpace.h" #include #include #include #include #include #include "compositeops/KoCompositeOps.h" +#include GrayAU16ColorSpace::GrayAU16ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_GRAYA_16, cmsSigGrayData, p) { addChannel(new KoChannelInfo(i18n("Gray"), 0 * sizeof(quint16), 0, KoChannelInfo::COLOR, KoChannelInfo::UINT16)); addChannel(new KoChannelInfo(i18n("Alpha"), 1 * sizeof(quint16), 1, KoChannelInfo::ALPHA, KoChannelInfo::UINT16)); init(); addStandardCompositeOps(this); } KoColorSpace *GrayAU16ColorSpace::clone() const { return new GrayAU16ColorSpace(name(), profile()->clone()); } void GrayAU16ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const GrayAU16Traits::channels_type *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("Gray"); - labElt.setAttribute("g", KoColorSpaceMaths< GrayAU16Traits::channels_type, qreal>::scaleToA(p[0])); + labElt.setAttribute("g", KisDomUtils::toString(KoColorSpaceMaths< GrayAU16Traits::channels_type, qreal>::scaleToA(p[0]))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void GrayAU16ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { GrayAU16Traits::channels_type *p = reinterpret_cast(pixel); - p[0] = KoColorSpaceMaths< qreal, GrayAU16Traits::channels_type >::scaleToA(elt.attribute("g").toDouble()); + p[0] = KoColorSpaceMaths< qreal, GrayAU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("g"))); p[1] = KoColorSpaceMathsTraits::max; } -void GrayAU16ColorSpace::toHSY(QVector channelValues, qreal *, qreal *, qreal *luma) const +void GrayAU16ColorSpace::toHSY(const QVector &channelValues, qreal *, qreal *, qreal *luma) const { *luma = channelValues[0]; } QVector GrayAU16ColorSpace::fromHSY(qreal *, qreal *, qreal *luma) const { QVector channelValues(2); channelValues.fill(*luma); channelValues[1]=1.0; return channelValues; } -void GrayAU16ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *, qreal *) const +void GrayAU16ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *, qreal *) const { *y = channelValues[0]; } QVector GrayAU16ColorSpace::fromYUV(qreal *y, qreal *, qreal *) const { QVector channelValues(2); channelValues.fill(*y); channelValues[1]=1.0; return channelValues; -} \ No newline at end of file +} diff --git a/plugins/color/lcms2engine/colorspaces/gray_u16/GrayU16ColorSpace.h b/plugins/color/lcms2engine/colorspaces/gray_u16/GrayU16ColorSpace.h index b97e8f58a4..cbe71f5837 100644 --- a/plugins/color/lcms2engine/colorspaces/gray_u16/GrayU16ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/gray_u16/GrayU16ColorSpace.h @@ -1,114 +1,114 @@ /* * Copyright (c) 2004-2006 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_COLORSPACE_GRAYSCALE_U16_H_ #define KIS_COLORSPACE_GRAYSCALE_U16_H_ #include #include "LcmsColorSpace.h" #include #include "KoColorModelStandardIds.h" typedef KoColorSpaceTrait GrayAU16Traits; class GrayAU16ColorSpace : public LcmsColorSpace { public: GrayAU16ColorSpace(const QString &name, KoColorProfile *p); virtual bool willDegrade(ColorSpaceIndependence) const { return false; } virtual KoID colorModelId() const { return GrayAColorModelID; } virtual KoID colorDepthId() const { return Integer16BitsColorDepthID; } virtual KoColorSpace *clone() const; virtual void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const; virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; static QString colorSpaceId() { return "GRAYAU16"; } }; class GrayAU16ColorSpaceFactory : public LcmsColorSpaceFactory { public: GrayAU16ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_GRAYA_16, cmsSigGrayData) { } virtual QString id() const { return GrayAU16ColorSpace::colorSpaceId(); } virtual QString name() const { return i18n("Grayscale (16-bit integer/channel)"); } virtual KoID colorModelId() const { return GrayAColorModelID; } virtual KoID colorDepthId() const { return Integer16BitsColorDepthID; } virtual int referenceDepth() const { return 16; } virtual bool userVisible() const { return true; } virtual KoColorSpace *createColorSpace(const KoColorProfile *p) const { return new GrayAU16ColorSpace(name(), p->clone()); } virtual QString defaultProfile() const { return "gray built-in"; } }; #endif // KIS_STRATEGY_COLORSPACE_GRAYSCALE_H_ diff --git a/plugins/color/lcms2engine/colorspaces/gray_u8/GrayU8ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/gray_u8/GrayU8ColorSpace.cpp index 9d8bb7881f..439dbd4bb7 100644 --- a/plugins/color/lcms2engine/colorspaces/gray_u8/GrayU8ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/gray_u8/GrayU8ColorSpace.cpp @@ -1,86 +1,87 @@ /* * Copyright (c) 2006 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 #include #include #include #include #include #include "compositeops/KoCompositeOps.h" +#include GrayAU8ColorSpace::GrayAU8ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_GRAYA_8, cmsSigGrayData, p) { addChannel(new KoChannelInfo(i18n("Gray"), 0, 0, KoChannelInfo::COLOR, KoChannelInfo::UINT8)); addChannel(new KoChannelInfo(i18n("Alpha"), 1, 1, KoChannelInfo::ALPHA, KoChannelInfo::UINT8)); init(); addStandardCompositeOps(this); } KoColorSpace *GrayAU8ColorSpace::clone() const { return new GrayAU8ColorSpace(name(), profile()->clone()); } void GrayAU8ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const GrayAU8Traits::channels_type *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("Gray"); - labElt.setAttribute("g", KoColorSpaceMaths< GrayAU8Traits::channels_type, qreal>::scaleToA(p[0])); + labElt.setAttribute("g", KisDomUtils::toString(KoColorSpaceMaths< GrayAU8Traits::channels_type, qreal>::scaleToA(p[0]))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void GrayAU8ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { GrayAU8Traits::channels_type *p = reinterpret_cast(pixel); - p[0] = KoColorSpaceMaths< qreal, GrayAU8Traits::channels_type >::scaleToA(elt.attribute("g").toDouble()); + p[0] = KoColorSpaceMaths< qreal, GrayAU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("g"))); p[1] = KoColorSpaceMathsTraits::max; } -void GrayAU8ColorSpace::toHSY(QVector channelValues, qreal *, qreal *, qreal *luma) const +void GrayAU8ColorSpace::toHSY(const QVector &channelValues, qreal *, qreal *, qreal *luma) const { *luma = channelValues[0]; } QVector GrayAU8ColorSpace::fromHSY(qreal *, qreal *, qreal *luma) const { QVector channelValues(2); channelValues.fill(*luma); channelValues[1]=1.0; return channelValues; } -void GrayAU8ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *, qreal *) const +void GrayAU8ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *, qreal *) const { *y = channelValues[0]; } QVector GrayAU8ColorSpace::fromYUV(qreal *y, qreal *, qreal *) const { QVector channelValues(2); channelValues.fill(*y); channelValues[1]=1.0; return channelValues; -} \ No newline at end of file +} diff --git a/plugins/color/lcms2engine/colorspaces/gray_u8/GrayU8ColorSpace.h b/plugins/color/lcms2engine/colorspaces/gray_u8/GrayU8ColorSpace.h index 33f68b2517..40b245b10b 100644 --- a/plugins/color/lcms2engine/colorspaces/gray_u8/GrayU8ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/gray_u8/GrayU8ColorSpace.h @@ -1,116 +1,116 @@ /* * Copyright (c) 2004-2006 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_GRAY_COLORSPACE_H_ #define KIS_GRAY_COLORSPACE_H_ #include #include #include #include "KoColorModelStandardIds.h" typedef KoColorSpaceTrait GrayAU8Traits; class GrayAU8ColorSpace : public LcmsColorSpace { public: GrayAU8ColorSpace(const QString &name, KoColorProfile *p); virtual bool willDegrade(ColorSpaceIndependence) const { return false; } virtual KoID colorModelId() const { return GrayAColorModelID; } virtual KoID colorDepthId() const { return Integer8BitsColorDepthID; } virtual KoColorSpace *clone() const; virtual void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const; virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; static QString colorSpaceId() { return "GRAYA"; } }; class GrayAU8ColorSpaceFactory : public LcmsColorSpaceFactory { public: GrayAU8ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_GRAYA_8, cmsSigGrayData) { } virtual QString id() const { return GrayAU8ColorSpace::colorSpaceId(); } virtual QString name() const { return i18n("Grayscale (8-bit integer/channel)"); } virtual KoID colorModelId() const { return GrayAColorModelID; } virtual KoID colorDepthId() const { return Integer8BitsColorDepthID; } virtual int referenceDepth() const { return 8; } virtual bool userVisible() const { return true; } virtual KoColorSpace *createColorSpace(const KoColorProfile *p) const { return new GrayAU8ColorSpace(name(), p->clone()); } virtual QString defaultProfile() const { return "gray built-in"; } }; #endif // KIS_STRATEGY_COLORSPACE_GRAYSCALE_H_ diff --git a/plugins/color/lcms2engine/colorspaces/gray_u8/calligragrayaplugin.json b/plugins/color/lcms2engine/colorspaces/gray_u8/calligragrayaplugin.json deleted file mode 100644 index 4f17e82cda..0000000000 --- a/plugins/color/lcms2engine/colorspaces/gray_u8/calligragrayaplugin.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "Id": "Grayscale Color Model with Alpha", - "X-Flake-PluginVersion": "28", - "X-KDE-Library": "calligragrayau8plugin", - "X-KDE-PluginInfo-Name": "calligragrayau8plugin", - "X-KDE-ServiceTypes": [ - "Calligra/ColorSpace" - ], - "X-Pigment-MinVersion": "28" -} diff --git a/plugins/color/lcms2engine/colorspaces/gray_u8/templates/.directory b/plugins/color/lcms2engine/colorspaces/gray_u8/templates/.directory deleted file mode 100644 index 00c98bb9f4..0000000000 --- a/plugins/color/lcms2engine/colorspaces/gray_u8/templates/.directory +++ /dev/null @@ -1,60 +0,0 @@ -[Desktop Entry] -Name=Grayscale -Name[bg]=Степени на сивото -Name[br]=SkeulLouet -Name[bs]=Sivi tonovi -Name[ca]=Escala de grisos -Name[ca@valencia]=Escala de grisos -Name[cs]=Odstíny šedé -Name[cy]=Graddlwyd -Name[da]=Gråskala -Name[de]=Graustufen -Name[el]=Διαβάθμιση του γκρι -Name[en_GB]=Greyscale -Name[eo]=Grizoskalo -Name[es]=Escala de grises -Name[et]=Halltoonid -Name[eu]=Gris-eskala -Name[fa]=مقیاس خاکستری -Name[fi]=Harmaasävy -Name[fr]=Niveaux de gris -Name[fy]=Griiswearden -Name[ga]=Liathscála -Name[gl]=Escala de grises -Name[he]=גווני אפור -Name[hi]=श्वेत-श्याम -Name[hne]=काला-सफेद -Name[hu]=Szürkeárnyalatok -Name[ia]=Scala de gris -Name[is]=Gráskali -Name[it]=Scala di grigio -Name[ja]=グレースケール -Name[kk]=Сұр реңкті -Name[ko]=그레이스케일 -Name[lv]=Pelēktoņu -Name[mai]=ग्रेस्केल -Name[mr]=करडी रंगछटा -Name[ms]=Skala Kelabu -Name[nb]=Gråtoner -Name[nds]=Griestöön -Name[ne]=ग्रेस्केल -Name[nl]=Grijswaarden -Name[oc]=Escala de grises -Name[pl]=Skala szarości -Name[pt]=Tons de Cinzento -Name[pt_BR]=Tons de Cinza -Name[ru]=Градации серого -Name[sk]=Čiernobiely -Name[sl]=Sivinska -Name[sv]=Gråskala -Name[ta]=பழுப்புநிற வண்ணம் -Name[tr]=Gri ton -Name[ug]=كۈلرەڭ -Name[uk]=Градації сірого -Name[uz]=Oq-qora -Name[uz@cyrillic]=Оқ-қора -Name[wa]=Liveas d' gris -Name[x-test]=xxGrayscalexx -Name[zh_CN]=灰度 -Name[zh_TW]=灰階 -X-KDE-DefaultTab=true diff --git a/plugins/color/lcms2engine/colorspaces/lab_f32/LabF32ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/lab_f32/LabF32ColorSpace.cpp index b347a0e038..e682e303b9 100644 --- a/plugins/color/lcms2engine/colorspaces/lab_f32/LabF32ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/lab_f32/LabF32ColorSpace.cpp @@ -1,108 +1,109 @@ /* * Copyright (c) 2006 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 * Library 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 "LabF32ColorSpace.h" #include #include #include "../compositeops/KoCompositeOps.h" -#include "KoColorConversions.h" +#include +#include LabF32ColorSpace::LabF32ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_LabA_FLT, cmsSigLabData, p) { const IccColorProfile *icc_p = dynamic_cast(p); Q_ASSERT(icc_p); QVector uiRanges(icc_p->getFloatUIMinMax()); Q_ASSERT(uiRanges.size() == 3); addChannel(new KoChannelInfo(i18n("Lightness"), 0 * sizeof(float), 0, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, sizeof(float), QColor(100, 100, 100), uiRanges[0])); addChannel(new KoChannelInfo(i18n("a*"), 1 * sizeof(float), 1, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, sizeof(float), QColor(150, 150, 150), uiRanges[1])); addChannel(new KoChannelInfo(i18n("b*"), 2 * sizeof(float), 2, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, sizeof(float), QColor(200, 200, 200), uiRanges[2])); addChannel(new KoChannelInfo(i18n("Alpha"), 3 * sizeof(float), 3, KoChannelInfo::ALPHA, KoChannelInfo::FLOAT32, sizeof(float))); init(); addStandardCompositeOps(this); } bool LabF32ColorSpace::willDegrade(ColorSpaceIndependence independence) const { if (independence == TO_RGBA16) { return true; } else { return false; } } KoColorSpace *LabF32ColorSpace::clone() const { return new LabF32ColorSpace(name(), profile()->clone()); } void LabF32ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoLabF32Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("Lab"); - labElt.setAttribute("L", KoColorSpaceMaths< KoLabF32Traits::channels_type, qreal>::scaleToA(p->L)); - labElt.setAttribute("a", KoColorSpaceMaths< KoLabF32Traits::channels_type, qreal>::scaleToA(p->a)); - labElt.setAttribute("b", KoColorSpaceMaths< KoLabF32Traits::channels_type, qreal>::scaleToA(p->b)); + labElt.setAttribute("L", KisDomUtils::toString(KoColorSpaceMaths< KoLabF32Traits::channels_type, qreal>::scaleToA(p->L))); + labElt.setAttribute("a", KisDomUtils::toString(KoColorSpaceMaths< KoLabF32Traits::channels_type, qreal>::scaleToA(p->a))); + labElt.setAttribute("b", KisDomUtils::toString(KoColorSpaceMaths< KoLabF32Traits::channels_type, qreal>::scaleToA(p->b))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void LabF32ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoLabF32Traits::Pixel *p = reinterpret_cast(pixel); - p->L = KoColorSpaceMaths< qreal, KoLabF32Traits::channels_type >::scaleToA(elt.attribute("L").toDouble()); - p->a = KoColorSpaceMaths< qreal, KoLabF32Traits::channels_type >::scaleToA(elt.attribute("a").toDouble()); - p->b = KoColorSpaceMaths< qreal, KoLabF32Traits::channels_type >::scaleToA(elt.attribute("b").toDouble()); + p->L = KoColorSpaceMaths< qreal, KoLabF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("L"))); + p->a = KoColorSpaceMaths< qreal, KoLabF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("a"))); + p->b = KoColorSpaceMaths< qreal, KoLabF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("b"))); p->alpha = 1.0; } -void LabF32ColorSpace::toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const +void LabF32ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { LabToLCH(channelValues[0],channelValues[1],channelValues[2], luma, sat, hue); } QVector LabF32ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); LCHToLab(*luma, *sat, *hue, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } -void LabF32ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const +void LabF32ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { *y =channelValues[0]; *u=channelValues[1]; *v=channelValues[2]; } QVector LabF32ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); channelValues[0]=*y; channelValues[1]=*u; channelValues[2]=*v; channelValues[3]=1.0; return channelValues; -} \ No newline at end of file +} diff --git a/plugins/color/lcms2engine/colorspaces/lab_f32/LabF32ColorSpace.h b/plugins/color/lcms2engine/colorspaces/lab_f32/LabF32ColorSpace.h index f69ec25515..9315c370ce 100644 --- a/plugins/color/lcms2engine/colorspaces/lab_f32/LabF32ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/lab_f32/LabF32ColorSpace.h @@ -1,122 +1,122 @@ /* * Copyright (c) 2006 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 * Library 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 LabF32ColorSpace_H_ #define LabF32ColorSpace_H_ #include "LcmsColorSpace.h" #include "KoColorModelStandardIds.h" // XXX: implement normalizedChannelValues? struct KoLabF32Traits; class LabF32ColorSpace : public LcmsColorSpace { public: LabF32ColorSpace(const QString &name, KoColorProfile *p); virtual bool willDegrade(ColorSpaceIndependence independence) const; static QString colorSpaceId() { return QString("LABAF32"); } virtual KoID colorModelId() const { return LABAColorModelID; } virtual KoID colorDepthId() const { return Float32BitsColorDepthID; } virtual KoColorSpace *clone() const; virtual void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const; virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; virtual bool hasHighDynamicRange() const { return true; } }; class LabF32ColorSpaceFactory : public LcmsColorSpaceFactory { public: LabF32ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_LabA_FLT, cmsSigLabData) { } virtual bool userVisible() const { return true; } virtual QString id() const { return LabF32ColorSpace::colorSpaceId(); } virtual QString name() const { return i18n("L*a*b* (32-bit float/channel)"); } virtual KoID colorModelId() const { return LABAColorModelID; } virtual KoID colorDepthId() const { return Float32BitsColorDepthID; } virtual int referenceDepth() const { return 32; } virtual KoColorSpace *createColorSpace(const KoColorProfile *p) const { return new LabF32ColorSpace(name(), p->clone()); } virtual QString defaultProfile() const { return "Lab identity built-in"; } virtual bool isHdr() const { return true; } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/lab_u16/LabColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/lab_u16/LabColorSpace.cpp index 3e56267fc5..64afdecfbb 100644 --- a/plugins/color/lcms2engine/colorspaces/lab_u16/LabColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/lab_u16/LabColorSpace.cpp @@ -1,124 +1,125 @@ /* * Copyright (c) 2006 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 * Library 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 "LabColorSpace.h" #include #include #include "../compositeops/KoCompositeOps.h" -#include "KoColorConversions.h" +#include +#include LabU16ColorSpace::LabU16ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_LABA_16, cmsSigLabData, p) { addChannel(new KoChannelInfo(i18n("Lightness"), 0 * sizeof(quint16), 0, KoChannelInfo::COLOR, KoChannelInfo::UINT16, sizeof(quint16), QColor(100, 100, 100))); addChannel(new KoChannelInfo(i18n("a*"), 1 * sizeof(quint16), 1, KoChannelInfo::COLOR, KoChannelInfo::UINT16, sizeof(quint16), QColor(150, 150, 150))); addChannel(new KoChannelInfo(i18n("b*"), 2 * sizeof(quint16), 2, KoChannelInfo::COLOR, KoChannelInfo::UINT16, sizeof(quint16), QColor(200, 200, 200))); addChannel(new KoChannelInfo(i18n("Alpha"), 3 * sizeof(quint16), 3, KoChannelInfo::ALPHA, KoChannelInfo::UINT16, sizeof(quint16))); init(); addStandardCompositeOps(this); } bool LabU16ColorSpace::willDegrade(ColorSpaceIndependence independence) const { if (independence == TO_RGBA8) { return true; } else { return false; } } QString LabU16ColorSpace::normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) const { const KoLabU16Traits::channels_type *pix = reinterpret_cast(pixel); Q_ASSERT(channelIndex < channelCount()); // These convert from lcms encoded format to standard ranges. switch (channelIndex) { case 0: return QString().setNum(100.0 * static_cast(pix[0]) / MAX_CHANNEL_L); case 1: return QString().setNum(100.0 * ((static_cast(pix[1]) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB)); case 2: return QString().setNum(100.0 * ((static_cast(pix[2]) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB)); case 3: return QString().setNum(100.0 * static_cast(pix[3]) / UINT16_MAX); default: return QString("Error"); } } KoColorSpace *LabU16ColorSpace::clone() const { return new LabU16ColorSpace(name(), profile()->clone()); } void LabU16ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoLabU16Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("Lab"); - labElt.setAttribute("L", KoColorSpaceMaths< KoLabU16Traits::channels_type, qreal>::scaleToA(p->L)); - labElt.setAttribute("a", KoColorSpaceMaths< KoLabU16Traits::channels_type, qreal>::scaleToA(p->a)); - labElt.setAttribute("b", KoColorSpaceMaths< KoLabU16Traits::channels_type, qreal>::scaleToA(p->b)); + labElt.setAttribute("L", KisDomUtils::toString(KoColorSpaceMaths< KoLabU16Traits::channels_type, qreal>::scaleToA(p->L))); + labElt.setAttribute("a", KisDomUtils::toString(KoColorSpaceMaths< KoLabU16Traits::channels_type, qreal>::scaleToA(p->a))); + labElt.setAttribute("b", KisDomUtils::toString(KoColorSpaceMaths< KoLabU16Traits::channels_type, qreal>::scaleToA(p->b))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void LabU16ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoLabU16Traits::Pixel *p = reinterpret_cast(pixel); - p->L = KoColorSpaceMaths< qreal, KoLabU16Traits::channels_type >::scaleToA(elt.attribute("L").toDouble()); - p->a = KoColorSpaceMaths< qreal, KoLabU16Traits::channels_type >::scaleToA(elt.attribute("a").toDouble()); - p->b = KoColorSpaceMaths< qreal, KoLabU16Traits::channels_type >::scaleToA(elt.attribute("b").toDouble()); + p->L = KoColorSpaceMaths< qreal, KoLabU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("L"))); + p->a = KoColorSpaceMaths< qreal, KoLabU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("a"))); + p->b = KoColorSpaceMaths< qreal, KoLabU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("b"))); p->alpha = KoColorSpaceMathsTraits::max; } -void LabU16ColorSpace::toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const +void LabU16ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { LabToLCH(channelValues[0],channelValues[1],channelValues[2], luma, sat, hue); } QVector LabU16ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); LCHToLab(*luma, *sat, *hue, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } -void LabU16ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const +void LabU16ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { *y =channelValues[0]; *u=channelValues[1]; *v=channelValues[2]; } QVector LabU16ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); channelValues[0]=*y; channelValues[1]=*u; channelValues[2]=*v; channelValues[3]=1.0; return channelValues; -} \ No newline at end of file +} diff --git a/plugins/color/lcms2engine/colorspaces/lab_u16/LabColorSpace.h b/plugins/color/lcms2engine/colorspaces/lab_u16/LabColorSpace.h index b0e422694d..3e4b297032 100644 --- a/plugins/color/lcms2engine/colorspaces/lab_u16/LabColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/lab_u16/LabColorSpace.h @@ -1,120 +1,120 @@ /* * Copyright (c) 2006 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 * Library 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 LabU16ColorSpace_H_ #define LabU16ColorSpace_H_ #include "LcmsColorSpace.h" #include "KoColorModelStandardIds.h" #define TYPE_LABA_16 (COLORSPACE_SH(PT_Lab) | CHANNELS_SH(3) | BYTES_SH(2) | EXTRA_SH(1)) struct KoLabF32Traits; class LabU16ColorSpace : public LcmsColorSpace { public: LabU16ColorSpace(const QString &name, KoColorProfile *p); virtual bool willDegrade(ColorSpaceIndependence independence) const; virtual QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) const; static QString colorSpaceId() { return QString("LABA"); } virtual KoID colorModelId() const { return LABAColorModelID; } virtual KoID colorDepthId() const { return Integer16BitsColorDepthID; } virtual KoColorSpace *clone() const; virtual void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const; virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; private: static const quint32 MAX_CHANNEL_L = 0xff00; static const quint32 MAX_CHANNEL_AB = 0xffff; static const quint32 CHANNEL_AB_ZERO_OFFSET = 0x8000; }; class LabU16ColorSpaceFactory : public LcmsColorSpaceFactory { public: LabU16ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_LABA_16, cmsSigLabData) { } virtual bool userVisible() const { return true; } virtual QString id() const { return LabU16ColorSpace::colorSpaceId(); } virtual QString name() const { return i18n("L*a*b* (16-bit integer/channel)"); } virtual KoID colorModelId() const { return LABAColorModelID; } virtual KoID colorDepthId() const { return Integer16BitsColorDepthID; } virtual int referenceDepth() const { return 16; } virtual KoColorSpace *createColorSpace(const KoColorProfile *p) const { return new LabU16ColorSpace(name(), p->clone()); } virtual QString defaultProfile() const { return "Lab identity built-in"; } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/lab_u8/LabU8ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/lab_u8/LabU8ColorSpace.cpp index afa6bfebf2..ec762f40f9 100644 --- a/plugins/color/lcms2engine/colorspaces/lab_u8/LabU8ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/lab_u8/LabU8ColorSpace.cpp @@ -1,120 +1,121 @@ /* * Copyright (c) 2006 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 * Library 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 "LabU8ColorSpace.h" #include #include #include "../compositeops/KoCompositeOps.h" -#include "KoColorConversions.h" +#include +#include LabU8ColorSpace::LabU8ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_LABA_8, cmsSigLabData, p) { addChannel(new KoChannelInfo(i18n("Lightness"), 0 * sizeof(quint8), 0, KoChannelInfo::COLOR, KoChannelInfo::UINT8, sizeof(quint8), QColor(100, 100, 100))); addChannel(new KoChannelInfo(i18n("a*"), 1 * sizeof(quint8), 1, KoChannelInfo::COLOR, KoChannelInfo::UINT8, sizeof(quint8), QColor(150, 150, 150))); addChannel(new KoChannelInfo(i18n("b*"), 2 * sizeof(quint8), 2, KoChannelInfo::COLOR, KoChannelInfo::UINT8, sizeof(quint8), QColor(200, 200, 200))); addChannel(new KoChannelInfo(i18n("Alpha"), 3 * sizeof(quint8), 3, KoChannelInfo::ALPHA, KoChannelInfo::UINT8, sizeof(quint8))); init(); addStandardCompositeOps(this); } bool LabU8ColorSpace::willDegrade(ColorSpaceIndependence /*independence*/) const { return false; } QString LabU8ColorSpace::normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) const { const KoLabU8Traits::channels_type *pix = reinterpret_cast(pixel); Q_ASSERT(channelIndex < channelCount()); // These convert from lcms encoded format to standard ranges. switch (channelIndex) { case 0: return QString().setNum(100.0 * static_cast(pix[0]) / MAX_CHANNEL_L); case 1: return QString().setNum(100.0 * ((static_cast(pix[1]) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB)); case 2: return QString().setNum(100.0 * ((static_cast(pix[2]) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB)); case 3: return QString().setNum(100.0 * static_cast(pix[3]) / UINT8_MAX); default: return QString("Error"); } } KoColorSpace *LabU8ColorSpace::clone() const { return new LabU8ColorSpace(name(), profile()->clone()); } void LabU8ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoLabU8Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("Lab"); - labElt.setAttribute("L", KoColorSpaceMaths< KoLabU8Traits::channels_type, qreal>::scaleToA(p->L)); - labElt.setAttribute("a", KoColorSpaceMaths< KoLabU8Traits::channels_type, qreal>::scaleToA(p->a)); - labElt.setAttribute("b", KoColorSpaceMaths< KoLabU8Traits::channels_type, qreal>::scaleToA(p->b)); + labElt.setAttribute("L", KisDomUtils::toString(KoColorSpaceMaths< KoLabU8Traits::channels_type, qreal>::scaleToA(p->L))); + labElt.setAttribute("a", KisDomUtils::toString(KoColorSpaceMaths< KoLabU8Traits::channels_type, qreal>::scaleToA(p->a))); + labElt.setAttribute("b", KisDomUtils::toString(KoColorSpaceMaths< KoLabU8Traits::channels_type, qreal>::scaleToA(p->b))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void LabU8ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoLabU8Traits::Pixel *p = reinterpret_cast(pixel); - p->L = KoColorSpaceMaths< qreal, KoLabU8Traits::channels_type >::scaleToA(elt.attribute("L").toDouble()); - p->a = KoColorSpaceMaths< qreal, KoLabU8Traits::channels_type >::scaleToA(elt.attribute("a").toDouble()); - p->b = KoColorSpaceMaths< qreal, KoLabU8Traits::channels_type >::scaleToA(elt.attribute("b").toDouble()); + p->L = KoColorSpaceMaths< qreal, KoLabU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("L"))); + p->a = KoColorSpaceMaths< qreal, KoLabU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("a"))); + p->b = KoColorSpaceMaths< qreal, KoLabU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("b"))); p->alpha = KoColorSpaceMathsTraits::max; } -void LabU8ColorSpace::toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const +void LabU8ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { LabToLCH(channelValues[0],channelValues[1],channelValues[2], luma, sat, hue); } QVector LabU8ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); LCHToLab(*luma, *sat, *hue, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } -void LabU8ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const +void LabU8ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { *y =channelValues[0]; *u=channelValues[1]; *v=channelValues[2]; } QVector LabU8ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); channelValues[0]=*y; channelValues[1]=*u; channelValues[2]=*v; channelValues[3]=1.0; return channelValues; -} \ No newline at end of file +} diff --git a/plugins/color/lcms2engine/colorspaces/lab_u8/LabU8ColorSpace.h b/plugins/color/lcms2engine/colorspaces/lab_u8/LabU8ColorSpace.h index 52b2afc5d9..7545823ee3 100644 --- a/plugins/color/lcms2engine/colorspaces/lab_u8/LabU8ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/lab_u8/LabU8ColorSpace.h @@ -1,112 +1,112 @@ /* * Copyright (c) 2006 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 * Library 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 LabU8ColorSpace_H_ #define LabU8ColorSpace_H_ #include "LcmsColorSpace.h" #include "KoColorModelStandardIds.h" #define TYPE_LABA_8 (COLORSPACE_SH(PT_Lab) | CHANNELS_SH(3) | BYTES_SH(1) | EXTRA_SH(1)) struct KoLabU8Traits; class LabU8ColorSpace : public LcmsColorSpace { public: LabU8ColorSpace(const QString &name, KoColorProfile *p); virtual bool willDegrade(ColorSpaceIndependence independence) const; virtual QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) const; static QString colorSpaceId() { return QString("LABAU8"); } virtual KoID colorModelId() const { return LABAColorModelID; } virtual KoID colorDepthId() const { return Integer8BitsColorDepthID; } virtual KoColorSpace* clone() const; virtual void colorToXML(const quint8* pixel, QDomDocument& doc, QDomElement& colorElt) const; virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; private: static const quint32 MAX_CHANNEL_L = 100; static const quint32 MAX_CHANNEL_AB = 255; static const quint32 CHANNEL_AB_ZERO_OFFSET = 128; }; class LabU8ColorSpaceFactory : public LcmsColorSpaceFactory { public: LabU8ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_LABA_8, cmsSigLabData) {} virtual bool userVisible() const { return true; } virtual QString id() const { return LabU8ColorSpace::colorSpaceId(); } virtual QString name() const { return i18n("L*a*b* (8-bit integer/channel)"); } virtual KoID colorModelId() const { return LABAColorModelID; } virtual KoID colorDepthId() const { return Integer8BitsColorDepthID; } virtual int referenceDepth() const { return 8; } virtual KoColorSpace *createColorSpace(const KoColorProfile *p) const { return new LabU8ColorSpace(name(), p->clone()); } virtual QString defaultProfile() const { return "Lab identity built-in"; } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.cpp index 89c5d7c994..2f251ef181 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.cpp @@ -1,110 +1,111 @@ /* * Copyright (c) 2006 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 * Library 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 "RgbF16ColorSpace.h" #include #include #include #include "compositeops/KoCompositeOps.h" #include "compositeops/RgbCompositeOpIn.h" #include "compositeops/RgbCompositeOpOut.h" #include "compositeops/RgbCompositeOpBumpmap.h" +#include RgbF16ColorSpace::RgbF16ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_RGBA_HALF_FLT, cmsSigRgbData, p) { addChannel(new KoChannelInfo(i18n("Red"), 0 * sizeof(half), 0, KoChannelInfo::COLOR, KoChannelInfo::FLOAT16, 2, QColor(255, 0, 0))); addChannel(new KoChannelInfo(i18n("Green"), 1 * sizeof(half), 1, KoChannelInfo::COLOR, KoChannelInfo::FLOAT16, 2, QColor(0, 255, 0))); addChannel(new KoChannelInfo(i18n("Blue"), 2 * sizeof(half), 2, KoChannelInfo::COLOR, KoChannelInfo::FLOAT16, 2, QColor(0, 0, 255))); addChannel(new KoChannelInfo(i18n("Alpha"), 3 * sizeof(half), 3, KoChannelInfo::ALPHA, KoChannelInfo::FLOAT16, 2)); init(); addStandardCompositeOps(this); addCompositeOp(new RgbCompositeOpIn(this)); addCompositeOp(new RgbCompositeOpOut(this)); addCompositeOp(new RgbCompositeOpBumpmap(this)); } bool RgbF16ColorSpace::willDegrade(ColorSpaceIndependence independence) const { if (independence == TO_RGBA16) { return true; } else { return false; } } KoColorSpace *RgbF16ColorSpace::clone() const { return new RgbF16ColorSpace(name(), profile()->clone()); } void RgbF16ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoRgbF16Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("RGB"); - labElt.setAttribute("r", KoColorSpaceMaths< KoRgbF16Traits::channels_type, qreal>::scaleToA(p->red)); - labElt.setAttribute("g", KoColorSpaceMaths< KoRgbF16Traits::channels_type, qreal>::scaleToA(p->green)); - labElt.setAttribute("b", KoColorSpaceMaths< KoRgbF16Traits::channels_type, qreal>::scaleToA(p->blue)); + labElt.setAttribute("r", KisDomUtils::toString(KoColorSpaceMaths< KoRgbF16Traits::channels_type, qreal>::scaleToA(p->red))); + labElt.setAttribute("g", KisDomUtils::toString(KoColorSpaceMaths< KoRgbF16Traits::channels_type, qreal>::scaleToA(p->green))); + labElt.setAttribute("b", KisDomUtils::toString(KoColorSpaceMaths< KoRgbF16Traits::channels_type, qreal>::scaleToA(p->blue))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void RgbF16ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoRgbF16Traits::Pixel *p = reinterpret_cast(pixel); - p->red = KoColorSpaceMaths< qreal, KoRgbF16Traits::channels_type >::scaleToA(elt.attribute("r").toDouble()); - p->green = KoColorSpaceMaths< qreal, KoRgbF16Traits::channels_type >::scaleToA(elt.attribute("g").toDouble()); - p->blue = KoColorSpaceMaths< qreal, KoRgbF16Traits::channels_type >::scaleToA(elt.attribute("b").toDouble()); + p->red = KoColorSpaceMaths< qreal, KoRgbF16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("r"))); + p->green = KoColorSpaceMaths< qreal, KoRgbF16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("g"))); + p->blue = KoColorSpaceMaths< qreal, KoRgbF16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("b"))); p->alpha = 1.0; } -void RgbF16ColorSpace::toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const +void RgbF16ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { RGBToHSY(channelValues[0],channelValues[1],channelValues[2], hue, sat, luma, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbF16ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); HSYToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; } -void RgbF16ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const +void RgbF16ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { RGBToYUV(channelValues[0],channelValues[1],channelValues[2], y, u, v, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbF16ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; -} \ No newline at end of file +} diff --git a/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.h b/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.h index 0626ab883f..c202988a58 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.h @@ -1,121 +1,121 @@ /* * Copyright (c) 2006 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 * Library 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 KORGBF16COLORSPACE_H_ #define KORGBF16COLORSPACE_H_ #include "LcmsColorSpace.h" #include "KoColorModelStandardIds.h" struct KoRgbF16Traits; class RgbF16ColorSpace : public LcmsColorSpace { public: RgbF16ColorSpace(const QString &name, KoColorProfile *p); virtual bool willDegrade(ColorSpaceIndependence independence) const; virtual KoID colorModelId() const { return RGBAColorModelID; } virtual KoID colorDepthId() const { return Float16BitsColorDepthID; } virtual KoColorSpace *clone() const; virtual void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const; virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; static QString colorSpaceId() { return QString("RGBAF16"); } virtual bool hasHighDynamicRange() const { return true; } }; class RgbF16ColorSpaceFactory : public LcmsColorSpaceFactory { public: RgbF16ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_RGBA_HALF_FLT, cmsSigRgbData) { } virtual QString id() const { return RgbF16ColorSpace::colorSpaceId(); } virtual QString name() const { return i18n("RGBA (16-bit floating/channel)"); } virtual bool userVisible() const { return true; } virtual KoID colorModelId() const { return RGBAColorModelID; } virtual KoID colorDepthId() const { return Float16BitsColorDepthID; } virtual int referenceDepth() const { return 16; } virtual KoColorSpace *createColorSpace(const KoColorProfile *p) const { return new RgbF16ColorSpace(name(), p->clone()); } virtual QString defaultProfile() const { return "sRGB-elle-V2-g10.icc"; } virtual bool isHdr() const { return true; } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.cpp index 91a900d379..e8308bbf41 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.cpp @@ -1,113 +1,114 @@ /* * Copyright (c) 2006 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 * Library 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 "RgbF32ColorSpace.h" #include #include #include "compositeops/KoCompositeOps.h" #include "compositeops/RgbCompositeOps.h" +#include RgbF32ColorSpace::RgbF32ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_RGBA_FLT, cmsSigRgbData, p) { const IccColorProfile *icc_p = dynamic_cast(p); Q_ASSERT(icc_p); QVector uiRanges(icc_p->getFloatUIMinMax()); Q_ASSERT(uiRanges.size() == 3); addChannel(new KoChannelInfo(i18n("Red"), 0 * sizeof(float), 0, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, 4, QColor(255, 0, 0), uiRanges[0])); addChannel(new KoChannelInfo(i18n("Green"), 1 * sizeof(float), 1, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, 4, QColor(0, 255, 0), uiRanges[1])); addChannel(new KoChannelInfo(i18n("Blue"), 2 * sizeof(float), 2, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, 4, QColor(0, 0, 255), uiRanges[2])); addChannel(new KoChannelInfo(i18n("Alpha"), 3 * sizeof(float), 3, KoChannelInfo::ALPHA, KoChannelInfo::FLOAT32, 4)); init(); addStandardCompositeOps(this); addCompositeOp(new RgbCompositeOpIn(this)); addCompositeOp(new RgbCompositeOpOut(this)); addCompositeOp(new RgbCompositeOpBumpmap(this)); } bool RgbF32ColorSpace::willDegrade(ColorSpaceIndependence independence) const { if (independence == TO_RGBA16) { return true; } else { return false; } } KoColorSpace *RgbF32ColorSpace::clone() const { return new RgbF32ColorSpace(name(), profile()->clone()); } void RgbF32ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoRgbF32Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("RGB"); - labElt.setAttribute("r", KoColorSpaceMaths< KoRgbF32Traits::channels_type, qreal>::scaleToA(p->red)); - labElt.setAttribute("g", KoColorSpaceMaths< KoRgbF32Traits::channels_type, qreal>::scaleToA(p->green)); - labElt.setAttribute("b", KoColorSpaceMaths< KoRgbF32Traits::channels_type, qreal>::scaleToA(p->blue)); + labElt.setAttribute("r", KisDomUtils::toString(KoColorSpaceMaths< KoRgbF32Traits::channels_type, qreal>::scaleToA(p->red))); + labElt.setAttribute("g", KisDomUtils::toString(KoColorSpaceMaths< KoRgbF32Traits::channels_type, qreal>::scaleToA(p->green))); + labElt.setAttribute("b", KisDomUtils::toString(KoColorSpaceMaths< KoRgbF32Traits::channels_type, qreal>::scaleToA(p->blue))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void RgbF32ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoRgbF32Traits::Pixel *p = reinterpret_cast(pixel); - p->red = KoColorSpaceMaths< qreal, KoRgbF32Traits::channels_type >::scaleToA(elt.attribute("r").toDouble()); - p->green = KoColorSpaceMaths< qreal, KoRgbF32Traits::channels_type >::scaleToA(elt.attribute("g").toDouble()); - p->blue = KoColorSpaceMaths< qreal, KoRgbF32Traits::channels_type >::scaleToA(elt.attribute("b").toDouble()); + p->red = KoColorSpaceMaths< qreal, KoRgbF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("r"))); + p->green = KoColorSpaceMaths< qreal, KoRgbF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("g"))); + p->blue = KoColorSpaceMaths< qreal, KoRgbF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("b"))); p->alpha = 1.0; } -void RgbF32ColorSpace::toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const +void RgbF32ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { RGBToHSY(channelValues[0],channelValues[1],channelValues[2], hue, sat, luma, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbF32ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); HSYToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; } -void RgbF32ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const +void RgbF32ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { RGBToYUV(channelValues[0],channelValues[1],channelValues[2], y, u, v, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbF32ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; -} \ No newline at end of file +} diff --git a/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.h b/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.h index 4a6c272d19..78d93d6c00 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.h @@ -1,121 +1,121 @@ /* * Copyright (c) 2006 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 * Library 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 KORGBF32COLORSPACE_H_ #define KORGBF32COLORSPACE_H_ #include "LcmsColorSpace.h" #include "KoColorModelStandardIds.h" struct KoRgbF32Traits; class RgbF32ColorSpace : public LcmsColorSpace { public: RgbF32ColorSpace(const QString &name, KoColorProfile *p); virtual bool willDegrade(ColorSpaceIndependence independence) const; virtual KoID colorModelId() const { return RGBAColorModelID; } virtual KoID colorDepthId() const { return Float32BitsColorDepthID; } virtual KoColorSpace *clone() const; virtual void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const; virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; static QString colorSpaceId() { return QString("RGBAF32"); } virtual bool hasHighDynamicRange() const { return true; } }; class RgbF32ColorSpaceFactory : public LcmsColorSpaceFactory { public: RgbF32ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_RGBA_FLT, cmsSigRgbData) { } virtual QString id() const { return RgbF32ColorSpace::colorSpaceId(); } virtual QString name() const { return i18n("RGBA (32-bit floating/channel)"); } virtual bool userVisible() const { return true; } virtual KoID colorModelId() const { return RGBAColorModelID; } virtual KoID colorDepthId() const { return Float32BitsColorDepthID; } virtual int referenceDepth() const { return 32; } virtual KoColorSpace *createColorSpace(const KoColorProfile *p) const { return new RgbF32ColorSpace(name(), p->clone()); } virtual QString defaultProfile() const { return "sRGB-elle-V2-g10.icc"; } virtual bool isHdr() const { return true; } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.cpp index 0dcfc112ed..873980caad 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.cpp @@ -1,107 +1,107 @@ /* * Copyright (c) 2006 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 * Library 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 "RgbU16ColorSpace.h" #include #include #include "compositeops/KoCompositeOps.h" #include "compositeops/RgbCompositeOps.h" #include "kis_dom_utils.h" RgbU16ColorSpace::RgbU16ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_BGRA_16, cmsSigRgbData, p) { addChannel(new KoChannelInfo(i18n("Blue"), 0 * sizeof(quint16), 2, KoChannelInfo::COLOR, KoChannelInfo::UINT16, 2, QColor(0, 0, 255))); addChannel(new KoChannelInfo(i18n("Green"), 1 * sizeof(quint16), 1, KoChannelInfo::COLOR, KoChannelInfo::UINT16, 2, QColor(0, 255, 0))); addChannel(new KoChannelInfo(i18n("Red"), 2 * sizeof(quint16), 0, KoChannelInfo::COLOR, KoChannelInfo::UINT16, 2, QColor(255, 0, 0))); addChannel(new KoChannelInfo(i18n("Alpha"), 3 * sizeof(quint16), 3, KoChannelInfo::ALPHA, KoChannelInfo::UINT16, 2)); init(); addStandardCompositeOps(this); addCompositeOp(new RgbCompositeOpIn(this)); addCompositeOp(new RgbCompositeOpOut(this)); addCompositeOp(new RgbCompositeOpBumpmap(this)); } bool RgbU16ColorSpace::willDegrade(ColorSpaceIndependence independence) const { if (independence == TO_RGBA8) { return true; } else { return false; } } KoColorSpace *RgbU16ColorSpace::clone() const { return new RgbU16ColorSpace(name(), profile()->clone()); } void RgbU16ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoBgrU16Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("RGB"); - labElt.setAttribute("r", KoColorSpaceMaths< KoBgrU16Traits::channels_type, qreal>::scaleToA(p->red)); - labElt.setAttribute("g", KoColorSpaceMaths< KoBgrU16Traits::channels_type, qreal>::scaleToA(p->green)); - labElt.setAttribute("b", KoColorSpaceMaths< KoBgrU16Traits::channels_type, qreal>::scaleToA(p->blue)); + labElt.setAttribute("r", KisDomUtils::toString(KoColorSpaceMaths< KoBgrU16Traits::channels_type, qreal>::scaleToA(p->red))); + labElt.setAttribute("g", KisDomUtils::toString(KoColorSpaceMaths< KoBgrU16Traits::channels_type, qreal>::scaleToA(p->green))); + labElt.setAttribute("b", KisDomUtils::toString(KoColorSpaceMaths< KoBgrU16Traits::channels_type, qreal>::scaleToA(p->blue))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void RgbU16ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoBgrU16Traits::Pixel *p = reinterpret_cast(pixel); p->red = KoColorSpaceMaths< qreal, KoBgrU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("r"))); p->green = KoColorSpaceMaths< qreal, KoBgrU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("g"))); p->blue = KoColorSpaceMaths< qreal, KoBgrU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("b"))); p->alpha = KoColorSpaceMathsTraits::max; } -void RgbU16ColorSpace::toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const +void RgbU16ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { RGBToHSY(channelValues[0],channelValues[1],channelValues[2], hue, sat, luma, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbU16ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); HSYToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; } -void RgbU16ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const +void RgbU16ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { RGBToYUV(channelValues[0],channelValues[1],channelValues[2], y, u, v, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbU16ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; } diff --git a/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.h b/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.h index fc18aae458..7b77312c71 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.h @@ -1,105 +1,105 @@ /* * Copyright (c) 2006 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 * Library 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 KORGBU16COLORSPACE_H_ #define KORGBU16COLORSPACE_H_ #include "LcmsColorSpace.h" #include "KoColorModelStandardIds.h" struct KoBgrU16Traits; class RgbU16ColorSpace : public LcmsColorSpace { public: RgbU16ColorSpace(const QString &name, KoColorProfile *p); virtual bool willDegrade(ColorSpaceIndependence independence) const; virtual KoID colorModelId() const { return RGBAColorModelID; } virtual KoID colorDepthId() const { return Integer16BitsColorDepthID; } virtual KoColorSpace *clone() const; virtual void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const; virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; static QString colorSpaceId() { return QString("RGBA16"); } }; class RgbU16ColorSpaceFactory : public LcmsColorSpaceFactory { public: RgbU16ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_BGRA_16, cmsSigRgbData) { } virtual QString id() const { return RgbU16ColorSpace::colorSpaceId(); } virtual QString name() const { return i18n("RGB (16-bit integer/channel)"); } virtual bool userVisible() const { return true; } virtual KoID colorModelId() const { return RGBAColorModelID; } virtual KoID colorDepthId() const { return Integer16BitsColorDepthID; } virtual int referenceDepth() const { return 16; } virtual KoColorSpace *createColorSpace(const KoColorProfile *p) const { return new RgbU16ColorSpace(name(), p->clone()); } virtual QString defaultProfile() const { return "sRGB-elle-V2-g10.icc";//this is a linear space, because 16bit is enough to only enjoy advantages of linear space } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.cpp index 54a077618c..67937e31ee 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.cpp @@ -1,145 +1,146 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "RgbU8ColorSpace.h" #include #include #include #include #include #include #include #include "compositeops/KoCompositeOps.h" #include "compositeops/RgbCompositeOps.h" +#include #define downscale(quantum) (quantum) //((unsigned char) ((quantum)/257UL)) #define upscale(value) (value) // ((quint8) (257UL*(value))) class KoRgbU8InvertColorTransformation : public KoColorTransformation { public: KoRgbU8InvertColorTransformation(const KoColorSpace *cs) : m_colorSpace(cs), m_psize(cs->pixelSize()) { } virtual void transform(const quint8 *src, quint8 *dst, qint32 nPixels) const { while (nPixels--) { dst[0] = KoColorSpaceMathsTraits::max - src[0]; dst[1] = KoColorSpaceMathsTraits::max - src[1]; dst[2] = KoColorSpaceMathsTraits::max - src[2]; dst[3] = src[3]; src += m_psize; dst += m_psize; } } private: const KoColorSpace *m_colorSpace; quint32 m_psize; }; RgbU8ColorSpace::RgbU8ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_BGRA_8, cmsSigRgbData, p) { addChannel(new KoChannelInfo(i18n("Blue"), 0, 2, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(0, 0, 255))); addChannel(new KoChannelInfo(i18n("Green"), 1, 1, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(0, 255, 0))); addChannel(new KoChannelInfo(i18n("Red"), 2, 0, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(255, 0, 0))); addChannel(new KoChannelInfo(i18n("Alpha"), 3, 3, KoChannelInfo::ALPHA, KoChannelInfo::UINT8)); init(); addStandardCompositeOps(this); addCompositeOp(new RgbCompositeOpIn(this)); addCompositeOp(new RgbCompositeOpOut(this)); addCompositeOp(new RgbCompositeOpBumpmap(this)); } KoColorTransformation *RgbU8ColorSpace::createInvertTransformation() const { return new KoRgbU8InvertColorTransformation(this); } KoColorSpace *RgbU8ColorSpace::clone() const { return new RgbU8ColorSpace(name(), profile()->clone()); } void RgbU8ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoBgrU8Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("RGB"); - labElt.setAttribute("r", KoColorSpaceMaths< KoBgrU8Traits::channels_type, qreal>::scaleToA(p->red)); - labElt.setAttribute("g", KoColorSpaceMaths< KoBgrU8Traits::channels_type, qreal>::scaleToA(p->green)); - labElt.setAttribute("b", KoColorSpaceMaths< KoBgrU8Traits::channels_type, qreal>::scaleToA(p->blue)); + labElt.setAttribute("r", KisDomUtils::toString(KoColorSpaceMaths< KoBgrU8Traits::channels_type, qreal>::scaleToA(p->red))); + labElt.setAttribute("g", KisDomUtils::toString(KoColorSpaceMaths< KoBgrU8Traits::channels_type, qreal>::scaleToA(p->green))); + labElt.setAttribute("b", KisDomUtils::toString(KoColorSpaceMaths< KoBgrU8Traits::channels_type, qreal>::scaleToA(p->blue))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void RgbU8ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoBgrU8Traits::Pixel *p = reinterpret_cast(pixel); - p->red = KoColorSpaceMaths< qreal, KoBgrU8Traits::channels_type >::scaleToA(elt.attribute("r").toDouble()); - p->green = KoColorSpaceMaths< qreal, KoBgrU8Traits::channels_type >::scaleToA(elt.attribute("g").toDouble()); - p->blue = KoColorSpaceMaths< qreal, KoBgrU8Traits::channels_type >::scaleToA(elt.attribute("b").toDouble()); + p->red = KoColorSpaceMaths< qreal, KoBgrU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("r"))); + p->green = KoColorSpaceMaths< qreal, KoBgrU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("g"))); + p->blue = KoColorSpaceMaths< qreal, KoBgrU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("b"))); p->alpha = KoColorSpaceMathsTraits::max; } quint8 RgbU8ColorSpace::intensity8(const quint8 *src) const { const KoBgrU8Traits::Pixel *p = reinterpret_cast(src); return (quint8)(p->red * 0.30 + p->green * 0.59 + p->blue * 0.11); } -void RgbU8ColorSpace::toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const +void RgbU8ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { RGBToHSY(channelValues[0],channelValues[1],channelValues[2], hue, sat, luma, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbU8ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); HSYToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; } -void RgbU8ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const +void RgbU8ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { RGBToYUV(channelValues[0],channelValues[1],channelValues[2], y, u, v, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbU8ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; -} \ No newline at end of file +} diff --git a/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.h b/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.h index 98d461c251..767fd713d0 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.h @@ -1,118 +1,118 @@ /* * Copyright (c) 2002 Patrick Julien * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KO_STRATEGY_COLORSPACE_RGB_H_ #define KO_STRATEGY_COLORSPACE_RGB_H_ #include #include #include "KoColorModelStandardIds.h" struct KoBgrU8Traits; class RgbU8ColorSpace : public LcmsColorSpace { public: RgbU8ColorSpace(const QString &name, KoColorProfile *p); virtual bool willDegrade(ColorSpaceIndependence) const { return false; } virtual KoColorTransformation *createInvertTransformation() const; virtual KoID colorModelId() const { return RGBAColorModelID; } virtual KoID colorDepthId() const { return Integer8BitsColorDepthID; } virtual KoColorSpace *clone() const; virtual void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const; virtual void colorFromXML(quint8 *pixel, const QDomElement &elt) const; virtual quint8 intensity8(const quint8 * src) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; static QString colorSpaceId() { return QString("RGBA"); } }; class RgbU8ColorSpaceFactory : public LcmsColorSpaceFactory { public: RgbU8ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_BGRA_8, cmsSigRgbData) {} virtual bool userVisible() const { return true; } virtual QString id() const { return RgbU8ColorSpace::colorSpaceId(); } virtual QString name() const { return i18n("RGB (8-bit integer/channel)"); } virtual KoID colorModelId() const { return RGBAColorModelID; } virtual KoID colorDepthId() const { return Integer8BitsColorDepthID; } virtual int referenceDepth() const { return 8; } virtual KoColorSpace *createColorSpace(const KoColorProfile *p) const { return new RgbU8ColorSpace(name(), p->clone()); } virtual QString defaultProfile() const { return "sRGB-elle-V2-srgbtrc.icc"; } }; #endif // KO_STRATEGY_COLORSPACE_RGB_H_ diff --git a/plugins/color/lcms2engine/colorspaces/xyz_f16/XyzF16ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/xyz_f16/XyzF16ColorSpace.cpp index a1cfe9de22..0a2b4a30bb 100644 --- a/plugins/color/lcms2engine/colorspaces/xyz_f16/XyzF16ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/xyz_f16/XyzF16ColorSpace.cpp @@ -1,104 +1,105 @@ /* * Copyright (c) 2007 Cyrille Berger (cberger@cberger.net) * * 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 * Library 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 "XyzF16ColorSpace.h" #include #include #include #include "compositeops/KoCompositeOps.h" -#include "KoColorConversions.h" +#include +#include XyzF16ColorSpace::XyzF16ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_XYZA_HALF_FLT, cmsSigXYZData, p) { addChannel(new KoChannelInfo(i18n("X"), KoXyzF16Traits::x_pos * sizeof(half), KoXyzF16Traits::x_pos, KoChannelInfo::COLOR, KoChannelInfo::FLOAT16, 2, Qt::cyan)); addChannel(new KoChannelInfo(i18n("Y"), KoXyzF16Traits::y_pos * sizeof(half), KoXyzF16Traits::y_pos, KoChannelInfo::COLOR, KoChannelInfo::FLOAT16, 2, Qt::magenta)); addChannel(new KoChannelInfo(i18n("Z"), KoXyzF16Traits::z_pos * sizeof(half), KoXyzF16Traits::z_pos, KoChannelInfo::COLOR, KoChannelInfo::FLOAT16, 2, Qt::yellow)); addChannel(new KoChannelInfo(i18n("Alpha"), KoXyzF16Traits::alpha_pos * sizeof(half), KoXyzF16Traits::alpha_pos, KoChannelInfo::ALPHA, KoChannelInfo::FLOAT16, 2)); init(); addStandardCompositeOps(this); } bool XyzF16ColorSpace::willDegrade(ColorSpaceIndependence independence) const { if (independence == TO_RGBA16) { return true; } else { return false; } } KoColorSpace *XyzF16ColorSpace::clone() const { return new XyzF16ColorSpace(name(), profile()->clone()); } void XyzF16ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoXyzF16Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("XYZ"); - labElt.setAttribute("x", KoColorSpaceMaths< KoXyzF16Traits::channels_type, qreal>::scaleToA(p->x)); - labElt.setAttribute("y", KoColorSpaceMaths< KoXyzF16Traits::channels_type, qreal>::scaleToA(p->y)); - labElt.setAttribute("z", KoColorSpaceMaths< KoXyzF16Traits::channels_type, qreal>::scaleToA(p->z)); + labElt.setAttribute("x", KisDomUtils::toString(KoColorSpaceMaths< KoXyzF16Traits::channels_type, qreal>::scaleToA(p->x))); + labElt.setAttribute("y", KisDomUtils::toString(KoColorSpaceMaths< KoXyzF16Traits::channels_type, qreal>::scaleToA(p->y))); + labElt.setAttribute("z", KisDomUtils::toString(KoColorSpaceMaths< KoXyzF16Traits::channels_type, qreal>::scaleToA(p->z))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void XyzF16ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoXyzF16Traits::Pixel *p = reinterpret_cast(pixel); - p->x = KoColorSpaceMaths< qreal, KoXyzF16Traits::channels_type >::scaleToA(elt.attribute("x").toDouble()); - p->y = KoColorSpaceMaths< qreal, KoXyzF16Traits::channels_type >::scaleToA(elt.attribute("y").toDouble()); - p->z = KoColorSpaceMaths< qreal, KoXyzF16Traits::channels_type >::scaleToA(elt.attribute("z").toDouble()); + p->x = KoColorSpaceMaths< qreal, KoXyzF16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("x"))); + p->y = KoColorSpaceMaths< qreal, KoXyzF16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("y"))); + p->z = KoColorSpaceMaths< qreal, KoXyzF16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("z"))); p->alpha = 1.0; } -void XyzF16ColorSpace::toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const +void XyzF16ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { qreal xyx, xyy, xyY = 0.0; XYZToxyY(channelValues[0],channelValues[1],channelValues[2], &xyx, &xyy, &xyY); LabToLCH(xyY,xyx,xyY, hue, sat, luma); } QVector XyzF16ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); qreal xyx, xyy, xyY = 0.0; LCHToLab(*luma, *sat, *hue, &xyY,&xyx,&xyy); xyYToXYZ(xyx, xyy, xyY, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } -void XyzF16ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const +void XyzF16ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { XYZToxyY(channelValues[0],channelValues[1],channelValues[2], u, v, y); } QVector XyzF16ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); xyYToXYZ(*u, *v, *y, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; -} \ No newline at end of file +} diff --git a/plugins/color/lcms2engine/colorspaces/xyz_f16/XyzF16ColorSpace.h b/plugins/color/lcms2engine/colorspaces/xyz_f16/XyzF16ColorSpace.h index b935e78bce..1d1fc1741e 100644 --- a/plugins/color/lcms2engine/colorspaces/xyz_f16/XyzF16ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/xyz_f16/XyzF16ColorSpace.h @@ -1,125 +1,125 @@ /* * Copyright (c) 2007 Cyrille Berger (cberger@cberger.net) * * 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 * Library 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 KIS_XYZ_F16_COLORSPACE_H_ #define KIS_XYZ_F16_COLORSPACE_H_ #include #define TYPE_XYZA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_XYZ)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)) #include struct KoXyzF16Traits; class XyzF16ColorSpace : public LcmsColorSpace { public: XyzF16ColorSpace(const QString &name, KoColorProfile *p); virtual bool willDegrade(ColorSpaceIndependence independence) const; virtual KoID colorModelId() const { return XYZAColorModelID; } virtual KoID colorDepthId() const { return Float32BitsColorDepthID; } virtual KoColorSpace *clone() const; virtual void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const; virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; static QString colorSpaceId() { return QString("XYZAF16"); } virtual bool hasHighDynamicRange() const { return true; } }; class XyzF16ColorSpaceFactory : public LcmsColorSpaceFactory { public: XyzF16ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_XYZA_HALF_FLT, cmsSigXYZData) { } virtual QString id() const { return XyzF16ColorSpace::colorSpaceId(); } virtual QString name() const { return i18n("XYZ (32-bit float/channel)"); } virtual bool userVisible() const { return true; } virtual KoID colorModelId() const { return XYZAColorModelID; } virtual KoID colorDepthId() const { return Float16BitsColorDepthID; } virtual int referenceDepth() const { return 16; } virtual KoColorSpace *createColorSpace(const KoColorProfile *p) const { return new XyzF16ColorSpace(name(), p->clone()); } virtual QString defaultProfile() const { return "XYZ identity built-in"; } virtual bool isHdr() const { return true; } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/xyz_f32/XyzF32ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/xyz_f32/XyzF32ColorSpace.cpp index 695317d755..bfcc55d559 100644 --- a/plugins/color/lcms2engine/colorspaces/xyz_f32/XyzF32ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/xyz_f32/XyzF32ColorSpace.cpp @@ -1,108 +1,109 @@ /* * Copyright (c) 2007 Cyrille Berger (cberger@cberger.net) * * 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 * Library 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 "XyzF32ColorSpace.h" #include #include #include #include "compositeops/KoCompositeOps.h" -#include "KoColorConversions.h" +#include +#include XyzF32ColorSpace::XyzF32ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_XYZA_FLT, cmsSigXYZData, p) { const IccColorProfile *icc_p = dynamic_cast(p); Q_ASSERT(icc_p); QVector uiRanges(icc_p->getFloatUIMinMax()); Q_ASSERT(uiRanges.size() == 3); addChannel(new KoChannelInfo(i18n("X"), KoXyzF32Traits::x_pos * sizeof(float), KoXyzF32Traits::x_pos, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, sizeof(float), Qt::cyan, uiRanges[0])); addChannel(new KoChannelInfo(i18n("Y"), KoXyzF32Traits::y_pos * sizeof(float), KoXyzF32Traits::y_pos, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, sizeof(float), Qt::magenta, uiRanges[1])); addChannel(new KoChannelInfo(i18n("Z"), KoXyzF32Traits::z_pos * sizeof(float), KoXyzF32Traits::z_pos, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, sizeof(float), Qt::yellow, uiRanges[2])); addChannel(new KoChannelInfo(i18n("Alpha"), KoXyzF32Traits::alpha_pos * sizeof(float), KoXyzF32Traits::alpha_pos, KoChannelInfo::ALPHA, KoChannelInfo::FLOAT32, sizeof(float))); init(); addStandardCompositeOps(this); } bool XyzF32ColorSpace::willDegrade(ColorSpaceIndependence independence) const { if (independence == TO_RGBA16) { return true; } else { return false; } } KoColorSpace *XyzF32ColorSpace::clone() const { return new XyzF32ColorSpace(name(), profile()->clone()); } void XyzF32ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoXyzF32Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("XYZ"); - labElt.setAttribute("x", KoColorSpaceMaths< KoXyzF32Traits::channels_type, qreal>::scaleToA(p->x)); - labElt.setAttribute("y", KoColorSpaceMaths< KoXyzF32Traits::channels_type, qreal>::scaleToA(p->y)); - labElt.setAttribute("z", KoColorSpaceMaths< KoXyzF32Traits::channels_type, qreal>::scaleToA(p->z)); + labElt.setAttribute("x", KisDomUtils::toString(KoColorSpaceMaths< KoXyzF32Traits::channels_type, qreal>::scaleToA(p->x))); + labElt.setAttribute("y", KisDomUtils::toString(KoColorSpaceMaths< KoXyzF32Traits::channels_type, qreal>::scaleToA(p->y))); + labElt.setAttribute("z", KisDomUtils::toString(KoColorSpaceMaths< KoXyzF32Traits::channels_type, qreal>::scaleToA(p->z))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void XyzF32ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoXyzF32Traits::Pixel *p = reinterpret_cast(pixel); - p->x = KoColorSpaceMaths< qreal, KoXyzF32Traits::channels_type >::scaleToA(elt.attribute("x").toDouble()); - p->y = KoColorSpaceMaths< qreal, KoXyzF32Traits::channels_type >::scaleToA(elt.attribute("y").toDouble()); - p->z = KoColorSpaceMaths< qreal, KoXyzF32Traits::channels_type >::scaleToA(elt.attribute("z").toDouble()); + p->x = KoColorSpaceMaths< qreal, KoXyzF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("x"))); + p->y = KoColorSpaceMaths< qreal, KoXyzF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("y"))); + p->z = KoColorSpaceMaths< qreal, KoXyzF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("z"))); p->alpha = 1.0; } -void XyzF32ColorSpace::toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const +void XyzF32ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { qreal xyx, xyy, xyY = 0.0; XYZToxyY(channelValues[0],channelValues[1],channelValues[2], &xyx, &xyy, &xyY); LabToLCH(xyY,xyx,xyY, hue, sat, luma); } QVector XyzF32ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); qreal xyx, xyy, xyY = 0.0; LCHToLab(*luma, *sat, *hue, &xyY,&xyx,&xyy); xyYToXYZ(xyx, xyy, xyY, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } -void XyzF32ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const +void XyzF32ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { XYZToxyY(channelValues[0],channelValues[1],channelValues[2], u, v, y); } QVector XyzF32ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); xyYToXYZ(*u, *v, *y, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; -} \ No newline at end of file +} diff --git a/plugins/color/lcms2engine/colorspaces/xyz_f32/XyzF32ColorSpace.h b/plugins/color/lcms2engine/colorspaces/xyz_f32/XyzF32ColorSpace.h index 20f100730b..72c96a889b 100644 --- a/plugins/color/lcms2engine/colorspaces/xyz_f32/XyzF32ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/xyz_f32/XyzF32ColorSpace.h @@ -1,125 +1,125 @@ /* * Copyright (c) 2007 Cyrille Berger (cberger@cberger.net) * * 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 * Library 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 KIS_XYZ_F32_COLORSPACE_H_ #define KIS_XYZ_F32_COLORSPACE_H_ #include #define TYPE_XYZA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_XYZ)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)) #include struct KoXyzF32Traits; class XyzF32ColorSpace : public LcmsColorSpace { public: XyzF32ColorSpace(const QString &name, KoColorProfile *p); virtual bool willDegrade(ColorSpaceIndependence independence) const; virtual KoID colorModelId() const { return XYZAColorModelID; } virtual KoID colorDepthId() const { return Float32BitsColorDepthID; } virtual KoColorSpace *clone() const; virtual void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const; virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; static QString colorSpaceId() { return QString("XYZAF32"); } virtual bool hasHighDynamicRange() const { return true; } }; class XyzF32ColorSpaceFactory : public LcmsColorSpaceFactory { public: XyzF32ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_XYZA_FLT, cmsSigXYZData) { } virtual QString id() const { return XyzF32ColorSpace::colorSpaceId(); } virtual QString name() const { return i18n("XYZ (32-bit float/channel)"); } virtual bool userVisible() const { return true; } virtual KoID colorModelId() const { return XYZAColorModelID; } virtual KoID colorDepthId() const { return Float32BitsColorDepthID; } virtual int referenceDepth() const { return 32; } virtual KoColorSpace *createColorSpace(const KoColorProfile *p) const { return new XyzF32ColorSpace(name(), p->clone()); } virtual QString defaultProfile() const { return "XYZ identity built-in"; } virtual bool isHdr() const { return true; } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/xyz_u16/XyzU16ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/xyz_u16/XyzU16ColorSpace.cpp index f2cde02d10..72d407c103 100644 --- a/plugins/color/lcms2engine/colorspaces/xyz_u16/XyzU16ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/xyz_u16/XyzU16ColorSpace.cpp @@ -1,104 +1,105 @@ /* * Copyright (c) 2007 Cyrille Berger (cberger@cberger.net) * * 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 * Library 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 "XyzU16ColorSpace.h" #include #include #include #include "compositeops/KoCompositeOps.h" -#include "KoColorConversions.h" +#include +#include XyzU16ColorSpace::XyzU16ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_XYZA_16, cmsSigXYZData, p) { addChannel(new KoChannelInfo(i18n("X"), KoXyzU16Traits::x_pos * sizeof(quint16), KoXyzU16Traits::x_pos, KoChannelInfo::COLOR, KoChannelInfo::UINT16, sizeof(quint16), Qt::cyan)); addChannel(new KoChannelInfo(i18n("Y"), KoXyzU16Traits::y_pos * sizeof(quint16), KoXyzU16Traits::y_pos, KoChannelInfo::COLOR, KoChannelInfo::UINT16, sizeof(quint16), Qt::magenta)); addChannel(new KoChannelInfo(i18n("Z"), KoXyzU16Traits::z_pos * sizeof(quint16), KoXyzU16Traits::z_pos, KoChannelInfo::COLOR, KoChannelInfo::UINT16, sizeof(quint16), Qt::yellow)); addChannel(new KoChannelInfo(i18n("Alpha"), KoXyzU16Traits::alpha_pos * sizeof(quint16), KoXyzU16Traits::alpha_pos, KoChannelInfo::ALPHA, KoChannelInfo::UINT16, sizeof(quint16))); init(); // ADD, ALPHA_DARKEN, BURN, DIVIDE, DODGE, ERASE, MULTIPLY, OVER, OVERLAY, SCREEN, SUBTRACT addStandardCompositeOps(this); } bool XyzU16ColorSpace::willDegrade(ColorSpaceIndependence independence) const { if (independence == TO_RGBA8) { return true; } else { return false; } } KoColorSpace *XyzU16ColorSpace::clone() const { return new XyzU16ColorSpace(name(), profile()->clone()); } void XyzU16ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoXyzU16Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("XYZ"); - labElt.setAttribute("x", KoColorSpaceMaths< KoXyzU16Traits::channels_type, qreal>::scaleToA(p->x)); - labElt.setAttribute("y", KoColorSpaceMaths< KoXyzU16Traits::channels_type, qreal>::scaleToA(p->y)); - labElt.setAttribute("z", KoColorSpaceMaths< KoXyzU16Traits::channels_type, qreal>::scaleToA(p->z)); + labElt.setAttribute("x", KisDomUtils::toString(KoColorSpaceMaths< KoXyzU16Traits::channels_type, qreal>::scaleToA(p->x))); + labElt.setAttribute("y", KisDomUtils::toString(KoColorSpaceMaths< KoXyzU16Traits::channels_type, qreal>::scaleToA(p->y))); + labElt.setAttribute("z", KisDomUtils::toString(KoColorSpaceMaths< KoXyzU16Traits::channels_type, qreal>::scaleToA(p->z))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void XyzU16ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoXyzU16Traits::Pixel *p = reinterpret_cast(pixel); - p->x = KoColorSpaceMaths< qreal, KoXyzU16Traits::channels_type >::scaleToA(elt.attribute("x").toDouble()); - p->y = KoColorSpaceMaths< qreal, KoXyzU16Traits::channels_type >::scaleToA(elt.attribute("y").toDouble()); - p->z = KoColorSpaceMaths< qreal, KoXyzU16Traits::channels_type >::scaleToA(elt.attribute("z").toDouble()); + p->x = KoColorSpaceMaths< qreal, KoXyzU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("x"))); + p->y = KoColorSpaceMaths< qreal, KoXyzU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("y"))); + p->z = KoColorSpaceMaths< qreal, KoXyzU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("z"))); p->alpha = KoColorSpaceMathsTraits::max; } -void XyzU16ColorSpace::toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const +void XyzU16ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { qreal xyx, xyy, xyY = 0.0; XYZToxyY(channelValues[0],channelValues[1],channelValues[2], &xyx, &xyy, &xyY); LabToLCH(xyY,xyx,xyY, hue, sat, luma); } QVector XyzU16ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); qreal xyx, xyy, xyY = 0.0; LCHToLab(*luma, *sat, *hue, &xyY,&xyx,&xyy); xyYToXYZ(xyx, xyy, xyY, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } -void XyzU16ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const +void XyzU16ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { XYZToxyY(channelValues[0],channelValues[1],channelValues[2], u, v, y); } QVector XyzU16ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); xyYToXYZ(*u, *v, *y, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; -} \ No newline at end of file +} diff --git a/plugins/color/lcms2engine/colorspaces/xyz_u16/XyzU16ColorSpace.h b/plugins/color/lcms2engine/colorspaces/xyz_u16/XyzU16ColorSpace.h index 7aa84c774d..7d53b906a8 100644 --- a/plugins/color/lcms2engine/colorspaces/xyz_u16/XyzU16ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/xyz_u16/XyzU16ColorSpace.h @@ -1,107 +1,107 @@ /* * Copyright (c) 2007 Cyrille Berger (cberger@cberger.net) * * 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 * Library 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 KIS_XYZ_U16_COLORSPACE_H_ #define KIS_XYZ_U16_COLORSPACE_H_ #include #include #include #define TYPE_XYZA_16 (COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)) class XyzU16ColorSpace : public LcmsColorSpace { public: XyzU16ColorSpace(const QString &name, KoColorProfile *p); virtual bool willDegrade(ColorSpaceIndependence independence) const; virtual KoID colorModelId() const { return XYZAColorModelID; } virtual KoID colorDepthId() const { return Integer16BitsColorDepthID; } virtual KoColorSpace *clone() const; virtual void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const; virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; static QString colorSpaceId() { return QString("XYZA16"); } }; class XyzU16ColorSpaceFactory : public LcmsColorSpaceFactory { public: XyzU16ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_XYZA_16, cmsSigXYZData) { } virtual QString id() const { return XyzU16ColorSpace::colorSpaceId(); } virtual QString name() const { return i18n("XYZ (16-bit integer/channel)"); } virtual bool userVisible() const { return true; } virtual KoID colorModelId() const { return XYZAColorModelID; } virtual KoID colorDepthId() const { return Integer16BitsColorDepthID; } virtual int referenceDepth() const { return 16; } virtual KoColorSpace *createColorSpace(const KoColorProfile *p) const { return new XyzU16ColorSpace(name(), p->clone()); } virtual QString defaultProfile() const { return "XYZ identity built-in"; } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/xyz_u8/XyzU8ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/xyz_u8/XyzU8ColorSpace.cpp index 117684e294..cbcea859be 100644 --- a/plugins/color/lcms2engine/colorspaces/xyz_u8/XyzU8ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/xyz_u8/XyzU8ColorSpace.cpp @@ -1,100 +1,101 @@ /* * Copyright (c) 2007 Cyrille Berger (cberger@cberger.net) * * 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 * Library 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 "XyzU8ColorSpace.h" #include #include #include #include "compositeops/KoCompositeOps.h" -#include "KoColorConversions.h" +#include +#include XyzU8ColorSpace::XyzU8ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_XYZA_8, cmsSigXYZData, p) { addChannel(new KoChannelInfo(i18n("X"), KoXyzU8Traits::x_pos * sizeof(quint8), KoXyzU8Traits::x_pos, KoChannelInfo::COLOR, KoChannelInfo::UINT8, sizeof(quint8), Qt::cyan)); addChannel(new KoChannelInfo(i18n("Y"), KoXyzU8Traits::y_pos * sizeof(quint8), KoXyzU8Traits::y_pos, KoChannelInfo::COLOR, KoChannelInfo::UINT8, sizeof(quint8), Qt::magenta)); addChannel(new KoChannelInfo(i18n("Z"), KoXyzU8Traits::z_pos * sizeof(quint8), KoXyzU8Traits::z_pos, KoChannelInfo::COLOR, KoChannelInfo::UINT8, sizeof(quint8), Qt::yellow)); addChannel(new KoChannelInfo(i18n("Alpha"), KoXyzU8Traits::alpha_pos * sizeof(quint8), KoXyzU8Traits::alpha_pos, KoChannelInfo::ALPHA, KoChannelInfo::UINT8, sizeof(quint8))); init(); addStandardCompositeOps(this); } bool XyzU8ColorSpace::willDegrade(ColorSpaceIndependence /*independence*/) const { return false; } KoColorSpace *XyzU8ColorSpace::clone() const { return new XyzU8ColorSpace(name(), profile()->clone()); } void XyzU8ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoXyzU8Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("XYZ"); - labElt.setAttribute("x", KoColorSpaceMaths< KoXyzU8Traits::channels_type, qreal>::scaleToA(p->x)); - labElt.setAttribute("y", KoColorSpaceMaths< KoXyzU8Traits::channels_type, qreal>::scaleToA(p->y)); - labElt.setAttribute("z", KoColorSpaceMaths< KoXyzU8Traits::channels_type, qreal>::scaleToA(p->z)); + labElt.setAttribute("x", KisDomUtils::toString(KoColorSpaceMaths< KoXyzU8Traits::channels_type, qreal>::scaleToA(p->x))); + labElt.setAttribute("y", KisDomUtils::toString(KoColorSpaceMaths< KoXyzU8Traits::channels_type, qreal>::scaleToA(p->y))); + labElt.setAttribute("z", KisDomUtils::toString(KoColorSpaceMaths< KoXyzU8Traits::channels_type, qreal>::scaleToA(p->z))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void XyzU8ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoXyzU8Traits::Pixel *p = reinterpret_cast(pixel); - p->x = KoColorSpaceMaths< qreal, KoXyzU8Traits::channels_type >::scaleToA(elt.attribute("x").toDouble()); - p->y = KoColorSpaceMaths< qreal, KoXyzU8Traits::channels_type >::scaleToA(elt.attribute("y").toDouble()); - p->z = KoColorSpaceMaths< qreal, KoXyzU8Traits::channels_type >::scaleToA(elt.attribute("z").toDouble()); + p->x = KoColorSpaceMaths< qreal, KoXyzU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("x"))); + p->y = KoColorSpaceMaths< qreal, KoXyzU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("y"))); + p->z = KoColorSpaceMaths< qreal, KoXyzU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("z"))); p->alpha = KoColorSpaceMathsTraits::max; } -void XyzU8ColorSpace::toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const +void XyzU8ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { qreal xyx, xyy, xyY = 0.0; XYZToxyY(channelValues[0],channelValues[1],channelValues[2], &xyx, &xyy, &xyY); LabToLCH(xyY,xyx,xyY, hue, sat, luma); } QVector XyzU8ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); qreal xyx, xyy, xyY = 0.0; LCHToLab(*luma, *sat, *hue, &xyY,&xyx,&xyy); xyYToXYZ(xyx, xyy, xyY, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } -void XyzU8ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const +void XyzU8ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { XYZToxyY(channelValues[0],channelValues[1],channelValues[2], u, v, y); } QVector XyzU8ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); xyYToXYZ(*u, *v, *y, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; -} \ No newline at end of file +} diff --git a/plugins/color/lcms2engine/colorspaces/xyz_u8/XyzU8ColorSpace.h b/plugins/color/lcms2engine/colorspaces/xyz_u8/XyzU8ColorSpace.h index e93ba91c59..a44046124c 100644 --- a/plugins/color/lcms2engine/colorspaces/xyz_u8/XyzU8ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/xyz_u8/XyzU8ColorSpace.h @@ -1,115 +1,115 @@ /* * Copyright (c) 2007 Cyrille Berger (cberger@cberger.net) * * 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 * Library 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 KIS_XYZ_U8_COLORSPACE_H_ #define KIS_XYZ_U8_COLORSPACE_H_ #include #include #define TYPE_XYZA_8 (COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)) struct KoXyzU8Traits; class XyzU8ColorSpace : public LcmsColorSpace { public: XyzU8ColorSpace(const QString &name, KoColorProfile *p); virtual bool willDegrade(ColorSpaceIndependence independence) const; static QString colorSpaceId() { return QString("XYZA8"); } virtual KoID colorModelId() const { return XYZAColorModelID; } virtual KoID colorDepthId() const { return Integer8BitsColorDepthID; } virtual KoColorSpace *clone() const; virtual void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const; virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; }; class XyzU8ColorSpaceFactory : public LcmsColorSpaceFactory { public: XyzU8ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_XYZA_8, cmsSigXYZData) { } virtual QString id() const { return XyzU8ColorSpace::colorSpaceId(); } virtual QString name() const { return i18n("XYZ (8-bit integer/channel)"); } virtual bool userVisible() const { return true; } virtual KoID colorModelId() const { return XYZAColorModelID; } virtual KoID colorDepthId() const { return Integer8BitsColorDepthID; } virtual int referenceDepth() const { return 8; } virtual KoColorSpace *createColorSpace(const KoColorProfile *p) const { return new XyzU8ColorSpace(name(), p->clone()); } virtual QString defaultProfile() const { return "XYZ identity built-in"; } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/ycbcr_f32/YCbCrF32ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/ycbcr_f32/YCbCrF32ColorSpace.cpp index 7674fe785d..dc425b1af8 100644 --- a/plugins/color/lcms2engine/colorspaces/ycbcr_f32/YCbCrF32ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/ycbcr_f32/YCbCrF32ColorSpace.cpp @@ -1,110 +1,111 @@ /* * Copyright (c) 2007 Cyrille Berger (cberger@cberger.net) * * 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 * Library 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 "YCbCrF32ColorSpace.h" #include #include #include #include "compositeops/KoCompositeOps.h" -#include "KoColorConversions.h" +#include +#include YCbCrF32ColorSpace::YCbCrF32ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_YCbCrA_FLT, cmsSigXYZData, p) { const IccColorProfile *icc_p = dynamic_cast(p); Q_ASSERT(icc_p); QVector uiRanges(icc_p->getFloatUIMinMax()); Q_ASSERT(uiRanges.size() == 3); addChannel(new KoChannelInfo(i18n("Y"), KoYCbCrF32Traits::Y_pos * sizeof(float), KoYCbCrF32Traits::Y_pos, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, sizeof(float), Qt::cyan, uiRanges[0])); addChannel(new KoChannelInfo(i18n("Cb"), KoYCbCrF32Traits::Cb_pos * sizeof(float), KoYCbCrF32Traits::Cb_pos, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, sizeof(float), Qt::magenta, uiRanges[1])); addChannel(new KoChannelInfo(i18n("Cr"), KoYCbCrF32Traits::Cr_pos * sizeof(float), KoYCbCrF32Traits::Cr_pos, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, sizeof(float), Qt::yellow, uiRanges[2])); addChannel(new KoChannelInfo(i18n("Alpha"), KoYCbCrF32Traits::alpha_pos * sizeof(float), KoYCbCrF32Traits::alpha_pos, KoChannelInfo::ALPHA, KoChannelInfo::FLOAT32, sizeof(float))); init(); addStandardCompositeOps(this); } bool YCbCrF32ColorSpace::willDegrade(ColorSpaceIndependence independence) const { if (independence == TO_RGBA16) { return true; } else { return false; } } KoColorSpace *YCbCrF32ColorSpace::clone() const { return new YCbCrF32ColorSpace(name(), profile()->clone()); } void YCbCrF32ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoYCbCrF32Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("YCbCr"); - labElt.setAttribute("Y", KoColorSpaceMaths< KoYCbCrF32Traits::channels_type, qreal>::scaleToA(p->Y)); - labElt.setAttribute("Cb", KoColorSpaceMaths< KoYCbCrF32Traits::channels_type, qreal>::scaleToA(p->Cb)); - labElt.setAttribute("Cr", KoColorSpaceMaths< KoYCbCrF32Traits::channels_type, qreal>::scaleToA(p->Cr)); + labElt.setAttribute("Y", KisDomUtils::toString(KoColorSpaceMaths< KoYCbCrF32Traits::channels_type, qreal>::scaleToA(p->Y))); + labElt.setAttribute("Cb", KisDomUtils::toString(KoColorSpaceMaths< KoYCbCrF32Traits::channels_type, qreal>::scaleToA(p->Cb))); + labElt.setAttribute("Cr", KisDomUtils::toString(KoColorSpaceMaths< KoYCbCrF32Traits::channels_type, qreal>::scaleToA(p->Cr))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void YCbCrF32ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoYCbCrF32Traits::Pixel *p = reinterpret_cast(pixel); - p->Y = KoColorSpaceMaths< qreal, KoYCbCrF32Traits::channels_type >::scaleToA(elt.attribute("Y").toDouble()); - p->Cb = KoColorSpaceMaths< qreal, KoYCbCrF32Traits::channels_type >::scaleToA(elt.attribute("Cb").toDouble()); - p->Cr = KoColorSpaceMaths< qreal, KoYCbCrF32Traits::channels_type >::scaleToA(elt.attribute("Cr").toDouble()); + p->Y = KoColorSpaceMaths< qreal, KoYCbCrF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("Y"))); + p->Cb = KoColorSpaceMaths< qreal, KoYCbCrF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("Cb"))); + p->Cr = KoColorSpaceMaths< qreal, KoYCbCrF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("Cr"))); p->alpha = 1.0; } -void YCbCrF32ColorSpace::toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const +void YCbCrF32ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { LabToLCH(channelValues[0],channelValues[1],channelValues[2], luma, sat, hue); } QVector YCbCrF32ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); LCHToLab(*luma, *sat, *hue, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } -void YCbCrF32ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const +void YCbCrF32ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { *y =channelValues[0]; *u=channelValues[1]; *v=channelValues[2]; } QVector YCbCrF32ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); channelValues[0]=*y; channelValues[1]=*u; channelValues[2]=*v; channelValues[3]=1.0; return channelValues; -} \ No newline at end of file +} diff --git a/plugins/color/lcms2engine/colorspaces/ycbcr_f32/YCbCrF32ColorSpace.h b/plugins/color/lcms2engine/colorspaces/ycbcr_f32/YCbCrF32ColorSpace.h index b88b669b28..ffb5dcf910 100644 --- a/plugins/color/lcms2engine/colorspaces/ycbcr_f32/YCbCrF32ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/ycbcr_f32/YCbCrF32ColorSpace.h @@ -1,126 +1,126 @@ /* * Copyright (c) 2007 Cyrille Berger (cberger@cberger.net) * * 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 * Library 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 KIS_YCBCR_F32_COLORSPACE_H_ #define KIS_YCBCR_F32_COLORSPACE_H_ #include #include #define TYPE_YCbCrA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|EXTRA_SH(1)|BYTES_SH(4)) struct KoYCbCrF32Traits; class YCbCrF32ColorSpace : public LcmsColorSpace { public: YCbCrF32ColorSpace(const QString &name, KoColorProfile *p); virtual bool willDegrade(ColorSpaceIndependence independence) const; static QString colorSpaceId() { return QString("YCBCRF32"); } virtual KoID colorModelId() const { return YCbCrAColorModelID; } virtual KoID colorDepthId() const { return Float32BitsColorDepthID; } virtual bool hasHighDynamicRange() const { return true; } virtual KoColorSpace *clone() const; virtual void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const; virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; }; class YCbCrF32ColorSpaceFactory : public LcmsColorSpaceFactory { public: YCbCrF32ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_YCbCrA_FLT, cmsSigYCbCrData) { } virtual QString id() const { return YCbCrF32ColorSpace::colorSpaceId(); } virtual QString name() const { return i18n("YCBCR (32-bit float/channel)"); } virtual bool userVisible() const { return true; } virtual KoID colorModelId() const { return YCbCrAColorModelID; } virtual KoID colorDepthId() const { return Float32BitsColorDepthID; } virtual int referenceDepth() const { return 32; } virtual KoColorSpace *createColorSpace(const KoColorProfile *p) const { return new YCbCrF32ColorSpace(name(), p->clone()); } virtual QString defaultProfile() const { return QString(); } virtual bool isHdr() const { return true; } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/ycbcr_u16/YCbCrU16ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/ycbcr_u16/YCbCrU16ColorSpace.cpp index 2c07b123bd..f2d2e44a15 100644 --- a/plugins/color/lcms2engine/colorspaces/ycbcr_u16/YCbCrU16ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/ycbcr_u16/YCbCrU16ColorSpace.cpp @@ -1,103 +1,104 @@ /* * Copyright (c) 2007 Cyrille Berger (cberger@cberger.net) * * 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 * Library 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 "YCbCrU16ColorSpace.h" #include #include #include #include "compositeops/KoCompositeOps.h" -#include "KoColorConversions.h" +#include +#include YCbCrU16ColorSpace::YCbCrU16ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_YCbCrA_16, cmsSigXYZData, p) { addChannel(new KoChannelInfo(i18n("Y"), KoYCbCrU16Traits::Y_pos * sizeof(quint16), KoYCbCrU16Traits::Y_pos, KoChannelInfo::COLOR, KoChannelInfo::UINT16, sizeof(quint16), Qt::cyan)); addChannel(new KoChannelInfo(i18n("Cb"), KoYCbCrU16Traits::Cb_pos * sizeof(quint16), KoYCbCrU16Traits::Cb_pos, KoChannelInfo::COLOR, KoChannelInfo::UINT16, sizeof(quint16), Qt::magenta)); addChannel(new KoChannelInfo(i18n("Cr"), KoYCbCrU16Traits::Cr_pos * sizeof(quint16), KoYCbCrU16Traits::Cr_pos, KoChannelInfo::COLOR, KoChannelInfo::UINT16, sizeof(quint16), Qt::yellow)); addChannel(new KoChannelInfo(i18n("Alpha"), KoYCbCrU16Traits::alpha_pos * sizeof(quint16), KoYCbCrU16Traits::alpha_pos, KoChannelInfo::ALPHA, KoChannelInfo::UINT16, sizeof(quint16))); init(); addStandardCompositeOps(this); } bool YCbCrU16ColorSpace::willDegrade(ColorSpaceIndependence independence) const { if (independence == TO_RGBA8) { return true; } else { return false; } } KoColorSpace *YCbCrU16ColorSpace::clone() const { return new YCbCrU16ColorSpace(name(), profile()->clone()); } void YCbCrU16ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoYCbCrU16Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("YCbCr"); - labElt.setAttribute("Y", KoColorSpaceMaths< KoYCbCrU16Traits::channels_type, qreal>::scaleToA(p->Y)); - labElt.setAttribute("Cb", KoColorSpaceMaths< KoYCbCrU16Traits::channels_type, qreal>::scaleToA(p->Cb)); - labElt.setAttribute("Cr", KoColorSpaceMaths< KoYCbCrU16Traits::channels_type, qreal>::scaleToA(p->Cr)); + labElt.setAttribute("Y", KisDomUtils::toString(KoColorSpaceMaths< KoYCbCrU16Traits::channels_type, qreal>::scaleToA(p->Y))); + labElt.setAttribute("Cb", KisDomUtils::toString(KoColorSpaceMaths< KoYCbCrU16Traits::channels_type, qreal>::scaleToA(p->Cb))); + labElt.setAttribute("Cr", KisDomUtils::toString(KoColorSpaceMaths< KoYCbCrU16Traits::channels_type, qreal>::scaleToA(p->Cr))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void YCbCrU16ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoYCbCrU16Traits::Pixel *p = reinterpret_cast(pixel); - p->Y = KoColorSpaceMaths< qreal, KoYCbCrU16Traits::channels_type >::scaleToA(elt.attribute("Y").toDouble()); - p->Cb = KoColorSpaceMaths< qreal, KoYCbCrU16Traits::channels_type >::scaleToA(elt.attribute("Cb").toDouble()); - p->Cr = KoColorSpaceMaths< qreal, KoYCbCrU16Traits::channels_type >::scaleToA(elt.attribute("Cr").toDouble()); + p->Y = KoColorSpaceMaths< qreal, KoYCbCrU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("Y"))); + p->Cb = KoColorSpaceMaths< qreal, KoYCbCrU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("Cb"))); + p->Cr = KoColorSpaceMaths< qreal, KoYCbCrU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("Cr"))); p->alpha = KoColorSpaceMathsTraits::max; } -void YCbCrU16ColorSpace::toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const +void YCbCrU16ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { LabToLCH(channelValues[0],channelValues[1],channelValues[2], luma, sat, hue); } QVector YCbCrU16ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); LCHToLab(*luma, *sat, *hue, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } -void YCbCrU16ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const +void YCbCrU16ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { *y =channelValues[0]; *u=channelValues[1]; *v=channelValues[2]; } QVector YCbCrU16ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); channelValues[0]=*y; channelValues[1]=*u; channelValues[2]=*v; channelValues[3]=1.0; return channelValues; -} \ No newline at end of file +} diff --git a/plugins/color/lcms2engine/colorspaces/ycbcr_u16/YCbCrU16ColorSpace.h b/plugins/color/lcms2engine/colorspaces/ycbcr_u16/YCbCrU16ColorSpace.h index d29cb55882..a6e941e96e 100644 --- a/plugins/color/lcms2engine/colorspaces/ycbcr_u16/YCbCrU16ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/ycbcr_u16/YCbCrU16ColorSpace.h @@ -1,115 +1,115 @@ /* * Copyright (c) 2007 Cyrille Berger (cberger@cberger.net) * * 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 * Library 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 KIS_YCBCR_U16_COLORSPACE_H_ #define KIS_YCBCR_U16_COLORSPACE_H_ #include #include #define TYPE_YCbCrA_16 (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)) struct KoYCbCrU16Traits; class YCbCrU16ColorSpace : public LcmsColorSpace { public: YCbCrU16ColorSpace(const QString &name, KoColorProfile *p); virtual bool willDegrade(ColorSpaceIndependence independence) const; static QString colorSpaceId() { return QString("YCBCRAU16"); } virtual KoID colorModelId() const { return YCbCrAColorModelID; } virtual KoID colorDepthId() const { return Integer16BitsColorDepthID; } virtual KoColorSpace *clone() const; virtual void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const; virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; }; class YCbCrU16ColorSpaceFactory : public LcmsColorSpaceFactory { public: YCbCrU16ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_YCbCrA_16, cmsSigYCbCrData) { } virtual QString id() const { return YCbCrU16ColorSpace::colorSpaceId(); } virtual QString name() const { return i18n("YCBCR (16-bit integer/channel)"); } virtual bool userVisible() const { return true; } virtual KoID colorModelId() const { return YCbCrAColorModelID; } virtual KoID colorDepthId() const { return Integer16BitsColorDepthID; } virtual int referenceDepth() const { return 16; } virtual KoColorSpace *createColorSpace(const KoColorProfile *p) const { return new YCbCrU16ColorSpace(name(), p->clone()); } virtual QString defaultProfile() const { return QString(); } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/ycbcr_u8/YCbCrU8ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/ycbcr_u8/YCbCrU8ColorSpace.cpp index 5703f6f41c..e1a2b24b0d 100644 --- a/plugins/color/lcms2engine/colorspaces/ycbcr_u8/YCbCrU8ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/ycbcr_u8/YCbCrU8ColorSpace.cpp @@ -1,99 +1,101 @@ /* * Copyright (c) 2007 Cyrille Berger (cberger@cberger.net) * * 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 * Library 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 "YCbCrU8ColorSpace.h" #include #include #include #include "compositeops/KoCompositeOps.h" -#include "KoColorConversions.h" +#include + +#include YCbCrU8ColorSpace::YCbCrU8ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_YCbCrA_8, cmsSigXYZData, p) { addChannel(new KoChannelInfo(i18n("Y"), KoYCbCrU8Traits::Y_pos * sizeof(quint8), KoYCbCrU8Traits::Y_pos, KoChannelInfo::COLOR, KoChannelInfo::UINT8, sizeof(quint8), Qt::cyan)); addChannel(new KoChannelInfo(i18n("Cb"), KoYCbCrU8Traits::Cb_pos * sizeof(quint8), KoYCbCrU8Traits::Cb_pos, KoChannelInfo::COLOR, KoChannelInfo::UINT8, sizeof(quint8), Qt::magenta)); addChannel(new KoChannelInfo(i18n("Cr"), KoYCbCrU8Traits::Cr_pos * sizeof(quint8), KoYCbCrU8Traits::Cr_pos, KoChannelInfo::COLOR, KoChannelInfo::UINT8, sizeof(quint8), Qt::yellow)); addChannel(new KoChannelInfo(i18n("Alpha"), KoYCbCrU8Traits::alpha_pos * sizeof(quint8), KoYCbCrU8Traits::alpha_pos, KoChannelInfo::ALPHA, KoChannelInfo::UINT8, sizeof(quint8))); init(); addStandardCompositeOps(this); } bool YCbCrU8ColorSpace::willDegrade(ColorSpaceIndependence /*independence*/) const { return false; } KoColorSpace *YCbCrU8ColorSpace::clone() const { return new YCbCrU8ColorSpace(name(), profile()->clone()); } void YCbCrU8ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoYCbCrU8Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("YCbCr"); - labElt.setAttribute("Y", KoColorSpaceMaths< KoYCbCrU8Traits::channels_type, qreal>::scaleToA(p->Y)); - labElt.setAttribute("Cb", KoColorSpaceMaths< KoYCbCrU8Traits::channels_type, qreal>::scaleToA(p->Cb)); - labElt.setAttribute("Cr", KoColorSpaceMaths< KoYCbCrU8Traits::channels_type, qreal>::scaleToA(p->Cr)); + labElt.setAttribute("Y", KisDomUtils::toString(KoColorSpaceMaths< KoYCbCrU8Traits::channels_type, qreal>::scaleToA(p->Y))); + labElt.setAttribute("Cb", KisDomUtils::toString(KoColorSpaceMaths< KoYCbCrU8Traits::channels_type, qreal>::scaleToA(p->Cb))); + labElt.setAttribute("Cr", KisDomUtils::toString(KoColorSpaceMaths< KoYCbCrU8Traits::channels_type, qreal>::scaleToA(p->Cr))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void YCbCrU8ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoYCbCrU8Traits::Pixel *p = reinterpret_cast(pixel); - p->Y = KoColorSpaceMaths< qreal, KoYCbCrU8Traits::channels_type >::scaleToA(elt.attribute("Y").toDouble()); - p->Cb = KoColorSpaceMaths< qreal, KoYCbCrU8Traits::channels_type >::scaleToA(elt.attribute("Cb").toDouble()); - p->Cr = KoColorSpaceMaths< qreal, KoYCbCrU8Traits::channels_type >::scaleToA(elt.attribute("Cr").toDouble()); + p->Y = KoColorSpaceMaths< qreal, KoYCbCrU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("Y"))); + p->Cb = KoColorSpaceMaths< qreal, KoYCbCrU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("Cb"))); + p->Cr = KoColorSpaceMaths< qreal, KoYCbCrU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("Cr"))); p->alpha = KoColorSpaceMathsTraits::max; } -void YCbCrU8ColorSpace::toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const +void YCbCrU8ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { LabToLCH(channelValues[0],channelValues[1],channelValues[2], luma, sat, hue); } QVector YCbCrU8ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); LCHToLab(*luma, *sat, *hue, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } -void YCbCrU8ColorSpace::toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const +void YCbCrU8ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { *y =channelValues[0]; *u=channelValues[1]; *v=channelValues[2]; } QVector YCbCrU8ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); channelValues[0]=*y; channelValues[1]=*u; channelValues[2]=*v; channelValues[3]=1.0; return channelValues; -} \ No newline at end of file +} diff --git a/plugins/color/lcms2engine/colorspaces/ycbcr_u8/YCbCrU8ColorSpace.h b/plugins/color/lcms2engine/colorspaces/ycbcr_u8/YCbCrU8ColorSpace.h index c78f26aca0..db0648ed0f 100644 --- a/plugins/color/lcms2engine/colorspaces/ycbcr_u8/YCbCrU8ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/ycbcr_u8/YCbCrU8ColorSpace.h @@ -1,115 +1,115 @@ /* * Copyright (c) 2007 Cyrille Berger (cberger@cberger.net) * * 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 * Library 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 KIS_YCBCR_U8_COLORSPACE_H_ #define KIS_YCBCR_U8_COLORSPACE_H_ #include #include #define TYPE_YCbCrA_8 (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)) struct KoYCbCrU8Traits; class YCbCrU8ColorSpace : public LcmsColorSpace { public: YCbCrU8ColorSpace(const QString &name, KoColorProfile *p); virtual bool willDegrade(ColorSpaceIndependence independence) const; static QString colorSpaceId() { return QString("YCBCRA8"); } virtual KoID colorModelId() const { return YCbCrAColorModelID; } virtual KoID colorDepthId() const { return Integer8BitsColorDepthID; } virtual KoColorSpace *clone() const; virtual void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const; virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const; - virtual void toHSY(QVector channelValues, qreal *hue, qreal *sat, qreal *luma) const; + virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const; - virtual void toYUV(QVector channelValues, qreal *y, qreal *u, qreal *v) const; + virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const; }; class YCbCrU8ColorSpaceFactory : public LcmsColorSpaceFactory { public: YCbCrU8ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_YCbCrA_8, cmsSigYCbCrData) { } virtual QString id() const { return YCbCrU8ColorSpace::colorSpaceId(); } virtual QString name() const { return i18n("YCBCR (8-bit integer/channel)"); } virtual bool userVisible() const { return true; } virtual KoID colorModelId() const { return YCbCrAColorModelID; } virtual KoID colorDepthId() const { return Integer8BitsColorDepthID; } virtual int referenceDepth() const { return 8; } virtual KoColorSpace *createColorSpace(const KoColorProfile *p) const { return new YCbCrU8ColorSpace(name(), p->clone()); } virtual QString defaultProfile() const { return QString(); } }; #endif diff --git a/plugins/color/lcms2engine/tests/CMakeLists.txt b/plugins/color/lcms2engine/tests/CMakeLists.txt index b8e7dc449a..102588534a 100644 --- a/plugins/color/lcms2engine/tests/CMakeLists.txt +++ b/plugins/color/lcms2engine/tests/CMakeLists.txt @@ -1,45 +1,43 @@ add_definitions(-DFILES_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data/") set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) include_directories( ../colorspaces/cmyk_u16 ../colorspaces/cmyk_u8 ../colorspaces/gray_u16 - ../colorspaces/gray_u16_no_alpha ../colorspaces/gray_u8 - ../colorspaces/gray_u8_no_alpha ../colorspaces/lab_u16 ../colorspaces/rgb_u16 ../colorspaces/rgb_u8 ../colorspaces/xyz_u16 ../colorprofiles .. ) if(OPENEXR_FOUND) include_directories(SYSTEM ${OPENEXR_INCLUDE_DIR}) endif() include_directories( ${LCMS2_INCLUDE_DIR} ) if(MSVC OR (WIN32 AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")) # avoid "cannot open file 'LIBC.lib'" error set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NODEFAULTLIB:LIBC.LIB") endif() ########### next target ############### set(TestKoLcmsColorProfile_test_SRCS TestKoLcmsColorProfile.cpp ) kde4_add_unit_test(TestKoLcmsColorProfile TESTNAME libs-pigment-TestKoLcmsColorProfile ${TestKoLcmsColorProfile_test_SRCS}) target_link_libraries(TestKoLcmsColorProfile kritawidgets kritapigment KF5::I18n Qt5::Test ${LCMS2_LIBRARIES} ) ########### next target ############### set(TestKoColorSpaceRegistry_test_SRCS TestKoColorSpaceRegistry.cpp ) kde4_add_broken_unit_test(TestKoColorSpaceRegistry TESTNAME libs-pigment-TestKoColorSpaceRegistry ${TestKoColorSpaceRegistry_test_SRCS}) target_link_libraries(TestKoColorSpaceRegistry kritawidgets kritapigment ${LCMS2_LIBRARIES} KF5::I18n Qt5::Test) diff --git a/plugins/dockers/advancedcolorselector/kis_common_colors_recalculation_runner.cpp b/plugins/dockers/advancedcolorselector/kis_common_colors_recalculation_runner.cpp index 9a14917d45..b9e1982416 100644 --- a/plugins/dockers/advancedcolorselector/kis_common_colors_recalculation_runner.cpp +++ b/plugins/dockers/advancedcolorselector/kis_common_colors_recalculation_runner.cpp @@ -1,213 +1,213 @@ #include "kis_common_colors_recalculation_runner.h" #include #include #include "KoColor.h" #include "KoColorSpaceRegistry.h" #include "kis_common_colors.h" enum ColorAxis {RedAxis=0, GreenAxis, BlueAxis}; class Color { public: Color(QRgb rgb) : r(qRed(rgb)), g(qGreen(rgb)), b(qBlue(rgb)) {} unsigned char r; unsigned char g; unsigned char b; inline unsigned char operator[](ColorAxis i) const { if(i==RedAxis) return r; if(i==GreenAxis) return g; return b; } }; class VBox { QList m_colors; public: VBox(QList rgbList) { QList colorList; for(int i=0; i colorList) : m_colors(colorList) {} int population() const { return m_colors.size(); } VBox divide() { ColorAxis axis = biggestAxis(); Q_ASSERT(axisSize(axis)>=3); unsigned char divpos = divPos(axis); QList newVBoxColors; for(int i=m_colors.size()-1; i>=0; i--) { Color c = m_colors.at(i); if(c[axis]>divpos) { m_colors.removeAt(i); newVBoxColors.append(c); } } return VBox(newVBoxColors); } QRgb mean() const { int r=0; int g=0; int b=0; for(int i=0;i0); return qRgb(r/size, g/size, b/size); } unsigned char axisSize(ColorAxis axis) const { unsigned char valMin = 255; unsigned char valMax = 0; for(int i=0; ivalMax) valMax=m_colors.at(i)[axis]; if(m_colors.at(i)[axis]sG && sR>sB) return RedAxis; if(sG>sR && sG>sB) return GreenAxis; return BlueAxis; } private: // unsigned char divPos(ColorAxis axis) const // { // QList values; // for(int i=0;im_colors.at(i)[axis]) min=m_colors.at(i)[axis]; if(maxsetColors(extractColors()); } QList KisCommonColorsRecalculationRunner::extractColors() { QList colors = getColors(); VBox startBox(colors); QList boxes; boxes.append(startBox); while (boxes.size()m_numColors*3/5) { int biggestBox=-1; int biggestBoxPopulation=-1; for(int i=0; ibiggestBoxPopulation && boxes.at(i).axisSize(boxes.at(i).biggestAxis())>=3) { biggestBox=i; biggestBoxPopulation=boxes.at(i).population(); } } if(biggestBox==-1 || boxes[biggestBox].population()<=3) break; VBox newBox = boxes[biggestBox].divide(); boxes.append(newBox); } while (boxes.size()m_numColors) { int biggestBox=-1; int biggestBoxAxisSize=-1; for(int i=0; ibiggestBoxAxisSize && boxes.at(i).axisSize(boxes.at(i).biggestAxis())>=3) { biggestBox=i; biggestBoxAxisSize=boxes.at(i).axisSize(boxes.at(i).biggestAxis()); } } if(biggestBox==-1 || boxes[biggestBox].population()<=3) break; VBox newBox = boxes[biggestBox].divide(); boxes.append(newBox); } const KoColorSpace* colorSpace = KoColorSpaceRegistry::instance()->rgb8(); QList colorList; for(int i=0; i=1) { colorList.append(KoColor(QColor(boxes.at(i).mean()), colorSpace)); } } return colorList; } QList KisCommonColorsRecalculationRunner::getColors() { int width = m_imageData.width(); int height = m_imageData.height(); QImage tmpImage; int pixelCount = height*width; if(pixelCount> (1<<16)) { qreal factor = sqrt((1<<16)/(qreal) pixelCount); - tmpImage=m_imageData.scaledToWidth(width*factor); + tmpImage = m_imageData.scaledToWidth(width*factor); } else { - tmpImage=m_imageData; + tmpImage = m_imageData; } width=tmpImage.width(); height=tmpImage.height(); QSet colorList; for (int i=0; i * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_animation_utils.h" #include "kundo2command.h" #include "kis_algebra_2d.h" #include "kis_image.h" #include "kis_node.h" #include "kis_keyframe_channel.h" #include "kis_post_execution_undo_adapter.h" #include "kis_global.h" #include "kis_tool_utils.h" #include "kis_image_animation_interface.h" namespace KisAnimationUtils { const QString addFrameActionName = i18n("New Frame"); const QString duplicateFrameActionName = i18n("Copy Frame"); const QString removeFrameActionName = i18n("Remove Frame"); const QString removeFramesActionName = i18n("Remove Frames"); const QString lazyFrameCreationActionName = i18n("Auto Frame Mode"); const QString dropFramesActionName = i18n("Drop Frames"); const QString showLayerActionName = i18n("Show in Timeline"); const QString newLayerActionName = i18n("New Layer"); const QString addExistingLayerActionName = i18n("Add Existing Layer"); const QString removeLayerActionName = i18n("Remove Layer"); const QString addOpacityKeyframeActionName = i18n("Add opacity keyframe"); const QString removeOpacityKeyframeActionName = i18n("Remove opacity keyframe"); bool createKeyframeLazy(KisImageSP image, KisNodeSP node, const QString &channelId, int time, bool copy) { KisKeyframeChannel *channel = node->getKeyframeChannel(channelId); bool createdChannel = false; if (!channel) { node->enableAnimation(); channel = node->getKeyframeChannel(channelId, true); if (!channel) return false; createdChannel = true; } if (copy) { if (channel->keyframeAt(time)) return false; KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Copy Keyframe")); KisKeyframeSP srcFrame = channel->activeKeyframeAt(time); channel->copyKeyframe(srcFrame, time, cmd); image->postExecutionUndoAdapter()->addCommand(toQShared(cmd)); } else { if (channel->keyframeAt(time)) { if (createdChannel) return false; if (image->animationInterface()->currentTime() == time && channelId == KisKeyframeChannel::Content.id()) { //shortcut: clearing the image instead if (KisToolUtils::clearImage(image, node, 0)) { return true; } } //fallback: erasing the keyframe and creating it again } KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Add Keyframe")); channel->addKeyframe(time, cmd); image->postExecutionUndoAdapter()->addCommand(toQShared(cmd)); } return true; } bool removeKeyframes(KisImageSP image, const FrameItemList &frames) { bool result = false; QScopedPointer cmd( new KUndo2Command(kundo2_i18np("Remove Keyframe", "Remove Keyframes", frames.size()))); // lisp-lovers present ;) Q_FOREACH (const FrameItem &item, frames) { const int time = item.time; KisNodeSP node = item.node; KisKeyframeChannel *channel = node->getKeyframeChannel(item.channel); if (!channel) continue; KisKeyframeSP keyframe = channel->keyframeAt(time); if (!keyframe) continue; channel->deleteKeyframe(keyframe, cmd.data()); result = true; } if (result) { image->postExecutionUndoAdapter()->addCommand(toQShared(cmd.take())); } return result; } bool removeKeyframe(KisImageSP image, KisNodeSP node, const QString &channel, int time) { QVector frames; frames << FrameItem(node, channel, time); return removeKeyframes(image, frames); } struct LessOperator { LessOperator(const QPoint &offset) : m_columnCoeff(-KisAlgebra2D::signPZ(offset.x())), m_rowCoeff(-1000000 * KisAlgebra2D::signZZ(offset.y())) { } bool operator()(const QPoint &lhs, const QPoint &rhs) { return m_columnCoeff * lhs.x() + m_rowCoeff * lhs.y() < m_columnCoeff * rhs.x() + m_rowCoeff * rhs.y(); } private: int m_columnCoeff; int m_rowCoeff; }; void sortPointsForSafeMove(QVector *points, const QPoint &offset) { qSort(points->begin(), points->end(), LessOperator(offset)); } bool moveKeyframes(KisImageSP image, const FrameItemList &srcFrames, const FrameItemList &dstFrames, bool copy) { if (srcFrames.size() != dstFrames.size()) return false; bool result = false; QScopedPointer cmd( new KUndo2Command(!copy ? kundo2_i18np("Move Keyframe", - "Move Keyframes", + "Move %1 Keyframes", srcFrames.size()) : kundo2_i18np("Copy Keyframe", - "Copy Keyframes", + "Copy %1 Keyframes", srcFrames.size()))); // lisp-lovers present ;) for (int i = 0; i < srcFrames.size(); i++) { const int srcTime = srcFrames[i].time; KisNodeSP srcNode = srcFrames[i].node; const int dstTime = dstFrames[i].time; KisNodeSP dstNode = dstFrames[i].node; if (srcNode == dstNode) { KisKeyframeChannel *content = srcNode->getKeyframeChannel(KisKeyframeChannel::Content.id()); if (!content) continue; KisKeyframeSP srcKeyframe = content->keyframeAt(srcTime); if (srcKeyframe) { if (copy) { content->copyKeyframe(srcKeyframe, dstTime, cmd.data()); } else { content->moveKeyframe(srcKeyframe, dstTime, cmd.data()); } } } else { KisKeyframeChannel *srcContent = srcNode->getKeyframeChannel(KisKeyframeChannel::Content.id()); KisKeyframeChannel *dstContent = dstNode->getKeyframeChannel(KisKeyframeChannel::Content.id()); if (!srcContent || !dstContent) continue; KisKeyframeSP srcKeyframe = srcContent->keyframeAt(srcTime); if (!srcKeyframe) continue; dstContent->copyExternalKeyframe(srcContent, srcTime, dstTime, cmd.data()); if (!copy) { srcContent->deleteKeyframe(srcKeyframe, cmd.data()); } } result = true; } if (result) { image->postExecutionUndoAdapter()->addCommand(toQShared(cmd.take())); } return result; } bool moveKeyframe(KisImageSP image, KisNodeSP node, const QString &channel, int srcTime, int dstTime) { QVector srcFrames; srcFrames << FrameItem(node, channel, srcTime); QVector dstFrames; dstFrames << FrameItem(node, channel, dstTime); return moveKeyframes(image, srcFrames, dstFrames); } } diff --git a/plugins/dockers/animation/timeline_color_scheme.cpp b/plugins/dockers/animation/timeline_color_scheme.cpp index 12cecda1b8..6c2f6d46b2 100644 --- a/plugins/dockers/animation/timeline_color_scheme.cpp +++ b/plugins/dockers/animation/timeline_color_scheme.cpp @@ -1,137 +1,139 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "timeline_color_scheme.h" #include #include #include #include #include #include #include "kis_debug.h" #include "krita_utils.h" #include Q_GLOBAL_STATIC(TimelineColorScheme, s_instance) struct TimelineColorScheme::Private { QColor baseColor; }; TimelineColorScheme::TimelineColorScheme() : m_d(new Private) { m_d->baseColor = QColor(137, 192, 221); } TimelineColorScheme::~TimelineColorScheme() { } TimelineColorScheme* TimelineColorScheme::instance() { return s_instance; } QColor TimelineColorScheme::selectorColor() const { return QColor(223, 148, 51); } QColor TimelineColorScheme::selectionColor() const { //return qApp->palette().color(QPalette::Highlight); return selectorColor(); } QColor TimelineColorScheme::activeLayerBackground() const { QColor color = qApp->palette().color(QPalette::Highlight); return color; } QBrush TimelineColorScheme::headerEmpty() const { return qApp->palette().brush(QPalette::Button); } QBrush TimelineColorScheme::headerCachedFrame() const { - return headerEmpty().color().darker(115); + QColor bgColor = qApp->palette().color(QPalette::Base); + int darkenCoeff = bgColor.value() > 128 ? 150 : 50; + return headerEmpty().color().darker(darkenCoeff); } QBrush TimelineColorScheme::headerActive() const { return selectorColor(); } QColor TimelineColorScheme::frameColor(bool present, bool active) const { QColor color = Qt::transparent; if (present && !active) { color = m_d->baseColor; } else if (present && active) { QColor bgColor = qApp->palette().color(QPalette::Base); int darkenCoeff = bgColor.value() > 128 ? 130 : 80; color = m_d->baseColor.darker(darkenCoeff); } else if (!present && active) { QColor bgColor = qApp->palette().color(QPalette::Base); return KritaUtils::blendColors(m_d->baseColor, bgColor, 0.2); } return color; } QColor TimelineColorScheme::onionSkinsSliderColor() const { return m_d->baseColor; } QColor TimelineColorScheme::onionSkinsButtonColor() const { QColor bgColor = qApp->palette().color(QPalette::Base); const int lighterCoeff = bgColor.value() > 128 ? 120 : 80; return m_d->baseColor.lighter(lighterCoeff); } QFont TimelineColorScheme::getOnionSkinsFont(const QString &maxString, const QSize &availableSize) const { QFont font = qApp->font(); while(font.pointSize() > 8) { QFontMetrics fm(font); QRect rc = fm.boundingRect(maxString); if (rc.width() > availableSize.width() || rc.height() > availableSize.height()) { font.setPointSize(font.pointSize() - 1); } else { break; } } return font; } diff --git a/plugins/dockers/defaultdockers/kis_layer_box.cpp b/plugins/dockers/defaultdockers/kis_layer_box.cpp index fbb9e2c8f2..3c7a88fb33 100644 --- a/plugins/dockers/defaultdockers/kis_layer_box.cpp +++ b/plugins/dockers/defaultdockers/kis_layer_box.cpp @@ -1,850 +1,853 @@ /* * kis_layer_box.cc - part of Krita aka Krayon aka KimageShop * * Copyright (c) 2002 Patrick Julien * Copyright (C) 2006 Gábor Lehel * Copyright (C) 2007 Thomas Zander * Copyright (C) 2007 Boudewijn Rempt * Copyright (c) 2011 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_layer_box.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 "kis_action.h" #include "kis_action_manager.h" #include "widgets/kis_cmb_composite.h" #include "widgets/kis_slider_spin_box.h" #include "KisViewManager.h" #include "kis_node_manager.h" #include "kis_node_model.h" #include "canvas/kis_canvas2.h" #include "KisDocument.h" #include "kis_dummies_facade_base.h" #include "kis_shape_controller.h" #include "kis_selection_mask.h" #include "kis_config.h" #include "KisView.h" #include "krita_utils.h" #include "sync_button_and_action.h" #include "kis_color_label_selector_widget.h" #include "kis_signals_blocker.h" #include "kis_color_filter_combo.h" #include "kis_node_filter_proxy_model.h" +#include "kis_layer_utils.h" + #include "ui_wdglayerbox.h" inline void KisLayerBox::connectActionToButton(KisViewManager* view, QAbstractButton *button, const QString &id) { Q_ASSERT(view); KisAction *action = view->actionManager()->actionByName(id); connect(button, SIGNAL(clicked()), action, SLOT(trigger())); connect(action, SIGNAL(sigEnableSlaves(bool)), button, SLOT(setEnabled(bool))); } inline void KisLayerBox::addActionToMenu(QMenu *menu, const QString &id) { if (m_canvas) { menu->addAction(m_canvas->viewManager()->actionManager()->actionByName(id)); } } KisLayerBox::KisLayerBox() : QDockWidget(i18n("Layers")) , m_canvas(0) , m_wdgLayerBox(new Ui_WdgLayerBox) , m_thumbnailCompressor(500, KisSignalCompressor::FIRST_INACTIVE) , m_colorLabelCompressor(900, KisSignalCompressor::FIRST_INACTIVE) { KisConfig cfg; QWidget* mainWidget = new QWidget(this); setWidget(mainWidget); m_opacityDelayTimer.setSingleShot(true); m_wdgLayerBox->setupUi(mainWidget); connect(m_wdgLayerBox->listLayers, SIGNAL(contextMenuRequested(const QPoint&, const QModelIndex&)), this, SLOT(slotContextMenuRequested(const QPoint&, const QModelIndex&))); connect(m_wdgLayerBox->listLayers, SIGNAL(collapsed(const QModelIndex&)), SLOT(slotCollapsed(const QModelIndex &))); connect(m_wdgLayerBox->listLayers, SIGNAL(expanded(const QModelIndex&)), SLOT(slotExpanded(const QModelIndex &))); connect(m_wdgLayerBox->listLayers, SIGNAL(selectionChanged(const QModelIndexList&)), SLOT(selectionChanged(const QModelIndexList&))); m_wdgLayerBox->bnAdd->setIcon(KisIconUtils::loadIcon("addlayer")); m_wdgLayerBox->bnDelete->setIcon(KisIconUtils::loadIcon("deletelayer")); m_wdgLayerBox->bnDelete->setIconSize(QSize(22, 22)); m_wdgLayerBox->bnRaise->setEnabled(false); m_wdgLayerBox->bnRaise->setIcon(KisIconUtils::loadIcon("arrowupblr")); m_wdgLayerBox->bnRaise->setIconSize(QSize(22, 22)); m_wdgLayerBox->bnLower->setEnabled(false); m_wdgLayerBox->bnLower->setIcon(KisIconUtils::loadIcon("arrowdown")); m_wdgLayerBox->bnLower->setIconSize(QSize(22, 22)); m_wdgLayerBox->bnProperties->setIcon(KisIconUtils::loadIcon("properties")); m_wdgLayerBox->bnProperties->setIconSize(QSize(22, 22)); m_wdgLayerBox->bnDuplicate->setIcon(KisIconUtils::loadIcon("duplicatelayer")); m_wdgLayerBox->bnDuplicate->setIconSize(QSize(22, 22)); if (cfg.sliderLabels()) { m_wdgLayerBox->opacityLabel->hide(); m_wdgLayerBox->doubleOpacity->setPrefix(QString("%1: ").arg(i18n("Opacity"))); } m_wdgLayerBox->doubleOpacity->setRange(0, 100, 0); m_wdgLayerBox->doubleOpacity->setSuffix("%"); connect(m_wdgLayerBox->doubleOpacity, SIGNAL(valueChanged(qreal)), SLOT(slotOpacitySliderMoved(qreal))); connect(&m_opacityDelayTimer, SIGNAL(timeout()), SLOT(slotOpacityChanged())); connect(m_wdgLayerBox->cmbComposite, SIGNAL(activated(int)), SLOT(slotCompositeOpChanged(int))); m_selectOpaque = new KisAction(i18n("&Select Opaque"), this); m_selectOpaque->setActivationFlags(KisAction::ACTIVE_LAYER); m_selectOpaque->setActivationConditions(KisAction::SELECTION_EDITABLE); m_selectOpaque->setObjectName("select_opaque"); connect(m_selectOpaque, SIGNAL(triggered(bool)), this, SLOT(slotSelectOpaque())); m_actions.append(m_selectOpaque); m_newLayerMenu = new QMenu(this); m_wdgLayerBox->bnAdd->setMenu(m_newLayerMenu); m_wdgLayerBox->bnAdd->setPopupMode(QToolButton::MenuButtonPopup); m_nodeModel = new KisNodeModel(this); m_filteringModel = new KisNodeFilterProxyModel(this); m_filteringModel->setNodeModel(m_nodeModel); /** * Connect model updateUI() to enable/disable controls. * Note: nodeActivated() is connected separately in setImage(), because * it needs particular order of calls: first the connection to the * node manager should be called, then updateUI() */ connect(m_nodeModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), SLOT(updateUI())); connect(m_nodeModel, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), SLOT(updateUI())); connect(m_nodeModel, SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)), SLOT(updateUI())); connect(m_nodeModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), SLOT(updateUI())); connect(m_nodeModel, SIGNAL(modelReset()), SLOT(slotModelReset())); KisAction *showGlobalSelectionMask = new KisAction(i18n("&Show Global Selection Mask"), this); showGlobalSelectionMask->setObjectName("show-global-selection-mask"); showGlobalSelectionMask->setToolTip(i18nc("@info:tooltip", "Shows global selection as a usual selection mask in Layers docker")); showGlobalSelectionMask->setCheckable(true); connect(showGlobalSelectionMask, SIGNAL(triggered(bool)), SLOT(slotEditGlobalSelection(bool))); m_actions.append(showGlobalSelectionMask); showGlobalSelectionMask->setChecked(cfg.showGlobalSelection()); m_colorSelector = new KisColorLabelSelectorWidget(this); connect(m_colorSelector, SIGNAL(currentIndexChanged(int)), SLOT(slotColorLabelChanged(int))); m_colorSelectorAction = new QWidgetAction(this); m_colorSelectorAction->setDefaultWidget(m_colorSelector); connect(m_nodeModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), &m_colorLabelCompressor, SLOT(start())); m_wdgLayerBox->listLayers->setModel(m_filteringModel); // this connection should be done *after* the setModel() call to // happen later than the internal selection model connect(m_filteringModel.data(), &KisNodeFilterProxyModel::rowsAboutToBeRemoved, this, &KisLayerBox::slotAboutToRemoveRows); connect(m_wdgLayerBox->cmbFilter, SIGNAL(selectedColorsChanged()), SLOT(updateLayerFiltering())); setEnabled(false); connect(&m_thumbnailCompressor, SIGNAL(timeout()), SLOT(updateThumbnail())); connect(&m_colorLabelCompressor, SIGNAL(timeout()), SLOT(updateAvailableLabels())); } KisLayerBox::~KisLayerBox() { delete m_wdgLayerBox; } void expandNodesRecursively(KisNodeSP root, QPointer filteringModel, KisNodeView *nodeView) { if (!root) return; if (filteringModel.isNull()) return; if (!nodeView) return; nodeView->blockSignals(true); KisNodeSP node = root->firstChild(); while (node) { QModelIndex idx = filteringModel->indexFromNode(node); if (idx.isValid()) { - if (node->collapsed()) { - nodeView->collapse(idx); - } + nodeView->setExpanded(idx, !node->collapsed()); } if (node->childCount() > 0) { expandNodesRecursively(node, filteringModel, nodeView); } node = node->nextSibling(); } nodeView->blockSignals(false); } void KisLayerBox::setMainWindow(KisViewManager* kisview) { m_nodeManager = kisview->nodeManager(); Q_FOREACH (KisAction *action, m_actions) { kisview->actionManager()-> addAction(action->objectName(), action); } connectActionToButton(kisview, m_wdgLayerBox->bnAdd, "add_new_paint_layer"); connectActionToButton(kisview, m_wdgLayerBox->bnDuplicate, "duplicatelayer"); KisActionManager *actionManager = kisview->actionManager(); KisAction *action = actionManager->createAction("RenameCurrentLayer"); connect(action, SIGNAL(triggered()), this, SLOT(slotRenameCurrentNode())); m_propertiesAction = actionManager->createAction("layer_properties"); new SyncButtonAndAction(m_propertiesAction, m_wdgLayerBox->bnProperties, this); connect(m_propertiesAction, SIGNAL(triggered()), this, SLOT(slotPropertiesClicked())); m_removeAction = actionManager->createAction("remove_layer"); new SyncButtonAndAction(m_removeAction, m_wdgLayerBox->bnDelete, this); connect(m_removeAction, SIGNAL(triggered()), this, SLOT(slotRmClicked())); action = actionManager->createAction("move_layer_up"); new SyncButtonAndAction(action, m_wdgLayerBox->bnRaise, this); connect(action, SIGNAL(triggered()), this, SLOT(slotRaiseClicked())); action = actionManager->createAction("move_layer_down"); new SyncButtonAndAction(action, m_wdgLayerBox->bnLower, this); connect(action, SIGNAL(triggered()), this, SLOT(slotLowerClicked())); } void KisLayerBox::setCanvas(KoCanvasBase *canvas) { if(m_canvas == canvas) return; setEnabled(canvas != 0); if (m_canvas) { m_canvas->disconnectCanvasObserver(this); m_nodeModel->setDummiesFacade(0, 0, 0, 0, 0); disconnect(m_image, 0, this, 0); disconnect(m_nodeManager, 0, this, 0); disconnect(m_nodeModel, 0, m_nodeManager, 0); m_nodeManager->slotSetSelectedNodes(KisNodeList()); } m_canvas = dynamic_cast(canvas); if (m_canvas) { m_image = m_canvas->image(); connect(m_image, SIGNAL(sigImageUpdated(QRect)), &m_thumbnailCompressor, SLOT(start())); KisDocument* doc = static_cast(m_canvas->imageView()->document()); KisShapeController *kritaShapeController = dynamic_cast(doc->shapeController()); KisDummiesFacadeBase *kritaDummiesFacade = static_cast(kritaShapeController); m_nodeModel->setDummiesFacade(kritaDummiesFacade, m_image, kritaShapeController, m_nodeManager->nodeSelectionAdapter(), m_nodeManager->nodeInsertionAdapter()); connect(m_image, SIGNAL(sigAboutToBeDeleted()), SLOT(notifyImageDeleted())); connect(m_image, SIGNAL(sigNodeCollapsedChanged()), SLOT(slotNodeCollapsedChanged())); // cold start if (m_nodeManager) { setCurrentNode(m_nodeManager->activeNode()); } else { setCurrentNode(m_canvas->imageView()->currentNode()); } // Connection KisNodeManager -> KisLayerBox connect(m_nodeManager, SIGNAL(sigUiNeedChangeActiveNode(KisNodeSP)), this, SLOT(setCurrentNode(KisNodeSP))); connect(m_nodeManager, SIGNAL(sigUiNeedChangeSelectedNodes(const QList &)), SLOT(slotNodeManagerChangedSelection(const QList &))); // Connection KisLayerBox -> KisNodeManager (isolate layer) connect(m_nodeModel, SIGNAL(toggleIsolateActiveNode()), m_nodeManager, SLOT(toggleIsolateActiveNode())); - m_wdgLayerBox->listLayers->expandAll(); expandNodesRecursively(m_image->rootLayer(), m_filteringModel, m_wdgLayerBox->listLayers); m_wdgLayerBox->listLayers->scrollTo(m_wdgLayerBox->listLayers->currentIndex()); updateAvailableLabels(); addActionToMenu(m_newLayerMenu, "add_new_paint_layer"); addActionToMenu(m_newLayerMenu, "add_new_group_layer"); addActionToMenu(m_newLayerMenu, "add_new_clone_layer"); addActionToMenu(m_newLayerMenu, "add_new_shape_layer"); addActionToMenu(m_newLayerMenu, "add_new_adjustment_layer"); addActionToMenu(m_newLayerMenu, "add_new_fill_layer"); addActionToMenu(m_newLayerMenu, "add_new_file_layer"); m_newLayerMenu->addSeparator(); addActionToMenu(m_newLayerMenu, "add_new_transparency_mask"); addActionToMenu(m_newLayerMenu, "add_new_filter_mask"); addActionToMenu(m_newLayerMenu, "add_new_transform_mask"); addActionToMenu(m_newLayerMenu, "add_new_selection_mask"); } } void KisLayerBox::unsetCanvas() { setEnabled(false); if (m_canvas) { m_newLayerMenu->clear(); } m_nodeModel->setDummiesFacade(0, 0, 0, 0, 0); disconnect(m_image, 0, this, 0); disconnect(m_nodeManager, 0, this, 0); disconnect(m_nodeModel, 0, m_nodeManager, 0); m_nodeManager->slotSetSelectedNodes(KisNodeList()); m_canvas = 0; } void KisLayerBox::notifyImageDeleted() { setCanvas(0); } void KisLayerBox::updateUI() { if (!m_canvas) return; if (!m_nodeManager) return; KisNodeSP activeNode = m_nodeManager->activeNode(); m_wdgLayerBox->bnRaise->setEnabled(activeNode && activeNode->isEditable(false) && (activeNode->nextSibling() || (activeNode->parent() && activeNode->parent() != m_image->root()))); m_wdgLayerBox->bnLower->setEnabled(activeNode && activeNode->isEditable(false) && (activeNode->prevSibling() || (activeNode->parent() && activeNode->parent() != m_image->root()))); m_wdgLayerBox->doubleOpacity->setEnabled(activeNode && activeNode->isEditable(false)); m_wdgLayerBox->cmbComposite->setEnabled(activeNode && activeNode->isEditable(false)); if (activeNode) { if (m_nodeManager->activePaintDevice()) { slotFillCompositeOps(m_nodeManager->activeColorSpace()); } else { slotFillCompositeOps(m_image->colorSpace()); } if (activeNode->inherits("KisMask")) { m_wdgLayerBox->cmbComposite->setEnabled(false); m_wdgLayerBox->doubleOpacity->setEnabled(false); } else if (activeNode->inherits("KisLayer")) { m_wdgLayerBox->doubleOpacity->setEnabled(true); KisLayerSP l = qobject_cast(activeNode.data()); slotSetOpacity(l->opacity() * 100.0 / 255); const KoCompositeOp* compositeOp = l->compositeOp(); if (compositeOp) { slotSetCompositeOp(compositeOp); } else { m_wdgLayerBox->cmbComposite->setEnabled(false); } const KisGroupLayer *group = qobject_cast(activeNode.data()); bool compositeSelectionActive = !(group && group->passThroughMode()); m_wdgLayerBox->cmbComposite->setEnabled(compositeSelectionActive); } } } /** * This method is callen *only* when non-GUI code requested the * change of the current node */ void KisLayerBox::setCurrentNode(KisNodeSP node) { m_filteringModel->setActiveNode(node); QModelIndex index = node ? m_filteringModel->indexFromNode(node) : QModelIndex(); m_filteringModel->setData(index, true, KisNodeModel::ActiveRole); updateUI(); } void KisLayerBox::slotModelReset() { if(m_nodeModel->hasDummiesFacade()) { QItemSelection selection; Q_FOREACH (const KisNodeSP node, m_nodeManager->selectedNodes()) { const QModelIndex &idx = m_filteringModel->indexFromNode(node); if(idx.isValid()){ QItemSelectionRange selectionRange(idx); selection << selectionRange; } } m_wdgLayerBox->listLayers->selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect); } updateUI(); } void KisLayerBox::slotSetCompositeOp(const KoCompositeOp* compositeOp) { KoID opId = KoCompositeOpRegistry::instance().getKoID(compositeOp->id()); m_wdgLayerBox->cmbComposite->blockSignals(true); m_wdgLayerBox->cmbComposite->selectCompositeOp(opId); m_wdgLayerBox->cmbComposite->blockSignals(false); } void KisLayerBox::slotFillCompositeOps(const KoColorSpace* colorSpace) { m_wdgLayerBox->cmbComposite->validate(colorSpace); } // range: 0-100 void KisLayerBox::slotSetOpacity(double opacity) { Q_ASSERT(opacity >= 0 && opacity <= 100); m_wdgLayerBox->doubleOpacity->blockSignals(true); m_wdgLayerBox->doubleOpacity->setValue(opacity); m_wdgLayerBox->doubleOpacity->blockSignals(false); } void KisLayerBox::slotContextMenuRequested(const QPoint &pos, const QModelIndex &index) { KisNodeList nodes = m_nodeManager->selectedNodes(); KisNodeSP activeNode = m_nodeManager->activeNode(); if (nodes.isEmpty() || !activeNode) return; if (m_canvas) { QMenu menu; const bool singleLayer = nodes.size() == 1; if (index.isValid()) { menu.addAction(m_propertiesAction); if (singleLayer) { addActionToMenu(&menu, "layer_style"); } { KisSignalsBlocker b(m_colorSelector); m_colorSelector->setCurrentIndex(singleLayer ? activeNode->colorLabelIndex() : -1); } menu.addAction(m_colorSelectorAction); if (singleLayer) { menu.addSeparator(); addActionToMenu(&menu, "show_in_timeline"); } menu.addSeparator(); addActionToMenu(&menu, "cut_layer_clipboard"); addActionToMenu(&menu, "copy_layer_clipboard"); addActionToMenu(&menu, "paste_layer_from_clipboard"); menu.addSeparator(); QMenu *selectMenu = menu.addMenu(i18n("&Select")); addActionToMenu(selectMenu, "select_all_layers"); addActionToMenu(selectMenu, "select_visible_layers"); addActionToMenu(selectMenu, "select_invisible_layers"); addActionToMenu(selectMenu, "select_locked_layers"); addActionToMenu(selectMenu, "select_unlocked_layers"); menu.addSeparator(); addActionToMenu(&menu, "create_quick_group"); addActionToMenu(&menu, "create_quick_clipping_group"); addActionToMenu(&menu, "quick_ungroup"); menu.addSeparator(); menu.addAction(m_removeAction); addActionToMenu(&menu, "duplicatelayer"); addActionToMenu(&menu, "merge_layer"); if (singleLayer) { addActionToMenu(&menu, "flatten_image"); addActionToMenu(&menu, "flatten_layer"); menu.addSeparator(); QMenu *convertToMenu = menu.addMenu(i18n("&Convert")); addActionToMenu(convertToMenu, "convert_to_paint_layer"); addActionToMenu(convertToMenu, "convert_to_transparency_mask"); addActionToMenu(convertToMenu, "convert_to_filter_mask"); addActionToMenu(convertToMenu, "convert_to_selection_mask"); QMenu *splitAlphaMenu = menu.addMenu(i18n("S&plit Alpha")); addActionToMenu(splitAlphaMenu, "split_alpha_into_mask"); addActionToMenu(splitAlphaMenu, "split_alpha_write"); addActionToMenu(splitAlphaMenu, "split_alpha_save_merged"); KisNodeSP node = m_filteringModel->nodeFromIndex(index); if (node && !node->inherits("KisTransformMask")) { addActionToMenu(&menu, "isolate_layer"); } } } if (singleLayer) { menu.addSeparator(); addActionToMenu(&menu, "add_new_transparency_mask"); addActionToMenu(&menu, "add_new_filter_mask"); addActionToMenu(&menu, "add_new_transform_mask"); addActionToMenu(&menu, "add_new_selection_mask"); menu.addSeparator(); menu.addAction(m_selectOpaque); } menu.exec(pos); } } void KisLayerBox::slotMergeLayer() { if (!m_canvas) return; m_nodeManager->mergeLayer(); } void KisLayerBox::slotMinimalView() { m_wdgLayerBox->listLayers->setDisplayMode(KisNodeView::MinimalMode); } void KisLayerBox::slotDetailedView() { m_wdgLayerBox->listLayers->setDisplayMode(KisNodeView::DetailedMode); } void KisLayerBox::slotThumbnailView() { m_wdgLayerBox->listLayers->setDisplayMode(KisNodeView::ThumbnailMode); } void KisLayerBox::slotRmClicked() { if (!m_canvas) return; m_nodeManager->removeNode(); } void KisLayerBox::slotRaiseClicked() { if (!m_canvas) return; m_nodeManager->raiseNode(); } void KisLayerBox::slotLowerClicked() { if (!m_canvas) return; m_nodeManager->lowerNode(); } void KisLayerBox::slotPropertiesClicked() { if (!m_canvas) return; if (KisNodeSP active = m_nodeManager->activeNode()) { m_nodeManager->nodeProperties(active); } } void KisLayerBox::slotCompositeOpChanged(int index) { Q_UNUSED(index); if (!m_canvas) return; QString compositeOp = m_wdgLayerBox->cmbComposite->selectedCompositeOp().id(); m_nodeManager->nodeCompositeOpChanged(m_nodeManager->activeColorSpace()->compositeOp(compositeOp)); } void KisLayerBox::slotOpacityChanged() { if (!m_canvas) return; m_nodeManager->nodeOpacityChanged(m_newOpacity, true); } void KisLayerBox::slotOpacitySliderMoved(qreal opacity) { m_newOpacity = opacity; m_opacityDelayTimer.start(200); } void KisLayerBox::slotCollapsed(const QModelIndex &index) { KisNodeSP node = m_filteringModel->nodeFromIndex(index); if (node) { node->setCollapsed(true); } } void KisLayerBox::slotExpanded(const QModelIndex &index) { KisNodeSP node = m_filteringModel->nodeFromIndex(index); if (node) { node->setCollapsed(false); } } void KisLayerBox::slotSelectOpaque() { if (!m_canvas) return; QAction *action = m_canvas->viewManager()->actionManager()->actionByName("selectopaque"); if (action) { action->trigger(); } } void KisLayerBox::slotNodeCollapsedChanged() { - m_wdgLayerBox->listLayers->expandAll(); expandNodesRecursively(m_image->rootLayer(), m_filteringModel, m_wdgLayerBox->listLayers); } inline bool isSelectionMask(KisNodeSP node) { return dynamic_cast(node.data()); } KisNodeSP KisLayerBox::findNonHidableNode(KisNodeSP startNode) { if (isSelectionMask(startNode) && startNode->parent() && !startNode->parent()->parent()) { KisNodeSP node = startNode->prevSibling(); while (node && isSelectionMask(node)) { node = node->prevSibling(); } if (!node) { node = startNode->nextSibling(); while (node && isSelectionMask(node)) { node = node->nextSibling(); } } if (!node) { node = m_image->root()->lastChild(); while (node && isSelectionMask(node)) { node = node->prevSibling(); } } KIS_ASSERT_RECOVER_NOOP(node && "cannot activate any node!"); startNode = node; } return startNode; } void KisLayerBox::slotEditGlobalSelection(bool showSelections) { KisNodeSP lastActiveNode = m_nodeManager->activeNode(); KisNodeSP activateNode = lastActiveNode; if (!showSelections) { activateNode = findNonHidableNode(activateNode); } m_nodeModel->setShowGlobalSelection(showSelections); if (showSelections) { KisNodeSP newMask = m_image->rootLayer()->selectionMask(); if (newMask) { activateNode = newMask; } } if (activateNode) { if (lastActiveNode != activateNode) { m_nodeManager->slotNonUiActivatedNode(activateNode); } else { setCurrentNode(lastActiveNode); } } } void KisLayerBox::selectionChanged(const QModelIndexList selection) { if (!m_nodeManager) return; /** * When the user clears the extended selection by clicking on the * empty area of the docker, the selection should be reset on to * the active layer, which might be even unselected(!). */ if (selection.isEmpty() && m_nodeManager->activeNode()) { QModelIndex selectedIndex = m_filteringModel->indexFromNode(m_nodeManager->activeNode()); m_wdgLayerBox->listLayers->selectionModel()-> setCurrentIndex(selectedIndex, QItemSelectionModel::ClearAndSelect); return; } QList selectedNodes; Q_FOREACH (const QModelIndex &idx, selection) { selectedNodes << m_filteringModel->nodeFromIndex(idx); } m_nodeManager->slotSetSelectedNodes(selectedNodes); updateUI(); } void KisLayerBox::slotAboutToRemoveRows(const QModelIndex &parent, int start, int end) { /** * Qt has changed its behavior when deleting an item. Previously * the selection priority was on the next item in the list, and * now it has shanged to the previous item. Here we just adjust * the selected item after the node removal. Please take care that * this method overrides what was done by the corresponding method * of QItemSelectionModel, which *has already done* its work. That * is why we use (start - 1) and (end + 1) in the activation * condition. * * See bug: https://bugs.kde.org/show_bug.cgi?id=345601 */ QModelIndex currentIndex = m_wdgLayerBox->listLayers->currentIndex(); QAbstractItemModel *model = m_filteringModel; if (currentIndex.isValid() && parent == currentIndex.parent() && currentIndex.row() >= start - 1 && currentIndex.row() <= end + 1) { QModelIndex old = currentIndex; if (model && end < model->rowCount(parent) - 1) // there are rows left below the change currentIndex = model->index(end + 1, old.column(), parent); else if (start > 0) // there are rows left above the change currentIndex = model->index(start - 1, old.column(), parent); else // there are no rows left in the table currentIndex = QModelIndex(); if (currentIndex.isValid() && currentIndex != old) { m_wdgLayerBox->listLayers->setCurrentIndex(currentIndex); } } } void KisLayerBox::slotNodeManagerChangedSelection(const KisNodeList &nodes) { if (!m_nodeManager) return; QModelIndexList newSelection; Q_FOREACH(KisNodeSP node, nodes) { newSelection << m_filteringModel->indexFromNode(node); } QItemSelectionModel *model = m_wdgLayerBox->listLayers->selectionModel(); if (KritaUtils::compareListsUnordered(newSelection, model->selectedIndexes())) { return; } QItemSelection selection; Q_FOREACH(const QModelIndex &idx, newSelection) { selection.select(idx, idx); } model->select(selection, QItemSelectionModel::ClearAndSelect); } void KisLayerBox::updateThumbnail() { m_wdgLayerBox->listLayers->updateNode(m_wdgLayerBox->listLayers->currentIndex()); } void KisLayerBox::slotRenameCurrentNode() { m_wdgLayerBox->listLayers->edit(m_wdgLayerBox->listLayers->currentIndex()); } void KisLayerBox::slotColorLabelChanged(int label) { KisNodeList nodes = m_nodeManager->selectedNodes(); Q_FOREACH(KisNodeSP node, nodes) { - node->setColorLabelIndex(label); + auto applyLabelFunc = + [label](KisNodeSP node) { + node->setColorLabelIndex(label); + }; + + KisLayerUtils::recursiveApplyNodes(node, applyLabelFunc); } } void KisLayerBox::updateAvailableLabels() { if (!m_image) return; m_wdgLayerBox->cmbFilter->updateAvailableLabels(m_image->root()); } void KisLayerBox::updateLayerFiltering() { m_filteringModel->setAcceptedLabels(m_wdgLayerBox->cmbFilter->selectedColors()); } #include "moc_kis_layer_box.cpp" diff --git a/plugins/dockers/griddocker/grid_config_widget.cpp b/plugins/dockers/griddocker/grid_config_widget.cpp index 02ba73e933..208685428f 100644 --- a/plugins/dockers/griddocker/grid_config_widget.cpp +++ b/plugins/dockers/griddocker/grid_config_widget.cpp @@ -1,231 +1,237 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "grid_config_widget.h" #include "ui_grid_config_widget.h" #include "kis_grid_config.h" #include "kis_guides_config.h" #include "kis_debug.h" #include "kis_aspect_ratio_locker.h" struct GridConfigWidget::Private { Private() : guiSignalsBlocked(false) {} KisGridConfig gridConfig; KisGuidesConfig guidesConfig; bool guiSignalsBlocked; }; GridConfigWidget::GridConfigWidget(QWidget *parent) : QWidget(parent), ui(new Ui::GridConfigWidget), m_d(new Private) { ui->setupUi(this); ui->colorMain->setAlphaChannelEnabled(true); ui->colorSubdivision->setAlphaChannelEnabled(true); ui->colorGuides->setAlphaChannelEnabled(true); setGridConfig(m_d->gridConfig); setGuidesConfig(m_d->guidesConfig); connect(ui->chkOffset, SIGNAL(toggled(bool)), ui->lblXOffset, SLOT(setVisible(bool))); connect(ui->chkOffset, SIGNAL(toggled(bool)), ui->lblYOffset, SLOT(setVisible(bool))); connect(ui->chkOffset, SIGNAL(toggled(bool)), ui->intXOffset, SLOT(setVisible(bool))); connect(ui->chkOffset, SIGNAL(toggled(bool)), ui->intYOffset, SLOT(setVisible(bool))); connect(ui->chkOffset, SIGNAL(toggled(bool)), ui->offsetAspectButton, SLOT(setVisible(bool))); ui->lblXOffset->setVisible(false); ui->lblYOffset->setVisible(false); ui->intXOffset->setVisible(false); ui->intYOffset->setVisible(false); ui->offsetAspectButton->setVisible(false); connect(ui->chkShowGrid, SIGNAL(stateChanged(int)), SLOT(slotGridGuiChanged())); connect(ui->chkSnapToGrid, SIGNAL(stateChanged(int)), SLOT(slotGridGuiChanged())); connect(ui->chkShowGuides, SIGNAL(stateChanged(int)), SLOT(slotGuidesGuiChanged())); connect(ui->chkSnapToGuides, SIGNAL(stateChanged(int)), SLOT(slotGuidesGuiChanged())); connect(ui->chkLockGuides, SIGNAL(stateChanged(int)), SLOT(slotGuidesGuiChanged())); connect(ui->intSubdivision, SIGNAL(valueChanged(int)), SLOT(slotGridGuiChanged())); connect(ui->selectMainStyle, SIGNAL(currentIndexChanged(int)), SLOT(slotGridGuiChanged())); connect(ui->colorMain, SIGNAL(changed(const QColor&)), SLOT(slotGridGuiChanged())); connect(ui->selectSubdivisionStyle, SIGNAL(currentIndexChanged(int)), SLOT(slotGridGuiChanged())); connect(ui->colorSubdivision, SIGNAL(changed(const QColor&)), SLOT(slotGridGuiChanged())); connect(ui->selectGuidesStyle, SIGNAL(currentIndexChanged(int)), SLOT(slotGuidesGuiChanged())); connect(ui->colorGuides, SIGNAL(changed(const QColor&)), SLOT(slotGuidesGuiChanged())); ui->chkOffset->setChecked(false); KisAspectRatioLocker *offsetLocker = new KisAspectRatioLocker(this); offsetLocker->connectSpinBoxes(ui->intXOffset, ui->intYOffset, ui->offsetAspectButton); KisAspectRatioLocker *spacingLocker = new KisAspectRatioLocker(this); spacingLocker->connectSpinBoxes(ui->intHSpacing, ui->intVSpacing, ui->spacingAspectButton); connect(offsetLocker, SIGNAL(sliderValueChanged()), SLOT(slotGridGuiChanged())); connect(offsetLocker, SIGNAL(aspectButtonChanged()), SLOT(slotGridGuiChanged())); connect(spacingLocker, SIGNAL(sliderValueChanged()), SLOT(slotGridGuiChanged())); connect(spacingLocker, SIGNAL(aspectButtonChanged()), SLOT(slotGridGuiChanged())); } GridConfigWidget::~GridConfigWidget() { delete ui; } void GridConfigWidget::setGridConfig(const KisGridConfig &value) { KisGridConfig currentConfig = fetchGuiGridConfig(); if (currentConfig == value) return; setGridConfigImpl(value); } void GridConfigWidget::setGuidesConfig(const KisGuidesConfig &value) { KisGuidesConfig currentConfig = fetchGuiGuidesConfig(); if (currentConfig == value) return; setGuidesConfigImpl(value); } void GridConfigWidget::setGridConfigImpl(const KisGridConfig &value) { m_d->gridConfig = value; m_d->guiSignalsBlocked = true; ui->offsetAspectButton->setKeepAspectRatio(m_d->gridConfig.offsetAspectLocked()); ui->spacingAspectButton->setKeepAspectRatio(m_d->gridConfig.spacingAspectLocked()); ui->chkShowGrid->setChecked(m_d->gridConfig.showGrid()); ui->chkSnapToGrid->setChecked(m_d->gridConfig.snapToGrid()); ui->intHSpacing->setValue(m_d->gridConfig.spacing().x()); ui->intVSpacing->setValue(m_d->gridConfig.spacing().y()); ui->intXOffset->setValue(m_d->gridConfig.offset().x()); ui->intYOffset->setValue(m_d->gridConfig.offset().y()); ui->intSubdivision->setValue(m_d->gridConfig.subdivision()); ui->selectMainStyle->setCurrentIndex(int(m_d->gridConfig.lineTypeMain())); ui->selectSubdivisionStyle->setCurrentIndex(int(m_d->gridConfig.lineTypeSubdivision())); ui->colorMain->setColor(m_d->gridConfig.colorMain()); ui->colorSubdivision->setColor(m_d->gridConfig.colorSubdivision()); m_d->guiSignalsBlocked = false; emit gridValueChanged(); } void GridConfigWidget::setGuidesConfigImpl(const KisGuidesConfig &value) { m_d->guidesConfig = value; m_d->guiSignalsBlocked = true; ui->chkShowGuides->setChecked(m_d->guidesConfig.showGuides()); ui->chkSnapToGuides->setChecked(m_d->guidesConfig.snapToGuides()); ui->chkLockGuides->setChecked(m_d->guidesConfig.lockGuides()); ui->selectGuidesStyle->setCurrentIndex(int(m_d->guidesConfig.guidesLineType())); ui->colorGuides->setColor(m_d->guidesConfig.guidesColor()); m_d->guiSignalsBlocked = false; emit guidesValueChanged(); } KisGridConfig GridConfigWidget::gridConfig() const { return m_d->gridConfig; } KisGuidesConfig GridConfigWidget::guidesConfig() const { return m_d->guidesConfig; } +void GridConfigWidget::setGridDivision(int w, int h) +{ + ui->intHSpacing->setMaximum(w); + ui->intVSpacing->setMaximum(h); +} + KisGridConfig GridConfigWidget::fetchGuiGridConfig() const { KisGridConfig config; config.setShowGrid(ui->chkShowGrid->isChecked()); config.setSnapToGrid(ui->chkSnapToGrid->isChecked()); QPoint pt; pt.rx() = ui->intHSpacing->value(); pt.ry() = ui->intVSpacing->value(); config.setSpacing(pt); pt.rx() = ui->intXOffset->value(); pt.ry() = ui->intYOffset->value(); config.setOffset(pt); config.setSubdivision(ui->intSubdivision->value()); config.setOffsetAspectLocked(ui->offsetAspectButton->keepAspectRatio()); config.setSpacingAspectLocked(ui->spacingAspectButton->keepAspectRatio()); config.setLineTypeMain(KisGridConfig::LineTypeInternal(ui->selectMainStyle->currentIndex())); config.setLineTypeSubdivision(KisGridConfig::LineTypeInternal(ui->selectSubdivisionStyle->currentIndex())); config.setColorMain(ui->colorMain->color()); config.setColorSubdivision(ui->colorSubdivision->color()); return config; } KisGuidesConfig GridConfigWidget::fetchGuiGuidesConfig() const { KisGuidesConfig config = m_d->guidesConfig; config.setShowGuides(ui->chkShowGuides->isChecked()); config.setSnapToGuides(ui->chkSnapToGuides->isChecked()); config.setLockGuides(ui->chkLockGuides->isChecked()); config.setGuidesLineType(KisGuidesConfig::LineTypeInternal(ui->selectGuidesStyle->currentIndex())); config.setGuidesColor(ui->colorGuides->color()); return config; } void GridConfigWidget::slotGridGuiChanged() { if (m_d->guiSignalsBlocked) return; KisGridConfig currentConfig = fetchGuiGridConfig(); if (currentConfig == m_d->gridConfig) return; setGridConfigImpl(currentConfig); } void GridConfigWidget::slotGuidesGuiChanged() { if (m_d->guiSignalsBlocked) return; KisGuidesConfig currentConfig = fetchGuiGuidesConfig(); if (currentConfig == m_d->guidesConfig) return; setGuidesConfigImpl(currentConfig); } diff --git a/plugins/dockers/griddocker/grid_config_widget.h b/plugins/dockers/griddocker/grid_config_widget.h index c277aaae8a..0b5f6d34af 100644 --- a/plugins/dockers/griddocker/grid_config_widget.h +++ b/plugins/dockers/griddocker/grid_config_widget.h @@ -1,67 +1,69 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef GRID_CONFIG_WIDGET_H #define GRID_CONFIG_WIDGET_H #include #include namespace Ui { class GridConfigWidget; } class KisGridConfig; class KisGuidesConfig; class GridConfigWidget : public QWidget { Q_OBJECT public: explicit GridConfigWidget(QWidget *parent = 0); ~GridConfigWidget(); void setGridConfig(const KisGridConfig &value); KisGridConfig gridConfig() const; void setGuidesConfig(const KisGuidesConfig &value); KisGuidesConfig guidesConfig() const; + void setGridDivision(int w, int h); + private Q_SLOTS: void slotGridGuiChanged(); void slotGuidesGuiChanged(); Q_SIGNALS: void gridValueChanged(); void guidesValueChanged(); private: KisGridConfig fetchGuiGridConfig() const; void setGridConfigImpl(const KisGridConfig &value); KisGuidesConfig fetchGuiGuidesConfig() const; void setGuidesConfigImpl(const KisGuidesConfig &value); private: Ui::GridConfigWidget *ui; struct Private; const QScopedPointer m_d; }; #endif // GRID_CONFIG_WIDGET_H diff --git a/plugins/dockers/griddocker/griddocker_dock.cpp b/plugins/dockers/griddocker/griddocker_dock.cpp index 70a999d864..2332a7b1a6 100644 --- a/plugins/dockers/griddocker/griddocker_dock.cpp +++ b/plugins/dockers/griddocker/griddocker_dock.cpp @@ -1,108 +1,111 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "griddocker_dock.h" //#include "gridwidget.h" // #include // #include #include #include #include "kis_canvas2.h" #include #include #include "kis_image.h" #include "kis_paint_device.h" #include "kis_signal_compressor.h" #include "grid_config_widget.h" #include "kis_grid_manager.h" #include "kis_grid_config.h" #include "kis_guides_manager.h" #include "kis_guides_config.h" GridDockerDock::GridDockerDock( ) : QDockWidget(i18n("Grid and Guides")) , m_canvas(0) { m_configWidget = new GridConfigWidget(this); connect(m_configWidget, SIGNAL(gridValueChanged()), SLOT(slotGuiGridConfigChanged())); connect(m_configWidget, SIGNAL(guidesValueChanged()), SLOT(slotGuiGuidesConfigChanged())); setWidget(m_configWidget); setEnabled(m_canvas); } GridDockerDock::~GridDockerDock() { } void GridDockerDock::setCanvas(KoCanvasBase * canvas) { if(canvas && m_canvas == canvas) return; if (m_canvas) { m_canvasConnections.clear(); m_canvas->disconnectCanvasObserver(this); m_canvas->image()->disconnect(this); } m_canvas = canvas ? dynamic_cast(canvas) : 0; setEnabled(m_canvas); if (m_canvas) { m_canvasConnections.addConnection( m_canvas->viewManager()->gridManager(), SIGNAL(sigRequestUpdateGridConfig(const KisGridConfig&)), this, SLOT(slotGridConfigUpdateRequested(const KisGridConfig&))); m_canvasConnections.addConnection( m_canvas->viewManager()->guidesManager(), SIGNAL(sigRequestUpdateGuidesConfig(const KisGuidesConfig&)), this, SLOT(slotGuidesConfigUpdateRequested(const KisGuidesConfig&))); + + QRect rc = m_canvas->image()->bounds(); + m_configWidget->setGridDivision(rc.width() / 2, rc.height() / 2); } } void GridDockerDock::unsetCanvas() { setCanvas(0); } void GridDockerDock::slotGuiGridConfigChanged() { if (!m_canvas) return; m_canvas->viewManager()->gridManager()->setGridConfig(m_configWidget->gridConfig()); } void GridDockerDock::slotGridConfigUpdateRequested(const KisGridConfig &config) { m_configWidget->setGridConfig(config); } void GridDockerDock::slotGuiGuidesConfigChanged() { if (!m_canvas) return; m_canvas->viewManager()->guidesManager()->setGuidesConfig(m_configWidget->guidesConfig()); } void GridDockerDock::slotGuidesConfigUpdateRequested(const KisGuidesConfig &config) { m_configWidget->setGuidesConfig(config); } diff --git a/plugins/dockers/imagedocker/imagedocker_dock.cpp b/plugins/dockers/imagedocker/imagedocker_dock.cpp index 632a704994..ce2ce450a5 100644 --- a/plugins/dockers/imagedocker/imagedocker_dock.cpp +++ b/plugins/dockers/imagedocker/imagedocker_dock.cpp @@ -1,564 +1,564 @@ /* * 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; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "imagedocker_dock.h" #include "image_strip_scene.h" #include "image_view.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ui_wdgimagedocker.h" #include "ui_wdgImageViewPopup.h" /////////////////////////////////////////////////////////////////////////////// // --------- ImageFilter --------------------------------------------------- // class ImageFilter: public QSortFilterProxyModel { virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const { QFileSystemModel* model = static_cast(sourceModel()); QModelIndex index = sourceModel()->index(source_row, 0, source_parent); if(model->isDir(index)) return true; QString ext = model->fileInfo(index).suffix().toLower(); if(s_supportedImageFormats.isEmpty()) { s_supportedImageFormats = QImageReader::supportedImageFormats(); } //QImageReader::supportedImageFormats return a list with mixed-case ByteArrays so //iterate over it manually to make it possible to do toLower(). Q_FOREACH (const QByteArray& format, s_supportedImageFormats) { if(format.toLower() == ext.toUtf8()) { return true; } } return false; } virtual bool filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const { Q_UNUSED(source_parent); return source_column == 0; } static QList s_supportedImageFormats; }; QList ImageFilter::s_supportedImageFormats; /////////////////////////////////////////////////////////////////////////////// // --------- ImageListModel ------------------------------------------------ // class ImageListModel: public QAbstractListModel { struct Data { QPixmap icon; QString text; qint64 id; }; public: void addImage(const QPixmap& pixmap, const QString& text, qint64 id) { Data data; - data.icon = pixmap.scaled(70, 70, Qt::KeepAspectRatio); + data.icon = pixmap.scaled(70, 70, Qt::KeepAspectRatio, Qt::SmoothTransformation); data.text = text; data.id = id; emit layoutAboutToBeChanged(); m_data.push_back(data); emit layoutChanged(); } qint64 imageID(int index) const { return m_data[index].id; } void removeImage(qint64 id) { typedef QList::iterator Iterator; for(Iterator data=m_data.begin(); data!=m_data.end(); ++data) { if(data->id == id) { emit layoutAboutToBeChanged(); m_data.erase(data); emit layoutChanged(); return; } } } int indexFromID(qint64 id) { for(int i=0; i m_data; }; /////////////////////////////////////////////////////////////////////////////// // --------- ImageDockerUI ------------------------------------------------- // struct ImageDockerUI: public QWidget, public Ui_wdgImageDocker { ImageDockerUI() { setupUi(this); } }; /////////////////////////////////////////////////////////////////////////////// // --------- PopupWidgetUI ------------------------------------------------- // struct PopupWidgetUI: public QWidget, public Ui_wdgImageViewPopup { PopupWidgetUI() { setupUi(this); } }; /////////////////////////////////////////////////////////////////////////////// // --------- ImageDockerDock ----------------------------------------------- // ImageDockerDock::ImageDockerDock(): QDockWidget(i18n("Reference Images")), m_canvas(0), m_currImageID(-1) { m_ui = new ImageDockerUI(); m_popupUi = new PopupWidgetUI(); m_zoomButtons = new QButtonGroup(); m_imgListModel = new ImageListModel(); m_imageStripScene = new ImageStripScene(); m_model = new QFileSystemModel(); m_proxyModel = new ImageFilter(); m_proxyModel->setSourceModel(m_model); m_proxyModel->setDynamicSortFilter(true); m_ui->bnBack->setIcon(KisIconUtils::loadIcon("arrow-left")); m_ui->bnUp->setIcon(KisIconUtils::loadIcon("arrow-up")); m_ui->bnHome->setIcon(KisIconUtils::loadIcon("go-home")); m_ui->bnImgPrev->setIcon(KisIconUtils::loadIcon("arrow-left")); m_ui->bnImgNext->setIcon(KisIconUtils::loadIcon("arrow-right")); m_ui->bnImgClose->setIcon(KisIconUtils::loadIcon("window-close")); m_ui->thumbView->setScene(m_imageStripScene); m_ui->treeView->setModel(m_proxyModel); m_ui->cmbImg->setModel(m_imgListModel); m_ui->bnPopup->setIcon(KisIconUtils::loadIcon("zoom-original")); m_ui->bnPopup->setPopupWidget(m_popupUi); m_popupUi->zoomSlider->setRange(5, 500); m_popupUi->zoomSlider->setValue(100); m_zoomButtons->addButton(m_popupUi->bnZoomFit , ImageView::VIEW_MODE_FIT); m_zoomButtons->addButton(m_popupUi->bnZoomAdjust, ImageView::VIEW_MODE_ADJUST); m_zoomButtons->addButton(m_popupUi->bnZoom25 , 25); m_zoomButtons->addButton(m_popupUi->bnZoom50 , 50); m_zoomButtons->addButton(m_popupUi->bnZoom75 , 75); m_zoomButtons->addButton(m_popupUi->bnZoom100 , 100); installEventFilter(this); m_ui->cmbPath->addItem(KisIconUtils::loadIcon("folder-pictures"), QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); m_ui->cmbPath->addItem(KisIconUtils::loadIcon("folder-documents"), QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation)); m_ui->cmbPath->addItem(KisIconUtils::loadIcon("go-home"), QDesktopServices::storageLocation(QDesktopServices::HomeLocation)); Q_FOREACH (const QFileInfo &info, QDir::drives()) { m_ui->cmbPath->addItem(KisIconUtils::loadIcon("drive-harddisk"), info.absolutePath()); } connect(m_ui->cmbPath, SIGNAL(activated(const QString&)), SLOT(slotChangeRoot(const QString&))); m_model->setRootPath(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); m_ui->treeView->setRootIndex(m_proxyModel->mapFromSource(m_model->index(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)))); connect(m_ui->treeView , SIGNAL(doubleClicked(const QModelIndex&)) , SLOT(slotItemDoubleClicked(const QModelIndex&))); connect(m_ui->bnBack , SIGNAL(clicked(bool)) , SLOT(slotBackButtonClicked())); connect(m_ui->bnHome , SIGNAL(clicked(bool)) , SLOT(slotHomeButtonClicked())); connect(m_ui->bnUp , SIGNAL(clicked(bool)) , SLOT(slotUpButtonClicked())); connect(m_imageStripScene , SIGNAL(sigImageActivated(const QString&)) , SLOT(slotOpenImage(QString))); connect(m_ui->bnImgNext , SIGNAL(clicked(bool)) , SLOT(slotNextImage())); connect(m_ui->bnImgPrev , SIGNAL(clicked(bool)) , SLOT(slotPrevImage())); connect(m_ui->bnImgClose , SIGNAL(clicked(bool)) , SLOT(slotCloseCurrentImage())); connect(m_ui->cmbImg , SIGNAL(activated(int)) , SLOT(slotImageChoosenFromComboBox(int))); connect(m_ui->imgView , SIGNAL(sigColorSelected(const QColor&)) , SLOT(slotColorSelected(const QColor))); connect(m_ui->imgView , SIGNAL(sigViewModeChanged(int, qreal)) , SLOT(slotViewModeChanged(int, qreal))); connect(m_popupUi->zoomSlider , SIGNAL(valueChanged(int)) , SLOT(slotZoomChanged(int))); connect(m_zoomButtons , SIGNAL(buttonClicked(int)) , SLOT(slotZoomChanged(int))); connect(m_zoomButtons , SIGNAL(buttonClicked(int)) , SLOT(slotCloseZoomPopup())); setWidget(m_ui); setAcceptDrops(true); } ImageDockerDock::~ImageDockerDock() { delete m_proxyModel; delete m_model; delete m_imageStripScene; delete m_imgListModel; delete m_zoomButtons; qDeleteAll(m_temporaryFiles); } void ImageDockerDock::dragEnterEvent(QDragEnterEvent *event) { event->setAccepted(event->mimeData()->hasImage() || event->mimeData()->hasUrls()); } void ImageDockerDock::dropEvent(QDropEvent *event) { QImage image; if (event->mimeData()->hasImage()) { image = qvariant_cast(event->mimeData()->imageData()); } if (!image.isNull()) { QTemporaryFile *file = new QTemporaryFile(QDir::tempPath () + QDir::separator() + "krita_reference_dnd_XXXXXX.png"); m_temporaryFiles.append(file); file->open(); image.save(file, "PNG"); file->close(); slotOpenImage(file->fileName()); } else if (event->mimeData()->hasUrls()) { QList urls = event->mimeData()->urls(); Q_FOREACH (const QUrl &url, urls) { QString path = url.path(); QFileInfo info(path); if (info.exists() && !QImageReader::imageFormat(path).isEmpty()) { slotOpenImage(path); } } } } void ImageDockerDock::showEvent(QShowEvent *) { if (m_imageStripScene->currentPath().isNull()) { updatePath(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); } } void ImageDockerDock::setCanvas(KoCanvasBase* canvas) { // Intentionally not disabled if there's no canvas // "Every connection you make emits a signal, so duplicate connections emit two signals" if(m_canvas) m_canvas->disconnectCanvasObserver(this); m_canvas = canvas; } void ImageDockerDock::addCurrentPathToHistory() { m_history.push_back(m_model->filePath(m_proxyModel->mapToSource(m_ui->treeView->rootIndex()))); } void ImageDockerDock::updatePath(const QString& path) { m_ui->bnBack->setDisabled(m_history.empty()); m_imageStripScene->setCurrentDirectory(path); } qint64 ImageDockerDock::generateImageID() const { static qint64 id = 0; return ++id; } void ImageDockerDock::setCurrentImage(qint64 imageID) { if(m_imgInfoMap.contains(m_currImageID)) m_imgInfoMap[m_currImageID].scrollPos = m_ui->imgView->getScrollPos(); m_ui->bnImgClose->setDisabled(imageID < 0); m_ui->bnPopup->setDisabled(imageID < 0); if(imageID < 0) { m_currImageID = -1; m_ui->imgView->setPixmap(QPixmap()); } else if(m_imgInfoMap.contains(imageID)) { ImageInfoIter info = m_imgInfoMap.find(imageID); m_ui->imgView->blockSignals(true); m_ui->imgView->setPixmap(info->pixmap); setZoom(*info); m_ui->imgView->blockSignals(false); m_ui->bnImgPrev->setDisabled(info == m_imgInfoMap.begin()); m_ui->bnImgNext->setDisabled((info+1) == m_imgInfoMap.end()); m_ui->cmbImg->blockSignals(true); m_ui->cmbImg->setCurrentIndex(m_imgListModel->indexFromID(imageID)); m_ui->cmbImg->blockSignals(false); m_currImageID = imageID; } } void ImageDockerDock::setZoom(const ImageInfo& info) { m_ui->imgView->setViewMode(info.viewMode, info.scale); m_ui->imgView->setScrollPos(info.scrollPos); int zoom = qRound(m_ui->imgView->getScale() * 100.0f); m_popupUi->zoomSlider->blockSignals(true); m_popupUi->zoomSlider->setValue(zoom); m_popupUi->zoomSlider->blockSignals(false); } // ------------ slots ------------------------------------------------- // void ImageDockerDock::slotItemDoubleClicked(const QModelIndex& index) { QModelIndex mappedIndex = m_proxyModel->mapToSource(index); mappedIndex = m_model->index(mappedIndex.row(), 0, mappedIndex.parent()); QString path(m_model->filePath(mappedIndex)); if(m_model->isDir(mappedIndex)) { addCurrentPathToHistory(); updatePath(path); m_ui->treeView->setRootIndex(m_proxyModel->mapFromSource(mappedIndex)); } else slotOpenImage(path); } void ImageDockerDock::slotBackButtonClicked() { if(!m_history.empty()) { QString path = m_history.last(); QModelIndex index = m_proxyModel->mapFromSource(m_model->index(path)); m_ui->treeView->setRootIndex(index); m_history.pop_back(); updatePath(path); } } void ImageDockerDock::slotHomeButtonClicked() { addCurrentPathToHistory(); QModelIndex index = m_proxyModel->mapFromSource(m_model->index(QDir::homePath())); m_ui->treeView->setRootIndex(index); updatePath(QDir::homePath()); } void ImageDockerDock::slotUpButtonClicked() { addCurrentPathToHistory(); QModelIndex index = m_proxyModel->mapToSource(m_ui->treeView->rootIndex()); QDir dir(m_model->filePath(index)); dir.makeAbsolute(); if(dir.cdUp()) { index = m_proxyModel->mapFromSource(m_model->index(dir.path())); m_ui->treeView->setRootIndex(index); updatePath(dir.path()); } } void ImageDockerDock::slotOpenImage(const QString& path) { QPixmap pixmap(path); if(!pixmap.isNull()) { QFileInfo fileInfo(path); ImageInfo imgInfo; imgInfo.id = generateImageID(); imgInfo.name = fileInfo.fileName(); imgInfo.path = fileInfo.absoluteFilePath(); imgInfo.viewMode = ImageView::VIEW_MODE_FIT; imgInfo.scale = 1.0f; imgInfo.pixmap = pixmap; imgInfo.scrollPos = QPoint(0, 0); m_imgInfoMap[imgInfo.id] = imgInfo; m_imgListModel->addImage(imgInfo.pixmap, imgInfo.name, imgInfo.id); setCurrentImage(imgInfo.id); m_ui->tabWidget->setCurrentIndex(1); } } void ImageDockerDock::slotCloseCurrentImage() { ImageInfoIter info = m_imgInfoMap.find(m_currImageID); if(info != m_imgInfoMap.end()) { ImageInfoIter next = info + 1; ImageInfoIter prev = info - 1; qint64 id = -1; if(next != m_imgInfoMap.end()) id = next->id; else if(info != m_imgInfoMap.begin()) id = prev->id; m_imgListModel->removeImage(info->id); m_imgInfoMap.erase(info); setCurrentImage(id); if(id < 0) m_ui->tabWidget->setCurrentIndex(0); } } void ImageDockerDock::slotNextImage() { ImageInfoIter info = m_imgInfoMap.find(m_currImageID); if(info != m_imgInfoMap.end()) { ++info; if(info != m_imgInfoMap.end()) setCurrentImage(info->id); } } void ImageDockerDock::slotPrevImage() { ImageInfoIter info = m_imgInfoMap.find(m_currImageID); if(info != m_imgInfoMap.end() && info != m_imgInfoMap.begin()) { --info; setCurrentImage(info->id); } } void ImageDockerDock::slotImageChoosenFromComboBox(int index) { setCurrentImage(m_imgListModel->imageID(index)); } void ImageDockerDock::slotZoomChanged(int zoom) { if(isImageLoaded()) { ImageInfoIter info = m_imgInfoMap.find(m_currImageID); switch(zoom) { case ImageView::VIEW_MODE_FIT: case ImageView::VIEW_MODE_ADJUST: info->viewMode = zoom; break; default: info->viewMode = ImageView::VIEW_MODE_FREE; info->scale = float(zoom) / 100.0f; break; } setZoom(*info); } } void ImageDockerDock::slotColorSelected(const QColor& color) { if (m_canvas) { m_canvas->resourceManager()->setForegroundColor(KoColor(color, KoColorSpaceRegistry::instance()->rgb8())); } } void ImageDockerDock::slotViewModeChanged(int viewMode, qreal scale) { if(isImageLoaded()) { m_imgInfoMap[m_currImageID].viewMode = viewMode; m_imgInfoMap[m_currImageID].scale = scale; int zoom = qRound(scale * 100.0); m_popupUi->zoomSlider->blockSignals(true); m_popupUi->zoomSlider->setValue(zoom); m_popupUi->zoomSlider->blockSignals(false); } } void ImageDockerDock::slotCloseZoomPopup() { m_ui->bnPopup->hidePopupWidget(); } void ImageDockerDock::slotChangeRoot(const QString &path) { m_model->setRootPath(path); m_ui->treeView->setRootIndex(m_proxyModel->mapFromSource(m_model->index(path))); updatePath(path); } bool ImageDockerDock::eventFilter(QObject *obj, QEvent *event) { Q_UNUSED(obj); if (event->type() == QEvent::Resize) { m_ui->treeView->setColumnWidth(0, width()); return true; } return false; } diff --git a/plugins/dockers/lut/lutdocker_dock.cpp b/plugins/dockers/lut/lutdocker_dock.cpp index d603512fd9..1114f15ab6 100644 --- a/plugins/dockers/lut/lutdocker_dock.cpp +++ b/plugins/dockers/lut/lutdocker_dock.cpp @@ -1,576 +1,576 @@ /* * 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 "kis_icon_utils.h" #include #include #include #include #include #include #include #include #include "widgets/squeezedcombobox.h" #include "kis_signals_blocker.h" #include "krita_utils.h" #include "ocio_display_filter.h" #include "black_white_point_chooser.h" 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) , m_ownDisplayFilter(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; m_chkUseOcio->setChecked(cfg.useOcio()); connect(m_chkUseOcio, SIGNAL(toggled(bool)), SLOT(updateDisplaySettings())); connect(m_colorManagement, SIGNAL(currentIndexChanged(int)), SLOT(slotColorManagementModeChanged())); m_txtConfigurationPath->setText(cfg.ocioConfigurationPath()); m_bnSelectConfigurationFile->setToolTip(i18n("Select custom configuration file.")); connect(m_bnSelectConfigurationFile,SIGNAL(clicked()), SLOT(selectOcioConfiguration())); m_txtLut->setText(cfg.ocioLutPath()); 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 // 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_cmbComponents, SIGNAL(currentIndexChanged(int)), SLOT(updateDisplaySettings())); m_draggingSlider = false; connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(resetOcioConfiguration())); m_displayFilter = 0; resetOcioConfiguration(); } LutDockerDock::~LutDockerDock() { if(m_ownDisplayFilter) delete m_displayFilter; } void LutDockerDock::setCanvas(KoCanvasBase* _canvas) { if (m_canvas) { m_canvas->disconnect(this); m_displayFilter = 0; } setEnabled(_canvas != 0); if (KisCanvas2* canvas = dynamic_cast(_canvas)) { m_canvas = canvas; if (m_canvas) { if (!m_canvas->displayFilter()) { m_displayFilter = new OcioDisplayFilter(this); m_ownDisplayFilter = true; resetOcioConfiguration(); updateDisplaySettings(); } else { m_displayFilter = dynamic_cast(m_canvas->displayFilter()); Q_ASSERT(m_displayFilter); m_ocioConfig = m_displayFilter->config; KisSignalsBlocker exposureBlocker(m_exposureDoubleWidget); m_exposureDoubleWidget->setValue(m_displayFilter->exposure); KisSignalsBlocker gammaBlocker(m_gammaDoubleWidget); m_gammaDoubleWidget->setValue(m_displayFilter->gamma); KisSignalsBlocker componentsBlocker(m_cmbComponents); m_cmbComponents->setCurrentIndex((int)m_displayFilter->swizzle); KisSignalsBlocker bwBlocker(m_bwPointChooser); m_bwPointChooser->setBlackPoint(m_displayFilter->blackPoint); m_bwPointChooser->setWhitePoint(m_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::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 { return m_chkUseOcio->isChecked() && m_ocioConfig; } qreal LutDockerDock::currentExposure() const { if (!m_displayFilter) return 0.0; return canChangeExposureAndGamma() ? m_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; return canChangeExposureAndGamma() ? m_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()->resourceProvider()->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()->resourceProvider()->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) KisConfig::INTERNAL); } bool ocioEnabled = m_chkUseOcio->isChecked(); m_colorManagement->setEnabled(ocioEnabled && canDoExternalColorCorrection); bool externalColorManagementEnabled = m_colorManagement->currentIndex() != (int)KisConfig::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); bool enableConfigPath = m_colorManagement->currentIndex() == (int) KisConfig::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) { m_displayFilter->config = m_ocioConfig; m_displayFilter->inputColorSpaceName = m_ocioConfig->getColorSpaceNameByIndex(m_cmbInputColorSpace->currentIndex()); m_displayFilter->displayDevice = m_ocioConfig->getDisplay(m_cmbDisplayDevice->currentIndex()); m_displayFilter->view = m_ocioConfig->getView(m_displayFilter->displayDevice, m_cmbView->currentIndex()); m_displayFilter->gamma = m_gammaDoubleWidget->value(); m_displayFilter->exposure = m_exposureDoubleWidget->value(); m_displayFilter->swizzle = (OCIO_CHANNEL_SWIZZLE)m_cmbComponents->currentIndex(); m_displayFilter->blackPoint = m_bwPointChooser->blackPoint(); m_displayFilter->whitePoint = m_bwPointChooser->whitePoint(); m_displayFilter->forceInternalColorManagement = m_colorManagement->currentIndex() == (int)KisConfig::INTERNAL; m_displayFilter->setLockCurrentColorVisualRepresentation(m_btnConvertCurrentColor->isChecked()); m_displayFilter->updateProcessor(); m_canvas->setDisplayFilter(m_displayFilter); } else { m_canvas->setDisplayFilter(0); } m_canvas->updateCanvas(); } void LutDockerDock::writeControls() { KisConfig cfg; cfg.setUseOcio(m_chkUseOcio->isChecked()); cfg.setOcioColorManagementMode((KisConfig::OcioColorManagementMode) m_colorManagement->currentIndex()); 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)); - dialog.setMimeTypeFilters(QStringList() << "application/x-opencolorio-configuration", "x-opencolorio-configuration"); + dialog.setMimeTypeFilters(QStringList() << "application/x-opencolorio-configuration"); filename = dialog.filename(); QFile f(filename); if (f.exists()) { m_txtConfigurationPath->setText(filename); KisConfig cfg; cfg.setOcioConfigurationPath(filename); writeControls(); resetOcioConfiguration(); } } void LutDockerDock::resetOcioConfiguration() { m_ocioConfig.reset(); KisConfig cfg; try { if (cfg.ocioColorManagementMode() == KisConfig::INTERNAL) { m_ocioConfig = defaultRawProfile(); } else if (cfg.ocioColorManagementMode() == KisConfig::OCIO_ENVIRONMENT) { m_ocioConfig = OCIO::Config::CreateFromEnv(); } else if (cfg.ocioColorManagementMode() == KisConfig::OCIO_CONFIG) { QString configFile = cfg.ocioConfigurationPath(); 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"; } refillControls(); } void LutDockerDock::refillControls() { if (!m_canvas) return; if (!m_canvas->viewManager()) return; if (!m_canvas->viewManager()->resourceProvider()) return; if (!m_canvas->viewManager()->image()) return; KIS_ASSERT_RECOVER_RETURN(m_ocioConfig); { // Color Management Mode KisConfig cfg; KisSignalsBlocker modeBlocker(m_colorManagement); m_colorManagement->setCurrentIndex((int) cfg.ocioColorManagementMode()); } { // Exposure KisSignalsBlocker exposureBlocker(m_exposureDoubleWidget); m_exposureDoubleWidget->setValue(m_canvas->viewManager()->resourceProvider()->HDRExposure()); } { // Gamma KisSignalsBlocker gammaBlocker(m_gammaDoubleWidget); m_gammaDoubleWidget->setValue(m_canvas->viewManager()->resourceProvider()->HDRGamma()); } { // Components const KoColorSpace *cs = m_canvas->viewManager()->image()->colorSpace(); KisSignalsBlocker componentsBlocker(m_cmbComponents); m_cmbComponents->clear(); m_cmbComponents->addSqueezedItem(i18n("Luminance")); m_cmbComponents->addSqueezedItem(i18n("All Channels")); Q_FOREACH (KoChannelInfo *channel, KoChannelInfo::displayOrderSorted(cs->channels())) { m_cmbComponents->addSqueezedItem(channel->name()); } m_cmbComponents->setCurrentIndex(1); // All Channels... } { // Input Color Space KisSignalsBlocker inputCSBlocker(m_cmbInputColorSpace); m_cmbInputColorSpace->clear(); 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); m_cmbInputColorSpace->addSqueezedItem(QString::fromUtf8(colorSpace->getName())); } } { // Display Device KisSignalsBlocker displayDeviceLocker(m_cmbDisplayDevice); m_cmbDisplayDevice->clear(); int numDisplays = m_ocioConfig->getNumDisplays(); for (int i = 0; i < numDisplays; ++i) { m_cmbDisplayDevice->addSqueezedItem(QString::fromUtf8(m_ocioConfig->getDisplay(i))); } } { // Lock Current Color KisSignalsBlocker locker(m_btnConvertCurrentColor); KisConfig cfg; m_btnConvertCurrentColor->setChecked(cfg.ocioLockColorVisualRepresentation()); } refillViewCombobox(); 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))); } } 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); KisConfig cfg; cfg.setOcioLutPath(filename); updateDisplaySettings(); } } void LutDockerDock::clearLut() { m_txtLut->clear(); updateDisplaySettings(); } diff --git a/plugins/dockers/lut/ocio_display_filter.cpp b/plugins/dockers/lut/ocio_display_filter.cpp index 6948a29a83..70e4c5058a 100644 --- a/plugins/dockers/lut/ocio_display_filter.cpp +++ b/plugins/dockers/lut/ocio_display_filter.cpp @@ -1,311 +1,321 @@ /* * Copyright (c) 2012 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 "ocio_display_filter.h" #include #include #include #include #include #include #include #include #include #include #include -#include -#include +#include + static const int LUT3D_EDGE_SIZE = 32; OcioDisplayFilter::OcioDisplayFilter(KisExposureGammaCorrectionInterface *interface, QObject *parent) : KisDisplayFilter(parent) , inputColorSpaceName(0) , displayDevice(0) , view(0) , swizzle(RGBA) , m_interface(interface) , m_lut3dTexID(0) , m_shaderDirty(true) { } OcioDisplayFilter::~OcioDisplayFilter() { } KisExposureGammaCorrectionInterface* OcioDisplayFilter::correctionInterface() const { return m_interface; } void OcioDisplayFilter::filter(quint8 *pixels, quint32 numPixels) { // processes that data _in_ place if (m_processor) { OCIO::PackedImageDesc img(reinterpret_cast(pixels), numPixels, 1, 4); m_processor->apply(img); } } void OcioDisplayFilter::approximateInverseTransformation(quint8 *pixels, quint32 numPixels) { // processes that data _in_ place if (m_revereseApproximationProcessor) { OCIO::PackedImageDesc img(reinterpret_cast(pixels), numPixels, 1, 4); m_revereseApproximationProcessor->apply(img); } } void OcioDisplayFilter::approximateForwardTransformation(quint8 *pixels, quint32 numPixels) { // processes that data _in_ place if (m_forwardApproximationProcessor) { OCIO::PackedImageDesc img(reinterpret_cast(pixels), numPixels, 1, 4); m_forwardApproximationProcessor->apply(img); } } bool OcioDisplayFilter::useInternalColorManagement() const { return forceInternalColorManagement; } bool OcioDisplayFilter::lockCurrentColorVisualRepresentation() const { return m_lockCurrentColorVisualRepresentation; } void OcioDisplayFilter::setLockCurrentColorVisualRepresentation(bool value) { m_lockCurrentColorVisualRepresentation = value; } QString OcioDisplayFilter::program() const { return m_program; } GLuint OcioDisplayFilter::lutTexture() const { return m_lut3dTexID; } void OcioDisplayFilter::updateProcessor() { if (!config) { return; } if (!displayDevice) { displayDevice = config->getDefaultDisplay(); } if (!view) { view = config->getDefaultView(displayDevice); } if (!inputColorSpaceName) { inputColorSpaceName = config->getColorSpaceNameByIndex(0); } OCIO::DisplayTransformRcPtr transform = OCIO::DisplayTransform::Create(); transform->setInputColorSpaceName(inputColorSpaceName); transform->setDisplay(displayDevice); transform->setView(view); OCIO::GroupTransformRcPtr approximateTransform = OCIO::GroupTransform::Create(); // fstop exposure control -- not sure how that translates to our exposure { float exposureGain = powf(2.0f, exposure); const qreal minRange = 0.001; if (qAbs(blackPoint - whitePoint) < minRange) { whitePoint = blackPoint + minRange; } const float oldMin[] = { blackPoint, blackPoint, blackPoint, 0.0f }; const float oldMax[] = { whitePoint, whitePoint, whitePoint, 1.0f }; const float newMin[] = { 0.0f, 0.0f, 0.0f, 0.0f }; const float newMax[] = { exposureGain, exposureGain, exposureGain, 1.0f }; float m44[16]; float offset4[4]; OCIO::MatrixTransform::Fit(m44, offset4, oldMin, oldMax, newMin, newMax); OCIO::MatrixTransformRcPtr mtx = OCIO::MatrixTransform::Create(); mtx->setValue(m44, offset4); transform->setLinearCC(mtx); // approximation (no color correction); approximateTransform->push_back(mtx); } // channel swizzle { int channelHot[4]; switch (swizzle) { case LUMINANCE: channelHot[0] = 1; channelHot[1] = 1; channelHot[2] = 1; channelHot[3] = 0; break; case RGBA: channelHot[0] = 1; channelHot[1] = 1; channelHot[2] = 1; channelHot[3] = 1; break; case R: channelHot[0] = 1; channelHot[1] = 0; channelHot[2] = 0; channelHot[3] = 0; break; case G: channelHot[0] = 0; channelHot[1] = 1; channelHot[2] = 0; channelHot[3] = 0; break; case B: channelHot[0] = 0; channelHot[1] = 0; channelHot[2] = 1; channelHot[3] = 0; break; case A: channelHot[0] = 0; channelHot[1] = 0; channelHot[2] = 0; channelHot[3] = 1; default: ; } float lumacoef[3]; config->getDefaultLumaCoefs(lumacoef); float m44[16]; float offset[4]; OCIO::MatrixTransform::View(m44, offset, channelHot, lumacoef); OCIO::MatrixTransformRcPtr swizzle = OCIO::MatrixTransform::Create(); swizzle->setValue(m44, offset); transform->setChannelView(swizzle); } // Post-display transform gamma { float exponent = 1.0f/std::max(1e-6f, static_cast(gamma)); const float exponent4f[] = { exponent, exponent, exponent, exponent }; OCIO::ExponentTransformRcPtr expTransform = OCIO::ExponentTransform::Create(); expTransform->setValue(exponent4f); transform->setDisplayCC(expTransform); // approximation (no color correction); approximateTransform->push_back(expTransform); } m_processor = config->getProcessor(transform); m_forwardApproximationProcessor = config->getProcessor(approximateTransform, OCIO::TRANSFORM_DIR_FORWARD); try { m_revereseApproximationProcessor = config->getProcessor(approximateTransform, OCIO::TRANSFORM_DIR_INVERSE); } catch (...) { warnKrita << "OCIO inverted matrix does not exist!"; //m_revereseApproximationProcessor; } m_shaderDirty = true; } void OcioDisplayFilter::updateShader() { // check whether we are allowed to use shaders -- though that should // work for everyone these days KisConfig cfg; if (!cfg.useOpenGL()) return; if (!m_shaderDirty) return; - QOpenGLFunctions_3_0 *glFuncs3 = QOpenGLContext::currentContext()->versionFunctions(); - KIS_ASSERT_RECOVER_RETURN(glFuncs3); + QOpenGLFunctions_2_0 *f = QOpenGLContext::currentContext()->versionFunctions(); + if (!f) { + qWarning() << "Could not create OpenGL 2.0 functions!"; + return; + } + + f->initializeOpenGLFunctions(); const int lut3DEdgeSize = cfg.ocioLutEdgeSize(); if (m_lut3d.size() == 0) { //dbgKrita << "generating lut"; - glFuncs3->glGenTextures(1, &m_lut3dTexID); + f->glGenTextures(1, &m_lut3dTexID); int num3Dentries = 3 * lut3DEdgeSize * lut3DEdgeSize * lut3DEdgeSize; m_lut3d.fill(0.0, num3Dentries); - glFuncs3->glActiveTexture(GL_TEXTURE1); - glFuncs3->glBindTexture(GL_TEXTURE_3D, m_lut3dTexID); - - glFuncs3->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glFuncs3->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glFuncs3->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glFuncs3->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glFuncs3->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - glFuncs3->glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F_ARB, - lut3DEdgeSize, lut3DEdgeSize, lut3DEdgeSize, - 0, GL_RGB, GL_FLOAT, &m_lut3d.constData()[0]); + f->glActiveTexture(GL_TEXTURE1); + f->glBindTexture(GL_TEXTURE_3D, m_lut3dTexID); + + f->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + f->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + f->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + f->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + f->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + f->glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F_ARB, + lut3DEdgeSize, lut3DEdgeSize, lut3DEdgeSize, + 0, GL_RGB, GL_FLOAT, &m_lut3d.constData()[0]); } // Step 1: Create a GPU Shader Description OCIO::GpuShaderDesc shaderDesc; - shaderDesc.setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_3); + if (KisOpenGL::supportsGLSL13()) { + shaderDesc.setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_3); + } + else { + shaderDesc.setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_0); + } + shaderDesc.setFunctionName("OCIODisplay"); shaderDesc.setLut3DEdgeLen(lut3DEdgeSize); // Step 2: Compute the 3D LUT QString lut3dCacheID = QString::fromLatin1(m_processor->getGpuLut3DCacheID(shaderDesc)); - if(lut3dCacheID != m_lut3dcacheid) - { + if (lut3dCacheID != m_lut3dcacheid) { //dbgKrita << "Computing 3DLut " << m_lut3dcacheid; m_lut3dcacheid = lut3dCacheID; m_processor->getGpuLut3D(&m_lut3d[0], shaderDesc); - glFuncs3->glBindTexture(GL_TEXTURE_3D, m_lut3dTexID); - glFuncs3->glTexSubImage3D(GL_TEXTURE_3D, 0, + f->glBindTexture(GL_TEXTURE_3D, m_lut3dTexID); + f->glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, lut3DEdgeSize, lut3DEdgeSize, lut3DEdgeSize, GL_RGB, GL_FLOAT, &m_lut3d[0]); } // Step 3: Generate the shader text QString shaderCacheID = QString::fromLatin1(m_processor->getGpuShaderTextCacheID(shaderDesc)); if (m_program.isEmpty() || shaderCacheID != m_shadercacheid) { //dbgKrita << "Computing Shader " << m_shadercacheid; m_shadercacheid = shaderCacheID; std::ostringstream os; os << m_processor->getGpuShaderText(shaderDesc) << "\n"; m_program = QString::fromLatin1(os.str().c_str()); } m_shaderDirty = false; } diff --git a/plugins/dockers/overview/overviewwidget.cc b/plugins/dockers/overview/overviewwidget.cc index a6e029610f..0d8af9dc2b 100644 --- a/plugins/dockers/overview/overviewwidget.cc +++ b/plugins/dockers/overview/overviewwidget.cc @@ -1,219 +1,219 @@ /* * Copyright (c) 2009 Cyrille Berger * Copyright (c) 2014 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; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "overviewwidget.h" #include #include #include #include #include #include #include #include #include #include #include OverviewWidget::OverviewWidget(QWidget * parent) : QWidget(parent) , m_compressor(new KisSignalCompressor(500, KisSignalCompressor::POSTPONE, this)) , m_canvas(0) , m_dragging(false) { setMouseTracking(true); connect(m_compressor, SIGNAL(timeout()), SLOT(startUpdateCanvasProjection())); KisConfig cfg; QRgb c = cfg.readEntry("OverviewWidgetColor", 0xFF454C); m_outlineColor = QColor(c); } OverviewWidget::~OverviewWidget() { } void OverviewWidget::setCanvas(KoCanvasBase * canvas) { if (m_canvas) { m_canvas->image()->disconnect(this); } m_canvas = dynamic_cast(canvas); if (m_canvas) { connect(m_canvas->image(), SIGNAL(sigImageUpdated(QRect)), m_compressor, SLOT(start()), Qt::UniqueConnection); connect(m_canvas->image(), SIGNAL(sigSizeChanged(QPointF, QPointF)), m_compressor, SLOT(start()), Qt::UniqueConnection); connect(m_canvas->canvasController()->proxyObject, SIGNAL(canvasOffsetXChanged(int)), this, SLOT(update()), Qt::UniqueConnection); m_compressor->start(); } } QSize OverviewWidget::calculatePreviewSize() { QSize imageSize(m_canvas->image()->bounds().size()); imageSize.scale(size(), Qt::KeepAspectRatio); return imageSize; } QPointF OverviewWidget::previewOrigin() { return QPointF((width() - m_pixmap.width())/2.0f, (height() - m_pixmap.height())/2.0f); } QPolygonF OverviewWidget::previewPolygon() { if (m_canvas) { const KisCoordinatesConverter* converter = m_canvas->coordinatesConverter(); QPolygonF canvasPoly = QPolygonF(QRectF(m_canvas->canvasWidget()->rect())); QPolygonF imagePoly = converter->widgetToImage(canvasPoly); - + QTransform imageToPreview = imageToPreviewTransform(); - + return imageToPreview.map(imagePoly); } return QPolygonF(); } QTransform OverviewWidget::imageToPreviewTransform() { QTransform imageToPreview; imageToPreview.scale(calculatePreviewSize().width()/(float)m_canvas->image()->width(), calculatePreviewSize().height()/(float)m_canvas->image()->height()); return imageToPreview; } void OverviewWidget::startUpdateCanvasProjection() { if (!m_canvas) return; KisImageSP image = m_canvas->image(); QSize previewSize = calculatePreviewSize(); if (isVisible() && previewSize.isValid()) { QImage img = image->projection()-> createThumbnail(previewSize.width(), previewSize.height(), image->bounds()); m_pixmap = QPixmap::fromImage(img); } update(); } void OverviewWidget::showEvent(QShowEvent *event) { Q_UNUSED(event); m_compressor->start(); } void OverviewWidget::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); if (m_canvas) { if (!m_pixmap.isNull()) { QSize newSize = calculatePreviewSize(); - m_pixmap = m_pixmap.scaled(newSize); + m_pixmap = m_pixmap.scaled(newSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); } m_compressor->start(); } } void OverviewWidget::mousePressEvent(QMouseEvent* event) { if (m_canvas) { QPointF previewPos = event->pos() - previewOrigin(); - + if (previewPolygon().containsPoint(previewPos, Qt::WindingFill)) { m_lastPos = previewPos; m_dragging = true; } } event->accept(); update(); } void OverviewWidget::mouseMoveEvent(QMouseEvent* event) { if (m_dragging) { QPointF previewPos = event->pos() - previewOrigin(); // position is mapped from preview image->image->canvas coordinates QTransform previewToImage = imageToPreviewTransform().inverted(); const KisCoordinatesConverter* converter = m_canvas->coordinatesConverter(); QPointF lastImagePos = previewToImage.map(m_lastPos); QPointF newImagePos = previewToImage.map(previewPos); - + QPointF lastWidgetPos = converter->imageToWidget(lastImagePos); QPointF newWidgetPos = converter->imageToWidget(newImagePos); - + QPointF diff = newWidgetPos - lastWidgetPos; m_canvas->canvasController()->pan(diff.toPoint()); m_lastPos = previewPos; } event->accept(); } void OverviewWidget::mouseReleaseEvent(QMouseEvent* event) { m_dragging = false; event->accept(); update(); } void OverviewWidget::wheelEvent(QWheelEvent* event) { float delta = event->delta(); - + if (delta > 0) { m_canvas->viewManager()->zoomController()->zoomAction()->zoomIn(); } else { m_canvas->viewManager()->zoomController()->zoomAction()->zoomOut(); } } void OverviewWidget::paintEvent(QPaintEvent* event) { QWidget::paintEvent(event); if (m_canvas) { QPainter p(this); p.translate(previewOrigin()); - + p.drawPixmap(0, 0, m_pixmap.width(), m_pixmap.height(), m_pixmap); QRect r = rect().translated(-previewOrigin().toPoint()); QPolygonF outline; outline << r.topLeft() << r.topRight() << r.bottomRight() << r.bottomLeft(); QPen pen; pen.setColor(m_outlineColor); pen.setStyle(Qt::DashLine); p.setPen(pen); p.drawPolygon(outline.intersected(previewPolygon())); pen.setStyle(Qt::SolidLine); p.setPen(pen); p.drawPolygon(previewPolygon()); } } diff --git a/plugins/dockers/specificcolorselector/kis_specific_color_selector_widget.cc b/plugins/dockers/specificcolorselector/kis_specific_color_selector_widget.cc index 6644da061d..bb4dea922b 100644 --- a/plugins/dockers/specificcolorselector/kis_specific_color_selector_widget.cc +++ b/plugins/dockers/specificcolorselector/kis_specific_color_selector_widget.cc @@ -1,213 +1,215 @@ /* * Copyright (c) 2008 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 Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_specific_color_selector_widget.h" #include #include #include #include #include #include #include #include #include #include #include #include "kis_color_input.h" #include #include "kis_debug.h" #include "kis_color_space_selector.h" #include "kis_signal_compressor.h" KisSpecificColorSelectorWidget::KisSpecificColorSelectorWidget(QWidget* parent) : QWidget(parent) , m_colorSpace(0) , m_spacer(0) , m_updateCompressor(new KisSignalCompressor(10, KisSignalCompressor::POSTPONE, this)) , m_customColorSpaceSelected(false) , m_displayRenderer(0) , m_fallbackRenderer(new KoDumbColorDisplayRenderer()) { m_layout = new QVBoxLayout(this); m_layout->setContentsMargins(0,0,0,0); m_layout->setSpacing(1); m_updateAllowed = true; connect(m_updateCompressor, SIGNAL(timeout()), SLOT(updateTimeout())); m_colorspaceSelector = new KisColorSpaceSelector(this); m_colorspaceSelector->layout()->setSpacing(1); connect(m_colorspaceSelector, SIGNAL(colorSpaceChanged(const KoColorSpace*)), this, SLOT(setCustomColorSpace(const KoColorSpace*))); m_chkShowColorSpaceSelector = new QCheckBox(i18n("Show Colorspace Selector"), this); connect(m_chkShowColorSpaceSelector, SIGNAL(toggled(bool)), m_colorspaceSelector, SLOT(setVisible(bool))); KConfigGroup cfg = KSharedConfig::openConfig()->group(""); m_chkShowColorSpaceSelector->setChecked(cfg.readEntry("SpecificColorSelector/ShowColorSpaceSelector", true)); m_colorspaceSelector->setVisible(m_chkShowColorSpaceSelector->isChecked()); + m_colorspaceSelector->showColorBrowserButton(false); + m_layout->addWidget(m_chkShowColorSpaceSelector); m_layout->addWidget(m_colorspaceSelector); m_spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding); m_layout->addItem(m_spacer); } KisSpecificColorSelectorWidget::~KisSpecificColorSelectorWidget() { KConfigGroup cfg = KSharedConfig::openConfig()->group(""); cfg.writeEntry("SpecificColorSelector/ShowColorSpaceSelector", m_chkShowColorSpaceSelector->isChecked()); delete m_fallbackRenderer; } bool KisSpecificColorSelectorWidget::customColorSpaceUsed() { return m_customColorSpaceSelected; } void KisSpecificColorSelectorWidget::setDisplayRenderer(KoColorDisplayRendererInterface *displayRenderer) { m_displayRenderer = displayRenderer; if (m_colorSpace) { setColorSpace(m_colorSpace); } } void KisSpecificColorSelectorWidget::setColorSpace(const KoColorSpace* cs) { Q_ASSERT(cs); dbgPlugins << cs->id() << " " << cs->profile()->name(); if (cs == m_colorSpace) { return; } m_colorSpace = KoColorSpaceRegistry::instance()->colorSpace(cs->colorModelId().id(), cs->colorDepthId().id(), cs->profile()); Q_ASSERT(m_colorSpace); Q_ASSERT(*m_colorSpace == *cs); m_color = KoColor(m_color, m_colorSpace); Q_FOREACH (KisColorInput* input, m_inputs) { delete input; } m_inputs.clear(); m_layout->removeItem(m_spacer); QList channels = KoChannelInfo::displayOrderSorted(m_colorSpace->channels()); KoColorDisplayRendererInterface *displayRenderer = m_displayRenderer ? m_displayRenderer : m_fallbackRenderer; Q_FOREACH (KoChannelInfo* channel, channels) { if (channel->channelType() == KoChannelInfo::COLOR) { KisColorInput* input = 0; switch (channel->channelValueType()) { case KoChannelInfo::UINT8: case KoChannelInfo::UINT16: case KoChannelInfo::UINT32: { input = new KisIntegerColorInput(this, channel, &m_color, displayRenderer); } break; case KoChannelInfo::FLOAT16: case KoChannelInfo::FLOAT32: { input = new KisFloatColorInput(this, channel, &m_color, displayRenderer); } break; default: Q_ASSERT(false); input = 0; } if (input) { connect(input, SIGNAL(updated()), this, SLOT(update())); connect(this, SIGNAL(updated()), input, SLOT(update())); m_inputs.append(input); m_layout->addWidget(input); } } } QList labels; int labelWidth = 0; Q_FOREACH (KisColorInput* input, m_inputs) { Q_FOREACH (QLabel* label, input->findChildren()) { labels.append(label); labelWidth = qMax(labelWidth, label->sizeHint().width()); } } Q_FOREACH (QLabel *label, labels) { label->setMinimumWidth(labelWidth); } bool allChannels8Bit = true; Q_FOREACH (KoChannelInfo* channel, channels) { if (channel->channelType() == KoChannelInfo::COLOR && channel->channelValueType() != KoChannelInfo::UINT8) { allChannels8Bit = false; } } if (allChannels8Bit) { KisColorInput* input = new KisHexColorInput(this, &m_color, displayRenderer); m_inputs.append(input); m_layout->addWidget(input); connect(input, SIGNAL(updated()), this, SLOT(update())); connect(this, SIGNAL(updated()), input, SLOT(update())); } m_layout->addItem(m_spacer); m_colorspaceSelector->blockSignals(true); m_colorspaceSelector->setCurrentColorSpace(cs); m_colorspaceSelector->blockSignals(false); m_updateAllowed = false; emit(updated()); m_updateAllowed = true; } void KisSpecificColorSelectorWidget::update() { if (m_updateAllowed) { m_updateCompressor->start(); } } void KisSpecificColorSelectorWidget::setColor(const KoColor& c) { m_updateAllowed = false; m_color.fromKoColor(c); emit(updated()); m_updateAllowed = true; } void KisSpecificColorSelectorWidget::updateTimeout() { emit(colorChanged(m_color)); } void KisSpecificColorSelectorWidget::setCustomColorSpace(const KoColorSpace *colorSpace) { m_customColorSpaceSelected = true; setColorSpace(colorSpace); setColor(m_color); } #include "moc_kis_specific_color_selector_widget.cpp" diff --git a/plugins/extensions/bigbrother/CMakeLists.txt b/plugins/extensions/bigbrother/CMakeLists.txt index 6510562d10..dea8909b5c 100644 --- a/plugins/extensions/bigbrother/CMakeLists.txt +++ b/plugins/extensions/bigbrother/CMakeLists.txt @@ -1,13 +1,13 @@ set(kritabigbrother_SOURCES bigbrother.cc actionseditor/kis_actions_editor.cpp actionseditor/kis_actions_editor_dialog.cpp actionseditor/kis_macro_model.cpp ) ki18n_wrap_ui(kritabigbrother_SOURCES actionseditor/wdgactionseditor.ui ) add_library(kritabigbrother MODULE ${kritabigbrother_SOURCES}) target_link_libraries(kritabigbrother kritaui) -install(TARGETS kritabigbrother DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) -install( FILES bigbrother.xmlgui DESTINATION ${DATA_INSTALL_DIR}/kritaplugins) +#install(TARGETS kritabigbrother DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) +#install( FILES bigbrother.xmlgui DESTINATION ${DATA_INSTALL_DIR}/kritaplugins) diff --git a/plugins/extensions/gmic/kis_gmic_settings_widget.cpp b/plugins/extensions/gmic/kis_gmic_settings_widget.cpp index 9bd786d2f6..0a38c2709d 100644 --- a/plugins/extensions/gmic/kis_gmic_settings_widget.cpp +++ b/plugins/extensions/gmic/kis_gmic_settings_widget.cpp @@ -1,678 +1,678 @@ /* * Copyright (c) 2013-2015 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. */ // Qt #include #include #include #include #include #include #include #include "kis_gmic_settings_widget.h" #include "Parameter.h" #include // kritaui lib #include #include #include // #include #include #include #include #include const static int DELAY = 250; KisGmicSettingsWidget::KisGmicSettingsWidget(Command * command) : KisConfigWidget(0, 0, DELAY), m_commandDefinition(command) { createSettingsWidget(CreateRole); } KisGmicSettingsWidget::~KisGmicSettingsWidget() { m_widgetToParameterIndexMapper.clear(); m_parameterToWidgetMapper.clear(); } void KisGmicSettingsWidget::createSettingsWidget(ROLE role) { QList parameters = m_commandDefinition->m_parameters; if (parameters.size() == 0) { dbgPlugins << "No parameters!"; return; } QGridLayout * gridLayout(0); if (role == CreateRole) { gridLayout = new QGridLayout(this); } int row = 0; for (int i = 0; i < parameters.size();i++) { Parameter * p = parameters.at(i); dbgPlugins << "Processing: " << qPrintable(p->typeName()) << " " << qPrintable(p->toString()); switch (p->m_type) { case Parameter::INT_P: { IntParameter * intParam = static_cast(p); KisSliderSpinBox * spinBox(0); if (role == CreateRole) { spinBox = new KisSliderSpinBox; spinBox->setMinimum(intParam->m_minValue); spinBox->setMaximum(intParam->m_maxValue); // map widget to parameter index m_widgetToParameterIndexMapper[spinBox] = i; mapParameterWidget(intParam, spinBox); // TODO: check if it should update preview!!!, only then recompute preview connect(spinBox, SIGNAL(valueChanged(int)), this, SIGNAL(sigConfigurationItemChanged())); connect(spinBox, SIGNAL(valueChanged(int)), this, SLOT(setIntValue(int))); gridLayout->addWidget(new QLabel(intParam->name()), row, 0); gridLayout->addWidget(spinBox, row, 1, 1, 3); row++; } else if (role == LoadRole) { spinBox = qobject_cast(widget(intParam)); } if (spinBox) { spinBox->setValue(intParam->m_value); } else { dbgPlugins << "Widget not available; Role: " << role << p->m_type; } break; } case Parameter::FLOAT_P: { FloatParameter * floatParam = static_cast(p); KisDoubleSliderSpinBox * spinBox(0); if (role == CreateRole) { spinBox = new KisDoubleSliderSpinBox; // TODO: check if 2 decimals is correct spinBox->setRange(floatParam->m_minValue, floatParam->m_maxValue, 2); m_widgetToParameterIndexMapper[spinBox] = i; mapParameterWidget(floatParam, spinBox); connect(spinBox, SIGNAL(valueChanged(qreal)), this, SIGNAL(sigConfigurationItemChanged())); connect(spinBox, SIGNAL(valueChanged(qreal)), this, SLOT(setFloatValue(qreal))); gridLayout->addWidget(new QLabel(floatParam->name()), row, 0); gridLayout->addWidget(spinBox, row, 1, 1, 3); row++; } else if (role == LoadRole) { spinBox = qobject_cast(widget(floatParam)); } if (spinBox) { spinBox->setValue(floatParam->m_value); } else { dbgPlugins << "Widget not available; Role: " << role << " TYPE " <m_type; } break; } case Parameter::CHOICE_P: { ChoiceParameter * choiceParam = static_cast(p); QComboBox * combo(0); if (role == CreateRole) { combo = new QComboBox; QStringListModel *model = new QStringListModel(); model->setStringList(choiceParam->m_choices); combo->setModel(model); m_widgetToParameterIndexMapper[combo] = i; mapParameterWidget(choiceParam, combo); connect(combo, SIGNAL(currentIndexChanged(QString)), this, SIGNAL(sigConfigurationItemChanged())); connect(combo, SIGNAL(currentIndexChanged(int)), this, SLOT(setChoiceIndex(int))); gridLayout->addWidget(new QLabel(choiceParam->name()), row, 0); gridLayout->addWidget(combo, row, 1, 1, 3); row++; } else if (role == LoadRole) { combo = qobject_cast(widget(choiceParam)); } if (combo) { combo->setCurrentIndex(choiceParam->m_value); } else { dbgPlugins << "Widget not available; Role: " << role << " TYPE " <m_type; } break; } case Parameter::NOTE_P: { if (role == CreateRole) { NoteParameter * noteParam = static_cast(p); QLabel * label = new QLabel; QString labelTxt = noteParam->m_label.replace(QString("\\n"), QString("
")); label->setText(labelTxt); label->setWordWrap(true); mapParameterWidget(noteParam, label); gridLayout->addWidget(label, row, 0, 1, 3); row++; } break; } case Parameter::LINK_P: { if (role == CreateRole) { LinkParameter * linkParam = static_cast(p); QLabel * label = new QLabel; label->setText(linkParam->m_link); label->setWordWrap(true); label->setOpenExternalLinks(true); mapParameterWidget(linkParam, label); gridLayout->addWidget(label, row, 0, 1, 3); row++; } break; } case Parameter::SEPARATOR_P: { if (role == CreateRole) { SeparatorParameter * linkParam = static_cast(p); KSeparator * kseparator = new KSeparator(Qt::Horizontal); mapParameterWidget(linkParam, kseparator); gridLayout->addWidget(kseparator, row, 0, 1, 4); row++; } break; } case Parameter::BOOL_P: { BoolParameter * boolParam = static_cast(p); QCheckBox *checkBox(0); if (role == CreateRole) { checkBox = new QCheckBox(boolParam->m_name); m_widgetToParameterIndexMapper[checkBox] = i; mapParameterWidget(boolParam, checkBox); connect(checkBox, SIGNAL(stateChanged(int)), this, SIGNAL(sigConfigurationItemChanged())); connect(checkBox, SIGNAL(toggled(bool)), this, SLOT(setBoolValue(bool))); gridLayout->addWidget(checkBox, row, 0, 1, 2); row++; } else if (role == LoadRole) { checkBox = qobject_cast(widget(boolParam)); } if (checkBox) { checkBox->setChecked(boolParam->m_value); } else { dbgPlugins << "Widget not available; Role: " << role << " TYPE " <m_type; } break; } case Parameter::COLOR_P: { ColorParameter * colorParam = static_cast(p); KColorButton *colorButton(0); if (role == CreateRole) { colorButton = new KColorButton; colorButton->setAlphaChannelEnabled(colorParam->m_hasAlpha); m_widgetToParameterIndexMapper[colorButton] = i; mapParameterWidget(colorParam, colorButton); connect(colorButton, SIGNAL(changed(QColor)), this, SIGNAL(sigConfigurationItemChanged())); connect(colorButton, SIGNAL(changed(QColor)), this, SLOT(setColorValue(QColor))); gridLayout->addWidget(new QLabel(colorParam->name()), row, 0); gridLayout->addWidget(colorButton, row, 1, 1, 3); row++; } else if (role == LoadRole) { colorButton = qobject_cast(widget(colorParam)); } if (colorButton) { colorButton->setColor(colorParam->m_value); } else { dbgPlugins << "Widget not available; Role: " << role << " TYPE " <m_type; } break; } case Parameter::FOLDER_P: { FolderParameter * folderParam = static_cast(p); KisFileNameRequester * urlRequester(0); if (role == CreateRole) { urlRequester = new KisFileNameRequester; - urlRequester->setMimeTypeFilters(KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Import)); + urlRequester->setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import)); urlRequester->setMode(KoFileDialog::OpenDirectory); m_widgetToParameterIndexMapper[ urlRequester ] = i; mapParameterWidget(folderParam, urlRequester); connect(urlRequester, SIGNAL(fileSelected(QString)), this, SIGNAL(sigConfigurationItemChanged())); connect(urlRequester, SIGNAL(fileSelected(QString)), this, SLOT(setFolderPathValue(QString))); gridLayout->addWidget(new QLabel(folderParam->name()), row, 0); gridLayout->addWidget(urlRequester, row, 1, 1, 3); row++; } else if (role == LoadRole) { urlRequester = qobject_cast(widget(folderParam)); } if (urlRequester) { urlRequester->setFileName(folderParam->toUiValue()); } else { dbgPlugins << "Widget not available; Role: " << role << " TYPE " <m_type; } break; } case Parameter::FILE_P: { FileParameter * fileParam = static_cast(p); KisFileNameRequester * urlRequester(0); if (role == CreateRole) { urlRequester = new KisFileNameRequester; urlRequester->setMode(KoFileDialog::OpenFile); m_widgetToParameterIndexMapper[ urlRequester ] = i; mapParameterWidget(fileParam, urlRequester); connect(urlRequester, SIGNAL(fileSelected(QString)), this, SIGNAL(sigConfigurationItemChanged())); connect(urlRequester, SIGNAL(fileSelected(QString)), this, SLOT(setFilePathValue(QString))); gridLayout->addWidget(new QLabel(fileParam->name()), row, 0); gridLayout->addWidget(urlRequester, row, 1, 1, 3); row++; } else if (role == LoadRole) { urlRequester = qobject_cast(widget(fileParam)); } if (urlRequester) { urlRequester->setFileName(fileParam->toUiValue()); } else { dbgPlugins << "Widget not available; Role: " << role << " TYPE " <m_type; } break; } case Parameter::TEXT_P: { TextParameter * textParam = static_cast(p); QPushButton * updateBtn(0); if (role == CreateRole) { updateBtn = new QPushButton(i18n("Update")); } if (!textParam->m_multiline) { QLineEdit * lineEdit(0); if (role == CreateRole) { lineEdit = new QLineEdit; lineEdit->setText(textParam->toUiValue()); m_widgetToParameterIndexMapper[lineEdit] = i; mapParameterWidget(textParam, lineEdit); connect(updateBtn, SIGNAL(clicked(bool)), lineEdit, SIGNAL(editingFinished())); connect(lineEdit, SIGNAL(editingFinished()), this, SIGNAL(sigConfigurationItemChanged())); connect(lineEdit, SIGNAL(editingFinished()), this, SLOT(setTextValue())); gridLayout->addWidget(new QLabel(textParam->name()), row, 0); gridLayout->addWidget(lineEdit, row, 1, 1, 1); gridLayout->addWidget(updateBtn, row, 3, 1, 1); row++; } else if (role == LoadRole) { lineEdit = qobject_cast(widget(textParam)); } if (lineEdit) { lineEdit->setText(textParam->toUiValue()); } else { dbgPlugins << "Widget not available; Role: " << role << " TYPE " <m_type; } } else /*if (textParam->m_multiline)*/ { QTextEdit * multiLineEdit(0); if (role == CreateRole) { multiLineEdit = new QTextEdit; multiLineEdit->setText(textParam->toUiValue()); m_widgetToParameterIndexMapper[multiLineEdit] = i; mapParameterWidget(textParam, multiLineEdit); connect(updateBtn, SIGNAL(clicked(bool)), multiLineEdit, SIGNAL(editingFinished())); connect(multiLineEdit, SIGNAL(editingFinished()), this, SIGNAL(sigConfigurationItemChanged())); connect(multiLineEdit, SIGNAL(editingFinished()), this, SLOT(setTextValue())); gridLayout->addWidget(new QLabel(textParam->name()), row, 0); gridLayout->addWidget(updateBtn, row, 1); row++; gridLayout->addWidget(multiLineEdit, row, 0, 1, 4, Qt::AlignCenter); row++; } else if (role == LoadRole) { multiLineEdit = qobject_cast(widget(textParam)); } if (multiLineEdit) { multiLineEdit->setText(textParam->toUiValue()); } else { dbgPlugins << "Widget not available; Role: " << role << " TYPE " <m_type; } } break; } default:{ dbgPlugins << "IGNORING : " << qPrintable(p->typeName()); break; } } } if (role == CreateRole) { setLayout(gridLayout); } } Command* KisGmicSettingsWidget::currentCommandSettings() { return m_commandDefinition; } void KisGmicSettingsWidget::setIntValue(int value) { Parameter * p = parameter(sender()); if (p->m_type != Parameter::INT_P) { return; } IntParameter * intParam = static_cast(p); intParam->m_value = value; } void KisGmicSettingsWidget::setFloatValue(qreal value) { Parameter * p = parameter(sender()); if (!p) { return; } if (p->m_type != Parameter::FLOAT_P) { return; } FloatParameter * floatParam = static_cast(p); floatParam->m_value = value; } void KisGmicSettingsWidget::setBoolValue(bool value) { Parameter * p = parameter(sender()); if (!p) { return; } if (p->m_type != Parameter::BOOL_P) { return; } BoolParameter * boolParam = static_cast(p); boolParam->m_value = value; } void KisGmicSettingsWidget::setChoiceIndex(int index) { dbgPlugins << "setting choice param: failed?"; Parameter * p = parameter(sender()); if (!p) { return; } if (p->m_type != Parameter::CHOICE_P) { return; } dbgPlugins << "NO!" << "Setting " << index; ChoiceParameter * choiceParam = static_cast(p); choiceParam->m_value = index; } void KisGmicSettingsWidget::setColorValue(const QColor& color) { Parameter * p = parameter(sender()); if (!p) { return; } if (p->m_type != Parameter::COLOR_P) { return; } ColorParameter * colorParam = static_cast(p); colorParam->m_value = color; } void KisGmicSettingsWidget::setTextValue() { Parameter * p = parameter(sender()); if (!p) { return; } if (p->m_type != Parameter::TEXT_P) { return; } TextParameter * textParam = static_cast(p); QString result = "INVALID"; if (textParam->m_multiline) { const QTextEdit * textEdit = qobject_cast< const QTextEdit *>(sender()); if (textEdit) { result = textEdit->toPlainText(); } } else { const QLineEdit * lineEdit = qobject_cast< const QLineEdit *>(sender()); if (lineEdit) { result = lineEdit->text(); } } textParam->fromUiValue(result); } Parameter * KisGmicSettingsWidget::parameter(QObject * widget) { QWidget *q = qobject_cast(widget); if (!q) { return 0; } if (!m_widgetToParameterIndexMapper.contains(q)) { dbgPlugins << "Widget-to-parameter map does not contain " << q; return 0; } int index = m_widgetToParameterIndexMapper[q]; Parameter * p = m_commandDefinition->m_parameters.at(index); return p; } QWidget* KisGmicSettingsWidget::widget(Parameter* parameter) { if (!parameter) { return 0; } if (!m_parameterToWidgetMapper.contains(parameter)) { return 0; } return qobject_cast(m_parameterToWidgetMapper[parameter]); } void KisGmicSettingsWidget::setFolderPathValue(const QString &path) { Parameter * p = parameter(sender()); if (!p) { return; } if (p->m_type != Parameter::FOLDER_P) { return; } FolderParameter * folderParam = static_cast(p); folderParam->fromUiValue(path); } void KisGmicSettingsWidget::setFilePathValue(const QString &path) { Parameter * p = parameter(sender()); if (!p) { return; } if (p->m_type != Parameter::FILE_P) { return; } FileParameter * fileParam = static_cast(p); fileParam->fromUiValue(path); } void KisGmicSettingsWidget::mapParameterWidget(Parameter* parameter, QWidget* widget) { m_parameterToWidgetMapper[parameter] = widget; } void KisGmicSettingsWidget::reload() { createSettingsWidget(LoadRole); } diff --git a/plugins/extensions/histogram/kis_histogram_widget.cc b/plugins/extensions/histogram/kis_histogram_widget.cc index 4a0de06c2a..61dff53efb 100644 --- a/plugins/extensions/histogram/kis_histogram_widget.cc +++ b/plugins/extensions/histogram/kis_histogram_widget.cc @@ -1,149 +1,147 @@ /* * Copyright (c) 2004 Boudewijn Rempt * (c) 2005 Bart Coppens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_histogram_widget.h" #include #include #include #include #include #include #include "KoChannelInfo.h" #include "kis_histogram_view.h" #include "kis_histogram.h" #include "kis_global.h" #include "kis_types.h" #include "kis_layer.h" #include "kis_paint_device.h" KisHistogramWidget::KisHistogramWidget(QWidget *parent, const char *name) : WdgHistogram(parent) { setObjectName(name); m_from = 0.0; m_width = 0.0; } KisHistogramWidget::~KisHistogramWidget() { } void KisHistogramWidget::setPaintDevice(KisPaintDeviceSP dev, const QRect &bounds) { grpType->disconnect(this); cmbChannel->disconnect(this); m_histogramView->setPaintDevice(dev, bounds); setActiveChannel(0); // So we have the colored one if there are colors // The channels cmbChannel->clear(); cmbChannel->addItems(m_histogramView->channelStrings()); cmbChannel->setCurrentIndex(0); // View display currentView->setMinimum(0); currentView->setMaximum(100); updateEnabled(); m_from = m_histogramView->currentProducer()->viewFrom(); m_width = m_histogramView->currentProducer()->viewWidth(); - connect(grpType, SIGNAL(clicked(int)), this, SLOT(slotTypeSwitched(int))); + connect(radioLinear,SIGNAL(clicked()), this, SLOT(slotTypeSwitched())); + connect(radioLog,SIGNAL(clicked()), this, SLOT(slotTypeSwitched())); connect(cmbChannel, SIGNAL(activated(int)), this, SLOT(setActiveChannel(int))); connect(zoomIn, SIGNAL(clicked()), this, SLOT(slotZoomIn())); connect(zoomOut, SIGNAL(clicked()), this, SLOT(slotZoomOut())); connect(currentView, SIGNAL(valueChanged(int)), this, SLOT(slide(int))); } void KisHistogramWidget::setActiveChannel(int channel) { m_histogramView->setActiveChannel(channel); updateEnabled(); } -void KisHistogramWidget::slotTypeSwitched(int id) +void KisHistogramWidget::slotTypeSwitched(void) { - if (id == LINEAR) - m_histogramView->setHistogramType(LINEAR); - else if (id == LOGARITHMIC) - m_histogramView->setHistogramType(LOGARITHMIC); + m_histogramView->setHistogramType( radioLinear->isChecked()?LINEAR:LOGARITHMIC ); } void KisHistogramWidget::setView(double from, double size) { m_from = from; m_width = size; if (m_from + m_width > 1.0) m_from = 1.0 - m_width; m_histogramView->setView(m_from, m_width); updateEnabled(); } void KisHistogramWidget::slotZoomIn() { if ((m_width / 2) >= m_histogramView->currentProducer()->maximalZoom()) { setView(m_from, m_width / 2); } } void KisHistogramWidget::slotZoomOut() { if (m_width * 2 <= 1.0) { setView(m_from, m_width * 2); } } void KisHistogramWidget::slide(int val) { // Beware: at the END (e.g. 100), we want to still view m_width: setView((static_cast(val) / 100.0) *(1.0 - m_width), m_width); } void KisHistogramWidget::updateEnabled() { if (m_histogramView->currentProducer()->maximalZoom() < 1.0) { if ((m_width / 2) >= m_histogramView->currentProducer()->maximalZoom()) { zoomIn->setEnabled(true); } else { zoomIn->setEnabled(false); } if (m_width * 2 <= 1.0) { zoomOut->setEnabled(true); } else { zoomOut->setEnabled(false); } if (m_width < 1.0) currentView->setEnabled(true); else currentView->setEnabled(false); } else { zoomIn->setEnabled(false); zoomOut->setEnabled(false); currentView->setEnabled(false); } } diff --git a/plugins/extensions/histogram/kis_histogram_widget.h b/plugins/extensions/histogram/kis_histogram_widget.h index 93f8433e7f..452cafa506 100644 --- a/plugins/extensions/histogram/kis_histogram_widget.h +++ b/plugins/extensions/histogram/kis_histogram_widget.h @@ -1,64 +1,64 @@ /* * 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. */ #ifndef KIS_HISTOGRAM_WIDGET_ #define KIS_HISTOGRAM_WIDGET_ #include "kis_types.h" #include "ui_wdghistogram.h" class WdgHistogram : public QWidget, public Ui::WdgHistogram { Q_OBJECT public: WdgHistogram(QWidget *parent) : QWidget(parent) { setupUi(this); } }; /** * The histogram widget takes a paint device or an image and * draws a histogram for the given KisHistogram. */ class KisHistogramWidget : public WdgHistogram { Q_OBJECT public: KisHistogramWidget(QWidget *parent, const char *name); virtual ~KisHistogramWidget(); void setPaintDevice(KisPaintDeviceSP dev, const QRect &bounds); private Q_SLOTS: void setActiveChannel(int channel); - void slotTypeSwitched(int id); + void slotTypeSwitched(void); void slotZoomIn(); void slotZoomOut(); void slide(int val); private: void setView(double from, double size); void updateEnabled(); double m_from, m_width; }; #endif // KIS_HISTOGRAM_WIDGET_ diff --git a/plugins/extensions/imagesplit/imagesplit.cpp b/plugins/extensions/imagesplit/imagesplit.cpp index 6381eb6c64..08341e9d15 100644 --- a/plugins/extensions/imagesplit/imagesplit.cpp +++ b/plugins/extensions/imagesplit/imagesplit.cpp @@ -1,167 +1,167 @@ /* * imagesplit.cc -- Part of Krita * * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org) * Copyright (c) 2011 Srikanth Tiyyagura * * This program is free software; you can redistribute 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 "imagesplit.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dlg_imagesplit.h" K_PLUGIN_FACTORY_WITH_JSON(ImagesplitFactory, "kritaimagesplit.json", registerPlugin();) Imagesplit::Imagesplit(QObject *parent, const QVariantList &) : KisViewPlugin(parent) { KisAction *action = createAction("imagesplit"); connect(action, SIGNAL(triggered()), this, SLOT(slotImagesplit())); } Imagesplit::~Imagesplit() { } void Imagesplit::saveAsImage(const QRect &imgSize, const QString &mimeType, const QString &url) { KisImageWSP image = m_view->image(); KisDocument *document = KisPart::instance()->createDocument(); document->prepareForImport(); KisImageWSP dst = new KisImage(document->createUndoStore(), imgSize.width(), imgSize.height(), image->colorSpace(), image->objectName()); dst->setResolution(image->xRes(), image->yRes()); document->setCurrentImage(dst); KisPaintLayer* paintLayer = new KisPaintLayer(dst, dst->nextLayerName(), 255); KisPainter gc(paintLayer->paintDevice()); gc.bitBlt(QPoint(0, 0), image->projection(), imgSize); dst->addNode(paintLayer, KisNodeSP(0)); dst->refreshGraph(); document->setFileBatchMode(true); document->setOutputMimeType(mimeType.toLatin1()); document->exportDocument(QUrl::fromLocalFile(url)); delete document; } void Imagesplit::slotImagesplit() { // Taking the title - url from caption function and removing file extension QStringList strList = ((m_view->document())->caption()).split('.'); QString suffix = strList.at(0); // Getting all mime types and converting them into names which are displayed at combo box - QStringList listMimeFilter = KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Export); + QStringList listMimeFilter = KisImportExportManager::mimeFilter(KisImportExportManager::Export); listMimeFilter.sort(); QStringList filteredMimeTypes; QStringList listFileType; Q_FOREACH (const QString & mimeType, listMimeFilter) { listFileType.append(KisMimeDatabase::descriptionForMimeType(mimeType)); filteredMimeTypes.append(mimeType); } listMimeFilter = filteredMimeTypes; Q_ASSERT(listMimeFilter.size() == listFileType.size()); DlgImagesplit *dlgImagesplit = new DlgImagesplit(m_view, suffix, listFileType); dlgImagesplit->setObjectName("Imagesplit"); Q_CHECK_PTR(dlgImagesplit); KisImageWSP image = m_view->image(); if (dlgImagesplit->exec() == QDialog::Accepted) { int numHorizontalLines = dlgImagesplit->horizontalLines(); int numVerticalLines = dlgImagesplit->verticalLines(); int img_width = image->width() / (numVerticalLines + 1); int img_height = image->height() / (numHorizontalLines + 1); if (dlgImagesplit->autoSave()) { for (int i = 0, k = 1; i < (numVerticalLines + 1); i++) { for (int j = 0; j < (numHorizontalLines + 1); j++, k++) { QString mimeTypeSelected = listMimeFilter.at(dlgImagesplit->cmbIndex); QString homepath = QDir::homePath(); QString suffix = KisMimeDatabase::suffixesForMimeType(mimeTypeSelected).first(); qDebug() << "suffix" << suffix; if (suffix.startsWith("*.")) { suffix = suffix.remove(0, 1); } qDebug() << "\tsuffix" << suffix; if (!suffix.startsWith(".")) { suffix = suffix.prepend('.'); } qDebug() << "\tsuffix" << suffix; QString fileName = dlgImagesplit->suffix() + '_' + QString::number(k) + suffix; QString url = homepath + '/' + fileName; saveAsImage(QRect((i * img_width), (j * img_height), img_width, img_height), listMimeFilter.at(dlgImagesplit->cmbIndex), url); } } } else { for (int i = 0; i < (numVerticalLines + 1); i++) { for (int j = 0; j < (numHorizontalLines + 1); j++) { KoFileDialog dialog(m_view->mainWindow(), KoFileDialog::SaveFile, "OpenDocument"); dialog.setCaption(i18n("Save Image on Split")); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); dialog.setMimeTypeFilters(listMimeFilter); QUrl url = QUrl::fromUserInput(dialog.filename()); QString mimefilter = KisMimeDatabase::mimeTypeForFile(url.toLocalFile()); if (url.isEmpty()) return; saveAsImage(QRect((i * img_width), (j * img_height), img_width, img_height), mimefilter, url.toLocalFile()); } } } } delete dlgImagesplit; } #include "imagesplit.moc" diff --git a/plugins/extensions/metadataeditor/CMakeLists.txt b/plugins/extensions/metadataeditor/CMakeLists.txt index c998706191..0aa8d25e59 100644 --- a/plugins/extensions/metadataeditor/CMakeLists.txt +++ b/plugins/extensions/metadataeditor/CMakeLists.txt @@ -1,14 +1,12 @@ -include_directories(${CMAKE_SOURCE_DIR}/krita/image/metadata) - set(kritametadataeditor_SOURCES metadataeditor.cc kis_entry_editor.cc kis_meta_data_editor.cc kis_meta_data_model.cpp ) ki18n_wrap_ui(kritametadataeditor_SOURCES editors/dublincore.ui editors/exif.ui ) add_library(kritametadataeditor MODULE ${kritametadataeditor_SOURCES}) target_link_libraries(kritametadataeditor kritaui ) install(TARGETS kritametadataeditor DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) install( FILES metadataeditor.xmlgui DESTINATION ${DATA_INSTALL_DIR}/kritaplugins ) install( FILES editors/dublincore.xmlgui editors/exif.xmlgui DESTINATION ${DATA_INSTALL_DIR}/kritaplugins/metadataeditor ) diff --git a/plugins/extensions/resourcemanager/dlg_create_bundle.cpp b/plugins/extensions/resourcemanager/dlg_create_bundle.cpp index 733d24e497..b8d754f2c9 100644 --- a/plugins/extensions/resourcemanager/dlg_create_bundle.cpp +++ b/plugins/extensions/resourcemanager/dlg_create_bundle.cpp @@ -1,387 +1,387 @@ /* * Copyright (c) 2014 Victor Lafon metabolic.ewilan@hotmail.fr * * 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 "dlg_create_bundle.h" #include "ui_wdgdlgcreatebundle.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisResourceBundle.h" #define ICON_SIZE 48 DlgCreateBundle::DlgCreateBundle(KisResourceBundle *bundle, QWidget *parent) : KoDialog(parent) , m_ui(new Ui::WdgDlgCreateBundle) , m_bundle(bundle) { m_page = new QWidget(); m_ui->setupUi(m_page); setMainWidget(m_page); setFixedSize(m_page->sizeHint()); setButtons(Ok | Cancel); setDefaultButton(Ok); setButtonText(Ok, i18n("Save")); connect(m_ui->bnSelectSaveLocation, SIGNAL(clicked()), SLOT(selectSaveLocation())); KoDocumentInfo info; info.updateParameters(); if (bundle) { setCaption(i18n("Edit Resource Bundle")); m_ui->lblSaveLocation->setText(QFileInfo(bundle->filename()).absolutePath()); m_ui->editBundleName->setText(bundle->name()); m_ui->editAuthor->setText(bundle->getMeta("author")); m_ui->editEmail->setText(bundle->getMeta("email")); m_ui->editLicense->setText(bundle->getMeta("license")); m_ui->editWebsite->setText(bundle->getMeta("website")); m_ui->editDescription->document()->setPlainText(bundle->getMeta("description")); m_ui->lblPreview->setPixmap(QPixmap::fromImage(bundle->image().scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation))); } else { setCaption(i18n("Create Resource Bundle")); KisConfig cfg; m_ui->editAuthor->setText(cfg.readEntry("BundleAuthorName", info.authorInfo("creator"))); m_ui->editEmail->setText(cfg.readEntry("BundleAuthorEmail", info.authorInfo("email"))); m_ui->editWebsite->setText(cfg.readEntry("BundleWebsite", "http://")); m_ui->editLicense->setText(cfg.readEntry("BundleLicense", "CC-BY-SA")); m_ui->lblSaveLocation->setText(cfg.readEntry("BundleExportLocation", QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation))); } m_ui->bnAdd->setIcon(KisIconUtils::loadIcon("arrow-right")); connect(m_ui->bnAdd, SIGNAL(clicked()), SLOT(addSelected())); m_ui->bnRemove->setIcon(KisIconUtils::loadIcon("arrow-left")); connect(m_ui->bnRemove, SIGNAL(clicked()), SLOT(removeSelected())); m_ui->cmbResourceTypes->addItem(i18n("Brushes"), QString("brushes")); m_ui->cmbResourceTypes->addItem(i18n("Brush Presets"), QString("presets")); m_ui->cmbResourceTypes->addItem(i18n("Gradients"), QString("gradients")); m_ui->cmbResourceTypes->addItem(i18n("Patterns"), QString("patterns")); m_ui->cmbResourceTypes->addItem(i18n("Palettes"), QString("palettes")); m_ui->cmbResourceTypes->addItem(i18n("Workspaces"), QString("workspaces")); connect(m_ui->cmbResourceTypes, SIGNAL(activated(int)), SLOT(resourceTypeSelected(int))); m_ui->tableAvailable->setIconSize(QSize(ICON_SIZE, ICON_SIZE)); m_ui->tableAvailable->setSelectionMode(QAbstractItemView::ExtendedSelection); m_ui->tableSelected->setIconSize(QSize(ICON_SIZE, ICON_SIZE)); m_ui->tableSelected->setSelectionMode(QAbstractItemView::ExtendedSelection); connect(m_ui->bnGetPreview, SIGNAL(clicked()), SLOT(getPreviewImage())); resourceTypeSelected(0); } DlgCreateBundle::~DlgCreateBundle() { delete m_ui; } QString DlgCreateBundle::bundleName() const { return m_ui->editBundleName->text().replace(" ", "_"); } QString DlgCreateBundle::authorName() const { return m_ui->editAuthor->text(); } QString DlgCreateBundle::email() const { return m_ui->editEmail->text(); } QString DlgCreateBundle::website() const { return m_ui->editWebsite->text(); } QString DlgCreateBundle::license() const { return m_ui->editLicense->text(); } QString DlgCreateBundle::description() const { return m_ui->editDescription->document()->toPlainText(); } QString DlgCreateBundle::saveLocation() const { return m_ui->lblSaveLocation->text(); } QString DlgCreateBundle::previewImage() const { return m_previewImage; } void DlgCreateBundle::accept() { QString name = m_ui->editBundleName->text().remove(" "); if (name.isEmpty()) { m_ui->editBundleName->setStyleSheet(QString(" border: 1px solid red")); QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("The resource bundle name cannot be empty.")); return; } else { QFileInfo fileInfo(m_ui->lblSaveLocation->text() + "/" + name + ".bundle"); if (fileInfo.exists()) { m_ui->editBundleName->setStyleSheet("border: 1px solid red"); QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("A bundle with this name already exists.")); return; } else { if (!m_bundle) { KisConfig cfg; cfg.writeEntry("BunleExportLocation", m_ui->lblSaveLocation->text()); cfg.writeEntry("BundleAuthorName", m_ui->editAuthor->text()); cfg.writeEntry("BundleAuthorEmail", m_ui->editEmail->text()); cfg.writeEntry("BundleWebsite", m_ui->editWebsite->text()); cfg.writeEntry("BundleLicense", m_ui->editLicense->text()); } KoDialog::accept(); } } } void DlgCreateBundle::selectSaveLocation() { KoFileDialog dialog(this, KoFileDialog::OpenDirectory, "resourcebundlesavelocation"); dialog.setDefaultDir(m_ui->lblSaveLocation->text()); dialog.setCaption(i18n("Select a directory to save the bundle")); QString location = dialog.filename(); m_ui->lblSaveLocation->setText(location); } void DlgCreateBundle::addSelected() { int row = m_ui->tableAvailable->currentRow(); Q_FOREACH (QListWidgetItem *item, m_ui->tableAvailable->selectedItems()) { m_ui->tableSelected->addItem(m_ui->tableAvailable->takeItem(m_ui->tableAvailable->row(item))); QString resourceType = m_ui->cmbResourceTypes->itemData(m_ui->cmbResourceTypes->currentIndex()).toString(); if (resourceType == "brushes") { m_selectedBrushes.append(item->data(Qt::UserRole).toString()); } else if (resourceType == "presets") { m_selectedPresets.append(item->data(Qt::UserRole).toString()); } else if (resourceType == "gradients") { m_selectedGradients.append(item->data(Qt::UserRole).toString()); } else if (resourceType == "patterns") { m_selectedPatterns.append(item->data(Qt::UserRole).toString()); } else if (resourceType == "palettes") { m_selectedPalettes.append(item->data(Qt::UserRole).toString()); } else if (resourceType == "workspaces") { m_selectedWorkspaces.append(item->data(Qt::UserRole).toString()); } } m_ui->tableAvailable->setCurrentRow(row); } void DlgCreateBundle::removeSelected() { int row = m_ui->tableSelected->currentRow(); Q_FOREACH (QListWidgetItem *item, m_ui->tableSelected->selectedItems()) { m_ui->tableAvailable->addItem(m_ui->tableSelected->takeItem(m_ui->tableSelected->row(item))); QString resourceType = m_ui->cmbResourceTypes->itemData(m_ui->cmbResourceTypes->currentIndex()).toString(); if (resourceType == "brushes") { m_selectedBrushes.removeAll(item->data(Qt::UserRole).toString()); } else if (resourceType == "presets") { m_selectedPresets.removeAll(item->data(Qt::UserRole).toString()); } else if (resourceType == "gradients") { m_selectedGradients.removeAll(item->data(Qt::UserRole).toString()); } else if (resourceType == "patterns") { m_selectedPatterns.removeAll(item->data(Qt::UserRole).toString()); } else if (resourceType == "palettes") { m_selectedPalettes.removeAll(item->data(Qt::UserRole).toString()); } else if (resourceType == "workspaces") { m_selectedWorkspaces.removeAll(item->data(Qt::UserRole).toString()); } } m_ui->tableSelected->setCurrentRow(row); } QPixmap imageToIcon(const QImage &img) { QPixmap pixmap(ICON_SIZE, ICON_SIZE); pixmap.fill(); QImage scaled = img.scaled(ICON_SIZE, ICON_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation); int x = (ICON_SIZE - scaled.width()) / 2; int y = (ICON_SIZE - scaled.height()) / 2; QPainter gc(&pixmap); gc.drawImage(x, y, scaled); gc.end(); return pixmap; } void DlgCreateBundle::resourceTypeSelected(int idx) { QString resourceType = m_ui->cmbResourceTypes->itemData(idx).toString(); m_ui->tableAvailable->clear(); m_ui->tableSelected->clear(); if (resourceType == "brushes") { KisBrushResourceServer *server = KisBrushServer::instance()->brushServer(); Q_FOREACH (KisBrushSP res, server->resources()) { QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); item->setData(Qt::UserRole, res->shortFilename()); if (m_selectedBrushes.contains(res->shortFilename())) { m_ui->tableSelected->addItem(item); } else { m_ui->tableAvailable->addItem(item); } } } else if (resourceType == "presets") { KisPaintOpPresetResourceServer* server = KisResourceServerProvider::instance()->paintOpPresetServer(); Q_FOREACH (KisPaintOpPresetSP res, server->resources()) { QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); item->setData(Qt::UserRole, res->shortFilename()); if (m_selectedPresets.contains(res->shortFilename())) { m_ui->tableSelected->addItem(item); } else { m_ui->tableAvailable->addItem(item); } } } else if (resourceType == "gradients") { KoResourceServer* server = KoResourceServerProvider::instance()->gradientServer(); Q_FOREACH (KoResource *res, server->resources()) { if (res->filename()!="Foreground to Transparent" && res->filename()!="Foreground to Background") { //technically we should read from the file-name whether or not the file can be opened, but this works for now. The problem is making sure that bundle-resource know where they are stored.// //dbgKrita<filename(); QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); item->setData(Qt::UserRole, res->shortFilename()); if (m_selectedGradients.contains(res->shortFilename())) { m_ui->tableSelected->addItem(item); } else { m_ui->tableAvailable->addItem(item); } } } } else if (resourceType == "patterns") { KoResourceServer* server = KoResourceServerProvider::instance()->patternServer(); Q_FOREACH (KoResource *res, server->resources()) { QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); item->setData(Qt::UserRole, res->shortFilename()); if (m_selectedPatterns.contains(res->shortFilename())) { m_ui->tableSelected->addItem(item); } else { m_ui->tableAvailable->addItem(item); } } } else if (resourceType == "palettes") { KoResourceServer* server = KoResourceServerProvider::instance()->paletteServer(); Q_FOREACH (KoResource *res, server->resources()) { QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); item->setData(Qt::UserRole, res->shortFilename()); if (m_selectedPalettes.contains(res->shortFilename())) { m_ui->tableSelected->addItem(item); } else { m_ui->tableAvailable->addItem(item); } } } else if (resourceType == "workspaces") { KoResourceServer* server = KisResourceServerProvider::instance()->workspaceServer(); Q_FOREACH (KoResource *res, server->resources()) { QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); item->setData(Qt::UserRole, res->shortFilename()); if (m_selectedWorkspaces.contains(res->shortFilename())) { m_ui->tableSelected->addItem(item); } else { m_ui->tableAvailable->addItem(item); } } } } void DlgCreateBundle::getPreviewImage() { KoFileDialog dialog(this, KoFileDialog::OpenFile, "BundlePreviewImage"); dialog.setCaption(i18n("Select file to use as dynamic file layer.")); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); - dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Import)); + dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import)); m_previewImage = dialog.filename(); QImage img(m_previewImage); img = img.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation); m_ui->lblPreview->setPixmap(QPixmap::fromImage(img)); } diff --git a/plugins/extensions/resourcemanager/resourcemanager.cpp b/plugins/extensions/resourcemanager/resourcemanager.cpp index 3dd0109690..03f1a6b709 100644 --- a/plugins/extensions/resourcemanager/resourcemanager.cpp +++ b/plugins/extensions/resourcemanager/resourcemanager.cpp @@ -1,305 +1,310 @@ /* * resourcemanager.cc -- Part of Krita * * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org) * * This program is free software; you can redistribute 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 "resourcemanager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dlg_bundle_manager.h" #include "dlg_create_bundle.h" class ResourceManager::Private { public: Private() { brushServer = KisBrushServer::instance()->brushServer(false); paintopServer = KisResourceServerProvider::instance()->paintOpPresetServer(false); gradientServer = KoResourceServerProvider::instance()->gradientServer(false); patternServer = KoResourceServerProvider::instance()->patternServer(false); paletteServer = KoResourceServerProvider::instance()->paletteServer(false); workspaceServer = KisResourceServerProvider::instance()->workspaceServer(false); } KisBrushResourceServer* brushServer; KisPaintOpPresetResourceServer * paintopServer; KoResourceServer* gradientServer; KoResourceServer *patternServer; KoResourceServer* paletteServer; KoResourceServer* workspaceServer; }; K_PLUGIN_FACTORY_WITH_JSON(ResourceManagerFactory, "kritaresourcemanager.json", registerPlugin();) ResourceManager::ResourceManager(QObject *parent, const QVariantList &) : KisViewPlugin(parent) , d(new Private()) { KisAction *action = new KisAction(i18n("Import Bundles..."), this); addAction("import_bundles", action); connect(action, SIGNAL(triggered()), this, SLOT(slotImportBundles())); action = new KisAction(i18n("Import Brushes..."), this); addAction("import_brushes", action); connect(action, SIGNAL(triggered()), this, SLOT(slotImportBrushes())); action = new KisAction(i18n("Import Gradients..."), this); addAction("import_gradients", action); connect(action, SIGNAL(triggered()), this, SLOT(slotImportGradients())); action = new KisAction(i18n("Import Palettes..."), this); addAction("import_palettes", action); connect(action, SIGNAL(triggered()), this, SLOT(slotImportPalettes())); action = new KisAction(i18n("Import Patterns..."), this); addAction("import_patterns", action); connect(action, SIGNAL(triggered()), this, SLOT(slotImportPatterns())); action = new KisAction(i18n("Import Presets..."), this); addAction("import_presets", action); connect(action, SIGNAL(triggered()), this, SLOT(slotImportPresets())); action = new KisAction(i18n("Import Workspaces..."), this); addAction("import_workspaces", action); connect(action, SIGNAL(triggered()), this, SLOT(slotImportWorkspaces())); action = new KisAction(i18n("Create Resource Bundle..."), this); addAction("create_bundle", action); connect(action, SIGNAL(triggered()), this, SLOT(slotCreateBundle())); action = new KisAction(i18n("Manage Resources..."), this); addAction("manage_bundles", action); connect(action, SIGNAL(triggered()), this, SLOT(slotManageBundles())); } ResourceManager::~ResourceManager() { } void ResourceManager::slotCreateBundle() { DlgCreateBundle dlgCreateBundle; if (dlgCreateBundle.exec() != QDialog::Accepted) { return; } saveBundle(dlgCreateBundle); } void ResourceManager::saveBundle(const DlgCreateBundle &dlgCreateBundle) { QString bundlePath = dlgCreateBundle.saveLocation() + "/" + dlgCreateBundle.bundleName() + ".bundle"; KisResourceBundle *newBundle = new KisResourceBundle(bundlePath); newBundle->addMeta("name", dlgCreateBundle.bundleName()); newBundle->addMeta("author", dlgCreateBundle.authorName()); newBundle->addMeta("email", dlgCreateBundle.email()); newBundle->addMeta("license", dlgCreateBundle.license()); newBundle->addMeta("website", dlgCreateBundle.website()); newBundle->addMeta("description", dlgCreateBundle.description()); QStringList res = dlgCreateBundle.selectedBrushes(); Q_FOREACH (const QString &r, res) { KoResource *res = d->brushServer->resourceByFilename(r).data(); newBundle->addResource("kis_brushes", res->filename(), d->brushServer->assignedTagsList(res), res->md5()); } res = dlgCreateBundle.selectedGradients(); Q_FOREACH (const QString &r, res) { KoResource *res = d->gradientServer->resourceByFilename(r); newBundle->addResource("ko_gradients", res->filename(), d->gradientServer->assignedTagsList(res), res->md5()); } res = dlgCreateBundle.selectedPalettes(); Q_FOREACH (const QString &r, res) { KoResource *res = d->paletteServer->resourceByFilename(r); newBundle->addResource("ko_palettes", res->filename(), d->paletteServer->assignedTagsList(res), res->md5()); } res = dlgCreateBundle.selectedPatterns(); Q_FOREACH (const QString &r, res) { KoResource *res = d->patternServer->resourceByFilename(r); newBundle->addResource("ko_patterns", res->filename(), d->patternServer->assignedTagsList(res), res->md5()); } res = dlgCreateBundle.selectedPresets(); Q_FOREACH (const QString &r, res) { KisPaintOpPresetSP preset = d->paintopServer->resourceByFilename(r); KoResource *res = preset.data(); newBundle->addResource("kis_paintoppresets", res->filename(), d->paintopServer->assignedTagsList(res), res->md5()); KisPaintOpSettingsSP settings = preset->settings(); if (settings->hasProperty("requiredBrushFile")) { QString brushFile = settings->getString("requiredBrushFile"); KisBrush *brush = d->brushServer->resourceByFilename(brushFile).data(); - newBundle->addResource("kis_brushes", brushFile, d->brushServer->assignedTagsList(brush), brush->md5()); + if (brush) { + newBundle->addResource("kis_brushes", brushFile, d->brushServer->assignedTagsList(brush), brush->md5()); + } + else { + qWarning() << "There is no brush with name" << brushFile; + } } } res = dlgCreateBundle.selectedWorkspaces(); Q_FOREACH (const QString &r, res) { KoResource *res = d->workspaceServer->resourceByFilename(r); newBundle->addResource("kis_workspaces", res->filename(), d->workspaceServer->assignedTagsList(res), res->md5()); } newBundle->addMeta("fileName", bundlePath); newBundle->addMeta("created", QDate::currentDate().toString("dd/MM/yyyy")); newBundle->setThumbnail(dlgCreateBundle.previewImage()); if (!newBundle->save()) { QMessageBox::critical(m_view->mainWindow(), i18nc("@title:window", "Krita"), i18n("Could not create the new bundle.")); } } void ResourceManager::slotManageBundles() { DlgBundleManager* dlg = new DlgBundleManager(this, m_view->actionManager()); if (dlg->exec() != QDialog::Accepted) { return; } } QStringList ResourceManager::importResources(const QString &title, const QStringList &mimes) const { KoFileDialog dialog(m_view->mainWindow(), KoFileDialog::OpenFiles, "krita_resources"); dialog.setCaption(title); dialog.setMimeTypeFilters(mimes); return dialog.filenames(); } void ResourceManager::slotImportBrushes() { QStringList resources = importResources(i18n("Import Brushes"), QStringList() << "image/x-gimp-brush" << "image/x-gimp-x-gimp-brush-animated" << "image/x-adobe-brushlibrary" << "image/png" << "image/svg+xml"); Q_FOREACH (const QString &res, resources) { d->brushServer->importResourceFile(res); } } void ResourceManager::slotImportPresets() { QStringList resources = importResources(i18n("Import Presets"), QStringList() << "application/x-krita-paintoppreset"); Q_FOREACH (const QString &res, resources) { d->paintopServer->importResourceFile(res); } } void ResourceManager::slotImportGradients() { QStringList resources = importResources(i18n("Import Gradients"), QStringList() << "image/svg+xml" << "application/x-gimp-gradient" << "applicaition/x-karbon-gradient"); Q_FOREACH (const QString &res, resources) { d->gradientServer->importResourceFile(res); } } void ResourceManager::slotImportBundles() { QStringList resources = importResources(i18n("Import Bundles"), QStringList() << "application/x-krita-bundle"); Q_FOREACH (const QString &res, resources) { KisResourceBundle *bundle = KisResourceServerProvider::instance()->resourceBundleServer()->createResource(res); bundle->load(); if (bundle->valid()) { if (!bundle->install()) { QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Could not install the resources for bundle %1.").arg(res)); } } else { QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Could not load bundle %1.").arg(res)); } QFileInfo fi(res); QString newFilename = KisResourceServerProvider::instance()->resourceBundleServer()->saveLocation() + fi.baseName() + bundle->defaultFileExtension(); QFileInfo fileInfo(newFilename); int i = 1; while (fileInfo.exists()) { fileInfo.setFile(KisResourceServerProvider::instance()->resourceBundleServer()->saveLocation() + fi.baseName() + QString("%1").arg(i) + bundle->defaultFileExtension()); i++; } bundle->setFilename(fileInfo.filePath()); QFile::copy(res, newFilename); KisResourceServerProvider::instance()->resourceBundleServer()->addResource(bundle, false); } } void ResourceManager::slotImportPatterns() { QStringList resources = importResources(i18n("Import Patterns"), QStringList() << "image/png" << "image/svg+xml" << "application/x-gimp-pattern" << "image/jpeg" << "image/tiff" << "image/bmp" << "image/xpg"); Q_FOREACH (const QString &res, resources) { d->patternServer->importResourceFile(res); } } void ResourceManager::slotImportPalettes() { QStringList resources = importResources(i18n("Import Palettes"), QStringList() << "image/x-gimp-color-palette"); Q_FOREACH (const QString &res, resources) { d->paletteServer->importResourceFile(res); } } void ResourceManager::slotImportWorkspaces() { QStringList resources = importResources(i18n("Import Workspaces"), QStringList() << "application/x-krita-workspace"); Q_FOREACH (const QString &res, resources) { d->workspaceServer->importResourceFile(res); } } #include "resourcemanager.moc" diff --git a/plugins/extensions/separate_channels/kis_channel_separator.cc b/plugins/extensions/separate_channels/kis_channel_separator.cc index de1246b3bd..9ca70aaee7 100644 --- a/plugins/extensions/separate_channels/kis_channel_separator.cc +++ b/plugins/extensions/separate_channels/kis_channel_separator.cc @@ -1,275 +1,275 @@ /* * This file is part of Krita * * Copyright (c) 2005 Michael Thaler * * ported from Gimp, Copyright (C) 1997 Eiichi Takamori * original pixelize.c for GIMP 0.54 by Tracy Scott * * This program is free software; you can redistribute 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_channel_separator.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_iterator_ng.h" #include #include #include #include #include #include KisChannelSeparator::KisChannelSeparator(KisViewManager * view) : m_view(view) { } void KisChannelSeparator::separate(KoUpdater * progressUpdater, enumSepAlphaOptions alphaOps, enumSepSource sourceOps, enumSepOutput outputOps, bool downscale, bool toColor) { KisImageWSP image = m_view->image(); if (!image) return; KisPaintDeviceSP src; // Use the flattened image, if required switch (sourceOps) { case ALL_LAYERS: // the content will be locked later src = image->projection(); break; case CURRENT_LAYER: src = m_view->activeDevice(); break; default: break; } if (!src) return; progressUpdater->setProgress(1); const KoColorSpace * dstCs = 0; quint32 numberOfChannels = src->channelCount(); const KoColorSpace * srcCs = src->colorSpace(); QList channels = srcCs->channels(); vKisPaintDeviceSP layers; QList::const_iterator begin = channels.constBegin(); QList::const_iterator end = channels.constEnd(); QRect rect = src->exactBounds(); m_view->image()->lock(); int i = 0; for (QList::const_iterator it = begin; it != end; ++it) { KoChannelInfo * ch = (*it); if (ch->channelType() == KoChannelInfo::ALPHA && alphaOps != CREATE_ALPHA_SEPARATION) { continue; } qint32 channelSize = ch->size(); qint32 channelPos = ch->pos(); qint32 destSize = 1; KisPaintDeviceSP dev; if (toColor) { // We don't downscale if we separate to color channels dev = new KisPaintDevice(srcCs); } else { if (channelSize == 1 || downscale) { dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), 0)); } else { dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), 0)); destSize = 2; } } dstCs = dev->colorSpace(); layers.push_back(dev); KisHLineConstIteratorSP srcIt = src->createHLineConstIteratorNG(rect.x(), rect.y(), rect.width()); KisHLineIteratorSP dstIt = dev->createHLineIteratorNG(rect.x(), rect.y(), rect.width()); for (qint32 row = 0; row < rect.height(); ++row) { do { if (toColor) { dstCs->singleChannelPixel(dstIt->rawData(), srcIt->oldRawData(), channelPos); if (alphaOps == COPY_ALPHA_TO_SEPARATIONS) { //dstCs->setAlpha(dstIt->rawData(), srcIt->oldRawData()[srcAlphaPos], 1); dstCs->setOpacity(dstIt->rawData(), srcCs->opacityU8(srcIt->oldRawData()), 1); } else { dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1); } } else { // To grayscale // Decide whether we need downscaling if (channelSize == 1 && destSize == 1) { // Both 8-bit channels dstIt->rawData()[0] = srcIt->oldRawData()[channelPos]; if (alphaOps == COPY_ALPHA_TO_SEPARATIONS) { dstCs->setOpacity(dstIt->rawData(), srcCs->opacityU8(srcIt->oldRawData()), 1); } else { dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1); } } else if (channelSize == 2 && destSize == 2) { // Both 16-bit dstIt->rawData()[0] = srcIt->oldRawData()[channelPos]; dstIt->rawData()[1] = srcIt->oldRawData()[channelPos + 1]; if (alphaOps == COPY_ALPHA_TO_SEPARATIONS) { dstCs->setOpacity(dstIt->rawData(), srcCs->opacityU8(srcIt->oldRawData()), 1); } else { dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1); } } else if (channelSize != 1 && destSize == 1) { // Downscale memset(dstIt->rawData(), srcCs->scaleToU8(srcIt->oldRawData(), channelPos), 1); // XXX: Do alpha dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1); } else if (channelSize != 2 && destSize == 2) { // Upscale dstIt->rawData()[0] = srcCs->scaleToU8(srcIt->oldRawData(), channelPos); // XXX: Do alpha dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1); } } } while (dstIt->nextPixel() && srcIt->nextPixel()); dstIt->nextRow(); srcIt->nextRow(); } ++i; progressUpdater->setProgress((i * 100) / numberOfChannels); if (progressUpdater->interrupted()) { break; } } vKisPaintDeviceSP_it deviceIt = layers.begin(); progressUpdater->setProgress(100); if (!progressUpdater->interrupted()) { KisUndoAdapter * undo = image->undoAdapter(); if (outputOps == TO_LAYERS) { undo->beginMacro(kundo2_i18n("Separate Image")); } // Flatten the image if required switch (sourceOps) { case(ALL_LAYERS): image->flatten(); break; default: break; } KisNodeCommandsAdapter adapter(m_view); for (QList::const_iterator it = begin; it != end; ++it) { KoChannelInfo * ch = (*it); if (ch->channelType() == KoChannelInfo::ALPHA && alphaOps != CREATE_ALPHA_SEPARATION) { // Don't make an separate separation of the alpha channel if the user didn't ask for it. continue; } if (outputOps == TO_LAYERS) { KisPaintLayerSP l = KisPaintLayerSP(new KisPaintLayer(image.data(), ch->name(), OPACITY_OPAQUE_U8, *deviceIt)); adapter.addNode(l.data(), image->rootLayer(), 0); } else { KoFileDialog dialog(m_view->mainWindow(), KoFileDialog::SaveFile, "OpenDocument"); dialog.setCaption(i18n("Export Layer") + '(' + ch->name() + ')'); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); - dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Export)); + dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Export)); QUrl url = QUrl::fromUserInput(dialog.filename()); if (url.isEmpty()) return; QString mimefilter = KisMimeDatabase::mimeTypeForFile(url.toLocalFile()); KisPaintLayerSP l = KisPaintLayerSP(new KisPaintLayer(image.data(), ch->name(), OPACITY_OPAQUE_U8, *deviceIt)); QRect r = l->exactBounds(); KisDocument *d = KisPart::instance()->createDocument(); d->prepareForImport(); KisImageWSP dst = KisImageWSP(new KisImage(d->createUndoStore(), r.width(), r.height(), (*deviceIt)->colorSpace(), l->name())); d->setCurrentImage(dst); dst->addNode(l->clone().data(), dst->rootLayer()); d->setOutputMimeType(mimefilter.toLatin1()); d->exportDocument(url); delete d; } ++deviceIt; } if (outputOps == TO_LAYERS) { undo->endMacro(); } m_view->image()->unlock(); image->setModified(); } } diff --git a/plugins/filters/fastcolortransfer/kis_wdg_fastcolortransfer.cpp b/plugins/filters/fastcolortransfer/kis_wdg_fastcolortransfer.cpp index f35ca6de92..2fd275c09e 100644 --- a/plugins/filters/fastcolortransfer/kis_wdg_fastcolortransfer.cpp +++ b/plugins/filters/fastcolortransfer/kis_wdg_fastcolortransfer.cpp @@ -1,147 +1,147 @@ /* * This file is part of Krita * * Copyright (c) 2006 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_wdg_fastcolortransfer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ui_wdgfastcolortransfer.h" KisWdgFastColorTransfer::KisWdgFastColorTransfer(QWidget * parent) : KisConfigWidget(parent) { m_widget = new Ui_WdgFastColorTransfer(); m_widget->setupUi(this); - m_widget->fileNameURLRequester->setMimeTypeFilters(KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Import)); + m_widget->fileNameURLRequester->setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import)); connect(m_widget->fileNameURLRequester, SIGNAL(textChanged(const QString&)), this, SIGNAL(sigConfigurationItemChanged())); } KisWdgFastColorTransfer::~KisWdgFastColorTransfer() { delete m_widget; } void KisWdgFastColorTransfer::setConfiguration(const KisPropertiesConfiguration* config) { QVariant value; if (config->getProperty("filename", value)) { widget()->fileNameURLRequester->setFileName(value.toString()); } } KisPropertiesConfiguration* KisWdgFastColorTransfer::configuration() const { KisFilterConfiguration* config = new KisFilterConfiguration("colortransfer", 1); QString fileName = this->widget()->fileNameURLRequester->fileName(); if (fileName.isEmpty()) return config; KisPaintDeviceSP ref; dbgPlugins << "Use as reference file : " << fileName; KisDocument *d = KisPart::instance()->createDocument(); KisImportExportManager manager(d); KisImportExportFilter::ConversionStatus status; QString s = manager.importDocument(fileName, QString(), status); dbgPlugins << "import returned" << s << "and status" << status; KisImageWSP importedImage = d->image(); if (importedImage) { ref = importedImage->projection(); } if (!ref) { dbgPlugins << "No reference image was specified."; delete d; return config; } // Convert ref to LAB const KoColorSpace* labCS = KoColorSpaceRegistry::instance()->lab16(); if (!labCS) { dbgPlugins << "The LAB colorspace is not available."; delete d; return config; } dbgPlugins << "convert ref to lab"; KUndo2Command* cmd = ref->convertTo(labCS, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); delete cmd; // Compute the means and sigmas of ref double meanL_ref = 0., meanA_ref = 0., meanB_ref = 0.; double sigmaL_ref = 0., sigmaA_ref = 0., sigmaB_ref = 0.; KisSequentialConstIterator refIt(ref, importedImage->bounds()); do { const quint16* data = reinterpret_cast(refIt.oldRawData()); quint32 L = data[0]; quint32 A = data[1]; quint32 B = data[2]; meanL_ref += L; meanA_ref += A; meanB_ref += B; sigmaL_ref += L * L; sigmaA_ref += A * A; sigmaB_ref += B * B; } while (refIt.nextPixel()); double totalSize = 1. / (importedImage->width() * importedImage->height()); meanL_ref *= totalSize; meanA_ref *= totalSize; meanB_ref *= totalSize; sigmaL_ref *= totalSize; sigmaA_ref *= totalSize; sigmaB_ref *= totalSize; dbgPlugins << totalSize << "" << meanL_ref << "" << meanA_ref << "" << meanB_ref << "" << sigmaL_ref << "" << sigmaA_ref << "" << sigmaB_ref; config->setProperty("filename", fileName); config->setProperty("meanL", meanL_ref); config->setProperty("meanA", meanA_ref); config->setProperty("meanB", meanB_ref); config->setProperty("sigmaL", sigmaL_ref); config->setProperty("sigmaA", sigmaA_ref); config->setProperty("sigmaB", sigmaB_ref); delete d; return config; } diff --git a/plugins/flake/artistictextshape/CMakeLists.txt b/plugins/flake/artistictextshape/CMakeLists.txt index 10c2d076eb..f1f2b9b423 100644 --- a/plugins/flake/artistictextshape/CMakeLists.txt +++ b/plugins/flake/artistictextshape/CMakeLists.txt @@ -1,43 +1,42 @@ project( artistictextshape ) include_directories( ${CMAKE_SOURCE_DIR}/libs/widgets - ${CMAKE_SOURCE_DIR}/libs/main # for KoFontComboBox.h ) set ( ArtisticTextShape_SRCS ArtisticTextShapePlugin.cpp ArtisticTextShape.cpp ArtisticTextRange.cpp ArtisticTextShapeFactory.cpp ArtisticTextTool.cpp ArtisticTextToolFactory.cpp ArtisticTextToolSelection.cpp ArtisticTextShapeConfigWidget.cpp ArtisticTextShapeOnPathWidget.cpp ArtisticTextShapeLoadingUpdater.cpp ArtisticTextLoadingContext.cpp AttachTextToPathCommand.cpp DetachTextFromPathCommand.cpp ChangeTextOffsetCommand.cpp ChangeTextFontCommand.cpp ChangeTextAnchorCommand.cpp AddTextRangeCommand.cpp RemoveTextRangeCommand.cpp MoveStartOffsetStrategy.cpp SelectTextStrategy.cpp ReplaceTextRangeCommand.cpp ) ki18n_wrap_ui( ArtisticTextShape_SRCS ArtisticTextShapeConfigWidget.ui ArtisticTextShapeOnPathWidget.ui ) qt5_add_resources(ArtisticTextShape_SRCS artistictextshape.qrc) add_library(krita_shape_artistictext MODULE ${ArtisticTextShape_SRCS} ) target_link_libraries(krita_shape_artistictext kritaflake) install( TARGETS krita_shape_artistictext DESTINATION ${KRITA_PLUGIN_INSTALL_DIR} ) diff --git a/plugins/flake/textshape/kotext/CMakeLists.txt b/plugins/flake/textshape/kotext/CMakeLists.txt index f5395bfcb2..067d54be28 100644 --- a/plugins/flake/textshape/kotext/CMakeLists.txt +++ b/plugins/flake/textshape/kotext/CMakeLists.txt @@ -1,136 +1,136 @@ -include_directories(${FONTCONFIG_INCLUDE_DIR}/fontconfig +include_directories(${FONTCONFIG_INCLUDE_DIR} ${FREETYPE_INCLUDE_DIRS}) set(kritatext_LIB_SRCS KoDocumentRdfBase.cpp KoText.cpp KoTextBlockData.cpp KoTextBlockBorderData.cpp KoTextBlockPaintStrategyBase.cpp KoTextOdfSaveHelper.cpp KoTextPaste.cpp KoTextDocument.cpp KoTextEditor.cpp KoTextEditor_undo.cpp KoTextEditor_format.cpp KoList.cpp KoTextEditingRegistry.cpp KoTextEditingFactory.cpp KoTextEditingPlugin.cpp KoTextRangeManager.cpp KoInlineTextObjectManager.cpp KoInlineObjectFactoryBase.cpp KoInlineObjectRegistry.cpp InsertInlineObjectActionBase_p.cpp InsertVariableAction.cpp InsertNamedVariableAction.cpp InsertTextReferenceAction.cpp InsertTextLocator.cpp KoInlineObject.cpp KoTextRange.cpp KoVariable.cpp KoVariableManager.cpp KoNamedVariable.cpp KoSection.cpp KoSectionEnd.cpp KoSectionUtils.cpp KoSectionModel.cpp KoTextLocator.cpp KoTextReference.cpp KoAnchorInlineObject.cpp KoAnchorTextRange.cpp KoTextShapeSavingContext.cpp KoAnnotation.cpp KoAnnotationManager.cpp KoBookmark.cpp KoBookmarkManager.cpp KoInlineNote.cpp KoInlineCite.cpp KoTextSoftPageBreak.cpp KoTextDebug.cpp KoTextPage.cpp KoPageProvider.cpp KoTableColumnAndRowStyleManager.cpp KoTextInlineRdf.cpp KoTextMeta.cpp KoTextTableTemplate.cpp OdfTextTrackStyles.cpp ToCBibGeneratorInfo.cpp KoTableOfContentsGeneratorInfo.cpp KoBibliographyInfo.cpp BibliographyGenerator.cpp styles/Styles_p.cpp styles/KoCharacterStyle.cpp styles/KoParagraphStyle.cpp styles/KoStyleManager.cpp styles/KoListStyle.cpp styles/KoListLevelProperties.cpp styles/KoTableStyle.cpp styles/KoTableColumnStyle.cpp styles/KoTableRowStyle.cpp styles/KoTableCellStyle.cpp styles/KoSectionStyle.cpp opendocument/KoTextSharedLoadingData.cpp opendocument/KoTextSharedSavingData.cpp opendocument/KoTextLoader.cpp opendocument/KoTextWriter_p.cpp opendocument/KoTextWriter.cpp changetracker/KoChangeTracker.cpp changetracker/KoChangeTrackerElement.cpp changetracker/KoFormatChangeInformation.cpp changetracker/KoDeletedRowColumnDataStore.cpp changetracker/KoDeletedRowData.cpp changetracker/KoDeletedColumnData.cpp changetracker/KoDeletedCellData.cpp commands/ChangeAnchorPropertiesCommand.cpp commands/ChangeListCommand.cpp commands/ChangeStylesCommand.cpp commands/ChangeStylesMacroCommand.cpp commands/DeleteAnchorsCommand.cpp commands/DeleteAnnotationsCommand.cpp commands/DeleteCommand.cpp commands/DeleteTableColumnCommand.cpp commands/DeleteTableRowCommand.cpp commands/InsertNoteCommand.cpp commands/InsertTableColumnCommand.cpp commands/InsertTableRowCommand.cpp commands/ResizeTableCommand.cpp commands/InsertInlineObjectCommand.cpp commands/ListItemNumberingCommand.cpp commands/TextPasteCommand.cpp commands/AddTextRangeCommand.cpp commands/AddAnnotationCommand.cpp commands/ParagraphFormattingCommand.cpp commands/RenameSectionCommand.cpp commands/NewSectionCommand.cpp commands/SplitSectionsCommand.cpp KoTextDrag.cpp KoTextCommandBase.cpp TextDebug.cpp ) add_library(kritatext SHARED ${kritatext_LIB_SRCS}) generate_export_header(kritatext BASE_NAME kritatext) target_link_libraries(kritatext kritaflake Qt5::Gui kritawidgetutils ) target_link_libraries(kritatext LINK_INTERFACE_LIBRARIES kritaflake Qt5::Gui ) target_include_directories(kritatext PUBLIC $ $ $ ) set_target_properties(kritatext PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritatext ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/plugins/flake/textshape/kotext/styles/KoCharacterStyle.cpp b/plugins/flake/textshape/kotext/styles/KoCharacterStyle.cpp index 0de7fa582a..b74bc5fcb3 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 #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 forground 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 forground 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 // 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/impex/csv/CMakeLists.txt b/plugins/impex/csv/CMakeLists.txt index 01d3bd8345..da860efbe1 100644 --- a/plugins/impex/csv/CMakeLists.txt +++ b/plugins/impex/csv/CMakeLists.txt @@ -1,39 +1,36 @@ add_subdirectory(tests) -include_directories( - ${CMAKE_SOURCE_DIR}/krita/image/metadata -) include_directories(SYSTEM ${Boost_INCLUDE_DIRS} ) # import set(kritacsvimport_SOURCES kis_csv_import.cpp csv_loader.cpp csv_read_line.cpp csv_layer_record.cpp ) add_library(kritacsvimport MODULE ${kritacsvimport_SOURCES}) target_link_libraries(kritacsvimport kritaui ) install(TARGETS kritacsvimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) # export set(kritacsvexport_SOURCES kis_csv_export.cpp csv_saver.cpp csv_layer_record.cpp ) add_library(kritacsvexport MODULE ${kritacsvexport_SOURCES}) target_link_libraries(kritacsvexport kritaui ) install(TARGETS kritacsvexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) install( PROGRAMS krita_csv.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) diff --git a/plugins/impex/exr/exr_converter.cc b/plugins/impex/exr/exr_converter.cc index 732f552185..c65baec6d3 100644 --- a/plugins/impex/exr/exr_converter.cc +++ b/plugins/impex/exr/exr_converter.cc @@ -1,1304 +1,1304 @@ /* * Copyright (c) 2005 Adrian Page * Copyright (c) 2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "exr_converter.h" #include #include #include #include #include #include #include "exr_extra_tags.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_iterator_ng.h" #include "kra/kis_kra_savexml_visitor.h" #include #include #include #include #include #include // Do not translate! #define HDR_LAYER "HDR Layer" template struct Rgba { _T_ r; _T_ g; _T_ b; _T_ a; }; struct ExrGroupLayerInfo; struct ExrLayerInfoBase { ExrLayerInfoBase() : colorSpace(0), parent(0) { } const KoColorSpace* colorSpace; QString name; const ExrGroupLayerInfo* parent; }; struct ExrGroupLayerInfo : public ExrLayerInfoBase { ExrGroupLayerInfo() : groupLayer(0) {} KisGroupLayerSP groupLayer; }; enum ImageType { IT_UNKNOWN, IT_FLOAT16, IT_FLOAT32, IT_UNSUPPORTED }; struct ExrPaintLayerInfo : public ExrLayerInfoBase { ExrPaintLayerInfo() : imageType(IT_UNKNOWN) { } ImageType imageType; QMap< QString, QString> channelMap; ///< first is either R, G, B or A second is the EXR channel name struct Remap { Remap(const QString& _original, const QString& _current) : original(_original), current(_current) { } QString original; QString current; }; QList< Remap > remappedChannels; ///< this is used to store in the metadata the mapping between exr channel name, and channels used in Krita void updateImageType(ImageType channelType); }; void ExrPaintLayerInfo::updateImageType(ImageType channelType) { if (imageType == IT_UNKNOWN) { imageType = channelType; } else if (imageType != channelType) { imageType = IT_UNSUPPORTED; } } struct ExrPaintLayerSaveInfo { QString name; ///< name of the layer with a "." at the end (ie "group1.group2.layer1.") KisPaintLayerSP layer; QList channels; Imf::PixelType pixelType; }; struct exrConverter::Private { Private() : doc(0), warnedAboutChangedAlpha(false), showNotifications(false) {} KisImageSP image; KisDocument *doc; bool warnedAboutChangedAlpha; bool showNotifications; template void unmultiplyAlpha(typename WrapperType::pixel_type *pixel); template void decodeData4(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype); template void decodeData1(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype); QDomDocument loadExtraLayersInfo(const Imf::Header &header); bool checkExtraLayersInfoConsistent(const QDomDocument &doc, std::set exrLayerNames); void makeLayerNamesUnique(QList& informationObjects); void recBuildPaintLayerSaveInfo(QList& informationObjects, const QString& name, KisGroupLayerSP parent); void reportLayersNotSaved(const QSet &layersNotSaved); QString fetchExtraLayersInfo(QList& informationObjects); }; exrConverter::exrConverter(KisDocument *doc, bool showNotifications) : m_d(new Private) { m_d->doc = doc; m_d->showNotifications = showNotifications; } exrConverter::~exrConverter() { } ImageType imfTypeToKisType(Imf::PixelType type) { switch (type) { case Imf::UINT: case Imf::NUM_PIXELTYPES: return IT_UNSUPPORTED; case Imf::HALF: return IT_FLOAT16; case Imf::FLOAT: return IT_FLOAT32; default: qFatal("Out of bound enum"); return IT_UNKNOWN; } } const KoColorSpace* kisTypeToColorSpace(QString model, ImageType imageType) { switch (imageType) { case IT_FLOAT16: return KoColorSpaceRegistry::instance()->colorSpace(model, Float16BitsColorDepthID.id(), ""); case IT_FLOAT32: return KoColorSpaceRegistry::instance()->colorSpace(model, Float32BitsColorDepthID.id(), ""); case IT_UNKNOWN: case IT_UNSUPPORTED: return 0; default: qFatal("Out of bound enum"); return 0; } } template static inline T alphaEpsilon() { return static_cast(HALF_EPSILON); } template static inline T alphaNoiseThreshold() { return static_cast(0.01); // 1% } template struct RgbPixelWrapper { typedef T channel_type; typedef Rgba pixel_type; RgbPixelWrapper(Rgba &_pixel) : pixel(_pixel) {} inline T alpha() const { return pixel.a; } inline bool checkMultipliedColorsConsistent() const { return !(pixel.a < alphaEpsilon() && (pixel.r > 0.0 || pixel.g > 0.0 || pixel.b > 0.0)); } inline bool checkUnmultipliedColorsConsistent(const Rgba &mult) const { const T alpha = pixel.a; - return alpha >= alphaNoiseThreshold() || + return abs(alpha) >= alphaNoiseThreshold() || (pixel.r * alpha == mult.r && pixel.g * alpha == mult.g && pixel.b * alpha == mult.b); } inline void setUnmultiplied(const Rgba &mult, qreal newAlpha) { pixel.r = mult.r / newAlpha; pixel.g = mult.g / newAlpha; pixel.b = mult.b / newAlpha; pixel.a = newAlpha; } Rgba &pixel; }; template struct GrayPixelWrapper { typedef T channel_type; typedef typename KoGrayTraits::Pixel pixel_type; GrayPixelWrapper(pixel_type &_pixel) : pixel(_pixel) {} inline T alpha() const { return pixel.alpha; } inline bool checkMultipliedColorsConsistent() const { return !(pixel.alpha < alphaEpsilon() && pixel.gray > 0.0); } inline bool checkUnmultipliedColorsConsistent(const pixel_type &mult) const { const T alpha = pixel.alpha; return alpha >= alphaNoiseThreshold() || pixel.gray * alpha == mult.gray; } inline void setUnmultiplied(const pixel_type &mult, qreal newAlpha) { pixel.gray = mult.gray / newAlpha; pixel.alpha = newAlpha; } pixel_type &pixel; }; template void exrConverter::Private::unmultiplyAlpha(typename WrapperType::pixel_type *pixel) { typedef typename WrapperType::pixel_type pixel_type; typedef typename WrapperType::channel_type channel_type; WrapperType srcPixel(*pixel); if (!srcPixel.checkMultipliedColorsConsistent()) { bool alphaWasModified = false; channel_type newAlpha = srcPixel.alpha(); pixel_type __dstPixelData; WrapperType dstPixel(__dstPixelData); /** * Division by a tiny alpha may result in an overflow of half * value. That is why we use safe iterational approach. */ while (1) { dstPixel.setUnmultiplied(srcPixel.pixel, newAlpha); if (dstPixel.checkUnmultipliedColorsConsistent(srcPixel.pixel)) { break; } newAlpha += alphaEpsilon(); alphaWasModified = true; } *pixel = dstPixel.pixel; if (alphaWasModified && !this->warnedAboutChangedAlpha) { QString msg = i18nc("@info", "The image contains pixels with zero alpha channel and non-zero " "color channels. Krita will have to modify those pixels to have " - "at least some alpha. The initial values will not " + "at least some alpha. The initial values will not " "be reverted on saving the image back." - "" + "

" "This will hardly make any visual difference just keep it in mind." - "" - "Modified alpha will have a range from %1 to %2", + "

" + "Modified alpha will have a range from %1 to %2", alphaEpsilon(), alphaNoiseThreshold()); if (this->showNotifications) { QMessageBox::information(0, i18nc("@title:window", "EXR image will be modified"), msg); } else { warnKrita << "WARNING:" << msg; } this->warnedAboutChangedAlpha = true; } } else if (srcPixel.alpha() > 0.0) { srcPixel.setUnmultiplied(srcPixel.pixel, srcPixel.alpha()); } } template void multiplyAlpha(Pixel *pixel) { T alpha = pixel->data[alphaPos]; if (alpha > 0.0) { for (int i = 0; i < size; ++i) { if (i != alphaPos) { pixel->data[i] *= alpha; } } pixel->data[alphaPos] = alpha; } } template void exrConverter::Private::decodeData4(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype) { typedef Rgba<_T_> Rgba; QVector pixels(width); bool hasAlpha = info.channelMap.contains("A"); for (int y = 0; y < height; ++y) { Imf::FrameBuffer frameBuffer; Rgba* frameBufferData = (pixels.data()) - xstart - (ystart + y) * width; frameBuffer.insert(info.channelMap["R"].toLatin1().constData(), Imf::Slice(ptype, (char *) &frameBufferData->r, sizeof(Rgba) * 1, sizeof(Rgba) * width)); frameBuffer.insert(info.channelMap["G"].toLatin1().constData(), Imf::Slice(ptype, (char *) &frameBufferData->g, sizeof(Rgba) * 1, sizeof(Rgba) * width)); frameBuffer.insert(info.channelMap["B"].toLatin1().constData(), Imf::Slice(ptype, (char *) &frameBufferData->b, sizeof(Rgba) * 1, sizeof(Rgba) * width)); if (hasAlpha) { frameBuffer.insert(info.channelMap["A"].toLatin1().constData(), Imf::Slice(ptype, (char *) &frameBufferData->a, sizeof(Rgba) * 1, sizeof(Rgba) * width)); } file.setFrameBuffer(frameBuffer); file.readPixels(ystart + y); Rgba *rgba = pixels.data(); KisHLineIteratorSP it = layer->paintDevice()->createHLineIteratorNG(0, y, width); do { if (hasAlpha) { unmultiplyAlpha >(rgba); } typename KoRgbTraits<_T_>::Pixel* dst = reinterpret_cast::Pixel*>(it->rawData()); dst->red = rgba->r; dst->green = rgba->g; dst->blue = rgba->b; if (hasAlpha) { dst->alpha = rgba->a; } else { dst->alpha = 1.0; } ++rgba; } while (it->nextPixel()); } } template void exrConverter::Private::decodeData1(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype) { typedef typename GrayPixelWrapper<_T_>::channel_type channel_type; typedef typename GrayPixelWrapper<_T_>::pixel_type pixel_type; KIS_ASSERT_RECOVER_RETURN( layer->paintDevice()->colorSpace()->colorModelId() == GrayAColorModelID); QVector pixels(width); Q_ASSERT(info.channelMap.contains("G")); dbgFile << "G -> " << info.channelMap["G"]; bool hasAlpha = info.channelMap.contains("A"); dbgFile << "Has Alpha:" << hasAlpha; for (int y = 0; y < height; ++y) { Imf::FrameBuffer frameBuffer; pixel_type* frameBufferData = (pixels.data()) - xstart - (ystart + y) * width; frameBuffer.insert(info.channelMap["G"].toLatin1().constData(), Imf::Slice(ptype, (char *) &frameBufferData->gray, sizeof(pixel_type) * 1, sizeof(pixel_type) * width)); if (hasAlpha) { frameBuffer.insert(info.channelMap["A"].toLatin1().constData(), Imf::Slice(ptype, (char *) &frameBufferData->alpha, sizeof(pixel_type) * 1, sizeof(pixel_type) * width)); } file.setFrameBuffer(frameBuffer); file.readPixels(ystart + y); pixel_type *srcPtr = pixels.data(); KisHLineIteratorSP it = layer->paintDevice()->createHLineIteratorNG(0, y, width); do { if (hasAlpha) { unmultiplyAlpha >(srcPtr); } pixel_type* dstPtr = reinterpret_cast(it->rawData()); dstPtr->gray = srcPtr->gray; dstPtr->alpha = hasAlpha ? srcPtr->alpha : channel_type(1.0); ++srcPtr; } while (it->nextPixel()); } } bool recCheckGroup(const ExrGroupLayerInfo& group, QStringList list, int idx1, int idx2) { if (idx1 > idx2) return true; if (group.name == list[idx2]) { return recCheckGroup(*group.parent, list, idx1, idx2 - 1); } return false; } ExrGroupLayerInfo* searchGroup(QList* groups, QStringList list, int idx1, int idx2) { if (idx1 > idx2) { return 0; } // Look for the group for (int i = 0; i < groups->size(); ++i) { if (recCheckGroup(groups->at(i), list, idx1, idx2)) { return &(*groups)[i]; } } // Create the group ExrGroupLayerInfo info; info.name = list.at(idx2); info.parent = searchGroup(groups, list, idx1, idx2 - 1); groups->append(info); return &groups->last(); } QDomDocument exrConverter::Private::loadExtraLayersInfo(const Imf::Header &header) { const Imf::StringAttribute *layersInfoAttribute = header.findTypedAttribute(EXR_KRITA_LAYERS); if (!layersInfoAttribute) return QDomDocument(); QString layersInfoString = QString::fromUtf8(layersInfoAttribute->value().c_str()); QDomDocument doc; doc.setContent(layersInfoString); return doc; } bool exrConverter::Private::checkExtraLayersInfoConsistent(const QDomDocument &doc, std::set exrLayerNames) { std::set extraInfoLayers; QDomElement root = doc.documentElement(); KIS_ASSERT_RECOVER(!root.isNull() && root.hasChildNodes()) { return false; }; QDomElement el = root.firstChildElement(); while(!el.isNull()) { KIS_ASSERT_RECOVER(el.hasAttribute(EXR_NAME)) { return false; }; QString layerName = el.attribute(EXR_NAME).toUtf8(); if (layerName != QString(HDR_LAYER)) { extraInfoLayers.insert(el.attribute(EXR_NAME).toUtf8().constData()); } el = el.nextSiblingElement(); } bool result = (extraInfoLayers == exrLayerNames); if (!result) { dbgKrita << "WARINING: Krita EXR extra layers info is inconsistent!"; dbgKrita << ppVar(extraInfoLayers.size()) << ppVar(exrLayerNames.size()); std::set::const_iterator it1 = extraInfoLayers.begin(); std::set::const_iterator it2 = exrLayerNames.begin(); std::set::const_iterator end1 = extraInfoLayers.end(); std::set::const_iterator end2 = exrLayerNames.end(); for (; it1 != end1; ++it1, ++it2) { dbgKrita << it1->c_str() << it2->c_str(); } } return result; } KisImageBuilder_Result exrConverter::decode(const QString &filename) { Imf::InputFile file(QFile::encodeName(filename)); Imath::Box2i dw = file.header().dataWindow(); int width = dw.max.x - dw.min.x + 1; int height = dw.max.y - dw.min.y + 1; int dx = dw.min.x; int dy = dw.min.y; // Display the attributes of a file for (Imf::Header::ConstIterator it = file.header().begin(); it != file.header().end(); ++it) { dbgFile << "Attribute: " << it.name() << " type: " << it.attribute().typeName(); } // fetch Krita's extra layer info, which might have been stored previously QDomDocument extraLayersInfo = m_d->loadExtraLayersInfo(file.header()); // Construct the list of LayerInfo QList informationObjects; QList groups; ImageType imageType = IT_UNKNOWN; const Imf::ChannelList &channels = file.header().channels(); std::set layerNames; channels.layers(layerNames); if (!extraLayersInfo.isNull() && !m_d->checkExtraLayersInfoConsistent(extraLayersInfo, layerNames)) { // it is inconsistent anyway extraLayersInfo = QDomDocument(); } // Check if there are A, R, G, B channels dbgFile << "Checking for ARGB channels, they can occur in single-layer _or_ multi-layer images:"; ExrPaintLayerInfo info; bool topLevelRGBFound = false; info.name = HDR_LAYER; for (Imf::ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { const Imf::Channel &channel = i.channel(); dbgFile << "Channel name = " << i.name() << " type = " << channel.type; QString qname = i.name(); QStringList topLevelChannelNames = QStringList() << "A" << "R" << "G" << "B" << ".A" << ".R" << ".G" << ".B" << "A." << "R." << "G." << "B.";; if (topLevelChannelNames.contains(qname)) { topLevelRGBFound = true; dbgFile << "Found top-level channel" << qname; info.channelMap[qname] = qname; info.updateImageType(imfTypeToKisType(channel.type)); } // Channel names that don't contain a "." or that contain a // "." only at the beginning or at the end are not considered // to be part of any layer. else if (!qname.contains('.') || !qname.mid(1).contains('.') || !qname.left(qname.size() - 1).contains('.')) { warnFile << "Found a top-level channel that is not part of the rendered image" << qname << ". Krita will not load this channel."; } } if (topLevelRGBFound) { dbgFile << "Toplevel layer" << info.name << ":Image type:" << imageType << "Layer type" << info.imageType; informationObjects.push_back(info); imageType = info.imageType; } dbgFile << "Extra layers:" << layerNames.size(); for (std::set::const_iterator i = layerNames.begin();i != layerNames.end(); ++i) { info = ExrPaintLayerInfo(); dbgFile << "layer name = " << i->c_str(); info.name = i->c_str(); Imf::ChannelList::ConstIterator layerBegin, layerEnd; channels.channelsInLayer(*i, layerBegin, layerEnd); for (Imf::ChannelList::ConstIterator j = layerBegin; j != layerEnd; ++j) { const Imf::Channel &channel = j.channel(); dbgFile << "\tchannel " << j.name() << " type = " << channel.type; info.updateImageType(imfTypeToKisType(channel.type)); QString qname = j.name(); QStringList list = qname.split('.'); QString layersuffix = list.last(); if (list.size() > 1) { info.name = list[list.size()-2]; info.parent = searchGroup(&groups, list, 0, list.size() - 3); } info.channelMap[layersuffix] = qname; } if (info.imageType != IT_UNKNOWN && info.imageType != IT_UNSUPPORTED) { informationObjects.push_back(info); if (imageType < info.imageType) { imageType = info.imageType; } } } dbgFile << "File has" << informationObjects.size() << "layer(s)"; // Set the colorspaces for (int i = 0; i < informationObjects.size(); ++i) { ExrPaintLayerInfo& info = informationObjects[i]; QString modelId; if (info.channelMap.size() == 1) { modelId = GrayAColorModelID.id(); QString key = info.channelMap.begin().key(); if (key != "G") { info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(key, "G")); QString channel = info.channelMap.begin().value(); info.channelMap.clear(); info.channelMap["G"] = channel; } } else if (info.channelMap.size() == 2) { modelId = GrayAColorModelID.id(); QMap::const_iterator it = info.channelMap.constBegin(); QMap::const_iterator end = info.channelMap.constEnd(); QString failingChannelKey; for (; it != end; ++it) { if (it.key() != "G" && it.key() != "A") { failingChannelKey = it.key(); break; } } info.remappedChannels.push_back( ExrPaintLayerInfo::Remap(failingChannelKey, "G")); QString failingChannelValue = info.channelMap[failingChannelKey]; info.channelMap.remove(failingChannelKey); info.channelMap["G"] = failingChannelValue; } else if (info.channelMap.size() == 3 || info.channelMap.size() == 4) { if (info.channelMap.contains("R") && info.channelMap.contains("G") && info.channelMap.contains("B")) { modelId = RGBAColorModelID.id(); } else if (info.channelMap.contains("X") && info.channelMap.contains("Y") && info.channelMap.contains("Z")) { modelId = XYZAColorModelID.id(); QMap newChannelMap; if (info.channelMap.contains("W")) { newChannelMap["A"] = info.channelMap["W"]; info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("W", "A")); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("X", "X")); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("Y", "Y")); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("Z", "Z")); } else if (info.channelMap.contains("A")) { newChannelMap["A"] = info.channelMap["A"]; } // The decode function expect R, G, B in the channel map newChannelMap["B"] = info.channelMap["X"]; newChannelMap["G"] = info.channelMap["Y"]; newChannelMap["R"] = info.channelMap["Z"]; info.channelMap = newChannelMap; } else { modelId = RGBAColorModelID.id(); QMap newChannelMap; QMap::iterator it = info.channelMap.begin(); newChannelMap["R"] = it.value(); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "R")); ++it; newChannelMap["G"] = it.value(); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "G")); ++it; newChannelMap["B"] = it.value(); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "B")); if (info.channelMap.size() == 4) { ++it; newChannelMap["A"] = it.value(); info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "A")); } info.channelMap = newChannelMap; } } else { dbgFile << info.name << "has" << info.channelMap.size() << "channels, and we don't know what to do."; } if (!modelId.isEmpty()) { info.colorSpace = kisTypeToColorSpace(modelId, info.imageType); } } // Get colorspace dbgFile << "Image type = " << imageType; const KoColorSpace* colorSpace = kisTypeToColorSpace(RGBAColorModelID.id(), imageType); if (!colorSpace) return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; dbgFile << "Colorspace: " << colorSpace->name(); // Set the colorspace on all groups for (int i = 0; i < groups.size(); ++i) { ExrGroupLayerInfo& info = groups[i]; info.colorSpace = colorSpace; } // Create the image m_d->image = new KisImage(m_d->doc->createUndoStore(), width, height, colorSpace, ""); if (!m_d->image) { return KisImageBuilder_RESULT_FAILURE; } /** * EXR semi-transparent images are expected to be rendered on * black to ensure correctness of the light model */ m_d->image->setDefaultProjectionColor(KoColor(Qt::black, colorSpace)); // Create group layers for (int i = 0; i < groups.size(); ++i) { ExrGroupLayerInfo& info = groups[i]; Q_ASSERT(info.parent == 0 || info.parent->groupLayer); KisGroupLayerSP groupLayerParent = (info.parent) ? info.parent->groupLayer : m_d->image->rootLayer(); info.groupLayer = new KisGroupLayer(m_d->image, info.name, OPACITY_OPAQUE_U8); m_d->image->addNode(info.groupLayer, groupLayerParent); } // Load the layers for (int i = informationObjects.size() - 1; i >= 0; --i) { ExrPaintLayerInfo& info = informationObjects[i]; if (info.colorSpace) { dbgFile << "Decoding " << info.name << " with " << info.channelMap.size() << " channels, and color space " << info.colorSpace->id(); KisPaintLayerSP layer = new KisPaintLayer(m_d->image, info.name, OPACITY_OPAQUE_U8, info.colorSpace); layer->setCompositeOpId(COMPOSITE_OVER); if (!layer) { return KisImageBuilder_RESULT_FAILURE; } switch (info.channelMap.size()) { case 1: case 2: // Decode the data - switch (imageType) { + switch (info.imageType) { case IT_FLOAT16: m_d->decodeData1(file, info, layer, width, dx, dy, height, Imf::HALF); break; case IT_FLOAT32: m_d->decodeData1(file, info, layer, width, dx, dy, height, Imf::FLOAT); break; case IT_UNKNOWN: case IT_UNSUPPORTED: qFatal("Impossible error"); } break; case 3: case 4: // Decode the data - switch (imageType) { + switch (info.imageType) { case IT_FLOAT16: m_d->decodeData4(file, info, layer, width, dx, dy, height, Imf::HALF); break; case IT_FLOAT32: m_d->decodeData4(file, info, layer, width, dx, dy, height, Imf::FLOAT); break; case IT_UNKNOWN: case IT_UNSUPPORTED: qFatal("Impossible error"); } break; default: qFatal("Invalid number of channels: %i", info.channelMap.size()); } // Check if should set the channels if (!info.remappedChannels.isEmpty()) { QList values; Q_FOREACH (const ExrPaintLayerInfo::Remap& remap, info.remappedChannels) { QMap map; map["original"] = KisMetaData::Value(remap.original); map["current"] = KisMetaData::Value(remap.current); values.append(map); } layer->metaData()->addEntry(KisMetaData::Entry(KisMetaData::SchemaRegistry::instance()->create("http://krita.org/exrchannels/1.0/" , "exrchannels"), "channelsmap", values)); } // Add the layer KisGroupLayerSP groupLayerParent = (info.parent) ? info.parent->groupLayer : m_d->image->rootLayer(); m_d->image->addNode(layer, groupLayerParent); } else { dbgFile << "No decoding " << info.name << " with " << info.channelMap.size() << " channels, and lack of a color space"; } } if (!extraLayersInfo.isNull()) { KisExrLayersSorter sorter(extraLayersInfo, m_d->image); } return KisImageBuilder_RESULT_OK; } KisImageBuilder_Result exrConverter::buildImage(const QString &filename) { return decode(filename); } KisImageWSP exrConverter::image() { return m_d->image; } template struct ExrPixel_ { _T_ data[size]; }; class Encoder { public: virtual ~Encoder() {} virtual void prepareFrameBuffer(Imf::FrameBuffer*, int line) = 0; virtual void encodeData(int line) = 0; }; template class EncoderImpl : public Encoder { public: EncoderImpl(Imf::OutputFile* _file, const ExrPaintLayerSaveInfo* _info, int width) : file(_file), info(_info), pixels(width), m_width(width) {} virtual ~EncoderImpl() {} virtual void prepareFrameBuffer(Imf::FrameBuffer*, int line); virtual void encodeData(int line); private: typedef ExrPixel_<_T_, size> ExrPixel; Imf::OutputFile* file; const ExrPaintLayerSaveInfo* info; QVector pixels; int m_width; }; template void EncoderImpl<_T_, size, alphaPos>::prepareFrameBuffer(Imf::FrameBuffer* frameBuffer, int line) { int xstart = 0; int ystart = 0; ExrPixel* frameBufferData = (pixels.data()) - xstart - (ystart + line) * m_width; for (int k = 0; k < size; ++k) { frameBuffer->insert(info->channels[k].toUtf8(), Imf::Slice(info->pixelType, (char *) &frameBufferData->data[k], sizeof(ExrPixel) * 1, sizeof(ExrPixel) * m_width)); } } template void EncoderImpl<_T_, size, alphaPos>::encodeData(int line) { ExrPixel *rgba = pixels.data(); KisHLineIteratorSP it = info->layer->paintDevice()->createHLineIteratorNG(0, line, m_width); do { const _T_* dst = reinterpret_cast < const _T_* >(it->oldRawData()); for (int i = 0; i < size; ++i) { rgba->data[i] = dst[i]; } if (alphaPos != -1) { multiplyAlpha<_T_, ExrPixel, size, alphaPos>(rgba); } ++rgba; } while (it->nextPixel()); } Encoder* encoder(Imf::OutputFile& file, const ExrPaintLayerSaveInfo& info, int width) { dbgFile << "Create encoder for" << info.layer->name() << info.channels << info.layer->colorSpace()->channelCount(); switch (info.layer->colorSpace()->channelCount()) { case 1: { if (info.layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) { Q_ASSERT(info.pixelType == Imf::HALF); return new EncoderImpl < half, 1, -1 > (&file, &info, width); } else if (info.layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) { Q_ASSERT(info.pixelType == Imf::FLOAT); return new EncoderImpl < float, 1, -1 > (&file, &info, width); } break; } case 2: { if (info.layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) { Q_ASSERT(info.pixelType == Imf::HALF); return new EncoderImpl(&file, &info, width); } else if (info.layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) { Q_ASSERT(info.pixelType == Imf::FLOAT); return new EncoderImpl(&file, &info, width); } break; } case 4: { if (info.layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) { Q_ASSERT(info.pixelType == Imf::HALF); return new EncoderImpl(&file, &info, width); } else if (info.layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) { Q_ASSERT(info.pixelType == Imf::FLOAT); return new EncoderImpl(&file, &info, width); } break; } default: qFatal("Impossible error"); } return 0; } void encodeData(Imf::OutputFile& file, const QList& informationObjects, int width, int height) { QList encoders; Q_FOREACH (const ExrPaintLayerSaveInfo& info, informationObjects) { encoders.push_back(encoder(file, info, width)); } for (int y = 0; y < height; ++y) { Imf::FrameBuffer frameBuffer; Q_FOREACH (Encoder* encoder, encoders) { encoder->prepareFrameBuffer(&frameBuffer, y); } file.setFrameBuffer(frameBuffer); Q_FOREACH (Encoder* encoder, encoders) { encoder->encodeData(y); } file.writePixels(1); } qDeleteAll(encoders); } KisImageBuilder_Result exrConverter::buildFile(const QString &filename, KisPaintLayerSP layer) { if (!layer) return KisImageBuilder_RESULT_INVALID_ARG; KisImageWSP image = layer->image(); if (!image) return KisImageBuilder_RESULT_EMPTY; // Make the header qint32 height = image->height(); qint32 width = image->width(); Imf::Header header(width, height); Imf::PixelType pixelType = Imf::NUM_PIXELTYPES; if(layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) { pixelType = Imf::HALF; } else if(layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) { pixelType = Imf::FLOAT; } if(pixelType >= Imf::NUM_PIXELTYPES) { return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; } header.channels().insert("R", Imf::Channel(pixelType)); header.channels().insert("G", Imf::Channel(pixelType)); header.channels().insert("B", Imf::Channel(pixelType)); header.channels().insert("A", Imf::Channel(pixelType)); ExrPaintLayerSaveInfo info; info.layer = layer; info.channels.push_back("R"); info.channels.push_back("G"); info.channels.push_back("B"); info.channels.push_back("A"); info.pixelType = pixelType; // Open file for writing Imf::OutputFile file(QFile::encodeName(filename), header); QList informationObjects; informationObjects.push_back(info); encodeData(file, informationObjects, width, height); return KisImageBuilder_RESULT_OK; } QString remap(const QMap& current2original, const QString& current) { if (current2original.contains(current)) { return current2original[current]; } return current; } void exrConverter::Private::makeLayerNamesUnique(QList& informationObjects) { typedef QMultiMap::iterator> NamesMap; NamesMap namesMap; { QList::iterator it = informationObjects.begin(); QList::iterator end = informationObjects.end(); for (; it != end; ++it) { namesMap.insert(it->name, it); } } Q_FOREACH (const QString &key, namesMap.keys()) { if (namesMap.count(key) > 1) { KIS_ASSERT_RECOVER(key.endsWith(".")) { continue; } QString strippedName = key.left(key.size() - 1); // trim the ending dot int nameCounter = 0; NamesMap::iterator it = namesMap.find(key); NamesMap::iterator end = namesMap.end(); for (; it != end; ++it) { QString newName = QString("%1_%2.") .arg(strippedName) .arg(nameCounter++); it.value()->name = newName; QList::iterator channelsIt = it.value()->channels.begin(); QList::iterator channelsEnd = it.value()->channels.end(); for (; channelsIt != channelsEnd; ++channelsIt) { channelsIt->replace(key, newName); } } } } } void exrConverter::Private::recBuildPaintLayerSaveInfo(QList& informationObjects, const QString& name, KisGroupLayerSP parent) { QSet layersNotSaved; for (uint i = 0; i < parent->childCount(); ++i) { KisNodeSP node = parent->at(i); if (KisPaintLayerSP paintLayer = dynamic_cast(node.data())) { QMap current2original; if (paintLayer->metaData()->containsEntry(KisMetaData::SchemaRegistry::instance()->create("http://krita.org/exrchannels/1.0/" , "exrchannels"), "channelsmap")) { const KisMetaData::Entry& entry = paintLayer->metaData()->getEntry(KisMetaData::SchemaRegistry::instance()->create("http://krita.org/exrchannels/1.0/" , "exrchannels"), "channelsmap"); QList< KisMetaData::Value> values = entry.value().asArray(); Q_FOREACH (const KisMetaData::Value& value, values) { QMap map = value.asStructure(); if (map.contains("original") && map.contains("current")) { current2original[map["current"].toString()] = map["original"].toString(); } } } ExrPaintLayerSaveInfo info; info.name = name + paintLayer->name() + '.'; info.layer = paintLayer; if (info.name == QString(HDR_LAYER) + ".") { info.channels.push_back("R"); info.channels.push_back("G"); info.channels.push_back("B"); info.channels.push_back("A"); } else { if (paintLayer->colorSpace()->colorModelId() == RGBAColorModelID) { info.channels.push_back(info.name + remap(current2original, "R")); info.channels.push_back(info.name + remap(current2original, "G")); info.channels.push_back(info.name + remap(current2original, "B")); info.channels.push_back(info.name + remap(current2original, "A")); } else if (paintLayer->colorSpace()->colorModelId() == GrayAColorModelID) { info.channels.push_back(info.name + remap(current2original, "G")); info.channels.push_back(info.name + remap(current2original, "A")); } else if (paintLayer->colorSpace()->colorModelId() == GrayColorModelID) { info.channels.push_back(info.name + remap(current2original, "G")); } else if (paintLayer->colorSpace()->colorModelId() == XYZAColorModelID) { info.channels.push_back(info.name + remap(current2original, "X")); info.channels.push_back(info.name + remap(current2original, "Y")); info.channels.push_back(info.name + remap(current2original, "Z")); info.channels.push_back(info.name + remap(current2original, "A")); } } if (paintLayer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) { info.pixelType = Imf::HALF; } else if (paintLayer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) { info.pixelType = Imf::FLOAT; } else { info.pixelType = Imf::NUM_PIXELTYPES; } if (info.pixelType < Imf::NUM_PIXELTYPES) { dbgFile << "Going to save layer" << info.name; informationObjects.push_back(info); } else { warnFile << "Will not save layer" << info.name; layersNotSaved << node; } } else if (KisGroupLayerSP groupLayer = dynamic_cast(node.data())) { recBuildPaintLayerSaveInfo(informationObjects, name + groupLayer->name() + '.', groupLayer); } else { /** * The EXR can store paint and group layers only. The rest will * go to /dev/null :( */ layersNotSaved.insert(node); } } if (!layersNotSaved.isEmpty()) { reportLayersNotSaved(layersNotSaved); } } void exrConverter::Private::reportLayersNotSaved(const QSet &layersNotSaved) { QString layersList; QTextStream textStream(&layersList); textStream.setCodec("UTF-8"); Q_FOREACH (KisNodeSP node, layersNotSaved) { textStream << "" << i18nc("@item:unsupported-node-message", "%1 (type: \"%2\")", node->name(), node->metaObject()->className()) << ""; } QString msg = i18nc("@info", "The following layers have a type that is not supported by EXR format:" "%1" "these layers will NOT be saved to the final EXR file", layersList); if (this->showNotifications) { QMessageBox::information(0, i18nc("@title:window", "Layers will be lost"), msg); } else { warnKrita << "WARNING:" << msg; } } QString exrConverter::Private::fetchExtraLayersInfo(QList& informationObjects) { KIS_ASSERT_RECOVER_NOOP(!informationObjects.isEmpty()); if (informationObjects.size() == 1 && informationObjects[0].name == QString(HDR_LAYER) + ".") { return QString(); } QDomDocument doc("krita-extra-layers-info"); doc.appendChild(doc.createElement("root")); QDomElement rootElement = doc.documentElement(); for (int i = 0; i < informationObjects.size(); i++) { ExrPaintLayerSaveInfo &info = informationObjects[i]; quint32 unused; KisSaveXmlVisitor visitor(doc, rootElement, unused, QString(), false); QDomElement el = visitor.savePaintLayerAttributes(info.layer.data(), doc); // cut the ending '.' QString strippedName = info.name.left(info.name.size() - 1); el.setAttribute(EXR_NAME, strippedName); rootElement.appendChild(el); } return doc.toString(); } KisImageBuilder_Result exrConverter::buildFile(const QString &filename, KisGroupLayerSP layer) { if (!layer) return KisImageBuilder_RESULT_INVALID_ARG; KisImageWSP image = layer->image(); if (!image) return KisImageBuilder_RESULT_EMPTY; qint32 height = image->height(); qint32 width = image->width(); Imf::Header header(width, height); QList informationObjects; m_d->recBuildPaintLayerSaveInfo(informationObjects, "", layer); if(informationObjects.isEmpty()) { return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; } m_d->makeLayerNamesUnique(informationObjects); QByteArray extraLayersInfo = m_d->fetchExtraLayersInfo(informationObjects).toUtf8(); if (!extraLayersInfo.isNull()) { header.insert(EXR_KRITA_LAYERS, Imf::StringAttribute(extraLayersInfo.constData())); } dbgFile << informationObjects.size() << " layers to save"; Q_FOREACH (const ExrPaintLayerSaveInfo& info, informationObjects) { if (info.pixelType < Imf::NUM_PIXELTYPES) { Q_FOREACH (const QString& channel, info.channels) { dbgFile << channel << " " << info.pixelType; header.channels().insert(channel.toUtf8().data(), Imf::Channel(info.pixelType)); } } } // Open file for writing Imf::OutputFile file(QFile::encodeName(filename), header); encodeData(file, informationObjects, width, height); return KisImageBuilder_RESULT_OK; } void exrConverter::cancel() { warnKrita << "WARNING: Cancelling of an EXR loading is not supported!"; } diff --git a/plugins/impex/exr/krita_exr.desktop b/plugins/impex/exr/krita_exr.desktop index 3d55bc6949..00953eab99 100644 --- a/plugins/impex/exr/krita_exr.desktop +++ b/plugins/impex/exr/krita_exr.desktop @@ -1,120 +1,120 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; Exec=krita %u GenericName=Application for Drawing and Handling of Images GenericName[bg]=Приложение за рисуване и обработка на изображения GenericName[bs]=Aplikacija za crtanje i upravljanje slikom GenericName[ca]=Aplicació per a dibuix i modificació d'imatges GenericName[ca@valencia]=Aplicació per a dibuix i modificació d'imatges GenericName[da]=Tegne- og billedbehandlingsprogram GenericName[de]=Programm zum Zeichnen und Bearbeiten von Bildern GenericName[el]=Εφαρμογή για επεξεργασία και χειρισμό εικόνων GenericName[en_GB]=Application for Drawing and Handling of Images GenericName[eo]=Aplikaĵo por Desegnado kaj Mastrumado de Bildoj GenericName[es]=Aplicación para dibujo y manipulación de imágenes GenericName[et]=Joonistamise ja pilditöötluse rakendus GenericName[eu]=Irudiak marrazteko eta manipulatzeko aplikazioa GenericName[fa]=کاربرد برای ترسیم و به کار بردن تصاویر GenericName[fi]=Ohjelma kuvien piirtämiseen ja käsittelyyn GenericName[fr]=Application pour dessiner et manipuler des images GenericName[fy]=Aplikaasje om ôfbyldings mei te tekenjen en te bewurkjen GenericName[ga]=Feidhmchlár le haghaidh Líníochta agus Láimhseála Íomhánna GenericName[gl]=Aplicativo de debuxo e edición de imaxes GenericName[he]=יישום לצביעה וניהול תמונות GenericName[hi]=छवियों को ड्रा करने तथा उन्हें प्रबन्धित करने का अनुप्रयोग GenericName[hne]=फोटू मन ल ड्रा करे अउ ओ मन ल प्रबन्धित करे के अनुपरयोग GenericName[hu]=Rajzoló és képkezelő GenericName[is]=Teikni og myndvinnsluforrit GenericName[it]=Applicazione di disegno e gestione di immagini GenericName[kk]=Кескінді салу және өңдеу бағдарламасы GenericName[ko]=그림 그리기 및 처리 프로그램 GenericName[lv]=Programma zīmēšanai un attēlu apstrādei GenericName[nb]=Program for tegning og bildehåndtering GenericName[nds]=Programm för't Teken un Bildhanteren GenericName[ne]=रेखाचित्र बनाउन र छविको ह्यान्डल गर्नका लागि अनुप्रयोग GenericName[nl]=Toepassing om afbeeldingen te tekenen en te bewerken -GenericName[pl]=Program do rysowania i obróbki obrazków +GenericName[pl]=Program do rysowania i obróbki obrazów GenericName[pt]=Aplicação de Desenho e Manipulação de Imagens GenericName[pt_BR]=Aplicativo de desenho e manipulação de imagens GenericName[ru]=Растровые изображения GenericName[sk]=Aplikácia na kresnenie a manilupáciu s obrázkami GenericName[sl]=Program za risanje in rokovanje s slikami GenericName[sv]=Program för att rita och hantera bilder GenericName[ta]=பிம்பங்களை கையாளுதல் மற்றும் வரைதலுக்கான பயன்னாடு GenericName[tr]=Çizim ve Resim İşleme Uygulaması GenericName[uk]=Програма для малювання і обробки зображень GenericName[uz]=Rasm chizish dasturi GenericName[uz@cyrillic]=Расм чизиш дастури GenericName[wa]=Programe po dessiner et apougnî des imådjes GenericName[x-test]=xxApplication for Drawing and Handling of Imagesxx GenericName[zh_CN]=绘制和操纵图像的应用程序 GenericName[zh_TW]=影像繪製與處理應用程式 Icon=calligrakrita MimeType=image/exr; Name=Krita Name[af]=Krita Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=繪圖_Krita StartupNotify=true Terminal=false Type=Application X-KDE-SubstituteUID=false X-KDE-Username= NoDisplay=true diff --git a/plugins/impex/jp2/krita_jp2.desktop b/plugins/impex/jp2/krita_jp2.desktop index 41a1d62655..e7ca394f71 100644 --- a/plugins/impex/jp2/krita_jp2.desktop +++ b/plugins/impex/jp2/krita_jp2.desktop @@ -1,120 +1,120 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; Exec=krita %u GenericName=Application for Drawing and Handling of Images GenericName[bg]=Приложение за рисуване и обработка на изображения GenericName[bs]=Aplikacija za crtanje i upravljanje slikom GenericName[ca]=Aplicació per a dibuix i modificació d'imatges GenericName[ca@valencia]=Aplicació per a dibuix i modificació d'imatges GenericName[da]=Tegne- og billedbehandlingsprogram GenericName[de]=Programm zum Zeichnen und Bearbeiten von Bildern GenericName[el]=Εφαρμογή για επεξεργασία και χειρισμό εικόνων GenericName[en_GB]=Application for Drawing and Handling of Images GenericName[eo]=Aplikaĵo por Desegnado kaj Mastrumado de Bildoj GenericName[es]=Aplicación para dibujo y manipulación de imágenes GenericName[et]=Joonistamise ja pilditöötluse rakendus GenericName[eu]=Irudiak marrazteko eta manipulatzeko aplikazioa GenericName[fa]=کاربرد برای ترسیم و به کار بردن تصاویر GenericName[fi]=Ohjelma kuvien piirtämiseen ja käsittelyyn GenericName[fr]=Application pour dessiner et manipuler des images GenericName[fy]=Aplikaasje om ôfbyldings mei te tekenjen en te bewurkjen GenericName[ga]=Feidhmchlár le haghaidh Líníochta agus Láimhseála Íomhánna GenericName[gl]=Aplicativo de debuxo e edición de imaxes GenericName[he]=יישום לצביעה וניהול תמונות GenericName[hi]=छवियों को ड्रा करने तथा उन्हें प्रबन्धित करने का अनुप्रयोग GenericName[hne]=फोटू मन ल ड्रा करे अउ ओ मन ल प्रबन्धित करे के अनुपरयोग GenericName[hu]=Rajzoló és képkezelő GenericName[is]=Teikni og myndvinnsluforrit GenericName[it]=Applicazione di disegno e gestione di immagini GenericName[kk]=Кескінді салу және өңдеу бағдарламасы GenericName[ko]=그림 그리기 및 처리 프로그램 GenericName[lv]=Programma zīmēšanai un attēlu apstrādei GenericName[nb]=Program for tegning og bildehåndtering GenericName[nds]=Programm för't Teken un Bildhanteren GenericName[ne]=रेखाचित्र बनाउन र छविको ह्यान्डल गर्नका लागि अनुप्रयोग GenericName[nl]=Toepassing om afbeeldingen te tekenen en te bewerken -GenericName[pl]=Program do rysowania i obróbki obrazków +GenericName[pl]=Program do rysowania i obróbki obrazów GenericName[pt]=Aplicação de Desenho e Manipulação de Imagens GenericName[pt_BR]=Aplicativo de desenho e manipulação de imagens GenericName[ru]=Растровые изображения GenericName[sk]=Aplikácia na kresnenie a manilupáciu s obrázkami GenericName[sl]=Program za risanje in rokovanje s slikami GenericName[sv]=Program för att rita och hantera bilder GenericName[ta]=பிம்பங்களை கையாளுதல் மற்றும் வரைதலுக்கான பயன்னாடு GenericName[tr]=Çizim ve Resim İşleme Uygulaması GenericName[uk]=Програма для малювання і обробки зображень GenericName[uz]=Rasm chizish dasturi GenericName[uz@cyrillic]=Расм чизиш дастури GenericName[wa]=Programe po dessiner et apougnî des imådjes GenericName[x-test]=xxApplication for Drawing and Handling of Imagesxx GenericName[zh_CN]=绘制和操纵图像的应用程序 GenericName[zh_TW]=影像繪製與處理應用程式 Icon=calligrakrita MimeType=image/jp2; Name=Krita Name[af]=Krita Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=繪圖_Krita StartupNotify=true Terminal=false Type=Application X-KDE-SubstituteUID=false X-KDE-Username= NoDisplay=true diff --git a/plugins/impex/jpeg/CMakeLists.txt b/plugins/impex/jpeg/CMakeLists.txt index 74d06c0282..c31356db90 100644 --- a/plugins/impex/jpeg/CMakeLists.txt +++ b/plugins/impex/jpeg/CMakeLists.txt @@ -1,42 +1,42 @@ add_subdirectory(tests) set(ICCJPEG_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/lcms") -include_directories(${ICCJPEG_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/krita/image/metadata) +include_directories(${ICCJPEG_SOURCE_DIR}) include_directories(SYSTEM ${LCMS2_INCLUDE_DIR} ${EXIV2_INCLUDE_DIR} ) set(libkritaconverter_LIB_SRCS kis_jpeg_converter.cc kis_jpeg_source.cpp kis_jpeg_destination.cpp ${ICCJPEG_SOURCE_DIR}/iccjpeg.c ) set(kritajpegimport_SOURCES kis_jpeg_import.cc ${libkritaconverter_LIB_SRCS} ) add_library(kritajpegimport MODULE ${kritajpegimport_SOURCES}) target_link_libraries(kritajpegimport kritaui ${JPEG_LIBRARIES} ${LCMS2_LIBRARIES} ${EXIV2_LIBRARIES} ) install(TARGETS kritajpegimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) set(kritajpegexport_SOURCES kis_jpeg_export.cc ${libkritaconverter_LIB_SRCS} ) ki18n_wrap_ui(kritajpegexport_SOURCES kis_wdg_options_jpeg.ui ) add_library(kritajpegexport MODULE ${kritajpegexport_SOURCES}) target_link_libraries(kritajpegexport kritaui ${JPEG_LIBRARIES} ${LCMS2_LIBRARIES} ${EXIV2_LIBRARIES} ) install(TARGETS kritajpegexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) install( PROGRAMS krita_jpeg.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) diff --git a/plugins/impex/odg/CMakeLists.txt b/plugins/impex/odg/CMakeLists.txt index 5e96fffbec..86709c79b0 100644 --- a/plugins/impex/odg/CMakeLists.txt +++ b/plugins/impex/odg/CMakeLists.txt @@ -1,17 +1,15 @@ -include_directories( ${CMAKE_SOURCE_DIR}/krita/image/metadata ) - set(kritaodgimport_SOURCES kis_odg_import.cc ) add_library(kritaodgimport MODULE ${kritaodgimport_SOURCES}) include_directories(SYSTEM ${PNG_INCLUDE_DIR}) add_definitions(${PNG_DEFINITIONS} ${KDE4_ENABLE_EXCEPTIONS}) target_link_libraries(kritaodgimport kritaui ${PNG_LIBRARIES} ) install(TARGETS kritaodgimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) install( PROGRAMS krita_odg.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) diff --git a/plugins/impex/ora/CMakeLists.txt b/plugins/impex/ora/CMakeLists.txt index 8d61fa8555..360d6e3052 100644 --- a/plugins/impex/ora/CMakeLists.txt +++ b/plugins/impex/ora/CMakeLists.txt @@ -1,34 +1,32 @@ -include_directories( ${CMAKE_SOURCE_DIR}/krita/ui/ora ${CMAKE_SOURCE_DIR}/krita/image/metadata) - set(libkritaconverter_LIB_SRCS ora_converter.cc ) set(kritaoraimport_SOURCES ora_import.cc ${libkritaconverter_LIB_SRCS} ) add_library(kritaoraimport MODULE ${kritaoraimport_SOURCES}) target_link_libraries(kritaoraimport kritaui ) install(TARGETS kritaoraimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) set(kritaoraexport_SOURCES ora_export.cc ${libkritaconverter_LIB_SRCS} ) add_library(kritaoraexport MODULE ${kritaoraexport_SOURCES}) target_link_libraries(kritaoraexport kritaui ) install(TARGETS kritaoraexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) install( PROGRAMS krita_ora.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) if(SHOULD_BUILD_FILEMANAGER_THUMBNAIL) install( FILES krita_ora_thumbnail.desktop DESTINATION ${SERVICES_INSTALL_DIR}) endif() diff --git a/plugins/impex/pdf/CMakeLists.txt b/plugins/impex/pdf/CMakeLists.txt index 96a6b3b0d6..535c0f67fc 100644 --- a/plugins/impex/pdf/CMakeLists.txt +++ b/plugins/impex/pdf/CMakeLists.txt @@ -1,12 +1,12 @@ -include_directories( ${CMAKE_BINARY_DIR}/filters/ ${POPPLER_INCLUDE_DIR} ) +include_directories( ${POPPLER_INCLUDE_DIR} ) set(kritapdfimport_SOURCES kis_pdf_import.cpp kis_pdf_import_widget.cpp ) ki18n_wrap_ui(kritapdfimport_SOURCES pdfimportwidgetbase.ui ) add_library(kritapdfimport MODULE ${kritapdfimport_SOURCES}) target_link_libraries(kritapdfimport kritaui ${POPPLER_LIBRARY} ) install(TARGETS kritapdfimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) install(PROGRAMS krita_pdf.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) diff --git a/plugins/impex/png/CMakeLists.txt b/plugins/impex/png/CMakeLists.txt index 4e5c58d72e..7502f9096d 100644 --- a/plugins/impex/png/CMakeLists.txt +++ b/plugins/impex/png/CMakeLists.txt @@ -1,31 +1,29 @@ add_subdirectory(tests) -include_directories( ${CMAKE_SOURCE_DIR}/krita/image/metadata ) - set(kritapngimport_SOURCES kis_png_import.cc ) add_library(kritapngimport MODULE ${kritapngimport_SOURCES}) include_directories(SYSTEM ${PNG_INCLUDE_DIR}) add_definitions(${PNG_DEFINITIONS} ${KDE4_ENABLE_EXCEPTIONS}) target_link_libraries(kritapngimport kritaui ${PNG_LIBRARIES} ) install(TARGETS kritapngimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) set(kritapngexport_SOURCES kis_png_export.cc ) ki18n_wrap_ui(kritapngexport_SOURCES kis_wdg_options_png.ui ) add_library(kritapngexport MODULE ${kritapngexport_SOURCES}) target_link_libraries(kritapngexport kritaui ${PNG_LIBRARIES}) install(TARGETS kritapngexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) install( PROGRAMS krita_png.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) diff --git a/plugins/impex/png/krita_png_export.json b/plugins/impex/png/krita_png_export.json index 0b6309e253..2f21274b2f 100644 --- a/plugins/impex/png/krita_png_export.json +++ b/plugins/impex/png/krita_png_export.json @@ -1,13 +1,13 @@ { "Id": "Krita PNG Export Filter", "NoDisplay": "true", "Type": "Service", - "X-KDE-Export": "image/png", - "X-KDE-Import": "application/x-krita,application/x-krita-paintoppreset", + "X-KDE-Export": "image/png,application/x-krita-paintoppreset", + "X-KDE-Import": "application/x-krita", "X-KDE-Library": "kritapngexport", "X-KDE-ServiceTypes": [ "Krita/FileFilter" ], "X-KDE-Weight": "1", "X-KDE-Extensions" : "png,kpp" } diff --git a/plugins/impex/qml/CMakeLists.txt b/plugins/impex/qml/CMakeLists.txt index 0fc69e64e5..e62187f12a 100644 --- a/plugins/impex/qml/CMakeLists.txt +++ b/plugins/impex/qml/CMakeLists.txt @@ -1,13 +1,10 @@ - -include_directories( ${CMAKE_SOURCE_DIR}/krita/image/metadata) - set(kritaqmlexport_SOURCES qml_converter.cc qml_export.cc ) add_library(kritaqmlexport MODULE ${kritaqmlexport_SOURCES}) target_link_libraries(kritaqmlexport kritaui ) install(TARGETS kritaqmlexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) diff --git a/plugins/impex/spriter/CMakeLists.txt b/plugins/impex/spriter/CMakeLists.txt index e58f7ea3f0..fbc037b8d6 100644 --- a/plugins/impex/spriter/CMakeLists.txt +++ b/plugins/impex/spriter/CMakeLists.txt @@ -1,20 +1,17 @@ -include_directories( - ${CMAKE_SOURCE_DIR}/krita/image/metadata -) include_directories(SYSTEM ${Boost_INCLUDE_DIRS} ) # export set(kritaspriterexport_SOURCES kis_spriter_export.cpp ) add_library(kritaspriterexport MODULE ${kritaspriterexport_SOURCES}) target_link_libraries(kritaspriterexport kritaui ) install(TARGETS kritaspriterexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) install( PROGRAMS krita_spriter.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) diff --git a/plugins/paintops/curvebrush/wdgcurveoptions.ui b/plugins/paintops/curvebrush/wdgcurveoptions.ui index b08a4654af..de0bb74b0f 100644 --- a/plugins/paintops/curvebrush/wdgcurveoptions.ui +++ b/plugins/paintops/curvebrush/wdgcurveoptions.ui @@ -1,104 +1,84 @@ WdgCurveOptions - - - 0 - 0 - 432 - 338 - - - - - 0 - 0 - - - - - 0 - 0 - - Line width: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + History size: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + Curves opacity: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + Paint connection line Smoothing Qt::Vertical - 352 - 267 + 0 + 0 KisDoubleSliderSpinBox QWidget
kis_slider_spin_box.h
1
diff --git a/plugins/paintops/defaultpaintops/CMakeLists.txt b/plugins/paintops/defaultpaintops/CMakeLists.txt index bd3a0e8476..0e06523a94 100644 --- a/plugins/paintops/defaultpaintops/CMakeLists.txt +++ b/plugins/paintops/defaultpaintops/CMakeLists.txt @@ -1,30 +1,28 @@ add_subdirectory(brush/tests) include_directories(brush) -include_directories(pen) -include_directories(eraser) include_directories(duplicate) set(kritadefaultpaintops_SOURCES defaultpaintops_plugin.cc brush/kis_brushop.cpp brush/kis_brushop_settings_widget.cpp duplicate/kis_duplicateop.cpp duplicate/kis_duplicateop_settings.cpp duplicate/kis_duplicateop_settings_widget.cpp duplicate/kis_duplicateop_option.cpp ) ki18n_wrap_ui(kritadefaultpaintops_SOURCES duplicate/wdgduplicateop.ui ) add_library(kritadefaultpaintops MODULE ${kritadefaultpaintops_SOURCES}) target_link_libraries(kritadefaultpaintops kritalibpaintop) install(TARGETS kritadefaultpaintops DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) install( FILES krita-paintbrush.png krita-eraser.png krita-duplicate.png DESTINATION ${DATA_INSTALL_DIR}/krita/images) diff --git a/plugins/paintops/defaultpresets/tangentnormal.kpp b/plugins/paintops/defaultpresets/tangentnormal.kpp index 0f9118783e..85792348a3 100644 Binary files a/plugins/paintops/defaultpresets/tangentnormal.kpp and b/plugins/paintops/defaultpresets/tangentnormal.kpp differ diff --git a/plugins/paintops/deform/deform_brush.cpp b/plugins/paintops/deform/deform_brush.cpp index 13c785a839..aece16ddbd 100644 --- a/plugins/paintops/deform/deform_brush.cpp +++ b/plugins/paintops/deform/deform_brush.cpp @@ -1,302 +1,296 @@ /* * Copyright (c) 2008,2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "deform_brush.h" #include "kis_painter.h" #include "kis_fixed_paint_device.h" #include #include #include #include #include #include #include #include #include const qreal degToRad = M_PI / 180.0; DeformBrush::DeformBrush() { m_firstPaint = false; m_counter = 1; m_deformAction = 0; } DeformBrush::~DeformBrush() { delete m_deformAction; } void DeformBrush::initDeformAction() { DeformModes mode = DeformModes(m_properties->action - 1); switch (mode) { case GROW: case SHRINK: { m_deformAction = new DeformScale(); break; } case SWIRL_CW: case SWIRL_CCW: { m_deformAction = new DeformRotation(); break; } case MOVE: { m_deformAction = new DeformMove(); static_cast(m_deformAction)->setFactor(m_properties->deformAmount); break; } case LENS_IN: case LENS_OUT: { m_deformAction = new DeformLens(); static_cast(m_deformAction)->setLensFactor(m_properties->deformAmount, 0.0); static_cast(m_deformAction)->setMode(mode == LENS_OUT); break; } case DEFORM_COLOR: { m_deformAction = new DeformColor(); static_cast(m_deformAction)->setFactor(m_properties->deformAmount); break; } default: { m_deformAction = new DeformBase(); break; } } } -bool DeformBrush::setupAction(DeformModes mode, const QPointF& pos) +bool DeformBrush::setupAction( + DeformModes mode, const QPointF& pos, QTransform const& rotation) { switch (mode) { case GROW: case SHRINK: { // grow or shrink, the sign decide qreal sign = (mode == GROW) ? 1.0 : -1.0; qreal factor; if (m_properties->useCounter) { factor = (1.0 + sign * (m_counter * m_counter / 100.0)); } else { factor = (1.0 + sign * (m_properties->deformAmount)); } dynamic_cast(m_deformAction)->setFactor(factor); break; } case SWIRL_CW: case SWIRL_CCW: { // CW or CCW, the sign decide qreal sign = (mode == SWIRL_CW) ? 1.0 : -1.0; qreal factor; if (m_properties->useCounter) { factor = m_counter * sign * degToRad; } else { factor = (360 * m_properties->deformAmount * 0.5) * sign * degToRad; } dynamic_cast(m_deformAction)->setAlpha(factor); break; } case MOVE: { if (m_firstPaint == false) { m_prevX = pos.x(); m_prevY = pos.y(); static_cast(m_deformAction)->setDistance(0.0, 0.0); m_firstPaint = true; return false; } else { - static_cast(m_deformAction)->setDistance(pos.x() - m_prevX, pos.y() - m_prevY); + qreal xDistance = pos.x() - m_prevX; + qreal yDistance = pos.y() - m_prevY; + rotation.map(xDistance, yDistance, &xDistance, &yDistance); + static_cast(m_deformAction)->setDistance(xDistance, yDistance); m_prevX = pos.x(); m_prevY = pos.y(); } break; } case LENS_IN: case LENS_OUT: { static_cast(m_deformAction)->setMaxDistance(m_sizeProperties->diameter * 0.5, m_sizeProperties->diameter * 0.5); break; } case DEFORM_COLOR: { // no run-time setup break; } default: { break; } } return true; } KisFixedPaintDeviceSP DeformBrush::paintMask(KisFixedPaintDeviceSP dab, KisPaintDeviceSP layer, qreal scale, qreal rotation, QPointF pos, qreal subPixelX, qreal subPixelY, int dabX, int dabY) { KisFixedPaintDeviceSP mask = new KisFixedPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); KisCrossDeviceColorPicker colorPicker(layer, dab); qreal fWidth = maskWidth(scale); qreal fHeight = maskHeight(scale); int dstWidth = qRound(m_maskRect.width()); int dstHeight = qRound(m_maskRect.height()); // clear if (dab->bounds().width() != dstWidth || dab->bounds().height() != dstHeight) { dab->setRect(m_maskRect.toRect()); dab->initialize(); } else { dab->clear(m_maskRect.toRect()); } - m_centerX = dstWidth * 0.5 + subPixelX; - m_centerY = dstHeight * 0.5 + subPixelY; + qreal const centerX = dstWidth * 0.5 + subPixelX; + qreal const centerY = dstHeight * 0.5 + subPixelY; - // major axis - m_majorAxis = 2.0 / fWidth; - // minor axis - m_minorAxis = 2.0 / fHeight; - // inverse square - m_inverseScale = 1.0 / scale; - // amount of precomputed data - m_maskRadius = 0.5 * fWidth; + qreal const majorAxis = 2.0 / fWidth; + qreal const minorAxis = 2.0 / fHeight; - qreal maskX; - qreal maskY; qreal distance; + QTransform forwardRotationMatrix; + forwardRotationMatrix.rotateRadians(-rotation); + QTransform reverseRotationMatrix; + reverseRotationMatrix.rotateRadians(rotation); + // if can't paint, stop - if (!setupAction(DeformModes(m_properties->action - 1), pos)) { + if (!setupAction(DeformModes(m_properties->action - 1), + pos, forwardRotationMatrix)) + { return 0; } - qreal cosa = cos(-rotation); - qreal sina = sin(-rotation); - - qreal bcosa = cos(rotation); - qreal bsina = sin(rotation); - - mask->setRect(dab->bounds()); mask->initialize(); quint8* maskPointer = mask->data(); qint8 maskPixelSize = mask->pixelSize(); quint8* dabPointer = dab->data(); int dabPixelSize = dab->colorSpace()->pixelSize(); for (int y = 0; y < dstHeight; y++) { for (int x = 0; x < dstWidth; x++) { - maskX = x - m_centerX; - maskY = y - m_centerY; - qreal rmaskX = cosa * maskX - sina * maskY; - qreal rmaskY = sina * maskX + cosa * maskY; + qreal maskX = x - centerX; + qreal maskY = y - centerY; + forwardRotationMatrix.map(maskX, maskY, &maskX, &maskY); + forwardRotationMatrix.map(maskX, maskY, &maskX, &maskY); + distance = norme(maskX * majorAxis, maskY * minorAxis); - distance = norme(rmaskX * m_majorAxis, rmaskY * m_minorAxis); if (distance > 1.0) { // leave there OPACITY TRANSPARENT pixel (default pixel) colorPicker.pickOldColor(x + dabX, y + dabY, dabPointer); dabPointer += dabPixelSize; *maskPointer = OPACITY_TRANSPARENT_U8; maskPointer += maskPixelSize; continue; } if (m_sizeProperties->density != 1.0) { if (m_sizeProperties->density < drand48()) { dabPointer += dabPixelSize; *maskPointer = OPACITY_TRANSPARENT_U8; maskPointer += maskPixelSize; continue; } } - m_deformAction->transform(&rmaskX, &rmaskY, distance); - - maskX = bcosa * rmaskX - bsina * rmaskY; - maskY = bsina * rmaskX + bcosa * rmaskY; + m_deformAction->transform(&maskX, &maskY, distance); + reverseRotationMatrix.map(maskX, maskY, &maskX, &maskY); maskX += pos.x(); maskY += pos.y(); if (!m_properties->useBilinear) { maskX = qRound(maskX); maskY = qRound(maskY); } if (m_properties->useOldData) { colorPicker.pickOldColor(maskX, maskY, dabPointer); } else { colorPicker.pickColor(maskX, maskY, dabPointer); } dabPointer += dabPixelSize; *maskPointer = OPACITY_OPAQUE_U8; maskPointer += maskPixelSize; } } m_counter++; return mask; } void DeformBrush::debugColor(const quint8* data, KoColorSpace * cs) { QColor rgbcolor; cs->toQColor(data, &rgbcolor); dbgPlugins << "RGBA: (" << rgbcolor.red() << ", " << rgbcolor.green() << ", " << rgbcolor.blue() << ", " << rgbcolor.alpha() << ")"; } QPointF DeformBrush::hotSpot(qreal scale, qreal rotation) { qreal fWidth = maskWidth(scale); qreal fHeight = maskHeight(scale); QTransform m; m.reset(); m.rotateRadians(rotation); m_maskRect = QRect(0, 0, fWidth, fHeight); m_maskRect.translate(-m_maskRect.center()); m_maskRect = m.mapRect(m_maskRect); m_maskRect.translate(-m_maskRect.topLeft()); return m_maskRect.center(); } diff --git a/plugins/paintops/deform/deform_brush.h b/plugins/paintops/deform/deform_brush.h index 9316e4a28c..e856517b6f 100644 --- a/plugins/paintops/deform/deform_brush.h +++ b/plugins/paintops/deform/deform_brush.h @@ -1,256 +1,251 @@ /* * Copyright (c) 2008, 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _DEFORM_BRUSH_H_ #define _DEFORM_BRUSH_H_ #include #include #include #include #if defined(_WIN32) || defined(_WIN64) #define srand48 srand inline double drand48() { return double(rand()) / RAND_MAX; } #endif enum DeformModes {GROW, SHRINK, SWIRL_CW, SWIRL_CCW, MOVE, LENS_IN, LENS_OUT, DEFORM_COLOR}; class DeformProperties { public: int action; qreal deformAmount; bool useBilinear; bool useCounter; bool useOldData; }; class DeformBase { public: DeformBase() {} virtual ~DeformBase() {} virtual void transform(qreal * x, qreal * y, qreal distance) { Q_UNUSED(x); Q_UNUSED(y); Q_UNUSED(distance); } }; /// Inverse weighted inverse scaling - grow&shrink class DeformScale : public DeformBase { public: void setFactor(qreal factor) { m_factor = factor; } qreal factor() { return m_factor; } virtual void transform(qreal* x, qreal* y, qreal distance) { qreal scaleFactor = (1.0 - distance) * m_factor + distance; *x = *x / scaleFactor; *y = *y / scaleFactor; } private: qreal m_factor; }; /// Inverse weighted rotation - swirlCW&&swirlCWW class DeformRotation : public DeformBase { public: void setAlpha(qreal alpha) { m_alpha = alpha; } virtual void transform(qreal* maskX, qreal* maskY, qreal distance) { distance = 1.0 - distance; qreal rotX = cos(-m_alpha * distance) * (*maskX) - sin(-m_alpha * distance) * (*maskY); qreal rotY = sin(-m_alpha * distance) * (*maskX) + cos(-m_alpha * distance) * (*maskY); *maskX = rotX; *maskY = rotY; } private: qreal m_alpha; }; /// Inverse move class DeformMove : public DeformBase { public: void setFactor(qreal factor) { m_factor = factor; } void setDistance(qreal dx, qreal dy) { m_dx = dx; m_dy = dy; } virtual void transform(qreal* maskX, qreal* maskY, qreal distance) { *maskX -= m_dx * m_factor * (1.0 - distance); *maskY -= m_dy * m_factor * (1.0 - distance); } private: qreal m_dx; qreal m_dy; qreal m_factor; }; /// Inverse lens distortion class DeformLens : public DeformBase { public: void setLensFactor(qreal k1, qreal k2) { m_k1 = k1; m_k2 = k2; } void setMaxDistance(qreal maxX, qreal maxY) { m_maxX = maxX; m_maxY = maxY; } void setMode(bool out) { m_out = out; } virtual void transform(qreal* maskX, qreal* maskY, qreal distance) { Q_UNUSED(distance); //normalize qreal normX = *maskX / m_maxX; qreal normY = *maskY / m_maxY; qreal radius_2 = normX * normX + normY * normY; qreal radius_4 = radius_2 * radius_2; if (m_out) { *maskX = normX * (1.0 + m_k1 * radius_2 + m_k2 * radius_4); *maskY = normY * (1.0 + m_k1 * radius_2 + m_k2 * radius_4); } else { *maskX = normX / (1.0 + m_k1 * radius_2 + m_k2 * radius_4); *maskY = normY / (1.0 + m_k1 * radius_2 + m_k2 * radius_4); } *maskX = m_maxX * (*maskX); *maskY = m_maxY * (*maskY); } private: qreal m_k1, m_k2; qreal m_maxX, m_maxY; bool m_out; }; /// Randomly disturb the pixels class DeformColor : public DeformBase { public: DeformColor() { srand48(time(0)); } void setFactor(qreal factor) { m_factor = factor; } virtual void transform(qreal* x, qreal* y, qreal distance) { Q_UNUSED(distance); qreal randomX = m_factor * ((drand48() * 2.0) - 1.0); qreal randomY = m_factor * ((drand48() * 2.0) - 1.0); *x += randomX; *y += randomY; } private: qreal m_factor; }; class DeformBrush { public: DeformBrush(); ~DeformBrush(); KisFixedPaintDeviceSP paintMask(KisFixedPaintDeviceSP dab, KisPaintDeviceSP layer, qreal scale, qreal rotation, QPointF pos, qreal subPixelX, qreal subPixelY, int dabX, int dabY); void setSizeProperties(KisBrushSizeProperties * properties) { m_sizeProperties = properties; } void setProperties(DeformProperties * properties) { m_properties = properties; } void initDeformAction(); QPointF hotSpot(qreal scale, qreal rotation); private: // return true if can paint - bool setupAction(DeformModes mode, const QPointF &pos); + bool setupAction( + DeformModes mode, const QPointF& pos, QTransform const& rotation); void debugColor(const quint8* data, KoColorSpace * cs); qreal maskWidth(qreal scale) { return m_sizeProperties->diameter * scale; } qreal maskHeight(qreal scale) { return m_sizeProperties->diameter * m_sizeProperties->aspect * scale; } inline qreal norme(qreal x, qreal y) { return x * x + y * y; } private: KisRandomSubAccessorSP m_srcAcc; bool m_firstPaint; qreal m_prevX, m_prevY; int m_counter; - qreal m_centerX; - qreal m_centerY; - qreal m_majorAxis; - qreal m_minorAxis; - qreal m_inverseScale; - qreal m_maskRadius; QRectF m_maskRect; DeformBase * m_deformAction; DeformProperties * m_properties; KisBrushSizeProperties * m_sizeProperties; }; #endif diff --git a/plugins/paintops/dynadraw/wdgdynaoptions.ui b/plugins/paintops/dynadraw/wdgdynaoptions.ui index 5a33a301ae..ce2cff3416 100644 --- a/plugins/paintops/dynadraw/wdgdynaoptions.ui +++ b/plugins/paintops/dynadraw/wdgdynaoptions.ui @@ -1,328 +1,334 @@ WdgDynaOptions 0 0 337 359 210 60 337 359 - - - - 0 - 10 - 321 - 341 - - - - - 321 - 341 - - - - - 321 - 341 - - - - 1 - - - - Dynamics settings - - - - - 20 - 0 - 281 - 122 - + + + + + 0 - - - - - Initial width: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - -99.000000000000000 - - - 0.050000000000000 - - - 1.500000000000000 - - - - - - - Mass: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - -99.000000000000000 - - - 0.050000000000000 - - - 0.500000000000000 - - - - - - - Drag: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - -99.000000000000000 - - - 0.050000000000000 - - - 0.150000000000000 - - - - - - - -99.000000000000000 - - - 0.050000000000000 - - - 0.050000000000000 - - - - - - - Width range: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - + + + Dynamics settings + + + + + + + + Initial width: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + -99.000000000000000 + + + 0.050000000000000 + + + 1.500000000000000 + + + + + + + Mass: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + -99.000000000000000 + + + 0.050000000000000 + + + 0.500000000000000 + + + + + + + Drag: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + -99.000000000000000 + + + 0.050000000000000 + + + 0.150000000000000 + + + + + + + Width range: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + -99.000000000000000 + + + 0.050000000000000 + + + 0.050000000000000 + + + + + + + + + Qt::Vertical + + + + 20 + 153 + + + + + + + + + Shape + + + + + + + + Diameter: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + true + + + Angle: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + + + + + Fixed angle + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 20 + 15 + + + + + + + + + + + + Circle + + + true + + + + + + + Two + + + + + + + + + + + Lines + + + + + + + Line spacing + + + 20.000000000000000 + + + + + + + Line count + + + 10 + + + + + + + + + Polygon + + + + + + + Wire + + + + + + + Paint connection + + + true + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + - - - - Shape - - - - - 0 - 130 - 301 - 141 - - - - - - - - - Circle - - - true - - - - - - - Two - - - - - - - - - - - Lines - - - - - - - Line spacing - - - 20.000000000000000 - - - - - - - Line count - - - 10 - - - - - - - - - Polygon - - - - - - - Wire - - - - - - - Paint connection - - - true - - - - - - - - - 1 - 2 - 301 - 65 - - - - - - - Diameter: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - true - - - Angle: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - false - - - - - - - Fixed angle - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - + + KisDoubleSliderSpinBox QWidget
kis_slider_spin_box.h
1
diff --git a/plugins/paintops/hairy/kis_hairy_paintop.cpp b/plugins/paintops/hairy/kis_hairy_paintop.cpp index ef1bc4a7c1..57daf72b1f 100644 --- a/plugins/paintops/hairy/kis_hairy_paintop.cpp +++ b/plugins/paintops/hairy/kis_hairy_paintop.cpp @@ -1,141 +1,141 @@ /* * Copyright (c) 2008-2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_hairy_paintop.h" #include "kis_hairy_paintop_settings.h" #include #include #include #include #include "kis_paint_device.h" #include "kis_painter.h" #include #include #include #include #include #include #include #include #include "kis_brush.h" KisHairyPaintOp::KisHairyPaintOp(const KisBrushBasedPaintOpSettings *settings, KisPainter * painter, KisNodeSP node, KisImageSP image) : KisPaintOp(painter) { Q_UNUSED(image) Q_ASSERT(settings); m_dev = node ? node->paintDevice() : 0; KisBrushOption brushOption; - brushOption.readOptionSetting(settings); + brushOption.readOptionSetting(settings, true); KisBrushSP brush = brushOption.brush(); KisFixedPaintDeviceSP dab = cachedDab(painter->device()->compositionSourceColorSpace()); if (brush->brushType() == IMAGE || brush->brushType() == PIPE_IMAGE) { dab = brush->paintDevice(source()->colorSpace(), 1.0, 0.0, KisPaintInformation()); } else { brush->mask(dab, painter->paintColor(), 1.0, 1.0, 0.0, KisPaintInformation()); } m_brush.fromDabWithDensity(dab, settings->getDouble(HAIRY_BRISTLE_DENSITY) * 0.01); m_brush.setInkColor(painter->paintColor()); loadSettings(settings); m_brush.setProperties(&m_properties); m_rotationOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); m_rotationOption.resetAllSensors(); m_opacityOption.resetAllSensors(); m_sizeOption.resetAllSensors(); } void KisHairyPaintOp::loadSettings(const KisBrushBasedPaintOpSettings* settings) { m_properties.inkAmount = settings->getInt(HAIRY_INK_AMOUNT); //TODO: wait for the transfer function with variable size m_properties.inkDepletionCurve = settings->getCubicCurve(HAIRY_INK_DEPLETION_CURVE).floatTransfer(m_properties.inkAmount); m_properties.inkDepletionEnabled = settings->getBool(HAIRY_INK_DEPLETION_ENABLED); m_properties.useSaturation = settings->getBool(HAIRY_INK_USE_SATURATION); m_properties.useOpacity = settings->getBool(HAIRY_INK_USE_OPACITY); m_properties.useWeights = settings->getBool(HAIRY_INK_USE_WEIGHTS); m_properties.pressureWeight = settings->getDouble(HAIRY_INK_PRESSURE_WEIGHT) / 100.0; m_properties.bristleLengthWeight = settings->getDouble(HAIRY_INK_BRISTLE_LENGTH_WEIGHT) / 100.0; m_properties.bristleInkAmountWeight = settings->getDouble(HAIRY_INK_BRISTLE_INK_AMOUNT_WEIGHT) / 100.0; m_properties.inkDepletionWeight = settings->getDouble(HAIRY_INK_DEPLETION_WEIGHT); m_properties.useSoakInk = settings->getBool(HAIRY_INK_SOAK); m_properties.useMousePressure = settings->getBool(HAIRY_BRISTLE_USE_MOUSEPRESSURE); m_properties.shearFactor = settings->getDouble(HAIRY_BRISTLE_SHEAR); m_properties.randomFactor = settings->getDouble(HAIRY_BRISTLE_RANDOM); m_properties.scaleFactor = settings->getDouble(HAIRY_BRISTLE_SCALE); m_properties.threshold = settings->getBool(HAIRY_BRISTLE_THRESHOLD); m_properties.antialias = settings->getBool(HAIRY_BRISTLE_ANTI_ALIASING); m_properties.useCompositing = settings->getBool(HAIRY_BRISTLE_USE_COMPOSITING); m_properties.connectedPath = settings->getBool(HAIRY_BRISTLE_CONNECTED); } KisSpacingInformation KisHairyPaintOp::paintAt(const KisPaintInformation& info) { Q_UNUSED(info); return KisSpacingInformation(0.5); } void KisHairyPaintOp::paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) { Q_UNUSED(currentDistance); if (!painter()) return; if (!m_dab) { m_dab = source()->createCompositionSourceDevice(); } else { m_dab->clear(); } // Hairy Brush is capable of working with zero scale, // so no additional checks for 'zero'ness are needed qreal scale = m_sizeOption.apply(pi2); scale *= KisLodTransform::lodToScale(painter()->device()); qreal rotation = m_rotationOption.apply(pi2); quint8 origOpacity = m_opacityOption.apply(painter(), pi2); setCurrentScale(scale); setCurrentRotation(rotation); m_brush.paintLine(m_dab, m_dev, pi1, pi2, scale * m_properties.scaleFactor, rotation); //QRect rc = m_dab->exactBounds(); QRect rc = m_dab->extent(); painter()->bitBlt(rc.topLeft(), m_dab, rc); painter()->renderMirrorMask(rc, m_dab); painter()->setOpacity(origOpacity); } diff --git a/plugins/paintops/hatching/.directory b/plugins/paintops/hatching/.directory deleted file mode 100644 index a62d4b57a7..0000000000 --- a/plugins/paintops/hatching/.directory +++ /dev/null @@ -1,3 +0,0 @@ -[Dolphin] -Timestamp=2010,6,21,13,13,23 -ViewMode=1 diff --git a/plugins/paintops/libpaintop/forms/wdgcurveoption.ui b/plugins/paintops/libpaintop/forms/wdgcurveoption.ui index afe1c2da3c..12798f93a2 100644 --- a/plugins/paintops/libpaintop/forms/wdgcurveoption.ui +++ b/plugins/paintops/libpaintop/forms/wdgcurveoption.ui @@ -1,251 +1,255 @@ WdgCurveOption 0 0 - 515 - 454 + 1110 + 658 - - - - - - 0 - 0 - - - - - 180 - 360 - - - - - 160 - 16777215 - - - - + 15 0 0 Strength: - + 0 0 20 20 16777215 16777215 - - - - Qt::Vertical + + + + + 0 + 0 + - + - 20 - 40 + 180 + 360 - - - - - - Enable Pen Settings + + + 160 + 16777215 + - - - - - + + + + + - - 0 + + 1 0 - - TextLabel - - - - - - - Qt::Vertical - - + - 20 - 40 + 0 + 230 - - - - - - - 0 - 0 - - - - TextLabel - - - - - - - - - - 0 - 0 - - - - TextLabel - - + + + + + + + 0 + 0 + + + + TextLabel + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + TextLabel + + + + - - - - Qt::Horizontal - - - - 40 - 20 - - - + + + + + + + 0 + 0 + + + + TextLabel + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + TextLabel + + + + - - - - - 0 - 0 - - + + - TextLabel + Share curve across all settings + + + true - - - - - 1 - 0 - + + + + Qt::Horizontal - + - 0 - 230 + 40 + 20 - + - - - - Share curve across all settings + + + + Qt::Vertical - - true + + + 20 + 40 + - + - - - - Qt::Vertical - - - - 20 - 40 - + + + + Enable Pen Settings - + KisDoubleSliderSpinBox QWidget
kis_slider_spin_box.h
1
KisMultiSensorsSelector QWidget
kis_multi_sensors_selector.h
1
KisCurveWidget QWidget
widgets/kis_curve_widget.h
1
diff --git a/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp b/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp index 3d49403666..242a38d979 100644 --- a/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp +++ b/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp @@ -1,179 +1,179 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_brush_based_paintop.h" #include "kis_properties_configuration.h" #include #include "kis_brush_option.h" #include #include "kis_painter.h" #include #include #include #include #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND Q_GLOBAL_STATIC(TextBrushInitializationWorkaround, s_instance) TextBrushInitializationWorkaround *TextBrushInitializationWorkaround::instance() { return s_instance; } void TextBrushInitializationWorkaround::preinitialize(const KisPropertiesConfiguration *settings) { if (KisBrushOption::isTextBrush(settings)) { KisBrushOption brushOption; - brushOption.readOptionSetting(settings); + brushOption.readOptionSetting(settings, true); m_brush = brushOption.brush(); m_settings = settings; } else { m_brush = 0; m_settings = 0; } } KisBrushSP TextBrushInitializationWorkaround::tryGetBrush(const KisPropertiesConfiguration *settings) { return settings && settings == m_settings ? m_brush : 0; } TextBrushInitializationWorkaround::TextBrushInitializationWorkaround() : m_settings(0) {} TextBrushInitializationWorkaround::~TextBrushInitializationWorkaround() {} void KisBrushBasedPaintOp::preinitializeOpStatically(const KisPaintOpSettingsSP settings) { TextBrushInitializationWorkaround::instance()->preinitialize(settings.data()); } #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ KisBrushBasedPaintOp::KisBrushBasedPaintOp(const KisPropertiesConfiguration* settings, KisPainter* painter) : KisPaintOp(painter), m_textureProperties(painter->device()->defaultBounds()->currentLevelOfDetail()) { Q_ASSERT(settings); #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND m_brush = TextBrushInitializationWorkaround::instance()->tryGetBrush(settings); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ if (!m_brush) { KisBrushOption brushOption; - brushOption.readOptionSetting(settings); + brushOption.readOptionSetting(settings, true); m_brush = brushOption.brush(); } m_brush->notifyStrokeStarted(); m_precisionOption.readOptionSetting(settings); m_dabCache = new KisDabCache(m_brush); m_dabCache->setPrecisionOption(&m_precisionOption); m_mirrorOption.readOptionSetting(settings); m_dabCache->setMirrorPostprocessing(&m_mirrorOption); m_textureProperties.fillProperties(settings); m_dabCache->setTexturePostprocessing(&m_textureProperties); } KisBrushBasedPaintOp::~KisBrushBasedPaintOp() { delete m_dabCache; } bool KisBrushBasedPaintOp::checkSizeTooSmall(qreal scale) { scale *= m_brush->scale(); return (scale * m_brush->width() < 0.01 || scale * m_brush->height() < 0.01); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation) const { // we parse dab rotation separately, so don't count it QSizeF metric = m_brush->characteristicSize(scale, scale, 0); return effectiveSpacing(metric.width(), metric.height(), 1.0, false, rotation); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation, const KisPressureSpacingOption &spacingOption, const KisPaintInformation &pi) const { qreal extraSpacingScale = 1.0; if (spacingOption.isChecked()) { extraSpacingScale = spacingOption.apply(pi); } // we parse dab rotation separately, so don't count it QSizeF metric = m_brush->characteristicSize(scale, scale, 0); return effectiveSpacing(metric.width(), metric.height(), extraSpacingScale, spacingOption.isotropicSpacing(), rotation); } inline qreal KisBrushBasedPaintOp::calcAutoSpacing(qreal value, qreal coeff) { return coeff * (value < 1.0 ? value : sqrt(value)); } QPointF KisBrushBasedPaintOp::calcAutoSpacing(const QPointF &pt, qreal coeff) const { const qreal lodScale = KisLodTransform::lodToScale(painter()->device()); const qreal invLodScale = 1.0 / lodScale; const QPointF lod0Point = invLodScale * pt; return lodScale * QPointF(calcAutoSpacing(lod0Point.x(), coeff), calcAutoSpacing(lod0Point.y(), coeff)); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal dabWidth, qreal dabHeight, qreal extraScale, bool isotropicSpacing, qreal rotation) const { QPointF spacing; if (!isotropicSpacing) { if (m_brush->autoSpacingActive()) { spacing = calcAutoSpacing(QPointF(dabWidth, dabHeight), m_brush->autoSpacingCoeff()); } else { spacing = QPointF(dabWidth, dabHeight); spacing *= m_brush->spacing(); } } else { qreal significantDimension = qMax(dabWidth, dabHeight); if (m_brush->autoSpacingActive()) { significantDimension = calcAutoSpacing(significantDimension, m_brush->autoSpacingCoeff()); } else { significantDimension *= m_brush->spacing(); } spacing = QPointF(significantDimension, significantDimension); rotation = 0.0; } spacing *= extraScale; return KisSpacingInformation(spacing, rotation); } bool KisBrushBasedPaintOp::canPaint() const { return m_brush != 0; } diff --git a/plugins/paintops/libpaintop/kis_brush_chooser.cpp b/plugins/paintops/libpaintop/kis_brush_chooser.cpp index 156354ba3b..aa7e24c4bd 100644 --- a/plugins/paintops/libpaintop/kis_brush_chooser.cpp +++ b/plugins/paintops/libpaintop/kis_brush_chooser.cpp @@ -1,363 +1,363 @@ /* * Copyright (c) 2004 Adrian Page * Copyright (c) 2009 Sven Langkamp * Copyright (c) 2010 Cyrille Berger * Copyright (c) 2010 Lukáš Tvrdý * Copyright (C) 2011 Srikanth Tiyyagura * * This program is free software; you can redistribute 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_chooser.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_brush_registry.h" #include "kis_brush_server.h" #include "widgets/kis_slider_spin_box.h" #include "widgets/kis_multipliers_double_slider_spinbox.h" #include "kis_spacing_selection_widget.h" #include "kis_signals_blocker.h" #include "kis_custom_brush_widget.h" #include "kis_clipboard_brush_widget.h" #include "kis_global.h" #include "kis_gbr_brush.h" #include "kis_debug.h" #include "kis_image.h" /// The resource item delegate for rendering the resource preview class KisBrushDelegate : public QAbstractItemDelegate { public: KisBrushDelegate(QObject * parent = 0) : QAbstractItemDelegate(parent) {} virtual ~KisBrushDelegate() {} /// reimplemented virtual void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const; /// reimplemented QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex &) const { return option.decorationSize; } }; void KisBrushDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { if (! index.isValid()) return; KisBrush *brush = static_cast(index.internalPointer()); QRect itemRect = option.rect; QImage thumbnail = brush->image(); if (thumbnail.height() > itemRect.height() || thumbnail.width() > itemRect.width()) { - thumbnail = thumbnail.scaled(itemRect.size() , Qt::KeepAspectRatio); + thumbnail = thumbnail.scaled(itemRect.size() , Qt::KeepAspectRatio, Qt::SmoothTransformation); } painter->save(); int dx = (itemRect.width() - thumbnail.width()) / 2; int dy = (itemRect.height() - thumbnail.height()) / 2; painter->drawImage(itemRect.x() + dx, itemRect.y() + dy, thumbnail); if (option.state & QStyle::State_Selected) { painter->setPen(QPen(option.palette.highlight(), 2.0)); painter->drawRect(option.rect); painter->setCompositionMode(QPainter::CompositionMode_HardLight); painter->setOpacity(0.65); painter->fillRect(option.rect, option.palette.highlight()); } painter->restore(); } KisBrushChooser::KisBrushChooser(QWidget *parent, const char *name) : QWidget(parent), m_stampBrushWidget(0), m_clipboardBrushWidget(0) { setObjectName(name); m_lbSize = new QLabel(i18n("Size:"), this); m_slSize = new KisDoubleSliderSpinBox(this); m_slSize->setRange(0, 1000, 2); m_slSize->setValue(5); m_slSize->setExponentRatio(3.0); m_slSize->setSuffix(i18n(" px")); m_slSize->setExponentRatio(3.0); QObject::connect(m_slSize, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetItemSize(qreal))); m_lbRotation = new QLabel(i18n("Rotation:"), this); m_slRotation = new KisDoubleSliderSpinBox(this); m_slRotation->setRange(0, 360, 0); m_slRotation->setValue(0); m_slRotation->setSuffix(QChar(Qt::Key_degree)); QObject::connect(m_slRotation, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetItemRotation(qreal))); m_lbSpacing = new QLabel(i18n("Spacing:"), this); m_slSpacing = new KisSpacingSelectionWidget(this); m_slSpacing->setSpacing(true, 1.0); connect(m_slSpacing, SIGNAL(sigSpacingChanged()), SLOT(slotSpacingChanged())); m_chkColorMask = new QCheckBox(i18n("Use color as mask"), this); QObject::connect(m_chkColorMask, SIGNAL(toggled(bool)), this, SLOT(slotSetItemUseColorAsMask(bool))); m_lbName = new QLabel(this); KisBrushResourceServer* rServer = KisBrushServer::instance()->brushServer(); QSharedPointer adapter(new KisBrushResourceServerAdapter(rServer)); m_itemChooser = new KoResourceItemChooser(adapter, this); m_itemChooser->showTaggingBar(true); m_itemChooser->setColumnCount(10); m_itemChooser->setRowHeight(30); m_itemChooser->setItemDelegate(new KisBrushDelegate(this)); m_itemChooser->setCurrentItem(0, 0); m_itemChooser->setSynced(true); connect(m_itemChooser, SIGNAL(resourceSelected(KoResource *)), this, SLOT(update(KoResource *))); QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->setObjectName("main layout"); mainLayout->addWidget(m_lbName); mainLayout->addWidget(m_itemChooser, 10); QPushButton *stampButton = new QPushButton(KisIconUtils::loadIcon("list-add"), i18n("Stamp"), this); QPushButton *clipboardButton = new QPushButton(KisIconUtils::loadIcon("edit-paste"), i18n("Clipboard"), this); stampButton->setToolTip(i18n("Creates a brush tip from the current image selection." "\n If no selection is present the whole image will be used.")); clipboardButton->setToolTip(i18n("Creates a brush tip from the image in the clipboard.")); m_itemChooser->addCustomButton(stampButton, 2); m_itemChooser->addCustomButton(clipboardButton, 3); connect(stampButton, SIGNAL(clicked()), this, SLOT(slotOpenStampBrush())); connect(clipboardButton, SIGNAL(clicked()), SLOT(slotOpenClipboardBrush())); QGridLayout *spacingLayout = new QGridLayout(); spacingLayout->setObjectName("spacing grid layout"); mainLayout->addLayout(spacingLayout, 1); spacingLayout->addWidget(m_lbSize, 1, 0); spacingLayout->addWidget(m_slSize, 1, 1); spacingLayout->addWidget(m_lbRotation, 2, 0); spacingLayout->addWidget(m_slRotation, 2, 1); spacingLayout->addWidget(m_lbSpacing, 3, 0); spacingLayout->addWidget(m_slSpacing, 3, 1); spacingLayout->setColumnStretch(1, 3); QPushButton *resetBrushButton = new QPushButton(i18n("Reset Predefined Tip"), this); resetBrushButton->setToolTip(i18n("Reloads Spacing from file\nSets Scale to 1.0\nSets Rotation to 0.0")); connect(resetBrushButton, SIGNAL(clicked()), SLOT(slotResetBrush())); QHBoxLayout *resetHLayout = new QHBoxLayout(); resetHLayout->addWidget(m_chkColorMask, 0); resetHLayout->addWidget(resetBrushButton, 0, Qt::AlignRight); spacingLayout->addLayout(resetHLayout, 4, 0, 1, 2); update(m_itemChooser->currentResource()); } KisBrushChooser::~KisBrushChooser() { } void KisBrushChooser::setBrush(KisBrushSP _brush) { m_itemChooser->setCurrentResource(_brush.data()); update(_brush.data()); } void KisBrushChooser::slotResetBrush() { KisBrush *brush = dynamic_cast(m_itemChooser->currentResource()); if (brush) { brush->load(); brush->setScale(1.0); brush->setAngle(0.0); slotActivatedBrush(brush); update(brush); emit sigBrushChanged(); } } void KisBrushChooser::slotSetItemSize(qreal sizeValue) { KisBrush *brush = dynamic_cast(m_itemChooser->currentResource()); if (brush) { int brushWidth = brush->width(); brush->setScale(sizeValue / qreal(brushWidth)); slotActivatedBrush(brush); emit sigBrushChanged(); } } void KisBrushChooser::slotSetItemRotation(qreal rotationValue) { KisBrush *brush = dynamic_cast(m_itemChooser->currentResource()); if (brush) { brush->setAngle(rotationValue / 180.0 * M_PI); slotActivatedBrush(brush); emit sigBrushChanged(); } } void KisBrushChooser::slotSpacingChanged() { KisBrush *brush = dynamic_cast(m_itemChooser->currentResource()); if (brush) { brush->setSpacing(m_slSpacing->spacing()); brush->setAutoSpacing(m_slSpacing->autoSpacingActive(), m_slSpacing->autoSpacingCoeff()); slotActivatedBrush(brush); emit sigBrushChanged(); } } void KisBrushChooser::slotSetItemUseColorAsMask(bool useColorAsMask) { KisGbrBrush *brush = dynamic_cast(m_itemChooser->currentResource()); if (brush) { brush->setUseColorAsMask(useColorAsMask); slotActivatedBrush(brush); emit sigBrushChanged(); } } void KisBrushChooser::slotOpenStampBrush() { if(!m_stampBrushWidget) { m_stampBrushWidget = new KisCustomBrushWidget(this, i18n("Stamp"), m_image); m_stampBrushWidget->setModal(false); connect(m_stampBrushWidget, SIGNAL(sigNewPredefinedBrush(KoResource *)), SLOT(slotNewPredefinedBrush(KoResource *))); } QDialog::DialogCode result = (QDialog::DialogCode)m_stampBrushWidget->exec(); if(result) { update(m_itemChooser->currentResource()); } } void KisBrushChooser::slotOpenClipboardBrush() { if(!m_clipboardBrushWidget) { m_clipboardBrushWidget = new KisClipboardBrushWidget(this, i18n("Clipboard"), m_image); m_clipboardBrushWidget->setModal(true); connect(m_clipboardBrushWidget, SIGNAL(sigNewPredefinedBrush(KoResource *)), SLOT(slotNewPredefinedBrush(KoResource *))); } QDialog::DialogCode result = (QDialog::DialogCode)m_clipboardBrushWidget->exec(); if(result) { update(m_itemChooser->currentResource()); } } void KisBrushChooser::update(KoResource * resource) { KisBrush* brush = dynamic_cast(resource); if (brush) { QString text = QString("%1 (%2 x %3)") .arg(i18n(brush->name().toUtf8().data())) .arg(brush->width()) .arg(brush->height()); m_lbName->setText(text); m_slSpacing->setSpacing(brush->autoSpacingActive(), brush->autoSpacingActive() ? brush->autoSpacingCoeff() : brush->spacing()); m_slRotation->setValue(brush->angle() * 180 / M_PI); m_slSize->setValue(brush->width() * brush->scale()); // useColorAsMask support is only in gimp brush so far KisGbrBrush *gimpBrush = dynamic_cast(resource); if (gimpBrush) { m_chkColorMask->setChecked(gimpBrush->useColorAsMask()); } m_chkColorMask->setEnabled(brush->hasColor() && gimpBrush); slotActivatedBrush(brush); emit sigBrushChanged(); } } void KisBrushChooser::slotActivatedBrush(KoResource * resource) { KisBrush* brush = dynamic_cast(resource); if (m_brush != brush) { if (m_brush) { m_brush->clearBrushPyramid(); } m_brush = brush; if (m_brush) { m_brush->prepareBrushPyramid(); } } } void KisBrushChooser::slotNewPredefinedBrush(KoResource *resource) { m_itemChooser->setCurrentResource(resource); update(resource); } void KisBrushChooser::setBrushSize(qreal xPixels, qreal yPixels) { Q_UNUSED(yPixels); qreal oldWidth = m_brush->width() * m_brush->scale(); qreal newWidth = oldWidth + xPixels; newWidth = qMax(newWidth, qreal(0.1)); m_slSize->setValue(newWidth); } void KisBrushChooser::setImage(KisImageWSP image) { m_image = image; } #include "moc_kis_brush_chooser.cpp" diff --git a/plugins/paintops/libpaintop/kis_brush_option.cpp b/plugins/paintops/libpaintop/kis_brush_option.cpp index e9f0bcc661..6ec520e054 100644 --- a/plugins/paintops/libpaintop/kis_brush_option.cpp +++ b/plugins/paintops/libpaintop/kis_brush_option.cpp @@ -1,92 +1,92 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2008 * Copyright (C) Sven Langkamp , (C) 2008 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_brush_option.h" #include #include #include "kis_properties_configuration.h" void KisBrushOption::writeOptionSetting(KisPropertiesConfiguration* setting) const { if (!m_brush) return; QDomDocument d; QDomElement e = d.createElement("Brush"); m_brush->toXML(d, e); d.appendChild(e); setting->setProperty("brush_definition", d.toString()); QString brushFileName = !m_brush->filename().isEmpty() ? m_brush->shortFilename() : QString(); setting->setProperty("requiredBrushFile", brushFileName); } QDomElement getBrushXMLElement(const KisPropertiesConfiguration* setting) { QDomElement element; QString brushDefinition = setting->getString("brush_definition"); if (!brushDefinition.isEmpty()) { QDomDocument d; d.setContent(brushDefinition, false); element = d.firstChildElement("Brush"); } return element; } -void KisBrushOption::readOptionSetting(const KisPropertiesConfiguration* setting) +void KisBrushOption::readOptionSetting(const KisPropertiesConfiguration* setting, bool forceCopy) { QDomElement element = getBrushXMLElement(setting); if (!element.isNull()) { - m_brush = KisBrush::fromXML(element); + m_brush = KisBrush::fromXML(element, forceCopy); } } #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND #include "kis_text_brush_factory.h" bool KisBrushOption::isTextBrush(const KisPropertiesConfiguration* setting) { static QString textBrushId = KisTextBrushFactory().id(); QDomElement element = getBrushXMLElement(setting); QString brushType = element.attribute("type"); return brushType == textBrushId; } #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ KisBrushSP KisBrushOption::brush() const { return m_brush; } void KisBrushOption::setBrush(KisBrushSP brush) { m_brush = brush; } diff --git a/plugins/paintops/libpaintop/kis_brush_option.h b/plugins/paintops/libpaintop/kis_brush_option.h index 53d2cee8f5..89c5c72deb 100644 --- a/plugins/paintops/libpaintop/kis_brush_option.h +++ b/plugins/paintops/libpaintop/kis_brush_option.h @@ -1,48 +1,48 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2008 * Copyright (C) Sven Langkamp , (C) 2008 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_BRUSH_OPTION_H_ #define KIS_BRUSH_OPTION_H_ #include "kis_brush.h" #include #include class KisPropertiesConfiguration; class PAINTOP_EXPORT KisBrushOption { public: void writeOptionSetting(KisPropertiesConfiguration* setting) const; - void readOptionSetting(const KisPropertiesConfiguration* setting); + void readOptionSetting(const KisPropertiesConfiguration* setting, bool forceCopy); KisBrushSP brush() const; void setBrush(KisBrushSP brush); #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND static bool isTextBrush(const KisPropertiesConfiguration* setting); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ private: KisBrushSP m_brush; }; #endif diff --git a/plugins/paintops/libpaintop/kis_brush_option_widget.cpp b/plugins/paintops/libpaintop/kis_brush_option_widget.cpp index 68c6546116..7ac9c0d6dd 100644 --- a/plugins/paintops/libpaintop/kis_brush_option_widget.cpp +++ b/plugins/paintops/libpaintop/kis_brush_option_widget.cpp @@ -1,118 +1,118 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2008 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_brush_option_widget.h" #include #include #include "kis_brush_selection_widget.h" #include "kis_brush.h" KisBrushOptionWidget::KisBrushOptionWidget() : KisPaintOpOption(KisPaintOpOption::GENERAL, true) { m_checkable = false; m_brushSelectionWidget = new KisBrushSelectionWidget(); connect(m_brushSelectionWidget, SIGNAL(sigPrecisionChanged()), SLOT(emitSettingChanged())); connect(m_brushSelectionWidget, SIGNAL(sigBrushChanged()), SLOT(brushChanged())); m_brushSelectionWidget->hide(); setConfigurationPage(m_brushSelectionWidget); m_brushOption.setBrush(brush()); setObjectName("KisBrushOptionWidget"); } KisBrushSP KisBrushOptionWidget::brush() const { return m_brushSelectionWidget->brush(); } void KisBrushOptionWidget::setAutoBrush(bool on) { m_brushSelectionWidget->setAutoBrush(on); } void KisBrushOptionWidget::setPredefinedBrushes(bool on) { m_brushSelectionWidget->setPredefinedBrushes(on); } void KisBrushOptionWidget::setCustomBrush(bool on) { m_brushSelectionWidget->setCustomBrush(on); } void KisBrushOptionWidget::setTextBrush(bool on) { m_brushSelectionWidget->setTextBrush(on); } void KisBrushOptionWidget::setImage(KisImageWSP image) { m_brushSelectionWidget->setImage(image); } void KisBrushOptionWidget::setPrecisionEnabled(bool value) { m_brushSelectionWidget->setPrecisionEnabled(value); } void KisBrushOptionWidget::writeOptionSetting(KisPropertiesConfiguration* settings) const { m_brushSelectionWidget->writeOptionSetting(settings); m_brushOption.writeOptionSetting(settings); } void KisBrushOptionWidget::readOptionSetting(const KisPropertiesConfiguration* setting) { m_brushSelectionWidget->readOptionSetting(setting); - m_brushOption.readOptionSetting(setting); + m_brushOption.readOptionSetting(setting, false); m_brushSelectionWidget->setCurrentBrush(m_brushOption.brush()); } void KisBrushOptionWidget::lodLimitations(KisPaintopLodLimitations *l) const { KisBrushSP brush = this->brush(); brush->lodLimitations(l); } void KisBrushOptionWidget::setBrushSize(qreal dxPixels, qreal dyPixels) { m_brushSelectionWidget->setBrushSize(dxPixels, dyPixels); } QSizeF KisBrushOptionWidget::brushSize() const { return m_brushSelectionWidget->brushSize(); } void KisBrushOptionWidget::brushChanged() { m_brushOption.setBrush(brush()); emitSettingChanged(); } bool KisBrushOptionWidget::presetIsValid() { return m_brushSelectionWidget->presetIsValid(); } #include "moc_kis_brush_option_widget.cpp" diff --git a/plugins/paintops/libpaintop/kis_clipboard_brush_widget.cpp b/plugins/paintops/libpaintop/kis_clipboard_brush_widget.cpp index 13c7731351..e2346f550c 100644 --- a/plugins/paintops/libpaintop/kis_clipboard_brush_widget.cpp +++ b/plugins/paintops/libpaintop/kis_clipboard_brush_widget.cpp @@ -1,159 +1,159 @@ /* * Copyright (c) 2005 Bart Coppens * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2013 Somsubhra Bairi * * This program is free software; you can redistribute 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_clipboard_brush_widget.h" #include #include #include #include #include #include #include #include "kis_image.h" #include "kis_clipboard.h" #include "kis_paint_device.h" #include "kis_gbr_brush.h" #include "kis_brush_server.h" KisClipboardBrushWidget::KisClipboardBrushWidget(QWidget *parent, const QString &caption, KisImageWSP image) : KisWdgClipboardBrush(parent), m_image(image) { setWindowTitle(caption); preview->setScaledContents(true); preview->setFixedSize(preview->size()); preview->setStyleSheet("border: 2px solid #222; border-radius: 4px; padding: 5px; font: normal 10px;"); KisBrushResourceServer* rServer = KisBrushServer::instance()->brushServer(); m_rServerAdapter = QSharedPointer(new KisBrushResourceServerAdapter(rServer)); m_brush = 0; m_clipboard = KisClipboard::instance(); connect(m_clipboard, SIGNAL(clipChanged()), this, SLOT(slotCreateBrush())); connect(colorAsmask, SIGNAL(toggled(bool)), this, SLOT(slotUpdateUseColorAsMask(bool))); connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotAddPredefined())); spacingWidget->setSpacing(true, 1.0); connect(spacingWidget, SIGNAL(sigSpacingChanged()), SLOT(slotSpacingChanged())); } KisClipboardBrushWidget::~KisClipboardBrushWidget() { } void KisClipboardBrushWidget::slotCreateBrush() { // do nothing if it's hidden otherwise it can break the active brush is something is copied if (m_clipboard->hasClip() && !isHidden()) { pd = m_clipboard->clip(QRect(0, 0, 0, 0), false); //Weird! Don't know how this works! if (pd) { QRect rc = pd->exactBounds(); m_brush = new KisGbrBrush(pd, rc.x(), rc.y(), rc.width(), rc.height()); m_brush->setSpacing(spacingWidget->spacing()); m_brush->setAutoSpacing(spacingWidget->autoSpacingActive(), spacingWidget->autoSpacingCoeff()); m_brush->setFilename(TEMPORARY_CLIPBOARD_BRUSH_FILENAME); m_brush->setName(TEMPORARY_CLIPBOARD_BRUSH_NAME); m_brush->setValid(true); preview->setPixmap(QPixmap::fromImage(m_brush->image())); } } else { preview->setText(i18n("Nothing copied\n to Clipboard")); } if(m_brush == 0) { buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); } else { buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); } } void KisClipboardBrushWidget::slotSpacingChanged() { if (m_brush) { m_brush->setSpacing(spacingWidget->spacing()); m_brush->setAutoSpacing(spacingWidget->autoSpacingActive(), spacingWidget->autoSpacingCoeff()); } } void KisClipboardBrushWidget::showEvent(QShowEvent *) { slotCreateBrush(); } void KisClipboardBrushWidget::slotUpdateUseColorAsMask(bool useColorAsMask) { if (m_brush) { static_cast(m_brush.data())->setUseColorAsMask(useColorAsMask); preview->setPixmap(QPixmap::fromImage(m_brush->brushTipImage())); } } void KisClipboardBrushWidget::slotAddPredefined() { if(!m_brush) return; QString dir = KoResourcePaths::saveLocation("data", "brushes"); QString extension = ".gbr"; QString name = nameEdit->text(); QString tempFileName; QFileInfo fileInfo; fileInfo.setFile(dir + name + extension); int i = 1; while (fileInfo.exists()) { fileInfo.setFile(dir + name + QString("%1").arg(i) + extension); i++; } tempFileName = fileInfo.filePath(); if (m_rServerAdapter) { - KisGbrBrush* resource = static_cast(m_brush.data())->clone(); + KisGbrBrush *resource = dynamic_cast(m_brush->clone()); resource->setFilename(tempFileName); if (nameEdit->text().isEmpty()) { resource->setName(QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm")); } else { resource->setName(name); } if (colorAsmask->isChecked()) { resource->makeMaskImage(); } m_rServerAdapter->addResource(resource); emit sigNewPredefinedBrush(resource); } close(); } #include "moc_kis_clipboard_brush_widget.cpp" diff --git a/plugins/paintops/libpaintop/kis_custom_brush_widget.cpp b/plugins/paintops/libpaintop/kis_custom_brush_widget.cpp index 84a83e257a..65ead643ee 100644 --- a/plugins/paintops/libpaintop/kis_custom_brush_widget.cpp +++ b/plugins/paintops/libpaintop/kis_custom_brush_widget.cpp @@ -1,250 +1,251 @@ /* * Copyright (c) 2005 Bart Coppens * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_custom_brush_widget.h" #include #include #include #include #include #include #include #include #include #include #include "kis_image.h" #include "kis_layer.h" #include "kis_paint_device.h" #include "kis_gbr_brush.h" #include "kis_imagepipe_brush.h" #include #include "kis_brush_server.h" #include "kis_paint_layer.h" #include "kis_group_layer.h" #include #include #include "kis_iterator_ng.h" KisCustomBrushWidget::KisCustomBrushWidget(QWidget *parent, const QString& caption, KisImageWSP image) : KisWdgCustomBrush(parent) , m_image(image) { setWindowTitle(caption); preview->setScaledContents(true); preview->setFixedSize(preview->size()); preview->setStyleSheet("border: 2px solid #222; border-radius: 4px; padding: 5px; font: normal 10px;"); KisBrushResourceServer* rServer = KisBrushServer::instance()->brushServer(); m_rServerAdapter = QSharedPointer(new KisBrushResourceServerAdapter(rServer)); m_brush = 0; connect(this, SIGNAL(accepted()), SLOT(slotAddPredefined())); connect(brushStyle, SIGNAL(activated(int)), this, SLOT(slotUpdateCurrentBrush(int))); connect(colorAsMask, SIGNAL(toggled(bool)), this, SLOT(slotUpdateUseColorAsMask(bool))); spacingWidget->setSpacing(true, 1.0); connect(spacingWidget, SIGNAL(sigSpacingChanged()), SLOT(slotSpacingChanged())); } KisCustomBrushWidget::~KisCustomBrushWidget() { } KisBrushSP KisCustomBrushWidget::brush() { return m_brush; } void KisCustomBrushWidget::showEvent(QShowEvent *) { slotUpdateCurrentBrush(0); } void KisCustomBrushWidget::slotUpdateCurrentBrush(int) { if (brushStyle->currentIndex() == 0) { comboBox2->setEnabled(false); } else { comboBox2->setEnabled(true); } if (m_image) { createBrush(); if (m_brush) { preview->setPixmap(QPixmap::fromImage(m_brush->brushTipImage())); } } } void KisCustomBrushWidget::slotSpacingChanged() { if (m_brush) { m_brush->setSpacing(spacingWidget->spacing()); m_brush->setAutoSpacing(spacingWidget->autoSpacingActive(), spacingWidget->autoSpacingCoeff()); } } void KisCustomBrushWidget::slotUpdateUseColorAsMask(bool useColorAsMask) { if (m_brush) { static_cast(m_brush.data())->setUseColorAsMask(useColorAsMask); preview->setPixmap(QPixmap::fromImage(m_brush->brushTipImage())); } } void KisCustomBrushWidget::slotAddPredefined() { QString dir = KoResourcePaths::saveLocation("data", "brushes"); QString extension; if (brushStyle->currentIndex() == 0) { extension = ".gbr"; } else { extension = ".gih"; } QString name = nameLineEdit->text(); QString tempFileName; { QFileInfo fileInfo; fileInfo.setFile(dir + name + extension); int i = 1; while (fileInfo.exists()) { fileInfo.setFile(dir + name + QString("%1").arg(i) + extension); i++; } tempFileName = fileInfo.filePath(); } // Add it to the brush server, so that it automatically gets to the mediators, and // so to the other brush choosers can pick it up, if they want to - if (m_rServerAdapter) { - KisGbrBrush * resource = static_cast(m_brush.data())->clone(); + if (m_rServerAdapter && m_brush) { + qDebug() << "m_brush" << m_brush; + KisGbrBrush *resource = dynamic_cast(m_brush->clone()); resource->setFilename(tempFileName); if (nameLineEdit->text().isEmpty()) { resource->setName(QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm")); } else { resource->setName(name); } if (colorAsMask->isChecked()) { resource->makeMaskImage(); } m_rServerAdapter->addResource(resource); emit sigNewPredefinedBrush(resource); } close(); } void KisCustomBrushWidget::createBrush() { if (!m_image) return; if (brushStyle->currentIndex() == 0) { KisSelectionSP selection = m_image->globalSelection(); // create copy of the data m_image->lock(); KisPaintDeviceSP dev = new KisPaintDevice(*m_image->projection()); m_image->unlock(); if (!selection) { m_brush = new KisGbrBrush(dev, 0, 0, m_image->width(), m_image->height()); } else { // apply selection mask QRect r = selection->selectedExactRect(); dev->crop(r); KisHLineIteratorSP pixelIt = dev->createHLineIteratorNG(r.x(), r.top(), r.width()); KisHLineConstIteratorSP maskIt = selection->projection()->createHLineIteratorNG(r.x(), r.top(), r.width()); for (qint32 y = r.top(); y <= r.bottom(); ++y) { do { dev->colorSpace()->applyAlphaU8Mask(pixelIt->rawData(), maskIt->oldRawData(), 1); } while (pixelIt->nextPixel() && maskIt->nextPixel()); pixelIt->nextRow(); maskIt->nextRow(); } QRect rc = dev->exactBounds(); m_brush = new KisGbrBrush(dev, rc.x(), rc.y(), rc.width(), rc.height()); } } else { // For each layer in the current image, create a new image, and add it to the list QVector< QVector > devices; devices.push_back(QVector()); int w = m_image->width(); int h = m_image->height(); m_image->lock(); // We only loop over the rootLayer. Since we actually should have a layer selection // list, no need to elaborate on that here and now KoProperties properties; properties.setProperty("visible", true); QList layers = m_image->root()->childNodes(QStringList("KisLayer"), properties); KisNodeSP node; Q_FOREACH (KisNodeSP node, layers) { devices[0].push_back(node->projection().data()); } QVector modes; switch (comboBox2->currentIndex()) { case 0: modes.push_back(KisParasite::Constant); break; case 1: modes.push_back(KisParasite::Random); break; case 2: modes.push_back(KisParasite::Incremental); break; case 3: modes.push_back(KisParasite::Pressure); break; case 4: modes.push_back(KisParasite::Angular); break; default: modes.push_back(KisParasite::Incremental); } m_brush = new KisImagePipeBrush(m_image->objectName(), w, h, devices, modes); m_image->unlock(); } static_cast(m_brush.data())->setUseColorAsMask(colorAsMask->isChecked()); m_brush->setSpacing(spacingWidget->spacing()); m_brush->setAutoSpacing(spacingWidget->autoSpacingActive(), spacingWidget->autoSpacingCoeff()); m_brush->setFilename(TEMPORARY_FILENAME); m_brush->setName(TEMPORARY_BRUSH_NAME); m_brush->setValid(true); } diff --git a/plugins/paintops/sketch/kis_sketch_paintop.cpp b/plugins/paintops/sketch/kis_sketch_paintop.cpp index cee5507b54..9a6ca52713 100644 --- a/plugins/paintops/sketch/kis_sketch_paintop.cpp +++ b/plugins/paintops/sketch/kis_sketch_paintop.cpp @@ -1,302 +1,302 @@ /* * 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 "kis_lod_transform.h" #include /* * Based on Harmony project http://github.com/mrdoob/harmony/ */ // chrome : diff 0.2, sketchy : 0.3, fur: 0.5 // fur : distance / thresholdDistance // shaded: opacity per line :/ // ((1 - (d / 1000)) * 0.1 * BRUSH_PRESSURE), offset == 0 // chrome: color per line :/ //this.context.strokeStyle = "rgba(" + Math.floor(Math.random() * COLOR[0]) + ", " + Math.floor(Math.random() * COLOR[1]) + ", " + Math.floor(Math.random() * COLOR[2]) + ", " + 0.1 * BRUSH_PRESSURE + " )"; // long fur // from: count + offset * -random // to: i point - (offset * -random) + random * 2 // probability distance / thresholdDistnace // shaded: probabity : paint always - 0.0 density KisSketchPaintOp::KisSketchPaintOp(const KisSketchPaintOpSettings *settings, KisPainter * painter, KisNodeSP node, KisImageSP image) : KisPaintOp(painter) { Q_UNUSED(image); Q_UNUSED(node); m_opacityOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); m_rotationOption.readOptionSetting(settings); m_sketchProperties.readOptionSetting(settings); - m_brushOption.readOptionSetting(settings); + m_brushOption.readOptionSetting(settings, true); 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_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(), scale, scale, 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) { Q_UNUSED(currentDistance); 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); } setCurrentScale(scale); setCurrentRotation(rotation); 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) { KisDistanceInformation di; paintLine(info, info, &di); return di.currentSpacing(); } diff --git a/plugins/paintops/spray/kis_spray_paintop.cpp b/plugins/paintops/spray/kis_spray_paintop.cpp index 5abf50f20f..f01eafa759 100644 --- a/plugins/paintops/spray/kis_spray_paintop.cpp +++ b/plugins/paintops/spray/kis_spray_paintop.cpp @@ -1,133 +1,133 @@ /* * Copyright (c) 2008-2012 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_spray_paintop.h" #include "kis_spray_paintop_settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include KisSprayPaintOp::KisSprayPaintOp(const KisSprayPaintOpSettings *settings, KisPainter *painter, KisNodeSP node, KisImageSP image) : KisPaintOp(painter) , m_settings(settings) , m_isPresetValid(true) , m_node(node) { Q_ASSERT(settings); Q_ASSERT(painter); Q_UNUSED(image); m_rotationOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); m_rotationOption.resetAllSensors(); m_opacityOption.resetAllSensors(); m_sizeOption.resetAllSensors(); - m_brushOption.readOptionSetting(settings); + m_brushOption.readOptionSetting(settings, true); m_colorProperties.fillProperties(settings); m_properties.loadSettings(settings); // first load tip properties as shape properties are dependent on diameter/scale/aspect m_shapeProperties.loadSettings(settings, m_properties.diameter * m_properties.scale, m_properties.diameter * m_properties.aspect * m_properties.scale); // TODO: what to do with proportional sizes? m_shapeDynamicsProperties.loadSettings(settings); if (!m_shapeProperties.enabled && !m_brushOption.brush()) { // in case the preset does not contain the definition for KisBrush m_isPresetValid = false; dbgKrita << "Preset is not valid. Painting is not possible. Use the preset editor to fix current brush engine preset."; } m_sprayBrush.setProperties(&m_properties, &m_colorProperties, &m_shapeProperties, &m_shapeDynamicsProperties, m_brushOption.brush()); m_sprayBrush.setFixedDab(cachedDab()); // spacing if ((m_properties.diameter * 0.5) > 1) { m_ySpacing = m_xSpacing = m_properties.diameter * 0.5 * m_properties.spacing; } else { m_ySpacing = m_xSpacing = 1.0; } m_spacing = m_xSpacing; } KisSprayPaintOp::~KisSprayPaintOp() { } KisSpacingInformation KisSprayPaintOp::paintAt(const KisPaintInformation& info) { if (!painter() || !m_isPresetValid) { return KisSpacingInformation(m_spacing); } if (!m_dab) { m_dab = source()->createCompositionSourceDevice(); } else { m_dab->clear(); } qreal rotation = m_rotationOption.apply(info); quint8 origOpacity = m_opacityOption.apply(painter(), info); // Spray Brush is capable of working with zero scale, // so no additional checks for 'zero'ness are needed const qreal scale = m_sizeOption.apply(info); const qreal additionalScale = KisLodTransform::lodToScale(painter()->device()); setCurrentRotation(rotation); setCurrentScale(scale * additionalScale); m_sprayBrush.paint(m_dab, m_node->paintDevice(), info, rotation, scale, additionalScale, painter()->paintColor(), painter()->backgroundColor()); QRect rc = m_dab->extent(); painter()->bitBlt(rc.topLeft(), m_dab, rc); painter()->renderMirrorMask(rc, m_dab); painter()->setOpacity(origOpacity); return KisSpacingInformation(m_spacing * additionalScale); } diff --git a/plugins/paintops/spray/kis_spray_shape_option.cpp b/plugins/paintops/spray/kis_spray_shape_option.cpp index f561201d8f..235a7b470c 100644 --- a/plugins/paintops/spray/kis_spray_shape_option.cpp +++ b/plugins/paintops/spray/kis_spray_shape_option.cpp @@ -1,202 +1,202 @@ /* * Copyright (c) 2008,2009,2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_spray_shape_option.h" #include #include #include #include #include #include "ui_wdgsprayshapeoptions.h" class KisShapeOptionsWidget: public QWidget, public Ui::WdgSprayShapeOptions { public: KisShapeOptionsWidget(QWidget *parent = 0) : QWidget(parent) { setupUi(this); - imageUrl->setMimeTypeFilters(KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Import)); + imageUrl->setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import)); } }; KisSprayShapeOption::KisSprayShapeOption() : KisPaintOpOption(KisPaintOpOption::GENERAL, true) { setObjectName("KisSprayShapeOption"); m_checkable = true; // save this to be able to restore it back m_maxSize = 1000; m_options = new KisShapeOptionsWidget(); m_useAspect = m_options->aspectButton->keepAspectRatio(); computeAspect(); //initializer slider values m_options->widthSpin->setRange(1, 1000, 0); m_options->widthSpin->setValue(6); m_options->widthSpin->setSuffix(i18n(" px")); m_options->heightSpin->setRange(1, 1000, 0); m_options->heightSpin->setValue(6); m_options->heightSpin->setSuffix(i18n(" px")); // UI signals connect(m_options->proportionalBox, SIGNAL(clicked(bool)), SLOT(changeSizeUI(bool))); connect(m_options->aspectButton, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(aspectToggled(bool))); connect(m_options->imageUrl, SIGNAL(textChanged(QString)), this, SLOT(prepareImage())); connect(m_options->widthSpin, SIGNAL(valueChanged(qreal)), SLOT(updateHeight(qreal))); connect(m_options->heightSpin, SIGNAL(valueChanged(qreal)), SLOT(updateWidth(qreal))); setupBrushPreviewSignals(); setConfigurationPage(m_options); } void KisSprayShapeOption::setupBrushPreviewSignals() { connect(m_options->proportionalBox, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_options->proportionalBox, SIGNAL(clicked(bool)), SLOT(emitSettingChanged())); connect(m_options->shapeBox, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged())); connect(m_options->widthSpin, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->heightSpin, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_options->aspectButton, SIGNAL(keepAspectRatioChanged(bool)), SLOT(emitSettingChanged())); connect(m_options->imageUrl, SIGNAL(textChanged(QString)), SLOT(emitSettingChanged())); } KisSprayShapeOption::~KisSprayShapeOption() { delete m_options; } int KisSprayShapeOption::shape() const { return m_options->shapeBox->currentIndex(); } void KisSprayShapeOption::writeOptionSetting(KisPropertiesConfiguration* setting) const { setting->setProperty(SPRAYSHAPE_ENABLED, isChecked()); setting->setProperty(SPRAYSHAPE_SHAPE, shape()); setting->setProperty(SPRAYSHAPE_USE_ASPECT, m_useAspect); setting->setProperty(SPRAYSHAPE_PROPORTIONAL, m_options->proportionalBox->isChecked()); setting->setProperty(SPRAYSHAPE_WIDTH, m_options->widthSpin->value()); setting->setProperty(SPRAYSHAPE_HEIGHT, m_options->heightSpin->value()); setting->setProperty(SPRAYSHAPE_IMAGE_URL, m_options->imageUrl->fileName()); } void KisSprayShapeOption::readOptionSetting(const KisPropertiesConfiguration* setting) { // in 2.2 there is not shape enabled so true by default setChecked(setting->getBool(SPRAYSHAPE_ENABLED, true)); m_options->shapeBox->setCurrentIndex(setting->getInt(SPRAYSHAPE_SHAPE)); m_options->proportionalBox->setChecked(setting->getBool(SPRAYSHAPE_PROPORTIONAL)); m_options->aspectButton->setKeepAspectRatio(setting->getBool(SPRAYSHAPE_USE_ASPECT, false)); m_options->widthSpin->setValue(setting->getInt(SPRAYSHAPE_WIDTH)); m_options->heightSpin->setValue(setting->getInt(SPRAYSHAPE_HEIGHT)); m_options->imageUrl->setFileName(setting->getString(SPRAYSHAPE_IMAGE_URL)); } void KisSprayShapeOption::prepareImage() { QString path = m_options->imageUrl->fileName(); if (QFile::exists(path)) { QImage image(path); if (!image.isNull()) { m_options->heightSpin->blockSignals(true); m_options->widthSpin->blockSignals(true); m_options->widthSpin->setValue(image.width()); m_options->heightSpin->setValue(image.height()); computeAspect(); m_options->heightSpin->blockSignals(false); m_options->widthSpin->blockSignals(false); } } } void KisSprayShapeOption::aspectToggled(bool toggled) { m_useAspect = toggled; } void KisSprayShapeOption::updateHeight(qreal value) { if (m_useAspect) { int newHeight = qRound(value * 1.0 / m_aspect); m_options->heightSpin->blockSignals(true); m_options->heightSpin->setValue(newHeight); m_options->heightSpin->blockSignals(false); } else { computeAspect(); } } void KisSprayShapeOption::updateWidth(qreal value) { if (m_useAspect) { int newWidth = qRound(value * m_aspect); m_options->widthSpin->blockSignals(true); m_options->widthSpin->setValue(newWidth); m_options->widthSpin->blockSignals(false); } else { computeAspect(); } } void KisSprayShapeOption::computeAspect() { qreal w = m_options->widthSpin->value(); qreal h = m_options->heightSpin->value(); m_aspect = w / h; } void KisSprayShapeOption::changeSizeUI(bool proportionalSize) { // if proportionalSize is false, pixel size is used if (!proportionalSize) { m_options->widthSpin->setMaximum(m_maxSize); m_options->widthSpin->setSuffix(i18n(" px")); m_options->heightSpin->setMaximum(m_maxSize); m_options->heightSpin->setSuffix(i18n(" px")); } else { m_options->widthSpin->setMaximum(100); m_options->widthSpin->setSuffix("%"); m_options->heightSpin->setMaximum(100); m_options->heightSpin->setSuffix("%"); } } diff --git a/plugins/paintops/spray/spray_brush.cpp b/plugins/paintops/spray/spray_brush.cpp index 566307be7a..bb35c5de18 100644 --- a/plugins/paintops/spray/spray_brush.cpp +++ b/plugins/paintops/spray/spray_brush.cpp @@ -1,520 +1,535 @@ /* * Copyright (c) 2008-2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "spray_brush.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_spray_paintop_settings.h" #include #include #include "random_gauss.h" #include SprayBrush::SprayBrush() { m_painter = 0; m_transfo = 0; m_rand = new RandomGauss(time(0)); } SprayBrush::~SprayBrush() { delete m_painter; delete m_transfo; delete m_rand; } +void SprayBrush::setProperties(KisSprayProperties * properties, + KisColorProperties * colorProperties, + KisShapeProperties * shapeProperties, + KisShapeDynamicsProperties * shapeDynamicsProperties, + KisBrushSP brush) +{ + m_properties = properties; + m_colorProperties = colorProperties; + m_shapeProperties = shapeProperties; + m_shapeDynamicsProperties = shapeDynamicsProperties; + m_brush = brush; + if (m_brush) { + m_brush->notifyStrokeStarted(); + } +} qreal SprayBrush::rotationAngle(KisRandomSourceSP randomSource) { qreal rotation = 0.0; if (m_shapeDynamicsProperties->fixedRotation) { rotation = deg2rad(m_shapeDynamicsProperties->fixedAngle); } if (m_shapeDynamicsProperties->randomRotation) { if (m_properties->gaussian) { rotation = linearInterpolation(rotation , M_PI * 2.0 * qBound(0.0, m_rand->nextGaussian(0.0, 0.50) , 1.0), m_shapeDynamicsProperties->randomRotationWeight); } else { rotation = linearInterpolation(rotation, M_PI * 2.0 * randomSource->generateNormalized(), m_shapeDynamicsProperties->randomRotationWeight); } } return rotation; } void SprayBrush::paint(KisPaintDeviceSP dab, KisPaintDeviceSP source, const KisPaintInformation& info, qreal rotation, qreal scale, qreal additionalScale, const KoColor &color, const KoColor &bgColor) { KisRandomSourceSP randomSource = info.randomSource(); // initializing painter if (!m_painter) { m_painter = new KisPainter(dab); m_painter->setFillStyle(KisPainter::FillStyleForegroundColor); m_painter->setMaskImageSize(m_shapeProperties->width, m_shapeProperties->height); m_dabPixelSize = dab->colorSpace()->pixelSize(); if (m_colorProperties->useRandomHSV) { m_transfo = dab->colorSpace()->createColorTransformation("hsv_adjustment", QHash()); } m_brushQImage = m_shapeProperties->image; if (!m_brushQImage.isNull()) { m_brushQImage = m_brushQImage.scaled(m_shapeProperties->width, m_shapeProperties->height); } m_imageDevice = new KisPaintDevice(dab->colorSpace()); } qreal x = info.pos().x(); qreal y = info.pos().y(); KisRandomAccessorSP accessor = dab->createRandomAccessorNG(qRound(x), qRound(y)); Q_ASSERT(color.colorSpace()->pixelSize() == dab->pixelSize()); m_inkColor = color; KisCrossDeviceColorPicker colorPicker(source, m_inkColor); // apply size sensor m_radius = m_properties->radius * scale * additionalScale; // jitter movement if (m_properties->jitterMovement) { x = x + ((2 * m_radius * randomSource->generateNormalized()) - m_radius) * m_properties->amount; y = y + ((2 * m_radius * randomSource->generateNormalized()) - m_radius) * m_properties->amount; } // this is wrong for every shape except pixel and anti-aliased pixel if (m_properties->useDensity) { m_particlesCount = (m_properties->coverage * (M_PI * pow2(m_radius)) / pow2(additionalScale)); } else { m_particlesCount = m_properties->particleCount; } QHash params; qreal nx, ny; int ix, iy; qreal angle; qreal length; qreal rotationZ = 0.0; qreal particleScale = 1.0; bool shouldColor = true; if (m_colorProperties->fillBackground) { m_painter->setPaintColor(bgColor); paintCircle(m_painter, x, y, m_radius); } QTransform m; m.reset(); m.rotateRadians(-rotation + deg2rad(m_properties->brushRotation)); m.scale(m_properties->scale, m_properties->scale); for (quint32 i = 0; i < m_particlesCount; i++) { // generate random angle angle = randomSource->generateNormalized() * M_PI * 2; // generate random length if (m_properties->gaussian) { length = qBound(0.0, m_rand->nextGaussian(0.0, 0.50) , 1.0); } else { length = randomSource->generateNormalized(); } if (m_shapeDynamicsProperties->enabled) { // rotation rotationZ = rotationAngle(randomSource); if (m_shapeDynamicsProperties->followCursor) { rotationZ = linearInterpolation(rotationZ, angle, m_shapeDynamicsProperties->followCursorWeigth); } if (m_shapeDynamicsProperties->followDrawingAngle) { rotationZ = linearInterpolation(rotationZ, info.drawingAngle(), m_shapeDynamicsProperties->followDrawingAngleWeight); } // random size - scale if (m_shapeDynamicsProperties->randomSize) { particleScale = randomSource->generateNormalized(); } } // generate polar coordinate nx = (m_radius * cos(angle) * length); ny = (m_radius * sin(angle) * length); // compute the height of the ellipse ny *= m_properties->aspect; // transform m.map(nx, ny, &nx, &ny); // color transformation if (shouldColor) { if (m_colorProperties->sampleInputColor) { colorPicker.pickOldColor(nx + x, ny + y, m_inkColor.data()); } // mix the color with background color if (m_colorProperties->mixBgColor) { KoMixColorsOp * mixOp = dab->colorSpace()->mixColorsOp(); const quint8 *colors[2]; colors[0] = m_inkColor.data(); colors[1] = bgColor.data(); qint16 colorWeights[2]; int MAX_16BIT = 255; qreal blend = info.pressure(); colorWeights[0] = static_cast(blend * MAX_16BIT); colorWeights[1] = static_cast((1.0 - blend) * MAX_16BIT); mixOp->mixColors(colors, colorWeights, 2, m_inkColor.data()); } if (m_colorProperties->useRandomHSV && m_transfo) { params["h"] = (m_colorProperties->hue / 180.0) * randomSource->generateNormalized(); params["s"] = (m_colorProperties->saturation / 100.0) * randomSource->generateNormalized(); params["v"] = (m_colorProperties->value / 100.0) * randomSource->generateNormalized(); m_transfo->setParameters(params); m_transfo->setParameter(3, 1);//sets the type to HSV. For some reason 0 is not an option. m_transfo->setParameter(4, false);//sets the colorize to false. m_transfo->transform(m_inkColor.data(), m_inkColor.data() , 1); } if (m_colorProperties->useRandomOpacity) { quint8 alpha = qRound(randomSource->generateNormalized() * OPACITY_OPAQUE_U8); m_inkColor.setOpacity(alpha); m_painter->setOpacity(alpha); } if (!m_colorProperties->colorPerParticle) { shouldColor = false; } m_painter->setPaintColor(m_inkColor); } qreal jitteredWidth = qMax(1.0 * additionalScale, m_shapeProperties->width * particleScale * additionalScale); qreal jitteredHeight = qMax(1.0 * additionalScale, m_shapeProperties->height * particleScale * additionalScale); if (m_shapeProperties->enabled){ switch (m_shapeProperties->shape){ // ellipse case 0: { if (m_shapeProperties->width == m_shapeProperties->height){ paintCircle(m_painter, nx + x, ny + y, jitteredWidth * 0.5); } else { paintEllipse(m_painter, nx + x, ny + y, jitteredWidth * 0.5 , jitteredHeight * 0.5, rotationZ); } break; } // rectangle case 1: { paintRectangle(m_painter, nx + x, ny + y, qRound(jitteredWidth) , qRound(jitteredHeight), rotationZ); break; } // wu-particle case 2: { paintParticle(accessor, m_inkColor, nx + x, ny + y); break; } // pixel case 3: { ix = qRound(nx + x); iy = qRound(ny + y); accessor->moveTo(ix, iy); memcpy(accessor->rawData(), m_inkColor.data(), m_dabPixelSize); break; } case 4: { if (!m_brushQImage.isNull()) { QTransform m; m.rotate(rad2deg(rotationZ)); m.scale(additionalScale, additionalScale); if (m_shapeDynamicsProperties->randomSize) { m.scale(particleScale, particleScale); } m_transformed = m_brushQImage.transformed(m, Qt::SmoothTransformation); m_imageDevice->convertFromQImage(m_transformed, 0); KisRandomAccessorSP ac = m_imageDevice->createRandomAccessorNG(0, 0); QRect rc = m_transformed.rect(); if (m_colorProperties->useRandomHSV && m_transfo) { for (int y = rc.y(); y < rc.y() + rc.height(); y++) { for (int x = rc.x(); x < rc.x() + rc.width(); x++) { ac->moveTo(x, y); m_transfo->transform(ac->rawData(), ac->rawData() , 1); } } } ix = qRound(nx + x - rc.width() * 0.5); iy = qRound(ny + y - rc.height() * 0.5); m_painter->bitBlt(QPoint(ix, iy), m_imageDevice, rc); m_imageDevice->clear(); break; } } } // Auto-brush } else { QPointF hotSpot = m_brush->hotSpot(particleScale * additionalScale, particleScale * additionalScale, -rotationZ, info); QPointF pos(nx + x, ny + y); QPointF pt = pos - hotSpot; qint32 ix; qreal xFraction; qint32 iy; qreal yFraction; KisPaintOp::splitCoordinate(pt.x(), &ix, &xFraction); KisPaintOp::splitCoordinate(pt.y(), &iy, &yFraction); //KisFixedPaintDeviceSP dab; if (m_brush->brushType() == IMAGE || m_brush->brushType() == PIPE_IMAGE) { m_fixedDab = m_brush->paintDevice(m_fixedDab->colorSpace(), particleScale * additionalScale, -rotationZ, info, xFraction, yFraction); if (m_colorProperties->useRandomHSV && m_transfo) { quint8 * dabPointer = m_fixedDab->data(); int pixelCount = m_fixedDab->bounds().width() * m_fixedDab->bounds().height(); m_transfo->transform(dabPointer, dabPointer, pixelCount); } } else { m_brush->mask(m_fixedDab, m_inkColor, particleScale * additionalScale, particleScale * additionalScale, -rotationZ, info, xFraction, yFraction); } m_painter->bltFixed(QPoint(ix, iy), m_fixedDab, m_fixedDab->bounds()); } if (m_colorProperties->colorPerParticle){ m_inkColor=color;//reset color// } } // recover from jittering of color, // m_inkColor.opacity is recovered with every paint } void SprayBrush::paintParticle(KisRandomAccessorSP &writeAccessor, const KoColor &color, qreal rx, qreal ry) { // opacity top left, right, bottom left, right KoColor pcolor(color); //int opacity = pcolor.opacityU8(); int ipx = int (rx); int ipy = int (ry); qreal fx = rx - ipx; qreal fy = ry - ipy; qreal btl = (1 - fx) * (1 - fy); qreal btr = (fx) * (1 - fy); qreal bbl = (1 - fx) * (fy); qreal bbr = (fx) * (fy); // this version overwrite pixels, e.g. when it sprays two particle next // to each other, the pixel with lower opacity can override other pixel. // Maybe some kind of compositing using here would be cool pcolor.setOpacity(btl); writeAccessor->moveTo(ipx , ipy); memcpy(writeAccessor->rawData(), pcolor.data(), m_dabPixelSize); pcolor.setOpacity(btr); writeAccessor->moveTo(ipx + 1, ipy); memcpy(writeAccessor->rawData(), pcolor.data(), m_dabPixelSize); pcolor.setOpacity(bbl); writeAccessor->moveTo(ipx, ipy + 1); memcpy(writeAccessor->rawData(), pcolor.data(), m_dabPixelSize); pcolor.setOpacity(bbr); writeAccessor->moveTo(ipx + 1, ipy + 1); memcpy(writeAccessor->rawData(), pcolor.data(), m_dabPixelSize); } void SprayBrush::paintCircle(KisPainter* painter, qreal x, qreal y, qreal radius) { QPainterPath path; path.addEllipse(QPointF(x,y),radius,radius); painter->fillPainterPath(path); } void SprayBrush::paintEllipse(KisPainter* painter, qreal x, qreal y, qreal a, qreal b, qreal angle) { QPainterPath path; path.addEllipse(QPointF(), a, b); QTransform t; t.translate(x, y); t.rotateRadians(angle); path = t.map(path); painter->fillPainterPath(path); } void SprayBrush::paintRectangle(KisPainter* painter, qreal x, qreal y, qreal width, qreal height, qreal angle) { QPainterPath path; path.addRect(QRectF(-0.5 * width, -0.5 * height, width, height)); QTransform t; t.translate(x, y); t.rotateRadians(angle); path = t.map(path); painter->fillPainterPath(path); } void SprayBrush::paintOutline(KisPaintDeviceSP dev , const KoColor &outlineColor, qreal posX, qreal posY, qreal radius) { QList antiPixels; KisRandomAccessorSP accessor = dev->createRandomAccessorNG(qRound(posX), qRound(posY)); for (int y = -radius + posY; y <= radius + posY; y++) { for (int x = -radius + posX; x <= radius + posX; x++) { accessor->moveTo(x, y); qreal alpha = dev->colorSpace()->opacityU8(accessor->rawData()); if (alpha != 0) { // top left accessor->moveTo(x - 1, y - 1); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x - 1, y - 1)); //continue; } // top accessor->moveTo(x, y - 1); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x, y - 1)); //continue; } // top right accessor->moveTo(x + 1, y - 1); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x + 1, y - 1)); //continue; } //left accessor->moveTo(x - 1, y); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x - 1, y)); //continue; } //right accessor->moveTo(x + 1, y); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x + 1, y)); //continue; } // bottom left accessor->moveTo(x - 1, y + 1); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x - 1, y + 1)); //continue; } // bottom accessor->moveTo(x, y + 1); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x, y + 1)); //continue; } // bottom right accessor->moveTo(x + 1, y + 1); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x + 1, y + 1)); //continue; } } } } // anti-alias it int size = antiPixels.size(); for (int i = 0; i < size; i++) { accessor->moveTo(antiPixels[i].x(), antiPixels[i].y()); memcpy(accessor->rawData(), outlineColor.data(), dev->colorSpace()->pixelSize()); } } void SprayBrush::setFixedDab(KisFixedPaintDeviceSP dab) { m_fixedDab = dab; } diff --git a/plugins/paintops/spray/spray_brush.h b/plugins/paintops/spray/spray_brush.h index 684b010691..76a8aee258 100644 --- a/plugins/paintops/spray/spray_brush.h +++ b/plugins/paintops/spray/spray_brush.h @@ -1,113 +1,106 @@ /* * Copyright (c) 2008-2010,2013 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _SPRAY_BRUSH_H_ #define _SPRAY_BRUSH_H_ #include #include "kis_types.h" #include "kis_painter.h" #include #include "kis_color_option.h" #include "kis_spray_shape_option.h" #include "kis_spray_shape_dynamics.h" #include "kis_sprayop_option.h" #include #include class KisPaintInformation; class RandomGauss; class SprayBrush { public: SprayBrush(); ~SprayBrush(); void paint(KisPaintDeviceSP dab, KisPaintDeviceSP source, const KisPaintInformation& info, qreal rotation, qreal scale, qreal additionalScale, const KoColor &color, const KoColor &bgColor); void setProperties(KisSprayProperties * properties, KisColorProperties * colorProperties, KisShapeProperties * shapeProperties, KisShapeDynamicsProperties * shapeDynamicsProperties, - KisBrushSP brush) { - m_properties = properties; - m_colorProperties = colorProperties; - m_shapeProperties = shapeProperties; - m_shapeDynamicsProperties = shapeDynamicsProperties; - m_brush = brush; - m_brush->notifyStrokeStarted(); - } + KisBrushSP brush); void setFixedDab(KisFixedPaintDeviceSP dab); private: KoColor m_inkColor; qreal m_radius; quint32 m_particlesCount; quint8 m_dabPixelSize; RandomGauss * m_rand; KisPainter * m_painter; KisPaintDeviceSP m_imageDevice; QImage m_brushQImage; QImage m_transformed; KoColorTransformation* m_transfo; const KisSprayProperties * m_properties; const KisColorProperties * m_colorProperties; const KisShapeProperties * m_shapeProperties; const KisShapeDynamicsProperties * m_shapeDynamicsProperties; KisBrushSP m_brush; KisFixedPaintDeviceSP m_fixedDab; private: /// rotation in radians according the settings (gauss distribution, uniform distribution or fixed angle) qreal rotationAngle(KisRandomSourceSP randomSource); /// Paints Wu Particle void paintParticle(KisRandomAccessorSP &writeAccessor, const KoColor &color, qreal rx, qreal ry); void paintCircle(KisPainter * painter, qreal x, qreal y, qreal radius); void paintEllipse(KisPainter * painter, qreal x, qreal y, qreal a, qreal b, qreal angle); void paintRectangle(KisPainter * painter, qreal x, qreal y, qreal width, qreal height, qreal angle); void paintOutline(KisPaintDeviceSP dev, const KoColor& painterColor, qreal posX, qreal posY, qreal radius); /// mix a with b.b mix with weight and a with 1.0 - weight inline qreal linearInterpolation(qreal a, qreal b, qreal weight) const { return (1.0 - weight) * a + weight * b; } // TODO: move this somewhere where I can reuse it /// convert radians to degrees inline qreal rad2deg(qreal rad) const { return rad * (180.0 / M_PI); } /// convert degrees to radians inline qreal deg2rad(quint16 deg) const { return deg * (M_PI / 180.0); } }; #endif diff --git a/plugins/paintops/tangentnormal/wdgtangenttiltoption.ui b/plugins/paintops/tangentnormal/wdgtangenttiltoption.ui index 4af226f0c7..6e755ccf9f 100644 --- a/plugins/paintops/tangentnormal/wdgtangenttiltoption.ui +++ b/plugins/paintops/tangentnormal/wdgtangenttiltoption.ui @@ -1,366 +1,370 @@ WdgTangentTiltOptions 0 0 - 425 - 298 + 564 + 409 255 255 425 260 A Brush Engine for Drawing 3d Tangent Normal Maps false Tangent Encoding Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Red: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 2 + X - X + Y - Y + Z - Z Green: + X - X + Y - Y + Z - Z Blue: 4 + X - X + Y - Y + Z - Z 0 0 100 100 QFrame::StyledPanel QFrame::Sunken Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop Tilt Options - + - - + + <html><head/><body><p>Use the tilt of the tablet to determine the normals.</p></body></html> Tilt true - + <html><head/><body><p>Use the drawing direction to determine the X and Y-axes, while tilt-elevation is used for the Z-axis.</p></body></html> Direction - + <html><head/><body><p>Use the rotation sensor available in certain pens to determine the X and Y-axes, while tilt-elevation is used for the Z-axis.</p></body></html> Rotation - + <html><head/><body><p>Mix Tilt and Drawing angle to determine the X and Y-axes, while tilt-elevation is used for the Z-axis.</p></body></html> Direction/Tilt Mix - - - - Direction/Tilt Mix Value - - - - + + + + + Elevation Sensitivity - - + + 0 0 - - + + + + Direction/Tilt Mix Value + + + + + 0 0 Qt::Vertical 20 40 KisDoubleSliderSpinBox QWidget
kis_slider_spin_box.h
1
KisNormalPreviewWidget QLabel
kis_normal_preview_widget.h
1
optionMix toggled(bool) sliderMixValue setVisible(bool) 20 20 20 20 optionMix toggled(bool) L_mix setVisible(bool) 20 20 20 20
diff --git a/plugins/tools/basictools/strokes/move_stroke_strategy.cpp b/plugins/tools/basictools/strokes/move_stroke_strategy.cpp index ae4e3b7675..bd1685596f 100644 --- a/plugins/tools/basictools/strokes/move_stroke_strategy.cpp +++ b/plugins/tools/basictools/strokes/move_stroke_strategy.cpp @@ -1,234 +1,238 @@ /* * 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 "move_stroke_strategy.h" #include #include "kis_image_interfaces.h" #include "kis_node.h" #include "commands_new/kis_update_command.h" #include "commands_new/kis_node_move_command2.h" #include "kis_layer_utils.h" #include "krita_utils.h" MoveStrokeStrategy::MoveStrokeStrategy(KisNodeList nodes, KisUpdatesFacade *updatesFacade, KisPostExecutionUndoAdapter *undoAdapter) : KisStrokeStrategyUndoCommandBased(kundo2_i18n("Move"), false, undoAdapter), m_nodes(), m_updatesFacade(updatesFacade), m_undoEnabled(true), m_updatesEnabled(true) { m_nodes = KisLayerUtils::sortAndFilterMergableInternalNodes(nodes, true); KritaUtils::filterContainer(m_nodes, [this](KisNodeSP node) { - return !KisLayerUtils::checkIsCloneOf(node, m_nodes); + return + !KisLayerUtils::checkIsCloneOf(node, m_nodes) && + node->isEditable(); }); Q_FOREACH(KisNodeSP subtree, m_nodes) { KisLayerUtils::recursiveApplyNodes( subtree, [this](KisNodeSP node) { - if (KisLayerUtils::checkIsCloneOf(node, m_nodes)) { + if (KisLayerUtils::checkIsCloneOf(node, m_nodes) || + !node->isEditable()) { + m_blacklistedNodes.insert(node); } }); } setSupportsWrapAroundMode(true); } MoveStrokeStrategy::MoveStrokeStrategy(const MoveStrokeStrategy &rhs, bool suppressUndo) : KisStrokeStrategyUndoCommandBased(rhs, suppressUndo), m_nodes(rhs.m_nodes), m_blacklistedNodes(rhs.m_blacklistedNodes), m_updatesFacade(rhs.m_updatesFacade), m_finalOffset(rhs.m_finalOffset), m_dirtyRect(rhs.m_dirtyRect), m_dirtyRects(rhs.m_dirtyRects), m_undoEnabled(rhs.m_undoEnabled), m_updatesEnabled(rhs.m_updatesEnabled) { } void MoveStrokeStrategy::saveInitialNodeOffsets(KisNodeSP node) { if (!m_blacklistedNodes.contains(node)) { m_initialNodeOffsets.insert(node, QPoint(node->x(), node->y())); } KisNodeSP child = node->firstChild(); while(child) { saveInitialNodeOffsets(child); child = child->nextSibling(); } } void MoveStrokeStrategy::initStrokeCallback() { Q_FOREACH(KisNodeSP node, m_nodes) { saveInitialNodeOffsets(node); } KisStrokeStrategyUndoCommandBased::initStrokeCallback(); } void MoveStrokeStrategy::finishStrokeCallback() { if (m_undoEnabled) { Q_FOREACH (KisNodeSP node, m_nodes) { KUndo2Command *updateCommand = new KisUpdateCommand(node, m_dirtyRects[node], m_updatesFacade, true); addMoveCommands(node, updateCommand); notifyCommandDone(KUndo2CommandSP(updateCommand), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); } } if (!m_updatesEnabled) { Q_FOREACH (KisNodeSP node, m_nodes) { m_updatesFacade->refreshGraphAsync(node, m_dirtyRects[node]); } } KisStrokeStrategyUndoCommandBased::finishStrokeCallback(); } void MoveStrokeStrategy::cancelStrokeCallback() { if (!m_nodes.isEmpty()) { // FIXME: make cancel job exclusive instead m_updatesFacade->blockUpdates(); moveAndUpdate(QPoint()); m_updatesFacade->unblockUpdates(); } KisStrokeStrategyUndoCommandBased::cancelStrokeCallback(); } void MoveStrokeStrategy::doStrokeCallback(KisStrokeJobData *data) { Data *d = dynamic_cast(data); if(!m_nodes.isEmpty() && d) { moveAndUpdate(d->offset); /** * NOTE: we do not care about threading here, because * all our jobs are declared sequential */ m_finalOffset = d->offset; } else { KisStrokeStrategyUndoCommandBased::doStrokeCallback(data); } } void MoveStrokeStrategy::moveAndUpdate(QPoint offset) { Q_FOREACH (KisNodeSP node, m_nodes) { QRect dirtyRect = moveNode(node, offset); m_dirtyRects[node] |= dirtyRect; if (m_updatesEnabled) { m_updatesFacade->refreshGraphAsync(node, dirtyRect); } } } QRect MoveStrokeStrategy::moveNode(KisNodeSP node, QPoint offset) { QRect dirtyRect; if (!m_blacklistedNodes.contains(node)) { dirtyRect = node->extent(); QPoint newOffset = m_initialNodeOffsets[node] + offset; /** * Some layers, e.g. clones need an update to change extent(), so * calculate the dirty rect manually */ QPoint currentOffset(node->x(), node->y()); dirtyRect |= dirtyRect.translated(newOffset - currentOffset); node->setX(newOffset.x()); node->setY(newOffset.y()); KisNodeMoveCommand2::tryNotifySelection(node); } KisNodeSP child = node->firstChild(); while(child) { dirtyRect |= moveNode(child, offset); child = child->nextSibling(); } return dirtyRect; } void MoveStrokeStrategy::addMoveCommands(KisNodeSP node, KUndo2Command *parent) { if (!m_blacklistedNodes.contains(node)) { QPoint nodeOffset(node->x(), node->y()); new KisNodeMoveCommand2(node, nodeOffset - m_finalOffset, nodeOffset, parent); } KisNodeSP child = node->firstChild(); while(child) { addMoveCommands(child, parent); child = child->nextSibling(); } } void MoveStrokeStrategy::setUndoEnabled(bool value) { m_undoEnabled = value; } void MoveStrokeStrategy::setUpdatesEnabled(bool value) { m_updatesEnabled = value; } bool checkSupportsLodMoves(KisNodeSP subtree) { return !KisLayerUtils::recursiveFindNode( subtree, [](KisNodeSP node) -> bool { return !node->supportsLodMoves(); }); } KisStrokeStrategy* MoveStrokeStrategy::createLodClone(int levelOfDetail) { Q_FOREACH (KisNodeSP node, m_nodes) { if (!checkSupportsLodMoves(node)) return 0; } MoveStrokeStrategy *clone = new MoveStrokeStrategy(*this, levelOfDetail > 0); clone->setUndoEnabled(false); this->setUpdatesEnabled(false); return clone; }