diff --git a/3rdparty/README.md b/3rdparty/README.md index f28552d02e..45003fb2d8 100644 --- a/3rdparty/README.md +++ b/3rdparty/README.md @@ -1,215 +1,220 @@ = 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: (http://tdm-gcc.tdragon.net/, version 5.1). MSVC cannot build G'Mic correctly. For some reason, cmake wants to use nmake even when using mingw, so copy mingw32-make.exe to nmake.exe. And remember to install the OpenMP plugin in tdm-gcc. -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 your path. + * Windows: (http://tdm-gcc.tdragon.net/, version 5.1). MSVC cannot build G'Mic correctly. Remember to install the OpenMP plugin in tdm-gcc. Make sure mingw's bin folder is in your path. +4. If you compile Qt on Windows, you will also need Python: https://www.python.org. Make sure to have python.exe in your 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. 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 == Prepare the externals build == 1. enter the BUILDROOT/b directory 2. run cmake: * Linux: export PATH=$BUILDROOT/i/bin cmake ../krita/3rdparty \ -DINSTALL_ROOT=$BUILDROOT/i \ -DEXTERNALS_DOWNLOAD_DIR=$BUILDROOT/d \ -DCMAKE_INSTALL_PREFIX=BUILDROOT/i * OSX: export PATH=$BUILDROOT/i/bin cmake ../krita/3rdparty/ \ -DCMAKE_INSTALL_PREFIX=$BUILDROOT/i \ -DEXTERNALS_DOWNLOAD_DIR=$BUILDROOT/d \ -DINSTALL_ROOT=$BUILDROOT/i * Windows 32 bits: TODO * 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 "MinGW Makefiles" + cmake ..\krita\3rdparty -DEXTERNALS_DOWNLOAD_DIR=/dev/d -DINSTALL_ROOT=/dev/i -G "MinGW Makefiles" 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 always, so it's better to build the dependencies independently. On Windows: cmake --build . --config RelWithDebInfo --target ext_patch cmake --build . --config RelWithDebInfo --target ext_png2ico cmake --build . --config RelWithDebInfo --target ext_gettext On all operating systems: cmake --build . --config RelWithDebInfo --target ext_qt cmake --build . --config RelWithDebInfo --target ext_zlib 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 + +On Windows: + + set FFTW_LIB_DIR=%BUILDROOT%\i\lib + dlltool.exe -k --output-lib %FFTW_LIB_DIR%\libfftw3-3.a --input-def %FFTW_LIB_DIR%\libfftw3-3.def + dlltool.exe -k --output-lib %FFTW_LIB_DIR%\libfftw3f-3.a --input-def %FFTW_LIB_DIR%\libfftw3f-3.def + dlltool.exe -k --output-lib %FFTW_LIB_DIR%\libfftw3l-3.a --input-def %FFTW_LIB_DIR%\libfftw3l-3.def + +On all operating systems + 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 On Windows and OSX cmake --build . --config RelWithDebInfo --target ext_kcrash 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 "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 + cmake ..\krita -G "MinGW Makefiles" -DBoost_DEBUG=OFF -DBOOST_INCLUDEDIR=c:\dev\i\include -DBOOST_DEBUG=ON -DBOOST_ROOT=c:\dev\i -DBOOST_LIBRARYDIR=c:\dev\i\lib -DCMAKE_INSTALL_PREFIX=c:\dev\i -DCMAKE_PREFIX_PATH=c:\dev\i -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_TESTING=OFF -DKDE4_BUILD_TESTS=OFF -DHAVE_MEMORY_LEAK_TRACKER=OFF -DPACKAGERS_BUILD=ON -Wno-dev -DDEFINE_NO_DEPRECATED=1 Or this to 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 + cmake ..\krita -G "MinGW Makefiles" -DBoost_DEBUG=OFF -DBOOST_INCLUDEDIR=c:\dev\i\include -DBOOST_DEBUG=ON -DBOOST_ROOT=c:\dev\i -DBOOST_LIBRARYDIR=c:\dev\i\lib -DCMAKE_INSTALL_PREFIX=c:\dev\i -DCMAKE_PREFIX_PATH=c:\dev\i -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_TESTING=OFF -DKDE4_BUILD_TESTS=OFF -DHAVE_MEMORY_LEAK_TRACKER=OFF -DPACKAGERS_BUILD=ON -Wno-dev -DDEFINE_NO_DEPRECATED=1 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=RelWithDebInfobg 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 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_boost/CMakeLists.txt b/3rdparty/ext_boost/CMakeLists.txt index 759e6cd761..b3ff554a7f 100755 --- a/3rdparty/ext_boost/CMakeLists.txt +++ b/3rdparty/ext_boost/CMakeLists.txt @@ -1,88 +1,88 @@ SET(PREFIX_ext_boost "${EXTPREFIX}" ) if (MSVC) if (${CMAKE_GENERATOR} STREQUAL "Visual Studio 14 2015 Win64") ExternalProject_Add( ext_boost DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://files.kde.org/krita/build/dependencies/boost_1_61_0.zip URL_MD5 015ae4afa6f3e597232bfe1dab949ace CONFIGURE_COMMAND /bootstrap.bat --prefix=${PREFIX_ext_boost} BUILD_COMMAND /b2.exe --with-system --build-dir=build-dir --prefix=${PREFIX_ext_boost} toolset=msvc-14.0 variant=release link=shared threading=multi architecture=x86 address-model=64 variant=release install INSTALL_COMMAND "" INSTALL_DIR ${EXTPREFIX_boost} UPDATE_COMMAND "" ALWAYS 0 BUILD_IN_SOURCE 1 ) ExternalProject_Add_Step( ext_boost post_install - COMMAND ${CMAKE_COMMAND} -E copy ${PREFIX_ext_boost}/lib/boost_system-vc140-mt-1_55.dll ${PREFIX_ext_boost}/bin/boost_system-vc140-mt-1_55.dll + COMMAND ${CMAKE_COMMAND} -E copy ${PREFIX_ext_boost}/lib/boost_system-vc140-mt-1_61.dll ${PREFIX_ext_boost}/bin/boost_system-vc140-mt-1_61.dll DEPENDEES install ) else() ExternalProject_Add( ext_boost DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://files.kde.org/krita/build/dependencies/boost_1_61_0.zip URL_MD5 015ae4afa6f3e597232bfe1dab949ace CONFIGURE_COMMAND /bootstrap.bat --prefix=${PREFIX_ext_boost} BUILD_COMMAND /b2.exe --with-system --build-dir=build-dir --prefix=${PREFIX_ext_boost} toolset=msvc-14.0 variant=release link=shared threading=multi architecture=x86 variant=release install INSTALL_COMMAND "" INSTALL_DIR ${EXTPREFIX_boost} UPDATE_COMMAND "" ALWAYS 0 BUILD_IN_SOURCE 1 ) ExternalProject_Add_Step( ext_boost post_install - COMMAND ${CMAKE_COMMAND} -E copy ${PREFIX_ext_boost}/lib/boost_system-vc140-mt-1_55.dll ${PREFIX_ext_boost}/bin/boost_system-vc140-mt-1_55.dll + COMMAND ${CMAKE_COMMAND} -E copy ${PREFIX_ext_boost}/lib/boost_system-vc140-mt-1_61.dll ${PREFIX_ext_boost}/bin/boost_system-vc140-mt-1_61.dll DEPENDEES install ) endif() elseif(MINGW) ExternalProject_Add( ext_boost DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://files.kde.org/krita/build/dependencies/boost_1_61_0.zip URL_MD5 015ae4afa6f3e597232bfe1dab949ace CONFIGURE_COMMAND /bootstrap.bat mingw --prefix=${PREFIX_ext_boost} BUILD_COMMAND /b2.exe --with-system --build-dir=build-dir --prefix=${PREFIX_ext_boost} toolset=gcc variant=release link=shared threading=multi architecture=x86 variant=release install INSTALL_COMMAND "" INSTALL_DIR ${EXTPREFIX_boost} UPDATE_COMMAND "" ALWAYS 0 BUILD_IN_SOURCE 1 ) ExternalProject_Add_Step( ext_boost post_install - COMMAND ${CMAKE_COMMAND} -E copy ${PREFIX_ext_boost}/lib/libboost_system-mgw51-mt-1_55.dll ${PREFIX_ext_boost}/bin/ + COMMAND ${CMAKE_COMMAND} -E copy ${PREFIX_ext_boost}/lib/libboost_system-mgw51-mt-1_61.dll ${PREFIX_ext_boost}/bin/ DEPENDEES install ) else() ExternalProject_Add( ext_boost DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://files.kde.org/krita/build/dependencies/boost_1_61_0.tar.bz2 URL_MD5 6095876341956f65f9d35939ccea1a9f CONFIGURE_COMMAND /bootstrap.sh --prefix=${PREFIX_ext_boost} --with-libraries=system BUILD_COMMAND /b2 install INSTALL_COMMAND "" INSTALL_DIR ${PREFIX_ext_boost} UPDATE_COMMAND "" ALWAYS 0 BUILD_IN_SOURCE 1 ) endif() diff --git a/3rdparty/ext_gettext/CMakeLists.txt b/3rdparty/ext_gettext/CMakeLists.txt index 6fc2698572..b660905a80 100644 --- a/3rdparty/ext_gettext/CMakeLists.txt +++ b/3rdparty/ext_gettext/CMakeLists.txt @@ -1,48 +1,46 @@ SET(PREFIX_ext_gettext "${EXTPREFIX}" ) if (MSVC OR MINGW) -ExternalProject_Add( if(CMAKE_CL_64) - ext_gettext - DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://files.kde.org/krita/build/dependencies/gettext0.19.8.1-iconv1.14-shared-32.zip - URL_MD5 f877ebff42736535415b4ddb9c631b86 - CONFIGURE_COMMAND "" - BUILD_COMMAND ${CMAKE_COMMAND} -E echo deploying gettext.exe binary + ExternalProject_Add( ext_gettext + DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} + URL http://files.kde.org/krita/build/dependencies/gettext0.19.8.1-iconv1.14-shared-64.zip + URL_MD5 f877ebff42736535415b4ddb9c631b86 + CONFIGURE_COMMAND "" + BUILD_COMMAND ${CMAKE_COMMAND} -E echo deploying gettext.exe binary INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory /bin ${PREFIX_ext_gettext}/bin COMMAND ${CMAKE_COMMAND} -E copy_directory /lib ${PREFIX_ext_gettext}/lib COMMAND ${CMAKE_COMMAND} -E copy_directory /include ${PREFIX_ext_gettext}/include UPDATE_COMMAND "" ALWAYS 0 - + ) else() - ext_gettext - DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://files.kde.org/krita/build/dependencies/gettext0.19.8.1-iconv1.14-shared-64.zip - URL_MD5 bced59a375aef8d325b746f0dfbe8f48 - CONFIGURE_COMMAND "" - BUILD_COMMAND ${CMAKE_COMMAND} -E echo deploying gettext.exe binary - INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory /bin ${PREFIX_ext_gettext}/bin - COMMAND ${CMAKE_COMMAND} -E copy_directory /lib ${PREFIX_ext_gettext}/lib - COMMAND ${CMAKE_COMMAND} -E copy_directory /include ${PREFIX_ext_gettext}/include - UPDATE_COMMAND "" - ALWAYS 0 - + ExternalProject_Add( ext_gettext + DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} + URL http://files.kde.org/krita/build/dependencies/gettext0.19.8.1-iconv1.14-shared-32.zip + URL_MD5 bced59a375aef8d325b746f0dfbe8f48 + CONFIGURE_COMMAND "" + BUILD_COMMAND ${CMAKE_COMMAND} -E echo deploying gettext.exe binary + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory /bin ${PREFIX_ext_gettext}/bin + COMMAND ${CMAKE_COMMAND} -E copy_directory /lib ${PREFIX_ext_gettext}/lib + COMMAND ${CMAKE_COMMAND} -E copy_directory /include ${PREFIX_ext_gettext}/include + UPDATE_COMMAND "" + ALWAYS 0 + ) endif() -) else (MSVC OR MINGW) -ExternalProject_Add( ext_gettext - DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://files.kde.org/krita/build/dependencies/gettext-0.19.8.tar.gz - URL_MD5 e4fffc004f21596becd1055cf36be31d + ExternalProject_Add( ext_gettext + DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} + URL http://files.kde.org/krita/build/dependencies/gettext-0.19.8.tar.gz + URL_MD5 e4fffc004f21596becd1055cf36be31d - INSTALL_DIR ${PREFIX_ext_gettext} - CONFIGURE_COMMAND /configure --prefix=${PREFIX_ext_gettext} --disable-java ${GLOBAL_AUTOMAKE_PROFILE} --disable-native-java + INSTALL_DIR ${PREFIX_ext_gettext} + CONFIGURE_COMMAND /configure --prefix=${PREFIX_ext_gettext} --disable-java ${GLOBAL_AUTOMAKE_PROFILE} --disable-native-java BUILD_COMMAND make INSTALL_COMMAND make install - UPDATE_COMMAND "" - ALWAYS 0 - DEPENDS ext_iconv -) + UPDATE_COMMAND "" + ALWAYS 0 + DEPENDS ext_iconv + ) endif (MSVC OR MINGW) diff --git a/3rdparty/ext_qt/CMakeLists.txt b/3rdparty/ext_qt/CMakeLists.txt index 3fece961c6..9fbaff3345 100644 --- a/3rdparty/ext_qt/CMakeLists.txt +++ b/3rdparty/ext_qt/CMakeLists.txt @@ -1,60 +1,60 @@ SET(EXTPREFIX_qt "${EXTPREFIX}") if (WIN32) ExternalProject_Add( ext_qt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL https://download.qt.io/official_releases/qt/5.6/5.6.1-1/single/qt-everywhere-opensource-src-5.6.1-1.zip URL_MD5 9d7ea0cadcec7b5a63e8e83686756978 PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/disable-wintab.diff COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/qtgui-private-headers.diff COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/0001-Don-t-request-the-MIME-image-every-time-Windows-asks.patch COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/0002-Hack-always-return-we-support-DIBV5.patch COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/0003-Hack-for-fullscreen-workaround.patch INSTALL_DIR ${EXTPREFIX_qt} CONFIGURE_COMMAND /configure.bat -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtdoc -skip qtenginio -skip qtgraphicaleffects -skip qtlocation -skip qtmultimedia -skip qtsensors -skip qtserialport -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtxmlpatterns -no-sql-sqlite -nomake examples -nomake tools -no-compile-examples -no-dbus -no-iconv -no-angle -no-ssl -no-openssl -no-wmf-backend -no-qml-debug -no-libproxy -no-system-proxies -no-nis -no-icu -no-mtdev -opensource -confirm-license -release -opengl desktop -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -prefix ${EXTPREFIX_qt} -platform win32-g++ # use this line for building Qt with debugging info enabled #CONFIGURE_COMMAND /configure.bat -release -force-debug-info -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtdoc -skip qtenginio -skip qtgraphicaleffects -skip qtlocation -skip qtmultimedia -skip qtsensors -skip qtserialport -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtxmlpatterns -no-sql-sqlite -nomake examples -nomake tools -no-compile-examples -no-dbus -no-iconv -no-angle -no-ssl -no-openssl -no-wmf-backend -no-qml-debug -no-libproxy -no-system-proxies -no-nis -no-icu -no-mtdev -opensource -confirm-license -release -opengl desktop -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -prefix ${EXTPREFIX_qt} -platform win32-g++ - BUILD_COMMAND nmake - INSTALL_COMMAND nmake install + BUILD_COMMAND mingw32-make + INSTALL_COMMAND mingw32-make install UPDATE_COMMAND "" BUILD_IN_SOURCE 1 ALWAYS 0 DEPENDS ext_patch ) else() if (APPLE) ExternalProject_Add( ext_qt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL https://download.qt.io/official_releases/qt/5.6/5.6.1-1/single/qt-everywhere-opensource-src-5.6.1-1.tar.gz URL_MD5 8fdec6d657bc370bd3183d8fe8e9c47a PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/macdeploy-qt.diff INSTALL_DIR ${EXTPREFIX_qt} CONFIGURE_COMMAND /configure -confirm-license -opensource -nomake examples -no-openssl -no-compile-examples -qt-freetype -qt-harfbuzz -opengl desktop -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtenginio -skip qtgraphicaleffects -skip qtlocation -skip qtmultimedia -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtxmlpatterns -prefix ${EXTPREFIX_qt} BUILD_COMMAND make INSTALL_COMMAND make install UPDATE_COMMAND "" BUILD_IN_SOURCE 1 ALWAYS 0 ) else() ExternalProject_Add( ext_qt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL https://download.qt.io/official_releases/qt/5.6/5.6.1-1/single/qt-everywhere-opensource-src-5.6.1-1.tar.gz URL_MD5 8fdec6d657bc370bd3183d8fe8e9c47a PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/qt-no-motion-compression.diff INSTALL_DIR ${EXTPREFIX_qt} CONFIGURE_COMMAND /configure -prefix ${EXTPREFIX_qt} -opensource -confirm-license -nomake examples -no-sql-sqlite -no-openssl -no-qml-debug -no-mtdev -no-journald -no-syslog -no-nis -no-cups -no-tslib -no-directfb -no-linuxfb -no-libproxy -no-pch -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -qt-harfbuzz -qt-freetype -qt-xcb -qt-xkbcommon-x11 -optimized-qmake -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtenginio -skip qtgraphicaleffects -skip qtlocation -skip qtmultimedia -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtandroidextras -skip qtserialport BUILD_COMMAND make INSTALL_COMMAND make install UPDATE_COMMAND "" BUILD_IN_SOURCE 1 ALWAYS 0 ) endif() endif() diff --git a/krita/data/shaders/simple_texture_legacy.frag b/krita/data/shaders/simple_texture_legacy.frag index 1b9a3ad472..ec53a86af4 100644 --- a/krita/data/shaders/simple_texture_legacy.frag +++ b/krita/data/shaders/simple_texture_legacy.frag @@ -1,22 +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); + gl_FragColor = OCIODisplay(col, texture1); #else /* USE_OCIO */ gl_FragColor = col; #endif /* USE_OCIO */ } diff --git a/krita/data/workspaces/Animation.kws b/krita/data/workspaces/Animation.kws index 7a1e11e959..9d8c87258e 100644 --- a/krita/data/workspaces/Animation.kws +++ b/krita/data/workspaces/Animation.kws @@ -1,4 +1,4 @@ - - + + diff --git a/krita/data/workspaces/Big_Paint.kws b/krita/data/workspaces/Big_Paint.kws index a9e3cc3d97..1196782864 100644 --- a/krita/data/workspaces/Big_Paint.kws +++ b/krita/data/workspaces/Big_Paint.kws @@ -1,4 +1,4 @@ - - + + diff --git a/krita/data/workspaces/Big_Paint_2.kws b/krita/data/workspaces/Big_Paint_2.kws index 6e73b64605..0eddcaa3ee 100644 --- a/krita/data/workspaces/Big_Paint_2.kws +++ b/krita/data/workspaces/Big_Paint_2.kws @@ -1,4 +1,4 @@ - - + + diff --git a/krita/data/workspaces/Big_Vector.kws b/krita/data/workspaces/Big_Vector.kws index 4b02e3a959..a60bc8f582 100644 --- a/krita/data/workspaces/Big_Vector.kws +++ b/krita/data/workspaces/Big_Vector.kws @@ -1,4 +1,4 @@ - - + + diff --git a/krita/data/workspaces/Default.kws b/krita/data/workspaces/Default.kws index 7235a1b12a..0d10953067 100644 --- a/krita/data/workspaces/Default.kws +++ b/krita/data/workspaces/Default.kws @@ -1,4 +1,4 @@ - - + + diff --git a/krita/data/workspaces/Small_Vector.kws b/krita/data/workspaces/Small_Vector.kws index 217af1f934..3a5f49ef49 100644 --- a/krita/data/workspaces/Small_Vector.kws +++ b/krita/data/workspaces/Small_Vector.kws @@ -1,4 +1,4 @@ - - + + diff --git a/krita/data/workspaces/VFX_Paint.kws b/krita/data/workspaces/VFX_Paint.kws index dd80759e44..3286ca2b2f 100644 --- a/krita/data/workspaces/VFX_Paint.kws +++ b/krita/data/workspaces/VFX_Paint.kws @@ -1,4 +1,4 @@ - - + + diff --git a/krita/main.cc b/krita/main.cc index 00b23e3238..3d688223ac 100644 --- a/krita/main.cc +++ b/krita/main.cc @@ -1,235 +1,235 @@ /* * 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("\\","_"); 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 - qputenv("LANG", language.toLatin1()); + qputenv("LANG", language.split(":").first().toUtf8()); 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()); QString path = qgetenv("PATH"); qputenv("PATH", QFile::encodeName(appdir.absolutePath() + "/bin" + ";" + appdir.absolutePath() + "/lib" + ";" + appdir.absolutePath() + "/Frameworks" + ";" + appdir.absolutePath() + ";" + path)); qDebug() << "PATH" << qgetenv("PATH"); #endif if (qApp->applicationDirPath().contains(KRITA_BUILD_DIR)) { qFatal("FATAL: You're trying to run krita from the build location. You can only run Krita from the installation location."); } #if defined HAVE_KCRASH KCrash::initialize(); #elif defined USE_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 396a806e69..e001e4162b 100644 --- a/krita/pics/Breeze-dark/breeze-dark-icons.qrc +++ b/krita/pics/Breeze-dark/breeze-dark-icons.qrc @@ -1,89 +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_applications-system.svg dark_configure.svg - dark_configure-toolbars.svg + dark_configure.svg dark_dialog-cancel.svg - dark_dialog-close.svg + dark_window-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-properties.svg + dark_configure.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_arrow-right.svg + dark_arrow-left.svg + dark_arrow-up.svg + dark_system-help.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_locked.svg dark_object-rotate-left.svg dark_object-rotate-right.svg - dark_object-unlocked.svg + dark_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_configure-shortcuts.svg b/krita/pics/Breeze-dark/dark_configure-shortcuts.svg deleted file mode 100644 index e3cd1e15aa..0000000000 --- a/krita/pics/Breeze-dark/dark_configure-shortcuts.svg +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - diff --git a/krita/pics/Breeze-dark/dark_configure-toolbars.svg b/krita/pics/Breeze-dark/dark_configure-toolbars.svg deleted file mode 100644 index 96a742ded2..0000000000 --- a/krita/pics/Breeze-dark/dark_configure-toolbars.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/krita/pics/Breeze-dark/dark_dialog-close.svg b/krita/pics/Breeze-dark/dark_dialog-close.svg deleted file mode 100644 index d2194f2382..0000000000 --- a/krita/pics/Breeze-dark/dark_dialog-close.svg +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - diff --git a/krita/pics/Breeze-dark/dark_document-properties.svg b/krita/pics/Breeze-dark/dark_document-properties.svg deleted file mode 100644 index 96a742ded2..0000000000 --- a/krita/pics/Breeze-dark/dark_document-properties.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/krita/pics/Breeze-dark/dark_go-next.svg b/krita/pics/Breeze-dark/dark_go-next.svg deleted file mode 100644 index 09e9f3b60c..0000000000 --- a/krita/pics/Breeze-dark/dark_go-next.svg +++ /dev/null @@ -1,283 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/krita/pics/Breeze-dark/dark_go-previous.svg b/krita/pics/Breeze-dark/dark_go-previous.svg deleted file mode 100644 index 314a0c3fe7..0000000000 --- a/krita/pics/Breeze-dark/dark_go-previous.svg +++ /dev/null @@ -1,247 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/krita/pics/Breeze-dark/dark_go-up.svg b/krita/pics/Breeze-dark/dark_go-up.svg deleted file mode 100644 index 45cb630c07..0000000000 --- a/krita/pics/Breeze-dark/dark_go-up.svg +++ /dev/null @@ -1,239 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/krita/pics/Breeze-dark/dark_help-contents.svg b/krita/pics/Breeze-dark/dark_help-contents.svg deleted file mode 100644 index 122dccd9dc..0000000000 --- a/krita/pics/Breeze-dark/dark_help-contents.svg +++ /dev/null @@ -1,157 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - diff --git a/krita/pics/Breeze-dark/dark_object-locked.svg b/krita/pics/Breeze-dark/dark_object-locked.svg deleted file mode 100644 index ea68bf6987..0000000000 --- a/krita/pics/Breeze-dark/dark_object-locked.svg +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/krita/pics/Breeze-dark/dark_object-unlocked.svg b/krita/pics/Breeze-dark/dark_object-unlocked.svg deleted file mode 100644 index 09827b14c4..0000000000 --- a/krita/pics/Breeze-dark/dark_object-unlocked.svg +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/krita/pics/Breeze-light/breeze-light-icons.qrc b/krita/pics/Breeze-light/breeze-light-icons.qrc index 3cfa9133d2..55b86f3944 100644 --- a/krita/pics/Breeze-light/breeze-light-icons.qrc +++ b/krita/pics/Breeze-light/breeze-light-icons.qrc @@ -1,90 +1,90 @@ 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_applications-system.svg light_configure.svg - light_configure-toolbars.svg + light_configure.svg light_dialog-cancel.svg - light_dialog-close.svg + light_window-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-properties.svg + light_configure.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_arrow-right.svg + light_arrow-left.svg + light_arrow-up.svg + light_system-help.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_locked.svg light_object-rotate-left.svg light_object-rotate-right.svg - light_object-unlocked.svg + light_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_configure-shortcuts.svg b/krita/pics/Breeze-light/light_configure-shortcuts.svg deleted file mode 100644 index 2cbbb8e5c3..0000000000 --- a/krita/pics/Breeze-light/light_configure-shortcuts.svg +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/krita/pics/Breeze-light/light_configure-toolbars.svg b/krita/pics/Breeze-light/light_configure-toolbars.svg deleted file mode 100644 index 18ad898ead..0000000000 --- a/krita/pics/Breeze-light/light_configure-toolbars.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/krita/pics/Breeze-light/light_dialog-close.svg b/krita/pics/Breeze-light/light_dialog-close.svg deleted file mode 100644 index d2194f2382..0000000000 --- a/krita/pics/Breeze-light/light_dialog-close.svg +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - diff --git a/krita/pics/Breeze-light/light_document-properties.svg b/krita/pics/Breeze-light/light_document-properties.svg deleted file mode 100644 index 18ad898ead..0000000000 --- a/krita/pics/Breeze-light/light_document-properties.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/krita/pics/Breeze-light/light_go-next.svg b/krita/pics/Breeze-light/light_go-next.svg deleted file mode 100644 index 5b9c73ed20..0000000000 --- a/krita/pics/Breeze-light/light_go-next.svg +++ /dev/null @@ -1,283 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/krita/pics/Breeze-light/light_go-previous.svg b/krita/pics/Breeze-light/light_go-previous.svg deleted file mode 100644 index 0ed5470edb..0000000000 --- a/krita/pics/Breeze-light/light_go-previous.svg +++ /dev/null @@ -1,247 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/krita/pics/Breeze-light/light_go-up.svg b/krita/pics/Breeze-light/light_go-up.svg deleted file mode 100644 index 28ef0150bf..0000000000 --- a/krita/pics/Breeze-light/light_go-up.svg +++ /dev/null @@ -1,239 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/krita/pics/Breeze-light/light_help-contents.svg b/krita/pics/Breeze-light/light_help-contents.svg deleted file mode 100644 index 610422ffff..0000000000 --- a/krita/pics/Breeze-light/light_help-contents.svg +++ /dev/null @@ -1,157 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - diff --git a/krita/pics/Breeze-light/light_object-locked.svg b/krita/pics/Breeze-light/light_object-locked.svg deleted file mode 100644 index 5d8335de80..0000000000 --- a/krita/pics/Breeze-light/light_object-locked.svg +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/krita/pics/Breeze-light/light_object-unlocked.svg b/krita/pics/Breeze-light/light_object-unlocked.svg deleted file mode 100644 index 2e852f5ff1..0000000000 --- a/krita/pics/Breeze-light/light_object-unlocked.svg +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/libs/brush/tests/kis_auto_brush_test.cpp b/libs/brush/tests/kis_auto_brush_test.cpp index e8ec39a070..3d8144c675 100644 --- a/libs/brush/tests/kis_auto_brush_test.cpp +++ b/libs/brush/tests/kis_auto_brush_test.cpp @@ -1,196 +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), KisDabShape(), 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, KisDabShape(), 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()); } } static void dabSizeHelper(KisBrushSP const& brush, QString const& name, KisDabShape const& shape, int expectedWidth, int expectedHeight) { qDebug() << name; QCOMPARE(brush->maskWidth(shape, 0.0, 0.0, KisPaintInformation()), expectedWidth); QCOMPARE(brush->maskHeight(shape, 0.0, 0.0, KisPaintInformation()), expectedHeight); } void KisAutoBrushTest::testDabSize() { 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); dabSizeHelper(a, "Identity", KisDabShape(), 10, 5); dabSizeHelper(a, "Double", KisDabShape(2.0, 1.0, 0.0), 20, 10); dabSizeHelper(a, "Halve", KisDabShape(0.5, 1.0, 0.0), 5, 3); dabSizeHelper(a, "180 deg", KisDabShape(1.0, 1.0, M_PI), 10, 5); dabSizeHelper(a, "90 deg", KisDabShape(1.0, 1.0, M_PI_2), 6, 10); // ceil rule dabSizeHelper(a, "-90 deg", KisDabShape(1.0, 1.0, -M_PI_2), 6, 11); // ceil rule dabSizeHelper(a, "45 deg", KisDabShape(1.0, 1.0, 0.25 * M_PI), 11, 11); dabSizeHelper(a, "2x, 45d", KisDabShape(2.0, 1.0, 0.25 * M_PI), 22, 22); dabSizeHelper(a, "0.5x, 45d", KisDabShape(0.5, 1.0, 0.25 * M_PI), 6, 6); dabSizeHelper(a, "0.5x, 45d", KisDabShape(0.5, 1.0, 0.25 * M_PI), 6, 6); - dabSizeHelper(a, "0.5y", KisDabShape(1.0, 0.5, 0.0), 10, 3); + dabSizeHelper(a, "0.5y", KisDabShape(1.0, 0.5, 0.0), 10, 5); } //#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, KisDabShape(), 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), KisDabShape(0.8, 1.0, 8.0), info); QImage res1 = fdev1->convertToQImage(0); KisBrushSP clone = brush->clone(); KisFixedPaintDeviceSP fdev2 = new KisFixedPaintDevice(cs); clone->mask(fdev2, KoColor(Qt::black, cs), KisDabShape(0.8, 1.0, 8.0), info); QImage res2 = fdev2->convertToQImage(0); QCOMPARE(res1, res2); } QTEST_MAIN(KisAutoBrushTest) diff --git a/libs/image/kis_selection_filters.cpp b/libs/image/kis_selection_filters.cpp index 411521a01a..b790436013 100644 --- a/libs/image/kis_selection_filters.cpp +++ b/libs/image/kis_selection_filters.cpp @@ -1,864 +1,864 @@ /* * Copyright (c) 2005 Michael Thaler * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_selection_filters.h" #include #include #include "kis_convolution_painter.h" #include "kis_convolution_kernel.h" #include "kis_pixel_selection.h" #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define RINT(x) floor ((x) + 0.5) KisSelectionFilter::~KisSelectionFilter() { } KUndo2MagicString KisSelectionFilter::name() { return KUndo2MagicString(); } QRect KisSelectionFilter::changeRect(const QRect& rect) { return rect; } void KisSelectionFilter::computeBorder(qint32* circ, qint32 xradius, qint32 yradius) { qint32 i; qint32 diameter = xradius * 2 + 1; double tmp; for (i = 0; i < diameter; i++) { if (i > xradius) tmp = (i - xradius) - 0.5; else if (i < xradius) tmp = (xradius - i) - 0.5; else tmp = 0.0; circ[i] = (qint32) RINT(yradius / (double) xradius * sqrt(xradius * xradius - tmp * tmp)); } } void KisSelectionFilter::rotatePointers(quint8** p, quint32 n) { quint32 i; quint8 *p0 = p[0]; for (i = 0; i < n - 1; i++) { p[i] = p[i + 1]; } p[i] = p0; } void KisSelectionFilter::computeTransition(quint8* transition, quint8** buf, qint32 width) { qint32 x = 0; if (width == 1) { if (buf[1][x] > 127 && (buf[0][x] < 128 || buf[2][x] < 128)) transition[x] = 255; else transition[x] = 0; return; } if (buf[1][x] > 127) { if (buf[0][x] < 128 || buf[0][x + 1] < 128 || buf[1][x + 1] < 128 || buf[2][x] < 128 || buf[2][x + 1] < 128) transition[x] = 255; else transition[x] = 0; } else transition[x] = 0; for (qint32 x = 1; x < width - 1; x++) { if (buf[1][x] >= 128) { if (buf[0][x - 1] < 128 || buf[0][x] < 128 || buf[0][x + 1] < 128 || buf[1][x - 1] < 128 || buf[1][x + 1] < 128 || buf[2][x - 1] < 128 || buf[2][x] < 128 || buf[2][x + 1] < 128) transition[x] = 255; else transition[x] = 0; } else transition[x] = 0; } if (buf[1][x] >= 128) { if (buf[0][x - 1] < 128 || buf[0][x] < 128 || buf[1][x - 1] < 128 || buf[2][x - 1] < 128 || buf[2][x] < 128) transition[x] = 255; else transition[x] = 0; } else transition[x] = 0; } KUndo2MagicString KisErodeSelectionFilter::name() { return kundo2_i18n("Erode Selection"); } QRect KisErodeSelectionFilter::changeRect(const QRect& rect) { const qint32 radius = 1; return rect.adjusted(-radius, -radius, radius, radius); } void KisErodeSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { // Erode (radius 1 pixel) a mask (1bpp) quint8* buf[3]; qint32 width = rect.width(); qint32 height = rect.height(); quint8* out = new quint8[width]; for (qint32 i = 0; i < 3; i++) buf[i] = new quint8[width + 2]; // load top of image pixelSelection->readBytes(buf[0] + 1, rect.x(), rect.y(), width, 1); buf[0][0] = buf[0][1]; buf[0][width + 1] = buf[0][width]; memcpy(buf[1], buf[0], width + 2); for (qint32 y = 0; y < height; y++) { if (y + 1 < height) { pixelSelection->readBytes(buf[2] + 1, rect.x(), rect.y() + y + 1, width, 1); buf[2][0] = buf[2][1]; buf[2][width + 1] = buf[2][width]; } else { memcpy(buf[2], buf[1], width + 2); } for (qint32 x = 0 ; x < width; x++) { qint32 min = 255; if (buf[0][x+1] < min) min = buf[0][x+1]; if (buf[1][x] < min) min = buf[1][x]; if (buf[1][x+1] < min) min = buf[1][x+1]; if (buf[1][x+2] < min) min = buf[1][x+2]; if (buf[2][x+1] < min) min = buf[2][x+1]; out[x] = min; } pixelSelection->writeBytes(out, rect.x(), rect.y() + y, width, 1); rotatePointers(buf, 3); } for (qint32 i = 0; i < 3; i++) delete[] buf[i]; delete[] out; } KUndo2MagicString KisDilateSelectionFilter::name() { return kundo2_i18n("Dilate Selection"); } QRect KisDilateSelectionFilter::changeRect(const QRect& rect) { const qint32 radius = 1; return rect.adjusted(-radius, -radius, radius, radius); } void KisDilateSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { // dilate (radius 1 pixel) a mask (1bpp) quint8* buf[3]; qint32 width = rect.width(); qint32 height = rect.height(); quint8* out = new quint8[width]; for (qint32 i = 0; i < 3; i++) buf[i] = new quint8[width + 2]; // load top of image pixelSelection->readBytes(buf[0] + 1, rect.x(), rect.y(), width, 1); buf[0][0] = buf[0][1]; buf[0][width + 1] = buf[0][width]; memcpy(buf[1], buf[0], width + 2); for (qint32 y = 0; y < height; y++) { if (y + 1 < height) { pixelSelection->readBytes(buf[2] + 1, rect.x(), rect.y() + y + 1, width, 1); buf[2][0] = buf[2][1]; buf[2][width + 1] = buf[2][width]; } else { memcpy(buf[2], buf[1], width + 2); } for (qint32 x = 0 ; x < width; x++) { qint32 max = 0; if (buf[0][x+1] > max) max = buf[0][x+1]; if (buf[1][x] > max) max = buf[1][x]; if (buf[1][x+1] > max) max = buf[1][x+1]; if (buf[1][x+2] > max) max = buf[1][x+2]; if (buf[2][x+1] > max) max = buf[2][x+1]; out[x] = max; } pixelSelection->writeBytes(out, rect.x(), rect.y() + y, width, 1); rotatePointers(buf, 3); } for (qint32 i = 0; i < 3; i++) delete[] buf[i]; delete[] out; } KisBorderSelectionFilter::KisBorderSelectionFilter(qint32 xRadius, qint32 yRadius) : m_xRadius(xRadius), m_yRadius(yRadius) { } KUndo2MagicString KisBorderSelectionFilter::name() { return kundo2_i18n("Border Selection"); } QRect KisBorderSelectionFilter::changeRect(const QRect& rect) { return rect.adjusted(-m_xRadius, -m_yRadius, m_xRadius, m_yRadius); } void KisBorderSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { if (m_xRadius <= 0 || m_yRadius <= 0) return; quint8 *buf[3]; quint8 **density; quint8 **transition; if (m_xRadius == 1 && m_yRadius == 1) { // optimize this case specifically quint8* source[3]; for (qint32 i = 0; i < 3; i++) source[i] = new quint8[rect.width()]; quint8* transition = new quint8[rect.width()]; pixelSelection->readBytes(source[0], rect.x(), rect.y(), rect.width(), 1); memcpy(source[1], source[0], rect.width()); if (rect.height() > 1) pixelSelection->readBytes(source[2], rect.x(), rect.y() + 1, rect.width(), 1); else memcpy(source[2], source[1], rect.width()); computeTransition(transition, source, rect.width()); pixelSelection->writeBytes(transition, rect.x(), rect.y(), rect.width(), 1); for (qint32 y = 1; y < rect.height(); y++) { rotatePointers(source, 3); if (y + 1 < rect.height()) pixelSelection->readBytes(source[2], rect.x(), rect.y() + y + 1, rect.width(), 1); else memcpy(source[2], source[1], rect.width()); computeTransition(transition, source, rect.width()); pixelSelection->writeBytes(transition, rect.x(), rect.y() + y, rect.width(), 1); } for (qint32 i = 0; i < 3; i++) delete[] source[i]; delete[] transition; return; } qint32* max = new qint32[rect.width() + 2 * m_xRadius]; for (qint32 i = 0; i < (rect.width() + 2 * m_xRadius); i++) max[i] = m_yRadius + 2; max += m_xRadius; for (qint32 i = 0; i < 3; i++) buf[i] = new quint8[rect.width()]; transition = new quint8*[m_yRadius + 1]; for (qint32 i = 0; i < m_yRadius + 1; i++) { transition[i] = new quint8[rect.width() + 2 * m_xRadius]; memset(transition[i], 0, rect.width() + 2 * m_xRadius); transition[i] += m_xRadius; } quint8* out = new quint8[rect.width()]; density = new quint8*[2 * m_xRadius + 1]; density += m_xRadius; for (qint32 x = 0; x < (m_xRadius + 1); x++) { // allocate density[][] density[ x] = new quint8[2 * m_yRadius + 1]; density[ x] += m_yRadius; density[-x] = density[x]; } for (qint32 x = 0; x < (m_xRadius + 1); x++) { // compute density[][] double tmpx, tmpy, dist; quint8 a; if (x > 0) tmpx = x - 0.5; else if (x < 0) tmpx = x + 0.5; else tmpx = 0.0; for (qint32 y = 0; y < (m_yRadius + 1); y++) { if (y > 0) tmpy = y - 0.5; else if (y < 0) tmpy = y + 0.5; else tmpy = 0.0; dist = ((tmpy * tmpy) / (m_yRadius * m_yRadius) + (tmpx * tmpx) / (m_xRadius * m_xRadius)); if (dist < 1.0) a = (quint8)(255 * (1.0 - sqrt(dist))); else a = 0; density[ x][ y] = a; density[ x][-y] = a; density[-x][ y] = a; density[-x][-y] = a; } } pixelSelection->readBytes(buf[0], rect.x(), rect.y(), rect.width(), 1); memcpy(buf[1], buf[0], rect.width()); if (rect.height() > 1) pixelSelection->readBytes(buf[2], rect.x(), rect.y() + 1, rect.width(), 1); else memcpy(buf[2], buf[1], rect.width()); computeTransition(transition[1], buf, rect.width()); for (qint32 y = 1; y < m_yRadius && y + 1 < rect.height(); y++) { // set up top of image rotatePointers(buf, 3); pixelSelection->readBytes(buf[2], rect.x(), rect.y() + y + 1, rect.width(), 1); computeTransition(transition[y + 1], buf, rect.width()); } for (qint32 x = 0; x < rect.width(); x++) { // set up max[] for top of image max[x] = -(m_yRadius + 7); for (qint32 j = 1; j < m_yRadius + 1; j++) if (transition[j][x]) { max[x] = j; break; } } for (qint32 y = 0; y < rect.height(); y++) { // main calculation loop rotatePointers(buf, 3); rotatePointers(transition, m_yRadius + 1); if (y < rect.height() - (m_yRadius + 1)) { pixelSelection->readBytes(buf[2], rect.x(), rect.y() + y + m_yRadius + 1, rect.width(), 1); computeTransition(transition[m_yRadius], buf, rect.width()); } else memcpy(transition[m_yRadius], transition[m_yRadius - 1], rect.width()); for (qint32 x = 0; x < rect.width(); x++) { // update max array if (max[x] < 1) { if (max[x] <= -m_yRadius) { if (transition[m_yRadius][x]) max[x] = m_yRadius; else max[x]--; } else if (transition[-max[x]][x]) max[x] = -max[x]; else if (transition[-max[x] + 1][x]) max[x] = -max[x] + 1; else max[x]--; } else max[x]--; if (max[x] < -m_yRadius - 1) max[x] = -m_yRadius - 1; } quint8 last_max = max[0][density[-1]]; qint32 last_index = 1; for (qint32 x = 0 ; x < rect.width(); x++) { // render scan line last_index--; if (last_index >= 0) { last_max = 0; for (qint32 i = m_xRadius; i >= 0; i--) if (max[x + i] <= m_yRadius && max[x + i] >= -m_yRadius && density[i][max[x+i]] > last_max) { last_max = density[i][max[x + i]]; last_index = i; } out[x] = last_max; } else { last_max = 0; for (qint32 i = m_xRadius; i >= -m_xRadius; i--) if (max[x + i] <= m_yRadius && max[x + i] >= -m_yRadius && density[i][max[x + i]] > last_max) { last_max = density[i][max[x + i]]; last_index = i; } out[x] = last_max; } if (last_max == 0) { qint32 i; for (i = x + 1; i < rect.width(); i++) { if (max[i] >= -m_yRadius) break; } if (i - x > m_xRadius) { for (; x < i - m_xRadius; x++) out[x] = 0; x--; } last_index = m_xRadius; } } pixelSelection->writeBytes(out, rect.x(), rect.y() + y, rect.width(), 1); } delete [] out; for (qint32 i = 0; i < 3; i++) delete[] buf[i]; max -= m_xRadius; delete[] max; for (qint32 i = 0; i < m_yRadius + 1; i++) { transition[i] -= m_xRadius; delete transition[i]; } delete[] transition; for (qint32 i = 0; i < m_xRadius + 1 ; i++) { density[i] -= m_yRadius; delete density[i]; } density -= m_xRadius; delete[] density; } KisFeatherSelectionFilter::KisFeatherSelectionFilter(qint32 radius) : m_radius(radius) { } KUndo2MagicString KisFeatherSelectionFilter::name() { return kundo2_i18n("Feather Selection"); } QRect KisFeatherSelectionFilter::changeRect(const QRect& rect) { return rect.adjusted(-m_radius, -m_radius, m_radius, m_radius); } void KisFeatherSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { // compute horizontal kernel const uint kernelSize = m_radius * 2 + 1; Matrix gaussianMatrix(1, kernelSize); const qreal multiplicand = 1 / (2 * M_PI * m_radius * m_radius); const qreal exponentMultiplicand = 1 / (2 * m_radius * m_radius); for (uint x = 0; x < kernelSize; x++) { uint xDistance = qAbs((int)m_radius - (int)x); gaussianMatrix(0, x) = multiplicand * exp( -(qreal)((xDistance * xDistance) + (m_radius * m_radius)) * exponentMultiplicand ); } KisConvolutionKernelSP kernelHoriz = KisConvolutionKernel::fromMatrix(gaussianMatrix, 0, gaussianMatrix.sum()); KisConvolutionKernelSP kernelVertical = KisConvolutionKernel::fromMatrix(gaussianMatrix.transpose(), 0, gaussianMatrix.sum()); KisPaintDeviceSP interm = new KisPaintDevice(pixelSelection->colorSpace()); KisConvolutionPainter horizPainter(interm); horizPainter.setChannelFlags(interm->colorSpace()->channelFlags(false, true)); horizPainter.applyMatrix(kernelHoriz, pixelSelection, rect.topLeft(), rect.topLeft(), rect.size(), BORDER_REPEAT); horizPainter.end(); KisConvolutionPainter verticalPainter(pixelSelection); verticalPainter.setChannelFlags(pixelSelection->colorSpace()->channelFlags(false, true)); verticalPainter.applyMatrix(kernelVertical, interm, rect.topLeft(), rect.topLeft(), rect.size(), BORDER_REPEAT); verticalPainter.end(); } KisGrowSelectionFilter::KisGrowSelectionFilter(qint32 xRadius, qint32 yRadius) : m_xRadius(xRadius), m_yRadius(yRadius) { } KUndo2MagicString KisGrowSelectionFilter::name() { return kundo2_i18n("Grow Selection"); } QRect KisGrowSelectionFilter::changeRect(const QRect& rect) { return rect.adjusted(-m_xRadius, -m_yRadius, m_xRadius, m_yRadius); } void KisGrowSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { if (m_xRadius <= 0 || m_yRadius <= 0) return; /** * Much code resembles Shrink filter, so please fix bugs * in both filters */ quint8 **buf; // caches the region's pixel data quint8 **max; // caches the largest values for each column max = new quint8* [rect.width() + 2 * m_xRadius]; buf = new quint8* [m_yRadius + 1]; for (qint32 i = 0; i < m_yRadius + 1; i++) { buf[i] = new quint8[rect.width()]; } quint8* buffer = new quint8[(rect.width() + 2 * m_xRadius) *(m_yRadius + 1)]; for (qint32 i = 0; i < rect.width() + 2 * m_xRadius; i++) { if (i < m_xRadius) max[i] = buffer; else if (i < rect.width() + m_xRadius) max[i] = &buffer[(m_yRadius + 1) * (i - m_xRadius)]; else max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius - 1)]; for (qint32 j = 0; j < m_xRadius + 1; j++) max[i][j] = 0; } /* offset the max pointer by m_xRadius so the range of the array is [-m_xRadius] to [region->w + m_xRadius] */ max += m_xRadius; quint8* out = new quint8[ rect.width()]; // holds the new scan line we are computing qint32* circ = new qint32[ 2 * m_xRadius + 1 ]; // holds the y coords of the filter's mask computeBorder(circ, m_xRadius, m_yRadius); /* offset the circ pointer by m_xRadius so the range of the array is [-m_xRadius] to [m_xRadius] */ circ += m_xRadius; memset(buf[0], 0, rect.width()); for (qint32 i = 0; i < m_yRadius && i < rect.height(); i++) { // load top of image pixelSelection->readBytes(buf[i + 1], rect.x(), rect.y() + i, rect.width(), 1); } for (qint32 x = 0; x < rect.width() ; x++) { // set up max for top of image max[x][0] = 0; // buf[0][x] is always 0 max[x][1] = buf[1][x]; // MAX (buf[1][x], max[x][0]) always = buf[1][x] for (qint32 j = 2; j < m_yRadius + 1; j++) { max[x][j] = MAX(buf[j][x], max[x][j-1]); } } for (qint32 y = 0; y < rect.height(); y++) { rotatePointers(buf, m_yRadius + 1); if (y < rect.height() - (m_yRadius)) pixelSelection->readBytes(buf[m_yRadius], rect.x(), rect.y() + y + m_yRadius, rect.width(), 1); else memset(buf[m_yRadius], 0, rect.width()); for (qint32 x = 0; x < rect.width(); x++) { /* update max array */ for (qint32 i = m_yRadius; i > 0; i--) { max[x][i] = MAX(MAX(max[x][i - 1], buf[i - 1][x]), buf[i][x]); } max[x][0] = buf[0][x]; } qint32 last_max = max[0][circ[-1]]; qint32 last_index = 1; for (qint32 x = 0; x < rect.width(); x++) { /* render scan line */ last_index--; if (last_index >= 0) { if (last_max == 255) out[x] = 255; else { last_max = 0; for (qint32 i = m_xRadius; i >= 0; i--) if (last_max < max[x + i][circ[i]]) { last_max = max[x + i][circ[i]]; last_index = i; } out[x] = last_max; } } else { last_index = m_xRadius; last_max = max[x + m_xRadius][circ[m_xRadius]]; for (qint32 i = m_xRadius - 1; i >= -m_xRadius; i--) if (last_max < max[x + i][circ[i]]) { last_max = max[x + i][circ[i]]; last_index = i; } out[x] = last_max; } } pixelSelection->writeBytes(out, rect.x(), rect.y() + y, rect.width(), 1); } /* undo the offsets to the pointers so we can free the malloced memmory */ circ -= m_xRadius; max -= m_xRadius; delete[] circ; delete[] buffer; delete[] max; for (qint32 i = 0; i < m_yRadius + 1; i++) delete[] buf[i]; delete[] buf; delete[] out; } KisShrinkSelectionFilter::KisShrinkSelectionFilter(qint32 xRadius, qint32 yRadius, bool edgeLock) : m_xRadius(xRadius), m_yRadius(yRadius), m_edgeLock(edgeLock) { } KUndo2MagicString KisShrinkSelectionFilter::name() { return kundo2_i18n("Shrink Selection"); } QRect KisShrinkSelectionFilter::changeRect(const QRect& rect) { return rect.adjusted(-m_xRadius, -m_yRadius, m_xRadius, m_yRadius); } void KisShrinkSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { if (m_xRadius <= 0 || m_yRadius <= 0) return; /* pretty much the same as fatten_region only different blame all bugs in this function on jaycox@gimp.org */ /* If edge_lock is true we assume that pixels outside the region we are passed are identical to the edge pixels. If edge_lock is false, we assume that pixels outside the region are 0 */ quint8 **buf; // caches the region's pixels quint8 **max; // caches the smallest values for each column qint32 last_max, last_index; max = new quint8* [rect.width() + 2 * m_xRadius]; buf = new quint8* [m_yRadius + 1]; for (qint32 i = 0; i < m_yRadius + 1; i++) { buf[i] = new quint8[rect.width()]; } qint32 buffer_size = (rect.width() + 2 * m_xRadius + 1) * (m_yRadius + 1); quint8* buffer = new quint8[buffer_size]; if (m_edgeLock) memset(buffer, 255, buffer_size); else memset(buffer, 0, buffer_size); for (qint32 i = 0; i < rect.width() + 2 * m_xRadius; i++) { if (i < m_xRadius) if (m_edgeLock) max[i] = buffer; else max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius)]; else if (i < rect.width() + m_xRadius) max[i] = &buffer[(m_yRadius + 1) * (i - m_xRadius)]; else if (m_edgeLock) max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius - 1)]; else max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius)]; } if (!m_edgeLock) for (qint32 j = 0 ; j < m_xRadius + 1; j++) max[0][j] = 0; // offset the max pointer by m_xRadius so the range of the array is [-m_xRadius] to [region->w + m_xRadius] max += m_xRadius; quint8* out = new quint8[rect.width()]; // holds the new scan line we are computing qint32* circ = new qint32[2 * m_xRadius + 1]; // holds the y coords of the filter's mask computeBorder(circ, m_xRadius, m_yRadius); // offset the circ pointer by m_xRadius so the range of the array is [-m_xRadius] to [m_xRadius] circ += m_xRadius; for (qint32 i = 0; i < m_yRadius && i < rect.height(); i++) // load top of image pixelSelection->readBytes(buf[i + 1], rect.x(), rect.y() + i, rect.width(), 1); if (m_edgeLock) memcpy(buf[0], buf[1], rect.width()); else memset(buf[0], 0, rect.width()); for (qint32 x = 0; x < rect.width(); x++) { // set up max for top of image max[x][0] = buf[0][x]; for (qint32 j = 1; j < m_yRadius + 1; j++) max[x][j] = MIN(buf[j][x], max[x][j-1]); } for (qint32 y = 0; y < rect.height(); y++) { rotatePointers(buf, m_yRadius + 1); if (y < rect.height() - m_yRadius) pixelSelection->readBytes(buf[m_yRadius], rect.x(), rect.y() + y + m_yRadius, rect.width(), 1); else if (m_edgeLock) memcpy(buf[m_yRadius], buf[m_yRadius - 1], rect.width()); else memset(buf[m_yRadius], 0, rect.width()); for (qint32 x = 0 ; x < rect.width(); x++) { // update max array for (qint32 i = m_yRadius; i > 0; i--) { max[x][i] = MIN(MIN(max[x][i - 1], buf[i - 1][x]), buf[i][x]); } max[x][0] = buf[0][x]; } last_max = max[0][circ[-1]]; last_index = 0; for (qint32 x = 0 ; x < rect.width(); x++) { // render scan line last_index--; if (last_index >= 0) { if (last_max == 0) out[x] = 0; else { last_max = 255; for (qint32 i = m_xRadius; i >= 0; i--) if (last_max > max[x + i][circ[i]]) { last_max = max[x + i][circ[i]]; last_index = i; } out[x] = last_max; } } else { last_index = m_xRadius; last_max = max[x + m_xRadius][circ[m_xRadius]]; for (qint32 i = m_xRadius - 1; i >= -m_xRadius; i--) if (last_max > max[x + i][circ[i]]) { last_max = max[x + i][circ[i]]; last_index = i; } out[x] = last_max; } } pixelSelection->writeBytes(out, rect.x(), rect.y() + y, rect.width(), 1); } // undo the offsets to the pointers so we can free the malloced memmory circ -= m_xRadius; max -= m_xRadius; delete[] circ; delete[] buffer; delete[] max; for (qint32 i = 0; i < m_yRadius + 1; i++) - delete buf[i]; + delete[] buf[i]; delete[] buf; delete[] out; } KUndo2MagicString KisSmoothSelectionFilter::name() { return kundo2_i18n("Smooth Selection"); } QRect KisSmoothSelectionFilter::changeRect(const QRect& rect) { const qint32 radius = 1; return rect.adjusted(-radius, -radius, radius, radius); } void KisSmoothSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { // Simple convolution filter to smooth a mask (1bpp) quint8 *buf[3]; qint32 width = rect.width(); qint32 height = rect.height(); quint8* out = new quint8[width]; for (qint32 i = 0; i < 3; i++) buf[i] = new quint8[width + 2]; // load top of image pixelSelection->readBytes(buf[0] + 1, rect.x(), rect.y(), width, 1); buf[0][0] = buf[0][1]; buf[0][width + 1] = buf[0][width]; memcpy(buf[1], buf[0], width + 2); for (qint32 y = 0; y < height; y++) { if (y + 1 < height) { pixelSelection->readBytes(buf[2] + 1, rect.x(), rect.y() + y + 1, width, 1); buf[2][0] = buf[2][1]; buf[2][width + 1] = buf[2][width]; } else { memcpy(buf[2], buf[1], width + 2); } for (qint32 x = 0 ; x < width; x++) { qint32 value = (buf[0][x] + buf[0][x+1] + buf[0][x+2] + buf[1][x] + buf[2][x+1] + buf[1][x+2] + buf[2][x] + buf[1][x+1] + buf[2][x+2]); out[x] = value / 9; } pixelSelection->writeBytes(out, rect.x(), rect.y() + y, width, 1); rotatePointers(buf, 3); } for (qint32 i = 0; i < 3; i++) delete[] buf[i]; delete[] out; } KUndo2MagicString KisInvertSelectionFilter::name() { return kundo2_i18n("Invert Selection"); } QRect KisInvertSelectionFilter::changeRect(const QRect& rect) { Q_UNUSED(rect); return QRect(); } void KisInvertSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { Q_UNUSED(rect); pixelSelection->invert(); } diff --git a/libs/image/tests/kis_convolution_painter_test.cpp b/libs/image/tests/kis_convolution_painter_test.cpp index 0da4cb4192..d8898fd789 100644 --- a/libs/image/tests/kis_convolution_painter_test.cpp +++ b/libs/image/tests/kis_convolution_painter_test.cpp @@ -1,440 +1,440 @@ /* * 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. */ #include //MSVC requires that Vc come first #include "kis_convolution_painter_test.h" #include #include #include #include #include #include #include "kis_paint_device.h" #include "kis_convolution_painter.h" #include "kis_convolution_kernel.h" #include #include #include "testutil.h" KisPaintDeviceSP initAsymTestDevice(QRect &imageRect, int &pixelSize, QByteArray &initialData) { KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); pixelSize = dev->pixelSize(); imageRect = QRect(0,0,5,5); initialData.resize(25 * pixelSize); quint8 *ptr = (quint8*) initialData.data(); for(int i = 0; i < 25; i++) { KoColor pixel(QColor(i,i,i,255), dev->colorSpace()); memcpy(ptr, pixel.data(), pixelSize); ptr += pixelSize; } dev->writeBytes((const quint8*)initialData.constData(), imageRect); return dev; } Matrix initSymmFilter(qreal &offset, qreal &factor) { Matrix filter; filter(0,0) = 1.0 / 21; filter(0,1) = 3.0 / 21; filter(0,2) = 1.0 / 21; filter(1,0) = 3.0 / 21; filter(1,1) = 5.0 / 21; filter(1,2) = 3.0 / 21; filter(2,0) = 1.0 / 21; filter(2,1) = 3.0 / 21; filter(2,2) = 1.0 / 21; offset = 0.0; factor = 1.0; return filter; } Matrix initAsymmFilter(qreal &offset, qreal &factor) { Matrix filter; filter(0,0) = 1.0; filter(1,0) = 2.0; filter(2,0) = 1.0; filter(0,1) = 0.0; filter(1,1) = 1.0; filter(2,1) = 0.0; filter(0,2) =-1.0; filter(1,2) =-2.0; filter(2,2) =-1.0; offset = 0.0; factor = 1.0; return filter; } void printPixel(QString prefix, int pixelSize, quint8 *data) { QString str = prefix; for(int i = 0; i < pixelSize; i++) { str += ' '; str += QString::number(data[i]); } dbgKrita << str; } void KisConvolutionPainterTest::testIdentityConvolution() { QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); dev->convertFromQImage(qimage, 0, 0, 0); KisConvolutionKernelSP kernel = new KisConvolutionKernel(3, 3, 0, 0); kernel->data()(0) = 0; kernel->data()(1) = 0; kernel->data()(2) = 0; kernel->data()(3) = 0; kernel->data()(4) = 1; kernel->data()(5) = 0; kernel->data()(6) = 0; kernel->data()(7) = 0; kernel->data()(8) = 0; KisConvolutionPainter gc(dev); gc.beginTransaction(); gc.applyMatrix(kernel, dev, QPoint(0, 0), QPoint(0, 0), QSize(qimage.width(), qimage.height())); gc.deleteTransaction(); QImage resultImage = dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, qimage, resultImage)) { resultImage.save("identity_convolution.png"); QFAIL(QString("Identity kernel did change image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisConvolutionPainterTest::testSymmConvolution() { qreal offset = 0.0; qreal factor = 1.0; Matrix filter = initSymmFilter(offset, factor); QRect imageRect; int pixelSize = 0; QByteArray initialData; KisPaintDeviceSP dev = initAsymTestDevice(imageRect, pixelSize, initialData); KisConvolutionKernelSP kernel = KisConvolutionKernel::fromMatrix(filter, offset, factor); KisConvolutionPainter gc(dev); gc.beginTransaction(); QRect filterRect = imageRect.adjusted(1,1,-1,-1); gc.applyMatrix(kernel, dev, filterRect.topLeft(), filterRect.topLeft(), filterRect.size()); gc.deleteTransaction(); QByteArray resultData(initialData.size(), 0); dev->readBytes((quint8*)resultData.data(), imageRect); QCOMPARE(resultData, initialData); } void KisConvolutionPainterTest::testAsymmConvolutionImp(QBitArray channelFlags) { qreal offset = 0.0; qreal factor = 1.0; Matrix filter = initAsymmFilter(offset, factor); QRect imageRect; int pixelSize = -1; QByteArray initialData; KisPaintDeviceSP dev = initAsymTestDevice(imageRect, pixelSize, initialData); KisConvolutionKernelSP kernel = KisConvolutionKernel::fromMatrix(filter, offset, factor); KisConvolutionPainter gc(dev); gc.beginTransaction(); gc.setChannelFlags(channelFlags); QRect filterRect = imageRect.adjusted(1,1,-1,-1); gc.applyMatrix(kernel, dev, filterRect.topLeft(), filterRect.topLeft(), filterRect.size()); gc.deleteTransaction(); QByteArray resultData(initialData.size(), 0); dev->readBytes((quint8*)resultData.data(), imageRect); QRect filteredRect = imageRect.adjusted(1, 1, -1, -1); quint8 *srcPtr = (quint8*) initialData.data(); quint8 *resPtr = (quint8*) resultData.data(); for(int row = 0; row < imageRect.height(); row++) { for(int col = 0; col < imageRect.width(); col++) { bool isFiltered = filteredRect.contains(col, row); int pixelValue = 8 + row * imageRect.width() + col; KoColor filteredPixel(QColor(pixelValue, pixelValue, pixelValue, 255), dev->colorSpace()); KoColor resultPixel(dev->colorSpace()); for(int j = 0; j < pixelSize; j++) { resultPixel.data()[j] = isFiltered && channelFlags[j] ? filteredPixel.data()[j] : srcPtr[j]; } if(memcmp(resPtr, resultPixel.data(), pixelSize)) { printPixel("Actual: ", pixelSize, resPtr); printPixel("Expected:", pixelSize, resultPixel.data()); QFAIL("Failed to filter area"); } srcPtr += pixelSize; resPtr += pixelSize; } } } void KisConvolutionPainterTest::testAsymmAllChannels() { QBitArray channelFlags = KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true); testAsymmConvolutionImp(channelFlags); } void KisConvolutionPainterTest::testAsymmSkipRed() { QBitArray channelFlags = KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true); channelFlags[2] = false; testAsymmConvolutionImp(channelFlags); } void KisConvolutionPainterTest::testAsymmSkipGreen() { QBitArray channelFlags = KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true); channelFlags[1] = false; testAsymmConvolutionImp(channelFlags); } void KisConvolutionPainterTest::testAsymmSkipBlue() { QBitArray channelFlags = KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true); channelFlags[0] = false; testAsymmConvolutionImp(channelFlags); } void KisConvolutionPainterTest::testAsymmSkipAlpha() { QBitArray channelFlags = KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true); channelFlags[3] = false; testAsymmConvolutionImp(channelFlags); } // #include void KisConvolutionPainterTest::benchmarkConvolution() { QImage referenceImage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); QRect imageRect(QPoint(), referenceImage.size()); KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); dev->convertFromQImage(referenceImage, 0, 0, 0); int diameter = 1; - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 4; i++) { KisCircleMaskGenerator* kas = new KisCircleMaskGenerator(diameter, 1.0, 5, 5, 2, false); KisConvolutionKernelSP kernel = KisConvolutionKernel::fromMaskGenerator(kas); KisConvolutionPainter gc(dev); QTime timer; timer.start(); // CALLGRIND_START_INSTRUMENTATION; gc.beginTransaction(); gc.applyMatrix(kernel, dev, imageRect.topLeft(), imageRect.topLeft(), imageRect.size()); gc.deleteTransaction(); // CALLGRIND_STOP_INSTRUMENTATION; dbgKrita << "Diameter:" << diameter << "time:" << timer.elapsed(); - if(diameter < 10) { + if(diameter < 4) { diameter += 2; } else { diameter += 8; } } } void KisConvolutionPainterTest::testGaussianBase(KisPaintDeviceSP dev, bool useFftw, const QString &prefix) { QBitArray channelFlags = KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true); KisPainter gc(dev); qreal horizontalRadius = 5, verticalRadius = 5; for(int i = 0; i < 3 ; i++, horizontalRadius+=5, verticalRadius+=5) { QTime timer; timer.start(); gc.beginTransaction(); if (( horizontalRadius > 0 ) && ( verticalRadius > 0 )) { KisPaintDeviceSP interm = new KisPaintDevice(dev->colorSpace()); KisConvolutionKernelSP kernelHoriz = KisGaussianKernel::createHorizontalKernel(horizontalRadius); KisConvolutionKernelSP kernelVertical = KisGaussianKernel::createVerticalKernel(verticalRadius); const QRect applyRect = dev->exactBounds(); KisConvolutionPainter::TestingEnginePreference enginePreference = useFftw ? KisConvolutionPainter::FFTW : KisConvolutionPainter::SPATIAL; KisConvolutionPainter horizPainter(interm, enginePreference); horizPainter.setChannelFlags(channelFlags); horizPainter.applyMatrix(kernelHoriz, dev, applyRect.topLeft() - QPoint(0, verticalRadius), applyRect.topLeft() - QPoint(0, verticalRadius), applyRect.size() + QSize(0, 2 * verticalRadius), BORDER_REPEAT); KisConvolutionPainter verticalPainter(dev, enginePreference); verticalPainter.setChannelFlags(channelFlags); verticalPainter.applyMatrix(kernelVertical, interm, applyRect.topLeft(), applyRect.topLeft(), applyRect.size(), BORDER_REPEAT); QImage result = dev->convertToQImage(0, applyRect.x(), applyRect.y(), applyRect.width(), applyRect.height()); QString engine = useFftw ? "fftw" : "spatial"; QString testCaseName = QString("test_gaussian_%1_%2_%3.png").arg(horizontalRadius).arg(verticalRadius).arg(engine); TestUtil::checkQImage(result, "convolution_painter_test", QString("gaussian_") + prefix, testCaseName); gc.revertTransaction(); } dbgKrita << "Elapsed time:" << timer.elapsed() << "ms"; } } void KisConvolutionPainterTest::testGaussian(bool useFftw) { QImage referenceImage(TestUtil::fetchDataFileLazy("kritaTransparent.png")); KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); dev->convertFromQImage(referenceImage, 0, 0, 0); testGaussianBase(dev, useFftw, ""); } void KisConvolutionPainterTest::testGaussianSpatial() { testGaussian(false); } void KisConvolutionPainterTest::testGaussianFFTW() { testGaussian(true); } void KisConvolutionPainterTest::testGaussianSmall(bool useFftw) { KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); KoColor c(Qt::yellow, dev->colorSpace()); for (int i = 0; i < 50; i++) { quint8 baseOpacity = 75; KoColor c(Qt::magenta, dev->colorSpace()); for (int j = 0; j <= 6; j++) { c.setOpacity(static_cast(baseOpacity + 30 * j)); dev->setPixel(i + j, i, c); } } testGaussianBase(dev, useFftw, "reduced"); } void KisConvolutionPainterTest::testGaussianSmallSpatial() { testGaussianSmall(false); } void KisConvolutionPainterTest::testGaussianSmallFFTW() { testGaussianSmall(true); } void KisConvolutionPainterTest::testGaussianDetails(bool useFftw) { QImage referenceImage(TestUtil::fetchDataFileLazy("resolution_test.png")); KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); dev->convertFromQImage(referenceImage, 0, 0, 0); testGaussianBase(dev, useFftw, "details"); } void KisConvolutionPainterTest::testGaussianDetailsSpatial() { testGaussianDetails(false); } void KisConvolutionPainterTest::testGaussianDetailsFFTW() { testGaussianDetails(true); } QTEST_MAIN(KisConvolutionPainterTest) diff --git a/libs/image/tests/kis_fixed_paint_device_test.cpp b/libs/image/tests/kis_fixed_paint_device_test.cpp index ed95ae122e..caad6a0f73 100644 --- a/libs/image/tests/kis_fixed_paint_device_test.cpp +++ b/libs/image/tests/kis_fixed_paint_device_test.cpp @@ -1,370 +1,370 @@ /* * 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_fixed_paint_device_test.h" #include #include #include #include #include #include "kis_painter.h" #include "kis_types.h" #include "kis_paint_device.h" #include "kis_fixed_paint_device.h" #include "kis_layer.h" #include "kis_paint_layer.h" #include "kis_selection.h" #include "kis_datamanager.h" #include "kis_global.h" #include "testutil.h" #include "kis_transaction.h" #include "kis_image.h" void KisFixedPaintDeviceTest::testCreation() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); dev = new KisFixedPaintDevice(cs); QVERIFY(dev->bounds() == QRect()); QVERIFY(*dev->colorSpace() == *cs); QVERIFY(dev->pixelSize() == cs->pixelSize()); dev->setRect(QRect(0, 0, 100, 100)); QVERIFY(dev->bounds() == QRect(0, 0, 100, 100)); dev->initialize(); QVERIFY(dev->data() != 0); quint8* data = dev->data(); for (uint i = 0; i < 100 * 100 * cs->pixelSize(); ++i) { QVERIFY(data[i] == 0); } } void logFailure(const QString & reason, const KoColorSpace * srcCs, const KoColorSpace * dstCs) { QString profile1("no profile"); QString profile2("no profile"); if (srcCs->profile()) profile1 = srcCs->profile()->name(); if (dstCs->profile()) profile2 = dstCs->profile()->name(); QWARN(QString("Failed %1 %2 -> %3 %4 %5") .arg(srcCs->name()) .arg(profile1) .arg(dstCs->name()) .arg(profile2) .arg(reason) .toLatin1()); } void KisFixedPaintDeviceTest::testColorSpaceConversion() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png"); const KoColorSpace* srcCs = KoColorSpaceRegistry::instance()->rgb8(); const KoColorSpace* dstCs = KoColorSpaceRegistry::instance()->lab16(); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(srcCs); dev->convertFromQImage(image, 0); dev->convertTo(dstCs); QVERIFY(dev->bounds() == QRect(0, 0, image.width(), image.height())); QVERIFY(dev->pixelSize() == dstCs->pixelSize()); QVERIFY(*dev->colorSpace() == *dstCs); } void KisFixedPaintDeviceTest::testRoundtripQImageConversion() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); dev->convertFromQImage(image, 0); QImage result = dev->convertToQImage(0, 0, 0, 640, 441); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_fixed_paint_device_test_test_roundtrip_qimage.png"); result.save("kis_fixed_paint_device_test_test_roundtrip_result.png"); QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisFixedPaintDeviceTest::testBltFixed() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs); fdev->convertFromQImage(image, 0); // Without opacity KisPaintDeviceSP dev = new KisPaintDevice(cs); KisPainter gc(dev); gc.bltFixed(QPoint(0, 0), fdev, image.rect()); QImage result = dev->convertToQImage(0, 0, 0, 640, 441); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, image, result, 1)) { fdev->convertToQImage(0).save("kis_fixed_paint_device_test_test_blt_fixed_expected.png"); result.save("kis_fixed_paint_device_test_test_blt_fixed_result.png"); QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisFixedPaintDeviceTest::testBltFixedOpacity() { // blt a semi-transparent image on a white paint device QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs); fdev->convertFromQImage(image, 0); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->fill(0, 0, 640, 441, KoColor(Qt::white, cs).data()); KisPainter gc(dev); gc.bltFixed(QPoint(0, 0), fdev, image.rect()); QImage result = dev->convertToQImage(0, 0, 0, 640, 441); QImage checkResult(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent_result.png"); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, checkResult, result, 1)) { checkResult.save("kis_fixed_paint_device_test_test_blt_fixed_opactiy_expected.png"); result.save("kis_fixed_paint_device_test_test_blt_fixed_opacity_result.png"); QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisFixedPaintDeviceTest::testSilly() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); dev->initialize(); dev->initialize(); } void KisFixedPaintDeviceTest::testClear() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); dev->clear(QRect(0, 0, 100, 100)); QVERIFY(dev->bounds() == QRect(0, 0, 100, 100)); QVERIFY(cs->opacityU8(dev->data() + (50 * 50 * cs->pixelSize())) == OPACITY_TRANSPARENT_U8); } void KisFixedPaintDeviceTest::testFill() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); quint8* red = new quint8[cs->pixelSize()]; memcpy(red, KoColor(Qt::red, cs).data(), cs->pixelSize()); cs->setOpacity(red, quint8(128), 1); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); dev->fill(0, 0, 100, 100, red); QVERIFY(dev->bounds() == QRect(0, 0, 100, 100)); QVERIFY(cs->opacityU8(dev->data()) == 128); QVERIFY(memcmp(dev->data(), red, cs->pixelSize()) == 0); //Compare fill will normal paint device dev = new KisFixedPaintDevice(cs); dev->setRect(QRect(0, 0, 150, 150)); dev->initialize(); dev->fill(50, 50, 50, 50, red); KisPaintDeviceSP dev2 = new KisPaintDevice(cs); dev2->fill(50, 50, 50, 50, red); QImage image = dev->convertToQImage(0); QImage checkImage = dev2->convertToQImage(0, 0, 0, 150, 150); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, image, checkImage)) { image.save("kis_fixed_paint_device_filled_result.png"); checkImage.save("kis_fixed_paint_device_filled_result_expected.png"); QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } delete[] red; } void KisFixedPaintDeviceTest::testBltFixedSmall() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "fixed_blit_small.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs); fdev->convertFromQImage(image, 0); // Without opacity KisPaintDeviceSP dev = new KisPaintDevice(cs); KisPainter gc(dev); gc.bltFixed(QPoint(0, 0), fdev, image.rect()); QImage result = dev->convertToQImage(0, 0, 0, 51, 51); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_fixed_paint_device_test_blt_small_image.png"); result.save("kis_fixed_paint_device_test_blt_small_result.png"); QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisFixedPaintDeviceTest::testBltPerformance() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs); fdev->convertFromQImage(image, 0); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->fill(0, 0, 640, 441, KoColor(Qt::white, cs).data()); QTime t; t.start(); int x; - for (x = 0; x < 1000; ++x) { + for (x = 0; x < 100; ++x) { KisPainter gc(dev); gc.bltFixed(QPoint(0, 0), fdev, image.rect()); } dbgKrita << x << "blits" << " done in " << t.elapsed() << "ms"; } inline void setPixel(KisFixedPaintDeviceSP dev, int x, int y, quint8 alpha) { KoColor c(Qt::black, dev->colorSpace()); c.setOpacity(alpha); dev->fill(x, y, 1, 1, c.data()); } inline quint8 pixel(KisFixedPaintDeviceSP dev, int x, int y) { KoColor c(Qt::black, dev->colorSpace()); dev->readBytes(c.data(), x, y, 1, 1); return c.opacityU8(); } void KisFixedPaintDeviceTest::testMirroring_data() { QTest::addColumn("rc"); QTest::addColumn("mirrorHorizontally"); QTest::addColumn("mirrorVertically"); QTest::newRow("4, false, false") << (QRect(99,99,4,4)) << false << false; QTest::newRow("4, false, true") << (QRect(99,99,4,4)) << false << true; QTest::newRow("4, true, false") << (QRect(99,99,4,4)) << true << false; QTest::newRow("4, true, true") << (QRect(99,99,4,4)) << true << true; QTest::newRow("5, false, false") << (QRect(99,99,5,5)) << false << false; QTest::newRow("5, false, true") << (QRect(99,99,5,5)) << false << true; QTest::newRow("5, true, false") << (QRect(99,99,5,5)) << true << false; QTest::newRow("5, true, true") << (QRect(99,99,5,5)) << true << true; } void KisFixedPaintDeviceTest::testMirroring() { QFETCH(QRect, rc); QFETCH(bool, mirrorHorizontally); QFETCH(bool, mirrorVertically); const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); dev->setRect(rc); dev->initialize(); KoColor c(Qt::black, cs); qsrand(1); int value = 0; for (int i = rc.x(); i < rc.x() + rc.width(); i++) { for (int j = rc.y(); j < rc.y() + rc.height(); j++) { setPixel(dev, i, j, value); value = qrand() % 255; } value = qrand() % 255; } //dev->convertToQImage(0).save("0_a.png"); dev->mirror(mirrorHorizontally, mirrorVertically); //dev->convertToQImage(0).save("0_b.png"); int startX; int endX; int incX; int startY; int endY; int incY; if (mirrorHorizontally) { startX = rc.x() + rc.width() - 1; endX = rc.x() - 1; incX = -1; } else { startX = rc.x(); endX = rc.x() + rc.width(); incX = 1; } if (mirrorVertically) { startY = rc.y() + rc.height() - 1; endY = rc.y() - 1; incY = -1; } else { startY = rc.y(); endY = rc.y() + rc.height(); incY = 1; } qsrand(1); value = 0; for (int i = startX; i != endX ; i += incX) { for (int j = startY; j != endY; j += incY) { QCOMPARE(pixel(dev, i, j), (quint8)value); value = qrand() % 255; } value = qrand() % 255; } } QTEST_MAIN(KisFixedPaintDeviceTest) diff --git a/libs/image/tests/kis_warp_transform_worker_test.cpp b/libs/image/tests/kis_warp_transform_worker_test.cpp index 764f526876..d29085a4fe 100644 --- a/libs/image/tests/kis_warp_transform_worker_test.cpp +++ b/libs/image/tests/kis_warp_transform_worker_test.cpp @@ -1,353 +1,353 @@ /* * 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_warp_transform_worker_test.h" #include #include "testutil.h" #include "kis_warptransform_worker.h" #include struct WarpTransforWorkerData { WarpTransforWorkerData() { TestUtil::TestProgressBar bar; KoProgressUpdater pu(&bar); updater = pu.startSubtask(); const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); // QImage image(TestUtil::fetchDataFileLazy("test_transform_quality.png")); QImage image(TestUtil::fetchDataFileLazy("test_transform_quality_second.png")); dev = new KisPaintDevice(cs); dev->convertFromQImage(image, 0); alpha = 1.0; bounds = dev->exactBounds(); origPoints << bounds.topLeft(); origPoints << bounds.topRight(); origPoints << bounds.bottomRight(); origPoints << bounds.bottomLeft(); origPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()); origPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) + QPointF(-20, 0); transfPoints << bounds.topLeft(); transfPoints << bounds.bottomLeft() + 0.6 * (bounds.topRight() - bounds.bottomLeft()); transfPoints << bounds.topLeft() + 0.8 * (bounds.bottomRight() - bounds.topLeft()); transfPoints << bounds.bottomLeft() + QPointF(200, 0); transfPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) + QPointF(40,20); transfPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) + QPointF(-20, 0) + QPointF(-40,20); } KisPaintDeviceSP dev; QVector origPoints; QVector transfPoints; qreal alpha; KoUpdaterPtr updater; QRectF bounds; }; void KisWarpTransformWorkerTest::test() { WarpTransforWorkerData d; KisWarpTransformWorker worker(KisWarpTransformWorker::RIGID_TRANSFORM, d.dev, d.origPoints, d.transfPoints, d.alpha, d.updater); QBENCHMARK_ONCE { worker.run(); } QImage result = d.dev->convertToQImage(0); TestUtil::checkQImage(result, "warp_transform_test", "simple", "tr"); } void KisWarpTransformWorkerTest::testQImage() { TestUtil::TestProgressBar bar; KoProgressUpdater pu(&bar); KoUpdaterPtr updater = pu.startSubtask(); // QImage image(TestUtil::fetchDataFileLazy("test_transform_quality.png")); QImage image(TestUtil::fetchDataFileLazy("test_transform_quality_second.png")); image = image.convertToFormat(QImage::Format_ARGB32); dbgKrita << ppVar(image.format()); QVector origPoints; QVector transfPoints; qreal alpha = 1.0; QRectF bounds(image.rect()); origPoints << bounds.topLeft(); origPoints << bounds.topRight(); origPoints << bounds.bottomRight(); origPoints << bounds.bottomLeft(); origPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()); origPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) + QPointF(-20, 0); transfPoints << bounds.topLeft(); transfPoints << bounds.bottomLeft() + 0.6 * (bounds.topRight() - bounds.bottomLeft()); transfPoints << bounds.topLeft() + 0.8 * (bounds.bottomRight() - bounds.topLeft()); transfPoints << bounds.bottomLeft() + QPointF(200, 0); transfPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) + QPointF(40,20); transfPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) + QPointF(-20, 0) + QPointF(-40,20); QImage result; QPointF newOffset; QBENCHMARK_ONCE { result = KisWarpTransformWorker::transformQImage( KisWarpTransformWorker::RIGID_TRANSFORM, origPoints, transfPoints, alpha, image, QPointF(), &newOffset); } dbgKrita << ppVar(newOffset); TestUtil::checkQImage(result, "warp_transform_test", "qimage", "tr"); } #include "kis_four_point_interpolator_forward.h" void KisWarpTransformWorkerTest::testForwardInterpolator() { QPolygonF src; src << QPointF(0, 0); src << QPointF(100, 0); src << QPointF(100, 100); src << QPointF(0, 100); QPolygonF dst; dst << QPointF(0, 0); dst << QPointF(100, 10); dst << QPointF(100, 120); dst << QPointF(0, 100); KisFourPointInterpolatorForward interp(src, dst); QCOMPARE(interp.map(QPointF(0,50)), QPointF(0,50)); QCOMPARE(interp.map(QPointF(50,0)), QPointF(50,5)); QCOMPARE(interp.map(QPointF(100,0)), QPointF(100,10)); QCOMPARE(interp.map(QPointF(100,50)), QPointF(100,65)); QCOMPARE(interp.map(QPointF(100,100)), QPointF(100,120)); QCOMPARE(interp.map(QPointF(50,100)), QPointF(50,110)); QCOMPARE(interp.map(QPointF(50,50)), QPointF(50,57.5)); } #include "kis_four_point_interpolator_backward.h" void KisWarpTransformWorkerTest::testBackwardInterpolatorXShear() { QPolygonF src; src << QPointF(0, 0); src << QPointF(100, 0); src << QPointF(100, 100); src << QPointF(0, 100); QPolygonF dst; dst << QPointF(0, 0); dst << QPointF(100, 0); dst << QPointF(120, 100); dst << QPointF(10, 100); KisFourPointInterpolatorBackward interp(src, dst); QCOMPARE(interp.map(QPointF(10,100)), QPointF(0,100)); QCOMPARE(interp.map(QPointF(5,50)), QPointF(0,50)); QCOMPARE(interp.map(QPointF(110,50)), QPointF(100,50)); QCOMPARE(interp.map(QPointF(57.5,50)), QPointF(50,50)); } void KisWarpTransformWorkerTest::testBackwardInterpolatorYShear() { QPolygonF src; src << QPointF(0, 0); src << QPointF(100, 0); src << QPointF(100, 100); src << QPointF(0, 100); QPolygonF dst; dst << QPointF(0, 0); dst << QPointF(100, 10); dst << QPointF(100, 120); dst << QPointF(0, 100); KisFourPointInterpolatorBackward interp(src, dst); QCOMPARE(interp.map(QPointF(100,10)), QPointF(100,0)); QCOMPARE(interp.map(QPointF(50,5)), QPointF(50,0)); QCOMPARE(interp.map(QPointF(50,110)), QPointF(50,100)); QCOMPARE(interp.map(QPointF(50,57.5)), QPointF(50,50)); } void KisWarpTransformWorkerTest::testBackwardInterpolatorXYShear() { QPolygonF src; src << QPointF(0, 0); src << QPointF(100, 0); src << QPointF(100, 100); src << QPointF(0, 100); QPolygonF dst; dst << QPointF(0, 0); dst << QPointF(100, 10); dst << QPointF(140, 120); dst << QPointF(20, 100); KisFourPointInterpolatorBackward interp(src, dst); QCOMPARE(interp.map(QPointF(100,10)), QPointF(100,0)); QCOMPARE(interp.map(QPointF(50,5)), QPointF(50,0)); QCOMPARE(interp.map(QPointF(80,110)), QPointF(50,100)); QCOMPARE(interp.map(QPointF(120,65)), QPointF(100,50)); QCOMPARE(interp.map(QPointF(10,50)), QPointF(0,50)); } void KisWarpTransformWorkerTest::testBackwardInterpolatorRoundTrip() { QPolygonF src; src << QPointF(0, 0); src << QPointF(100, 0); src << QPointF(100, 100); src << QPointF(0, 100); QPolygonF dst; dst << QPointF(100, 100); dst << QPointF(20, 140); dst << QPointF(10, 80); dst << QPointF(15, 5); KisFourPointInterpolatorForward f(src, dst); KisFourPointInterpolatorBackward b(src, dst); for (qreal y = 0; y <= 100; y += 1.0) { for (qreal x = 0; x <= 100; x += 1.0) { QPointF pt(x, y); QPointF fwdPt = f.map(pt); QPointF bwdPt = b.map(fwdPt); //dbgKrita << "R:" << ppVar(pt) << ppVar(fwdPt) << ppVar(bwdPt) << (bwdPt - pt); QVERIFY((bwdPt - pt).manhattanLength() < 1e-3); } } } #include "kis_grid_interpolation_tools.h" void KisWarpTransformWorkerTest::testGridSize() { QCOMPARE(GridIterationTools::calcGridDimension(1, 7, 4), 3); QCOMPARE(GridIterationTools::calcGridDimension(1, 8, 4), 3); QCOMPARE(GridIterationTools::calcGridDimension(1, 9, 4), 4); QCOMPARE(GridIterationTools::calcGridDimension(0, 7, 4), 3); QCOMPARE(GridIterationTools::calcGridDimension(1, 8, 4), 3); QCOMPARE(GridIterationTools::calcGridDimension(4, 9, 4), 3); QCOMPARE(GridIterationTools::calcGridDimension(0, 9, 4), 4); QCOMPARE(GridIterationTools::calcGridDimension(-1, 9, 4), 5); QCOMPARE(GridIterationTools::calcGridDimension(0, 300, 8), 39); } void KisWarpTransformWorkerTest::testBackwardInterpolatorExtrapolation() { QPolygonF src; src << QPointF(0, 0); src << QPointF(100, 0); src << QPointF(100, 100); src << QPointF(0, 100); QPolygonF dst(src); std::rotate(dst.begin(), dst.begin() + 1, dst.end()); KisFourPointInterpolatorBackward interp(src, dst); // standard checks QCOMPARE(interp.map(QPointF(0,0)), QPointF(0,100)); QCOMPARE(interp.map(QPointF(100,0)), QPointF(0,0)); QCOMPARE(interp.map(QPointF(100,100)), QPointF(100,0)); QCOMPARE(interp.map(QPointF(0,100)), QPointF(100,100)); // extrapolate! QCOMPARE(interp.map(QPointF(-10,0)), QPointF(0,110)); QCOMPARE(interp.map(QPointF(0,-10)), QPointF(-10,100)); QCOMPARE(interp.map(QPointF(-10,-10)), QPointF(-10,110)); QCOMPARE(interp.map(QPointF(110,0)), QPointF(0,-10)); QCOMPARE(interp.map(QPointF(100,-10)), QPointF(-10,0)); QCOMPARE(interp.map(QPointF(110,-10)), QPointF(-10,-10)); QCOMPARE(interp.map(QPointF(110,100)), QPointF(100, -10)); QCOMPARE(interp.map(QPointF(100,110)), QPointF(110, 0)); QCOMPARE(interp.map(QPointF(110,110)), QPointF(110,-10)); QCOMPARE(interp.map(QPointF(-10,100)), QPointF(100, 110)); QCOMPARE(interp.map(QPointF(0,110)), QPointF(110, 100)); QCOMPARE(interp.map(QPointF(-10,110)), QPointF(110,110)); } #include "krita_utils.h" void KisWarpTransformWorkerTest::testNeedChangeRects() { WarpTransforWorkerData d; KisWarpTransformWorker worker(KisWarpTransformWorker::RIGID_TRANSFORM, d.dev, d.origPoints, d.transfPoints, d.alpha, d.updater); QCOMPARE(KritaUtils::sampleRectWithPoints(d.bounds.toAlignedRect()).size(), 9); - QCOMPARE(worker.approxChangeRect(d.bounds.toAlignedRect()), QRect(-89,-89, 1072,1076)); + QCOMPARE(worker.approxChangeRect(d.bounds.toAlignedRect()), QRect(-44,-44, 982,986)); } QTEST_MAIN(KisWarpTransformWorkerTest) diff --git a/libs/pigment/resources/KoResource.cpp b/libs/pigment/resources/KoResource.cpp index 3ce3bb948b..177286767c 100644 --- a/libs/pigment/resources/KoResource.cpp +++ b/libs/pigment/resources/KoResource.cpp @@ -1,142 +1,154 @@ /* This file is part of the KDE project Copyright (c) 2003 Patrick Julien Copyright (c) 2005 Boudewijn Rempt Copyright (c) 2007 Jan Hambrecht This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "KoHashGenerator.h" #include "KoHashGeneratorProvider.h" struct Q_DECL_HIDDEN KoResource::Private { QString name; QString filename; bool valid; bool removable; QByteArray md5; QImage image; + bool permanent; }; KoResource::KoResource(const QString& filename) : d(new Private) { d->filename = filename; d->valid = false; QFileInfo fileInfo(filename); d->removable = fileInfo.isWritable(); + d->permanent = false; } KoResource::~KoResource() { delete d; } KoResource::KoResource(const KoResource &rhs) : d(new Private(*rhs.d)) { } bool KoResource::saveToDevice(QIODevice *dev) const { Q_UNUSED(dev) d->md5 = QByteArray(); return true; } QImage KoResource::image() const { return d->image; } void KoResource::setImage(const QImage &image) { d->image = image; } QByteArray KoResource::md5() const { if (d->md5.isEmpty()) { const_cast(this)->setMD5(generateMD5()); } return d->md5; } void KoResource::setMD5(const QByteArray &md5) { d->md5 = md5; } QByteArray KoResource::generateMD5() const { KoHashGenerator *hashGenerator = KoHashGeneratorProvider::instance()->getGenerator("MD5"); return hashGenerator->generateHash(d->filename); } QString KoResource::filename() const { return d->filename; } void KoResource::setFilename(const QString& filename) { d->filename = filename; QFileInfo fileInfo(filename); d->removable = ! fileInfo.exists() || fileInfo.isWritable(); } QString KoResource::shortFilename() const { QFileInfo fileInfo(d->filename); return fileInfo.fileName(); } QString KoResource::name() const { return d->name; } void KoResource::setName(const QString& name) { d->name = name; } bool KoResource::valid() const { return d->valid; } void KoResource::setValid(bool valid) { d->valid = valid; } bool KoResource::removable() const { return d->removable; } QString KoResource::defaultFileExtension() const { return QString(); } +bool KoResource::permanent() const +{ + return d->permanent; +} + +void KoResource::setPermanent(bool permanent) +{ + d->permanent = permanent; +} + diff --git a/libs/pigment/resources/KoResource.h b/libs/pigment/resources/KoResource.h index 88f121b50e..be20bed321 100644 --- a/libs/pigment/resources/KoResource.h +++ b/libs/pigment/resources/KoResource.h @@ -1,127 +1,131 @@ /* This file is part of the KDE project Copyright (c) 2003 Patrick Julien 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; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef KORESOURCE_H #define KORESOURCE_H #include #include #include #include class QDomDocument; class QDomElement; /** * The KoResource class provides a representation of resources. This * includes, but not limited to, brushes and patterns. */ class KRITAPIGMENT_EXPORT KoResource { public: /** * Creates a new KoResource object using @p filename. No file is opened * in the constructor, you have to call load. * * @param filename the file name to save and load from. */ explicit KoResource(const QString &filename); virtual ~KoResource(); bool operator ==(const KoResource &other) const { return other.md5() == md5(); } public: /** * Load this resource. * @return true if loading the resource succeeded. */ virtual bool load() = 0; virtual bool loadFromDevice(QIODevice *dev) = 0; /** * Save this resource. *@return true if saving the resource succeeded. */ virtual bool save() = 0; virtual bool saveToDevice(QIODevice* dev) const; /** * @returns a QImage thumbnail image representing this resource. * * This image could be null. The image can be in any valid format. */ QImage image() const; void setImage(const QImage &image); /// @return the md5sum calculated over the contents of the resource. QByteArray md5() const; /// @returns true if resource can be removed by the user bool removable() const; /// @return the full path to this resource QString filename() const; void setFilename(const QString& filename); /// @return the name of the file without the path QString shortFilename() const; /// @return the user-visible name of the resource QString name() const; void setName(const QString& name); /// @return true if the resource is ready for use bool valid() const; void setValid(bool valid); /// @return the default file extension which should be used when saving the resource virtual QString defaultFileExtension() const; + /// @return true if the resource is permanent and can't be removed by the user + bool permanent() const; + void setPermanent(bool permanent); + protected: /// override generateMD5 and in your resource subclass virtual QByteArray generateMD5() const; /// call this when the contents of the resource change so the md5 needs to be recalculated void setMD5(const QByteArray &md5); protected: KoResource(const KoResource &rhs); private: struct Private; Private* const d; }; static inline bool operator==(const KoResource &resource1, const KoResource &resource2) { return (resource1.md5() == resource2.md5()); } static inline uint qHash(const KoResource &resource) { return qHash(resource.md5()); } #endif // KORESOURCE_H_ diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt index e2d481ff30..4d162af777 100644 --- a/libs/ui/CMakeLists.txt +++ b/libs/ui/CMakeLists.txt @@ -1,550 +1,550 @@ # 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 ${EXIV2_INCLUDE_DIR} ) include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR} ${OCIO_INCLUDE_DIR} ${Boost_INCLUDE_DIRS} ) add_subdirectory( tests ) if (APPLE) find_library(FOUNDATION_LIBRARY Foundation) endif () set(kritaui_LIB_SRCS canvas/kis_canvas_widget_base.cpp canvas/kis_canvas2.cpp canvas/kis_canvas_updates_compressor.cpp canvas/kis_canvas_controller.cpp canvas/kis_paintop_transformation_connector.cpp canvas/kis_display_color_converter.cpp canvas/kis_display_filter.cpp canvas/kis_exposure_gamma_correction_interface.cpp canvas/kis_tool_proxy.cpp canvas/kis_canvas_decoration.cc canvas/kis_coordinates_converter.cpp canvas/kis_grid_manager.cpp canvas/kis_grid_decoration.cpp canvas/kis_grid_config.cpp canvas/kis_prescaled_projection.cpp canvas/kis_qpainter_canvas.cpp canvas/kis_projection_backend.cpp canvas/kis_update_info.cpp canvas/kis_image_patch.cpp canvas/kis_image_pyramid.cpp canvas/kis_infinity_manager.cpp canvas/kis_change_guides_command.cpp canvas/kis_guides_decoration.cpp canvas/kis_guides_manager.cpp canvas/kis_guides_config.cpp canvas/kis_snap_config.cpp canvas/kis_snap_line_strategy.cpp dialogs/kis_about_application.cpp dialogs/kis_dlg_adj_layer_props.cc dialogs/kis_dlg_adjustment_layer.cc dialogs/kis_dlg_filter.cpp dialogs/kis_dlg_generator_layer.cpp dialogs/kis_dlg_file_layer.cpp dialogs/kis_dlg_image_properties.cc dialogs/kis_dlg_layer_properties.cc dialogs/kis_dlg_preferences.cc dialogs/slider_and_spin_box_sync.cpp dialogs/kis_dlg_blacklist_cleanup.cpp dialogs/kis_dlg_layer_style.cpp dialogs/kis_dlg_png_import.cpp dialogs/kis_dlg_import_image_sequence.cpp dialogs/kis_delayed_save_dialog.cpp dialogs/kis_internal_color_selector.cpp flake/kis_node_dummies_graph.cpp flake/kis_dummies_facade_base.cpp flake/kis_dummies_facade.cpp flake/kis_node_shapes_graph.cpp flake/kis_node_shape.cpp flake/kis_shape_controller.cpp flake/kis_shape_layer.cc flake/kis_shape_layer_canvas.cpp flake/kis_shape_selection.cpp flake/kis_shape_selection_canvas.cpp flake/kis_shape_selection_model.cpp flake/kis_take_all_shapes_command.cpp brushhud/kis_uniform_paintop_property_widget.cpp brushhud/kis_brush_hud.cpp brushhud/kis_round_hud_button.cpp brushhud/kis_dlg_brush_hud_config.cpp brushhud/kis_brush_hud_properties_list.cpp brushhud/kis_brush_hud_properties_config.cpp kis_aspect_ratio_locker.cpp kis_autogradient.cc kis_bookmarked_configurations_editor.cc kis_bookmarked_configurations_model.cc kis_bookmarked_filter_configurations_model.cc kis_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 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 widgets/kis_elided_label.cpp widgets/kis_spinbox_color_selector.cpp widgets/kis_screen_color_picker.cpp widgets/kis_visual_color_selector.cpp widgets/KoDualColorButton.cpp widgets/kis_color_input.cpp widgets/kis_color_button.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 brushhud/kis_dlg_brush_hud_config.ui forms/wdgdlginternalcolorselector.ui dialogs/kis_delayed_save_dialog.ui input/config/kis_input_configuration_page.ui input/config/kis_edit_profiles_dialog.ui input/config/kis_input_configuration_page_item.ui input/config/kis_mouse_input_editor.ui input/config/kis_wheel_input_editor.ui input/config/kis_key_input_editor.ui layerstyles/wdgBevelAndEmboss.ui layerstyles/wdgblendingoptions.ui layerstyles/WdgColorOverlay.ui layerstyles/wdgContour.ui layerstyles/wdgdropshadow.ui layerstyles/WdgGradientOverlay.ui layerstyles/wdgInnerGlow.ui layerstyles/wdglayerstyles.ui layerstyles/WdgPatternOverlay.ui layerstyles/WdgSatin.ui layerstyles/WdgStroke.ui layerstyles/wdgstylesselector.ui layerstyles/wdgTexture.ui wdgsplash.ui input/wintab/kis_screen_size_choice_dialog.ui ) QT5_WRAP_CPP(kritaui_HEADERS_MOC KisNodePropertyAction_p.h) add_library(kritaui SHARED ${kritaui_HEADERS_MOC} ${kritaui_LIB_SRCS} ) generate_export_header(kritaui BASE_NAME kritaui) target_link_libraries(kritaui KF5::CoreAddons KF5::Completion KF5::I18n KF5::ItemViews Qt5::Network 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_link_libraries(kritaui LINK_INTERFACE_LIBRARIES kritaimage kritalibbrush kritapigment kritawidgets 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/input/kis_input_manager.cpp b/libs/ui/input/kis_input_manager.cpp index 34e482f9a7..b287628c31 100644 --- a/libs/ui/input/kis_input_manager.cpp +++ b/libs/ui/input/kis_input_manager.cpp @@ -1,608 +1,613 @@ /* This file is part of the KDE project * * Copyright (C) 2012 Arjen Hiemstra * 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.h" #include #include #include #include #include #include "kis_tool_proxy.h" #include #include #include #include #include #include #include "kis_abstract_input_action.h" #include "kis_tool_invocation_action.h" #include "kis_pan_action.h" #include "kis_alternate_invocation_action.h" #include "kis_rotate_canvas_action.h" #include "kis_zoom_action.h" #include "kis_show_palette_action.h" #include "kis_change_primary_setting_action.h" #include "kis_shortcut_matcher.h" #include "kis_stroke_shortcut.h" #include "kis_single_action_shortcut.h" #include "kis_touch_shortcut.h" #include "kis_input_profile.h" #include "kis_input_profile_manager.h" #include "kis_shortcut_configuration.h" #include #include #include "kis_extended_modifiers_mapper.h" #include "kis_input_manager_p.h" template uint qHash(QPointer value) { return reinterpret_cast(value.data()); } #define start_ignore_cursor_events() d->blockMouseEvents() #define stop_ignore_cursor_events() d->allowMouseEvents() #define break_if_should_ignore_cursor_events() if (d->ignoringQtCursorEvents()) break; +#define break_if_tablet_active() if (d->tabletActive) break; // Touch rejection: if touch is disabled on canvas, no need to block mouse press events #define touch_start_block_press_events() d->touchHasBlockedPressEvents = d->disableTouchOnCanvas; #define touch_stop_block_press_events() d->touchHasBlockedPressEvents = false; #define break_if_touch_blocked_press_events() if (d->touchHasBlockedPressEvents) break; #define touch_eat_one_mouse_press() if (d->disableTouchOnCanvas) d->eatOneMousePress(); KisInputManager::KisInputManager(QObject *parent) : QObject(parent), d(new Private(this)) { d->setupActions(); connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*,int)), SLOT(slotToolChanged())); connect(&d->moveEventCompressor, SIGNAL(timeout()), SLOT(slotCompressedMoveEvent())); -#ifndef Q_OS_MAC QApplication::instance()-> installEventFilter(new Private::ProximityNotifier(d, this)); -#endif } KisInputManager::~KisInputManager() { delete d; } void KisInputManager::addTrackedCanvas(KisCanvas2 *canvas) { d->canvasSwitcher.addCanvas(canvas); } void KisInputManager::removeTrackedCanvas(KisCanvas2 *canvas) { d->canvasSwitcher.removeCanvas(canvas); } void KisInputManager::toggleTabletLogger() { KisTabletDebugger::instance()->toggleDebugging(); } void KisInputManager::attachPriorityEventFilter(QObject *filter, int priority) { Private::PriorityList::iterator begin = d->priorityEventFilter.begin(); Private::PriorityList::iterator it = begin; Private::PriorityList::iterator end = d->priorityEventFilter.end(); it = std::find_if(begin, end, [filter] (const Private::PriorityPair &a) { return a.second == filter; }); if (it != end) return; it = std::find_if(begin, end, [priority] (const Private::PriorityPair &a) { return a.first > priority; }); d->priorityEventFilter.insert(it, qMakePair(priority, filter)); d->priorityEventFilterSeqNo++; } void KisInputManager::detachPriorityEventFilter(QObject *filter) { Private::PriorityList::iterator it = d->priorityEventFilter.begin(); Private::PriorityList::iterator end = d->priorityEventFilter.end(); it = std::find_if(it, end, [filter] (const Private::PriorityPair &a) { return a.second == filter; }); if (it != end) { d->priorityEventFilter.erase(it); } } void KisInputManager::setupAsEventFilter(QObject *receiver) { if (d->eventsReceiver) { d->eventsReceiver->removeEventFilter(this); } d->eventsReceiver = receiver; if (d->eventsReceiver) { d->eventsReceiver->installEventFilter(this); } } void KisInputManager::stopIgnoringEvents() { stop_ignore_cursor_events(); } void KisInputManager::slotFocusOnEnter(bool value) { Q_UNUSED(value); // not used anymore } #if defined (__clang__) #pragma GCC diagnostic ignored "-Wswitch" #endif bool KisInputManager::eventFilter(QObject* object, QEvent* event) { if (object != d->eventsReceiver) return false; if (d->eventEater.eventFilter(object, event)) return false; if (!d->matcher.hasRunningShortcut()) { int savedPriorityEventFilterSeqNo = d->priorityEventFilterSeqNo; for (auto it = d->priorityEventFilter.begin(); it != d->priorityEventFilter.end(); /*noop*/) { const QPointer &filter = it->second; if (filter.isNull()) { it = d->priorityEventFilter.erase(it); d->priorityEventFilterSeqNo++; savedPriorityEventFilterSeqNo++; continue; } if (filter->eventFilter(object, event)) return true; /** * If the filter removed itself from the filters list or * added something there, just exit the loop */ if (d->priorityEventFilterSeqNo != savedPriorityEventFilterSeqNo) { return true; } ++it; } // KoToolProxy needs to pre-process some events to ensure the // global shortcuts (not the input manager's ones) are not // executed, in particular, this line will accept events when the // tool is in text editing, preventing shortcut triggering d->toolProxy->processEvent(event); } // Continue with the actual switch statement... return eventFilterImpl(event); } template bool KisInputManager::compressMoveEventCommon(Event *event) { /** * We construct a copy of this event object, so we must ensure it * has a correct type. */ static_assert(std::is_same::value || std::is_same::value, "event should a mouse or a tablet event"); bool retval = false; /** * Compress the events if the tool doesn't need high resolution input */ if ((event->type() == QEvent::MouseMove || event->type() == QEvent::TabletMove) && (!d->matcher.supportsHiResInputEvents() || d->testingCompressBrushEvents)) { d->compressedMoveEvent.reset(new Event(*event)); d->moveEventCompressor.start(); /** * On Linux Qt eats the rest of unneeded events if we * ignore the first of the chunk of tablet events. So * generally we should never activate this feature. Only * for testing purposes! */ if (d->testingAcceptCompressedTabletEvents) { event->setAccepted(true); } retval = true; } else { slotCompressedMoveEvent(); retval = d->handleCompressedTabletEvent(event); } return retval; } bool KisInputManager::eventFilterImpl(QEvent * event) { // TODO: Handle touch events correctly. bool retval = false; switch (event->type()) { case QEvent::MouseButtonPress: case QEvent::MouseButtonDblClick: { d->debugEvent(event); + //Block mouse press events on Genius tablets + break_if_tablet_active(); break_if_should_ignore_cursor_events(); break_if_touch_blocked_press_events(); QMouseEvent *mouseEvent = static_cast(event); if (d->tryHidePopupPalette()) { retval = true; } else { //Make sure the input actions know we are active. KisAbstractInputAction::setInputManager(this); retval = d->matcher.buttonPressed(mouseEvent->button(), mouseEvent); } + //Reset signal compressor to prevent processing events before press late + d->resetCompressor(); event->setAccepted(retval); break; } case QEvent::MouseButtonRelease: { d->debugEvent(event); break_if_should_ignore_cursor_events(); break_if_touch_blocked_press_events(); QMouseEvent *mouseEvent = static_cast(event); retval = d->matcher.buttonReleased(mouseEvent->button(), mouseEvent); event->setAccepted(retval); break; } case QEvent::ShortcutOverride: { d->debugEvent(event); QKeyEvent *keyEvent = static_cast(event); Qt::Key key = KisExtendedModifiersMapper::workaroundShiftAltMetaHell(keyEvent); if (!keyEvent->isAutoRepeat()) { retval = d->matcher.keyPressed(key); } else { retval = d->matcher.autoRepeatedKeyPressed(key); } /** * Workaround for temporary switching of tools by * KoCanvasControllerWidget. We don't need this switch because * we handle it ourselves. */ retval |= !d->forwardAllEventsToTool && (keyEvent->key() == Qt::Key_Space || keyEvent->key() == Qt::Key_Escape); break; } case QEvent::KeyRelease: { d->debugEvent(event); QKeyEvent *keyEvent = static_cast(event); if (!keyEvent->isAutoRepeat()) { Qt::Key key = KisExtendedModifiersMapper::workaroundShiftAltMetaHell(keyEvent); retval = d->matcher.keyReleased(key); } break; } case QEvent::MouseMove: { d->debugEvent(event); break_if_should_ignore_cursor_events(); QMouseEvent *mouseEvent = static_cast(event); retval = compressMoveEventCommon(mouseEvent); break; } case QEvent::Wheel: { d->debugEvent(event); QWheelEvent *wheelEvent = static_cast(event); KisSingleActionShortcut::WheelAction action; /** * Ignore delta 0 events on OSX, since they are triggered by tablet * proximity when using Wacom devices. */ #ifdef Q_OS_MAC if(wheelEvent->delta() == 0) { retval = true; break; } #endif if(wheelEvent->orientation() == Qt::Horizontal) { if(wheelEvent->delta() < 0) { action = KisSingleActionShortcut::WheelRight; } else { action = KisSingleActionShortcut::WheelLeft; } } else { if(wheelEvent->delta() > 0) { action = KisSingleActionShortcut::WheelUp; } else { action = KisSingleActionShortcut::WheelDown; } } //Make sure the input actions know we are active. KisAbstractInputAction::setInputManager(this); retval = d->matcher.wheelEvent(action, wheelEvent); break; } case QEvent::Enter: d->debugEvent(event); d->containsPointer = true; //Make sure the input actions know we are active. KisAbstractInputAction::setInputManager(this); stop_ignore_cursor_events(); touch_stop_block_press_events(); d->matcher.enterEvent(); break; case QEvent::Leave: d->debugEvent(event); d->containsPointer = false; /** * We won't get a TabletProximityLeave event when the tablet * is hovering above some other widget, so restore cursor * events processing right now. */ stop_ignore_cursor_events(); touch_stop_block_press_events(); d->matcher.leaveEvent(); break; case QEvent::FocusIn: d->debugEvent(event); KisAbstractInputAction::setInputManager(this); //Clear all state so we don't have half-matched shortcuts dangling around. d->matcher.reinitialize(); { // Emulate pressing of the key that are already pressed KisExtendedModifiersMapper mapper; Qt::KeyboardModifiers modifiers = mapper.queryStandardModifiers(); Q_FOREACH (Qt::Key key, mapper.queryExtendedModifiers()) { QKeyEvent kevent(QEvent::ShortcutOverride, key, modifiers); eventFilterImpl(&kevent); } } stop_ignore_cursor_events(); break; case QEvent::TabletRelease: { #ifdef Q_OS_MAC stop_ignore_cursor_events(); #endif // break_if_touch_blocked_press_events(); d->debugEvent(event); QTabletEvent *tabletEvent = static_cast(event); retval = d->matcher.buttonReleased(tabletEvent->button(), tabletEvent); retval = true; event->setAccepted(true); break; } case QEvent::TabletMove: { d->debugEvent(event); QTabletEvent *tabletEvent = static_cast(event); retval = compressMoveEventCommon(tabletEvent); /** * The flow of tablet events means the tablet is in the * proximity area, so activate it even when the * TabletEnterProximity event was missed (may happen when * changing focus of the window with tablet in the proximity * area) */ start_ignore_cursor_events(); break; } case QEvent::TabletPress: { d->debugEvent(event); QTabletEvent *tabletEvent = static_cast(event); if (d->tryHidePopupPalette()) { retval = true; } else { //Make sure the input actions know we are active. KisAbstractInputAction::setInputManager(this); retval = d->matcher.buttonPressed(tabletEvent->button(), tabletEvent); } event->setAccepted(true); retval = true; start_ignore_cursor_events(); + //Reset signal compressor to prevent processing events before press late + d->resetCompressor(); d->eatOneMousePress(); break; } case QEvent::TouchBegin: touch_start_block_press_events(); touch_eat_one_mouse_press(); if (d->tryHidePopupPalette()) { retval = true; } else { KisAbstractInputAction::setInputManager(this); retval = d->matcher.touchBeginEvent(static_cast(event)); event->accept(); } // d->resetSavedTabletEvent(event->type()); break; case QEvent::TouchUpdate: { QTouchEvent *tevent = static_cast(event); #ifdef Q_OS_MAC int count = 0; Q_FOREACH (const QTouchEvent::TouchPoint &point, tevent->touchPoints()) { if (point.state() != Qt::TouchPointReleased) { count++; } } if (count < 2 && tevent->touchPoints().length() > count) { touch_stop_block_press_events(); d->saveTouchEvent(tevent); retval = d->matcher.touchEndEvent(tevent); delete d->lastTouchEvent; d->lastTouchEvent = 0; } else { #endif touch_start_block_press_events(); KisAbstractInputAction::setInputManager(this); retval = d->matcher.touchUpdateEvent(tevent); #ifdef Q_OS_MAC } #endif event->accept(); // d->resetSavedTabletEvent(event->type()); break; } case QEvent::TouchEnd: touch_stop_block_press_events(); d->saveTouchEvent(static_cast(event)); retval = d->matcher.touchEndEvent(static_cast(event)); event->accept(); // d->resetSavedTabletEvent(event->type()); delete d->lastTouchEvent; d->lastTouchEvent = 0; break; default: break; } return !retval ? d->processUnhandledEvent(event) : true; } void KisInputManager::slotCompressedMoveEvent() { if (d->compressedMoveEvent) { // touch_stop_block_press_events(); (void) d->handleCompressedTabletEvent(d->compressedMoveEvent.data()); d->compressedMoveEvent.reset(); dbgKrita << "Compressed move event received."; } else { dbgKrita << "Unexpected empty move event"; } } KisCanvas2* KisInputManager::canvas() const { return d->canvas; } KisToolProxy* KisInputManager::toolProxy() const { return d->toolProxy; } QTouchEvent *KisInputManager::lastTouchEvent() const { return d->lastTouchEvent; } void KisInputManager::slotToolChanged() { auto toolManager = KoToolManager::instance(); auto tool = toolManager->toolById(canvas(), toolManager->activeToolId()); if (tool->isInTextMode()) { d->forwardAllEventsToTool = true; d->matcher.suppressAllActions(true); } else { d->forwardAllEventsToTool = false; d->matcher.suppressAllActions(false); } d->maskSyntheticEvents(tool->maskSyntheticEvents()); } QPointF KisInputManager::widgetToDocument(const QPointF& position) { const QPointF half = QPointF(.5f, .5f); QPointF pixel = position + half; return d->canvas->coordinatesConverter()->widgetToDocument(pixel); } void KisInputManager::profileChanged() { d->matcher.clearShortcuts(); KisInputProfile *profile = KisInputProfileManager::instance()->currentProfile(); if (profile) { const QList shortcuts = profile->allShortcuts(); for (KisShortcutConfiguration * const shortcut : shortcuts) { dbgUI << "Adding shortcut" << shortcut->keys() << "for action" << shortcut->action()->name(); switch(shortcut->type()) { case KisShortcutConfiguration::KeyCombinationType: d->addKeyShortcut(shortcut->action(), shortcut->mode(), shortcut->keys()); break; case KisShortcutConfiguration::MouseButtonType: d->addStrokeShortcut(shortcut->action(), shortcut->mode(), shortcut->keys(), shortcut->buttons()); break; case KisShortcutConfiguration::MouseWheelType: d->addWheelShortcut(shortcut->action(), shortcut->mode(), shortcut->keys(), shortcut->wheel()); break; case KisShortcutConfiguration::GestureType: d->addTouchShortcut(shortcut->action(), shortcut->mode(), shortcut->gesture()); break; default: break; } } } else { dbgKrita << "No Input Profile Found: canvas interaction will be impossible"; } } diff --git a/libs/ui/input/kis_input_manager_p.cpp b/libs/ui/input/kis_input_manager_p.cpp index 114842104a..13a968e17d 100644 --- a/libs/ui/input/kis_input_manager_p.cpp +++ b/libs/ui/input/kis_input_manager_p.cpp @@ -1,518 +1,537 @@ /* * 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); } }; Qt::MouseEventSource source = static_cast(event)->source(); 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 && source != 0))) { -#ifdef Q_OS_MAC - if (eatSyntheticEvents && source != 2) { -#endif - // Drop mouse events if enabled or event was synthetic & synthetic events are disabled - debugEvent(); - return true; -#ifdef Q_OS_MAC - } -#endif + (hungry + // On Mac, we need mouse events when the tablet is in proximity, but not pressed down + // since tablet move events are not generated until after tablet press. + #ifndef Q_OS_MAC + || (eatSyntheticEvents && source != 0) + #endif + )) { + // 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(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: { 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. + // Don't block mouse events on tablet since tablet move events are not generated until + // after tablet press. +#ifndef Q_OS_MAC d->blockMouseEvents(); +#else + // Notify input manager that tablet proximity is entered for Genius tablets. + d->setTabletActive(true); +#endif break; case QEvent::TabletLeaveProximity: d->debugEvent(event); d->allowMouseEvents(); +#ifdef Q_OS_MAC + d->setTabletActive(false); +#endif 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(); } +void KisInputManager::Private::setTabletActive(bool value) { + tabletActive = value; +} + +void KisInputManager::Private::resetCompressor() { + compressedMoveEvent.reset(); + moveEventCompressor.stop(); +} + 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_input_manager_p.h b/libs/ui/input/kis_input_manager_p.h index a4a0c24ee8..46229392ed 100644 --- a/libs/ui/input/kis_input_manager_p.h +++ b/libs/ui/input/kis_input_manager_p.h @@ -1,145 +1,147 @@ /* * 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 #include #include #include #include #include #include "kis_input_manager.h" #include "kis_shortcut_matcher.h" #include "kis_shortcut_configuration.h" #include "kis_canvas2.h" #include "kis_tool_proxy.h" #include "kis_signal_compressor.h" #include "input/kis_tablet_debugger.h" #include "kis_timed_signal_threshold.h" #include "kis_signal_auto_connection.h" class KisToolInvocationAction; class KisInputManager::Private { public: Private(KisInputManager *qq); bool tryHidePopupPalette(); void addStrokeShortcut(KisAbstractInputAction* action, int index, const QList< Qt::Key >& modifiers, Qt::MouseButtons buttons); void addKeyShortcut(KisAbstractInputAction* action, int index,const QList &keys); void addTouchShortcut( KisAbstractInputAction* action, int index, KisShortcutConfiguration::GestureAction gesture ); void addWheelShortcut(KisAbstractInputAction* action, int index, const QList< Qt::Key >& modifiers, KisShortcutConfiguration::MouseWheelMovement wheelAction); bool processUnhandledEvent(QEvent *event); void setupActions(); void saveTouchEvent( QTouchEvent* event ); bool handleCompressedTabletEvent(QEvent *event); KisInputManager *q; KisCanvas2 *canvas = 0; KisToolProxy *toolProxy = 0; bool forwardAllEventsToTool = false; bool ignoringQtCursorEvents(); bool disableTouchOnCanvas = false; bool touchHasBlockedPressEvents = false; KisShortcutMatcher matcher; QTouchEvent *lastTouchEvent = 0; KisToolInvocationAction *defaultInputAction = 0; QObject *eventsReceiver = 0; KisSignalCompressor moveEventCompressor; QScopedPointer compressedMoveEvent; bool testingAcceptCompressedTabletEvents = false; bool testingCompressBrushEvents = false; - + bool tabletActive = false; // Indicates whether or not tablet is in proximity typedef QPair > PriorityPair; typedef QList PriorityList; PriorityList priorityEventFilter; int priorityEventFilterSeqNo; void blockMouseEvents(); void allowMouseEvents(); void eatOneMousePress(); void maskSyntheticEvents(bool value); + void setTabletActive(bool value); + void resetCompressor(); template void debugEvent(QEvent *event) { if (!KisTabletDebugger::instance()->debugEnabled()) return; QString msg1 = useBlocking && ignoringQtCursorEvents() ? "[BLOCKED] " : "[ ]"; Event *specificEvent = static_cast(event); dbgTablet << KisTabletDebugger::instance()->eventToString(*specificEvent, msg1); } class ProximityNotifier : public QObject { public: ProximityNotifier(Private *_d, QObject *p); bool eventFilter(QObject* object, QEvent* event ); private: KisInputManager::Private *d; }; class CanvasSwitcher : public QObject { public: CanvasSwitcher(Private *_d, QObject *p); void addCanvas(KisCanvas2 *canvas); void removeCanvas(KisCanvas2 *canvas); bool eventFilter(QObject* object, QEvent* event ); private: void setupFocusThreshold(QObject *object); private: KisInputManager::Private *d; QMap canvasResolver; int eatOneMouseStroke; KisTimedSignalThreshold focusSwitchThreshold; KisSignalAutoConnectionsStore thresholdConnections; }; CanvasSwitcher canvasSwitcher; struct EventEater { bool eventFilter(QObject* target, QEvent* event); // This should be called after we're certain a tablet stroke has started. void activate(); // This should be called after a tablet stroke has ended. void deactivate(); // On Windows, we sometimes receive mouse events very late, so watch & wait. void eatOneMousePress(); bool hungry{false}; // Continue eating mouse strokes bool peckish{false}; // Eat a single mouse press event bool eatSyntheticEvents{true}; // Mask all synthetic events }; EventEater eventEater; bool containsPointer = true; }; diff --git a/libs/ui/kis_aspect_ratio_locker.cpp b/libs/ui/kis_aspect_ratio_locker.cpp index dfd26f524e..211cbf45d4 100644 --- a/libs/ui/kis_aspect_ratio_locker.cpp +++ b/libs/ui/kis_aspect_ratio_locker.cpp @@ -1,188 +1,200 @@ /* * 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_aspect_ratio_locker.h" #include #include #include #include "kis_signals_blocker.h" #include "kis_assert.h" #include "kis_debug.h" #include "kis_slider_spin_box.h" #include "kis_int_parse_spin_box.h" #include "kis_double_parse_spin_box.h" struct SliderWrapper { template SliderWrapper(Slider *slider) : m_slider(QVariant::fromValue(slider)), m_object(slider) {} void setValue(qreal value) { if (m_slider.canConvert()) { m_slider.value()->setValue(qRound(value)); } else if (m_slider.canConvert()) { m_slider.value()->setValue(value); } else if (m_slider.canConvert()) { m_slider.value()->setValue(qRound(value)); } else if (m_slider.canConvert()) { m_slider.value()->setValue(value); + + } else if (m_slider.canConvert()) { + m_slider.value()->setValue(qRound(value)); + + } else if (m_slider.canConvert()) { + m_slider.value()->setValue(value); } } qreal value() const { qreal result = 0.0; if (m_slider.canConvert()) { result = m_slider.value()->value(); } else if (m_slider.canConvert()) { result = m_slider.value()->value(); } else if (m_slider.canConvert()) { result = m_slider.value()->value(); } else if (m_slider.canConvert()) { result = m_slider.value()->value(); + + } else if (m_slider.canConvert()) { + result = m_slider.value()->value(); + + } else if (m_slider.canConvert()) { + result = m_slider.value()->value(); } return result; } bool isDragging() const { bool result = false; if (m_slider.canConvert()) { result = m_slider.value()->isDragging(); } else if (m_slider.canConvert()) { result = m_slider.value()->isDragging(); } return result; } QObject* object() const { return m_object; } private: QVariant m_slider; QObject *m_object; }; struct KisAspectRatioLocker::Private { QScopedPointer spinOne; QScopedPointer spinTwo; KoAspectButton *aspectButton = 0; qreal aspectRatio = 1.0; bool blockUpdatesOnDrag = false; }; KisAspectRatioLocker::KisAspectRatioLocker(QObject *parent) : QObject(parent), m_d(new Private) { } KisAspectRatioLocker::~KisAspectRatioLocker() { } template void KisAspectRatioLocker::connectSpinBoxes(SpinBoxType *spinOne, SpinBoxType *spinTwo, KoAspectButton *aspectButton) { m_d->spinOne.reset(new SliderWrapper(spinOne)); m_d->spinTwo.reset(new SliderWrapper(spinTwo)); m_d->aspectButton = aspectButton; if (QVariant::fromValue(spinOne->value()).type() == QVariant::Double) { connect(spinOne, SIGNAL(valueChanged(qreal)), SLOT(slotSpinOneChanged())); connect(spinTwo, SIGNAL(valueChanged(qreal)), SLOT(slotSpinTwoChanged())); } else { connect(spinOne, SIGNAL(valueChanged(int)), SLOT(slotSpinOneChanged())); connect(spinTwo, SIGNAL(valueChanged(int)), SLOT(slotSpinTwoChanged())); } connect(m_d->aspectButton, SIGNAL(keepAspectRatioChanged(bool)), SLOT(slotAspectButtonChanged())); slotAspectButtonChanged(); } -template void KisAspectRatioLocker::connectSpinBoxes(QSpinBox *spinOne, QSpinBox *spinTwo, KoAspectButton *aspectButton); -template void KisAspectRatioLocker::connectSpinBoxes(QDoubleSpinBox *spinOne, QDoubleSpinBox *spinTwo, KoAspectButton *aspectButton); -template void KisAspectRatioLocker::connectSpinBoxes(KisSliderSpinBox *spinOne, KisSliderSpinBox *spinTwo, KoAspectButton *aspectButton); -template void KisAspectRatioLocker::connectSpinBoxes(KisDoubleSliderSpinBox *spinOne, KisDoubleSliderSpinBox *spinTwo, KoAspectButton *aspectButton); -template void KisAspectRatioLocker::connectSpinBoxes(KisIntParseSpinBox *spinOne, KisIntParseSpinBox *spinTwo, KoAspectButton *aspectButton); -template void KisAspectRatioLocker::connectSpinBoxes(KisDoubleParseSpinBox *spinOne, KisDoubleParseSpinBox *spinTwo, KoAspectButton *aspectButton); +template KRITAUI_EXPORT void KisAspectRatioLocker::connectSpinBoxes(QSpinBox *spinOne, QSpinBox *spinTwo, KoAspectButton *aspectButton); +template KRITAUI_EXPORT void KisAspectRatioLocker::connectSpinBoxes(QDoubleSpinBox *spinOne, QDoubleSpinBox *spinTwo, KoAspectButton *aspectButton); +template KRITAUI_EXPORT void KisAspectRatioLocker::connectSpinBoxes(KisSliderSpinBox *spinOne, KisSliderSpinBox *spinTwo, KoAspectButton *aspectButton); +template KRITAUI_EXPORT void KisAspectRatioLocker::connectSpinBoxes(KisDoubleSliderSpinBox *spinOne, KisDoubleSliderSpinBox *spinTwo, KoAspectButton *aspectButton); +template KRITAUI_EXPORT void KisAspectRatioLocker::connectSpinBoxes(KisIntParseSpinBox *spinOne, KisIntParseSpinBox *spinTwo, KoAspectButton *aspectButton); +template KRITAUI_EXPORT void KisAspectRatioLocker::connectSpinBoxes(KisDoubleParseSpinBox *spinOne, KisDoubleParseSpinBox *spinTwo, KoAspectButton *aspectButton); void KisAspectRatioLocker::slotSpinOneChanged() { if (m_d->aspectButton->keepAspectRatio()) { KisSignalsBlocker b(m_d->spinTwo->object()); m_d->spinTwo->setValue(m_d->aspectRatio * m_d->spinOne->value()); } if (!m_d->blockUpdatesOnDrag || !m_d->spinOne->isDragging()) { emit sliderValueChanged(); } } void KisAspectRatioLocker::slotSpinTwoChanged() { if (m_d->aspectButton->keepAspectRatio()) { KisSignalsBlocker b(m_d->spinOne->object()); m_d->spinOne->setValue(m_d->spinTwo->value() / m_d->aspectRatio); } if (!m_d->blockUpdatesOnDrag || !m_d->spinTwo->isDragging()) { emit sliderValueChanged(); } } void KisAspectRatioLocker::slotAspectButtonChanged() { if (m_d->aspectButton->keepAspectRatio() && m_d->spinTwo->value() > 0 && m_d->spinOne->value() > 0) { m_d->aspectRatio = qreal(m_d->spinTwo->value()) / m_d->spinOne->value(); } else { m_d->aspectRatio = 1.0; } if (!m_d->spinTwo->isDragging()) { emit aspectButtonChanged(); } } void KisAspectRatioLocker::setBlockUpdateSignalOnDrag(bool value) { m_d->blockUpdatesOnDrag = value; } diff --git a/libs/ui/kis_mirror_manager.cpp b/libs/ui/kis_mirror_manager.cpp index 63b09c1fb3..d9648519fb 100644 --- a/libs/ui/kis_mirror_manager.cpp +++ b/libs/ui/kis_mirror_manager.cpp @@ -1,75 +1,87 @@ /* * 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 "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); 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) { connect(m_mirrorCanvas, SIGNAL(toggled(bool)), dynamic_cast(m_imageView->canvasController()), SLOT(mirrorCanvas(bool))); + + if (!hasDecoration()) { + m_imageView->canvasBase()->addDecoration(new KisMirrorAxis(m_imageView->viewManager()->resourceProvider(), m_imageView)); + } } updateAction(); } void KisMirrorManager::updateAction() { if (m_imageView) { m_mirrorCanvas->setEnabled(true); m_mirrorCanvas->setChecked(m_imageView->canvasIsMirrored()); } else { m_mirrorCanvas->setEnabled(false); m_mirrorCanvas->setChecked(false); } } + +KisMirrorAxis* KisMirrorManager::hasDecoration() { + + if (m_imageView && m_imageView->canvasBase() && m_imageView->canvasBase()->decoration("mirror_axis")) { + return dynamic_cast(m_imageView->canvasBase()->decoration("mirror_axis").data()); + } + return 0; +} diff --git a/libs/ui/kis_mirror_manager.h b/libs/ui/kis_mirror_manager.h index d8db64d56e..065d0113e7 100644 --- a/libs/ui/kis_mirror_manager.h +++ b/libs/ui/kis_mirror_manager.h @@ -1,54 +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_MIRROR_MANAGER_H #define KIS_MIRROR_MANAGER_H #include #include #include "KisView.h" class KisViewManager; class KActionCollection; class KisMirrorAxis; class KisMirrorManager : public QObject { Q_OBJECT public: KisMirrorManager(KisViewManager* view); virtual ~KisMirrorManager(); void setup(KActionCollection* collection); void setView(QPointer imageView); private Q_SLOTS: void updateAction(); private: QPointer m_imageView; QAction *m_mirrorCanvas; + KisMirrorAxis* hasDecoration(); + }; #endif // KIS_PAINTING_ASSISTANTS_MANAGER_H diff --git a/libs/ui/opengl/kis_opengl_canvas2.cpp b/libs/ui/opengl/kis_opengl_canvas2.cpp index 4d003dd3b1..640ef7d732 100644 --- a/libs/ui/opengl/kis_opengl_canvas2.cpp +++ b/libs/ui/opengl/kis_opengl_canvas2.cpp @@ -1,819 +1,831 @@ /* 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; bool proofingConfigIsUpdated=false; 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()); setAcceptDrops(true); 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(); 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() { KisOpenGL::initializeContext(context()); initializeOpenGLFunctions(); KisConfig cfg; d->openGLImageTextures->setProofingConfig(canvas()->proofingConfiguration()); 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); } 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->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 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; 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, 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; 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 (haveGLSL13 && useHiQualityFiltering) { shaderText.append("#define HIGHQ_SCALING\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()); 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 = 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); if (canvas()->proofingConfigUpdated()) { d->openGLImageTextures->setProofingConfig(canvas()->proofingConfiguration()); canvas()->setProofingConfigUpdated(false); } 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); } + +#ifdef Q_OS_MAC + /** + * There is a bug on OSX: if we issue frame redraw before the tiles finished + * uploading, the tiles will become corrupted. Depending on the GPU/driver + * version either the tile itself, or its mipmaps will become totally + * transparent. + */ + + glFinish(); +#endif + 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 62c188a0eb..395edaabe7 100644 --- a/libs/ui/opengl/kis_opengl_canvas2_p.h +++ b/libs/ui/opengl/kis_opengl_canvas2_p.h @@ -1,122 +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 */ 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 +#elif defined Q_OS_LINUX || defined Q_OS_MAC 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/tests/kis_derived_resources_test.cpp b/libs/ui/tests/kis_derived_resources_test.cpp index a19198426e..759b57b7c5 100644 --- a/libs/ui/tests/kis_derived_resources_test.cpp +++ b/libs/ui/tests/kis_derived_resources_test.cpp @@ -1,106 +1,143 @@ /* * 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_derived_resources_test.h" #include #include #include #include #include "kis_canvas_resource_provider.h" #include #include #include #include #include #include #include - +#include #include "testutil.h" +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/"); + +} + void KisDerivedResourcesTest::test() { KisDocument* doc = createEmptyDocument(); + + addResourceTypes(); + KisMainWindow* mainWindow = KisPart::instance()->createMainWindow(); QPointer view = new KisView(doc, mainWindow->resourceManager(), mainWindow->actionCollection(), mainWindow); KisViewManager *viewManager = new KisViewManager(mainWindow, mainWindow->actionCollection()); KoCanvasResourceManager *manager = viewManager->resourceProvider()->resourceManager(); QApplication::processEvents(); QString presetFileName = "autobrush_300px.kpp"; QVariant i; KisPaintOpPresetSP preset; if (!presetFileName.isEmpty()) { QString fullFileName = TestUtil::fetchDataFileLazy(presetFileName); preset = new KisPaintOpPreset(fullFileName); bool presetValid = preset->load(); Q_ASSERT(presetValid); Q_UNUSED(presetValid); i.setValue(preset); } QVERIFY(i.isValid()); QSignalSpy spy(manager, SIGNAL(canvasResourceChanged(int, const QVariant &))); manager->setResource(KisCanvasResourceProvider::CurrentPaintOpPreset, i); QCOMPARE(spy[0][0].toInt(), (int)KisCanvasResourceProvider::CurrentPaintOpPreset); QCOMPARE(spy[0][1].value(), preset); QCOMPARE(spy[1][0].toInt(), (int)KisCanvasResourceProvider::EraserMode); QCOMPARE(spy[1][1].toBool(), false); QCOMPARE(spy[2][0].toInt(), (int)KisCanvasResourceProvider::LodAvailability); QCOMPARE(spy[2][1].toBool(), true); - QCOMPARE(spy[3][0].toInt(), (int)KisCanvasResourceProvider::Opacity); + QCOMPARE(spy[3][0].toInt(), (int)KisCanvasResourceProvider::Size); QCOMPARE(spy[3][1].toDouble(), 1.0); - QCOMPARE(spy[4][0].toInt(), (int)KisCanvasResourceProvider::CurrentEffectiveCompositeOp); - QCOMPARE(spy[4][1].toString(), COMPOSITE_OVER); + QCOMPARE(spy[4][0].toInt(), (int)KisCanvasResourceProvider::Flow); + QCOMPARE(spy[4][1].toDouble(), 1.0); + + QCOMPARE(spy[5][0].toInt(), (int)KisCanvasResourceProvider::Opacity); + QCOMPARE(spy[5][1].toDouble(), 1.0); + + QCOMPARE(spy[6][0].toInt(), (int)KisCanvasResourceProvider::CurrentEffectiveCompositeOp); + QCOMPARE(spy[6][1].toString(), COMPOSITE_OVER); spy.clear(); preset->settings()->setPaintOpOpacity(0.8); QCOMPARE(spy.size(), 1); QCOMPARE(spy[0][0].toInt(), (int)KisCanvasResourceProvider::Opacity); QCOMPARE(spy[0][1].toDouble(), 0.8); spy.clear(); mainWindow->hide(); QApplication::processEvents(); delete view; delete doc; delete mainWindow; } QTEST_MAIN(KisDerivedResourcesTest) diff --git a/libs/ui/widgets/kis_iconwidget.cc b/libs/ui/widgets/kis_iconwidget.cc index 988b188ccd..d3454b9152 100644 --- a/libs/ui/widgets/kis_iconwidget.cc +++ b/libs/ui/widgets/kis_iconwidget.cc @@ -1,86 +1,95 @@ /* * Copyright (c) 2000 Matthias Elter * Copyright (c) 2003 Patrick Julien * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details.g * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "widgets/kis_iconwidget.h" #include #include #include #include #include KisIconWidget::KisIconWidget(QWidget *parent, const char *name) : KisPopupButton(parent) { setObjectName(name); m_resource = 0; } void KisIconWidget::slotSetItem(KoResource * resource) { m_resource = resource; update(); } void KisIconWidget::paintEvent(QPaintEvent *event) { QPushButton::paintEvent(event); QPainter p(this); const qint32 cw = width(); const qint32 ch = height(); const qint32 border = 3; const qint32 iconWidth = cw - (border*2); const qint32 iconHeight = ch - (border*2); // Round off the corners of the preview QRegion clipRegion(border, border, iconWidth, iconHeight); clipRegion -= QRegion(border, border, 1, 1); clipRegion -= QRegion(cw-border-1, border, 1, 1); clipRegion -= QRegion(cw-border-1, ch-border-1, 1, 1); clipRegion -= QRegion(border, ch-border-1, 1, 1); p.setClipRegion(clipRegion); p.setClipping(true); p.setBrush(Qt::white); p.drawRect(QRect(0,0,cw,ch)); if (m_resource) { p.drawImage(QRect(border, border, iconWidth, iconHeight), m_resource->image()); } p.setClipping(false); } void KisIconWidget::setResourceAdapter(QSharedPointer adapter) { Q_ASSERT(adapter); - adapter->connectToResourceServer(); - connect(adapter.data(), SIGNAL(resourceChanged(KoResource*)), this, SLOT(slotAdapterResourceChanged(KoResource*))); + m_adapter = adapter; + m_adapter->connectToResourceServer(); + connect(m_adapter.data(), SIGNAL(resourceChanged(KoResource*)), this, SLOT(slotAdapterResourceChanged(KoResource*))); + connect(m_adapter.data(), SIGNAL(removingResource(KoResource*)), this, SLOT(slotAdapterResourceRemoved(KoResource*))); } void KisIconWidget::slotAdapterResourceChanged(KoResource* resource) { if (m_resource == resource) { update(); } } + +void KisIconWidget::slotAdapterResourceRemoved(KoResource* resource) +{ + if (m_resource == resource) { + m_resource = 0; + } +} diff --git a/libs/ui/widgets/kis_iconwidget.h b/libs/ui/widgets/kis_iconwidget.h index 7cd56f120a..480c3f51cd 100644 --- a/libs/ui/widgets/kis_iconwidget.h +++ b/libs/ui/widgets/kis_iconwidget.h @@ -1,56 +1,58 @@ /* * Copyright (c) 2000 Matthias Elter * Copyright (c) 2003 Patrick Julien * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_ICONWIDGET_H_ #define KIS_ICONWIDGET_H_ #include class KoResource; class KoAbstractResourceServerAdapter; /** * The icon widget is used in the control box where the current color and brush * are shown. */ class KisIconWidget : public KisPopupButton { Q_OBJECT public: KisIconWidget(QWidget *parent = 0, const char *name = 0); /** * Set an resource server adapter that the widget will observe. */ void setResourceAdapter(QSharedPointer adapter); public Q_SLOTS: void slotSetItem(KoResource * resource); void slotAdapterResourceChanged(KoResource * resource); + void slotAdapterResourceRemoved(KoResource * resource); protected: virtual void paintEvent(QPaintEvent *); private: KoResource *m_resource; + QSharedPointer m_adapter; }; #endif // KIS_ICONWIDGET_H_ diff --git a/libs/widgets/KoResourceItemChooser.cpp b/libs/widgets/KoResourceItemChooser.cpp index 147900c18a..cdc158714c 100644 --- a/libs/widgets/KoResourceItemChooser.cpp +++ b/libs/widgets/KoResourceItemChooser.cpp @@ -1,540 +1,540 @@ /* 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 "KoResourceItemChooser.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoResourceServerAdapter.h" #include "KoResourceItemView.h" #include "KoResourceItemDelegate.h" #include "KoResourceModel.h" #include #include "KoResourceTaggingManager.h" #include "KoTagFilterWidget.h" #include "KoTagChooserWidget.h" #include "KoResourceItemChooserSync.h" class Q_DECL_HIDDEN KoResourceItemChooser::Private { public: Private() : model(0) , view(0) , buttonGroup(0) , viewModeButton(0) , usePreview(false) , previewScroller(0) , previewLabel(0) , splitter(0) , tiledPreview(false) , grayscalePreview(false) , synced(false) , updatesBlocked(false) , savedResourceWhileReset(0) {} KoResourceModel *model; KoResourceTaggingManager *tagManager; KoResourceItemView *view; QButtonGroup *buttonGroup; QToolButton *viewModeButton; bool usePreview; QScrollArea *previewScroller; QLabel *previewLabel; QSplitter *splitter; QGridLayout *buttonLayout; bool tiledPreview; bool grayscalePreview; bool synced; bool updatesBlocked; KoResource *savedResourceWhileReset; QList customButtons; }; KoResourceItemChooser::KoResourceItemChooser(QSharedPointer resourceAdapter, QWidget *parent, bool usePreview) : QWidget(parent) , d(new Private()) { Q_ASSERT(resourceAdapter); d->splitter = new QSplitter(this); d->model = new KoResourceModel(resourceAdapter, this); connect(d->model, SIGNAL(beforeResourcesLayoutReset(KoResource *)), SLOT(slotBeforeResourcesLayoutReset(KoResource *))); connect(d->model, SIGNAL(afterResourcesLayoutReset()), SLOT(slotAfterResourcesLayoutReset())); d->view = new KoResourceItemView(this); d->view->setModel(d->model); d->view->setItemDelegate(new KoResourceItemDelegate(this)); d->view->setSelectionMode(QAbstractItemView::SingleSelection); d->view->viewport()->installEventFilter(this); connect(d->view, SIGNAL(currentResourceChanged(QModelIndex)), this, SLOT(activated(QModelIndex))); connect(d->view, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(contextMenuRequested(QPoint))); connect(d->view, SIGNAL(sigSizeChanged()), this, SLOT(updateView())); d->splitter->addWidget(d->view); d->splitter->setStretchFactor(0, 2); d->usePreview = usePreview; if (d->usePreview) { d->previewScroller = new QScrollArea(this); d->previewScroller->setWidgetResizable(true); d->previewScroller->setBackgroundRole(QPalette::Dark); d->previewScroller->setVisible(true); d->previewScroller->setAlignment(Qt::AlignCenter); d->previewLabel = new QLabel(this); d->previewScroller->setWidget(d->previewLabel); d->splitter->addWidget(d->previewScroller); if (d->splitter->count() == 2) { d->splitter->setSizes(QList() << 280 << 160); } } d->splitter->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); connect(d->splitter, SIGNAL(splitterMoved(int, int)), SIGNAL(splitterMoved())); d->buttonGroup = new QButtonGroup(this); d->buttonGroup->setExclusive(false); QGridLayout *layout = new QGridLayout(this); d->buttonLayout = new QGridLayout(); QPushButton *button = new QPushButton(this); button->setIcon(koIcon("document-open")); button->setToolTip(i18nc("@info:tooltip", "Import resource")); button->setEnabled(true); d->buttonGroup->addButton(button, Button_Import); d->buttonLayout->addWidget(button, 0, 0); button = new QPushButton(this); button->setIcon(koIcon("trash-empty")); button->setToolTip(i18nc("@info:tooltip", "Delete resource")); button->setEnabled(false); d->buttonGroup->addButton(button, Button_Remove); d->buttonLayout->addWidget(button, 0, 1); connect(d->buttonGroup, SIGNAL(buttonClicked(int)), this, SLOT(slotButtonClicked(int))); d->buttonLayout->setColumnStretch(0, 1); d->buttonLayout->setColumnStretch(1, 1); d->buttonLayout->setColumnStretch(2, 2); d->buttonLayout->setSpacing(0); d->buttonLayout->setMargin(0); d->viewModeButton = new QToolButton(this); d->viewModeButton->setIcon(koIcon("view-choose")); d->viewModeButton->setPopupMode(QToolButton::InstantPopup); d->viewModeButton->setVisible(false); d->tagManager = new KoResourceTaggingManager(d->model, this); connect(d->tagManager, SIGNAL(updateView()), this, SLOT(updateView())); layout->addWidget(d->tagManager->tagChooserWidget(), 0, 0); layout->addWidget(d->viewModeButton, 0, 1); layout->addWidget(d->splitter, 1, 0, 1, 2); layout->addWidget(d->tagManager->tagFilterWidget(), 2, 0, 1, 2); layout->addLayout(d->buttonLayout, 3, 0, 1, 2); layout->setMargin(0); layout->setSpacing(0); updateButtonState(); showTaggingBar(false); activated(d->model->index(0, 0)); } KoResourceItemChooser::~KoResourceItemChooser() { disconnect(); delete d; } void KoResourceItemChooser::slotButtonClicked(int button) { if (button == Button_Import) { QString extensions = d->model->extensions(); QStringList mimeTypes; Q_FOREACH(const QString &suffix, extensions.split(":")) { mimeTypes << KisMimeDatabase::mimeTypeForSuffix(suffix); } KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument"); dialog.setMimeTypeFilters(mimeTypes); dialog.setCaption(i18nc("@title:window", "Choose File to Add")); QString filename = dialog.filename(); d->model->importResourceFile(filename); } else if (button == Button_Remove) { QModelIndex index = d->view->currentIndex(); int row = index.row(); int column = index.column(); if (index.isValid()) { KoResource *resource = resourceFromModelIndex(index); if (resource) { d->model->removeResource(resource); } } if (column == 0) { int rowMin = --row; row = qBound(0, rowMin, row); } int columnMin = --column; column = qBound(0, columnMin, column); setCurrentItem(row, column); activated(d->model->index(row, column)); } updateButtonState(); } void KoResourceItemChooser::showButtons(bool show) { foreach (QAbstractButton * button, d->buttonGroup->buttons()) { show ? button->show() : button->hide(); } Q_FOREACH (QAbstractButton *button, d->customButtons) { show ? button->show() : button->hide(); } } void KoResourceItemChooser::addCustomButton(QAbstractButton *button, int cell) { d->buttonLayout->addWidget(button, 0, cell); d->buttonLayout->setColumnStretch(2, 1); d->buttonLayout->setColumnStretch(3, 1); } void KoResourceItemChooser::showTaggingBar(bool show) { d->tagManager->showTaggingBar(show); } void KoResourceItemChooser::setRowCount(int rowCount) { int resourceCount = d->model->resourcesCount(); d->model->setColumnCount(static_cast(resourceCount) / rowCount); //Force an update to get the right row height (in theory) QRect geometry = d->view->geometry(); d->view->setViewMode(KoResourceItemView::FIXED_ROWS); d->view->setGeometry(geometry.adjusted(0, 0, 0, 1)); d->view->setGeometry(geometry); } void KoResourceItemChooser::setColumnCount(int columnCount) { d->model->setColumnCount(columnCount); } void KoResourceItemChooser::setRowHeight(int rowHeight) { d->view->verticalHeader()->setDefaultSectionSize(rowHeight); } void KoResourceItemChooser::setColumnWidth(int columnWidth) { d->view->horizontalHeader()->setDefaultSectionSize(columnWidth); } void KoResourceItemChooser::setItemDelegate(QAbstractItemDelegate *delegate) { d->view->setItemDelegate(delegate); } KoResource *KoResourceItemChooser::currentResource() const { QModelIndex index = d->view->currentIndex(); if (index.isValid()) { return resourceFromModelIndex(index); } return 0; } void KoResourceItemChooser::setCurrentResource(KoResource *resource) { // don't update if the change came from the same chooser if (d->updatesBlocked) { return; } QModelIndex index = d->model->indexFromResource(resource); if (!index.isValid()) return; d->view->setCurrentIndex(index); updatePreview(resource); } void KoResourceItemChooser::slotBeforeResourcesLayoutReset(KoResource *activateAfterReset) { d->savedResourceWhileReset = activateAfterReset ? activateAfterReset : currentResource(); } void KoResourceItemChooser::slotAfterResourcesLayoutReset() { if (d->savedResourceWhileReset) { this->blockSignals(true); setCurrentResource(d->savedResourceWhileReset); this->blockSignals(false); } } void KoResourceItemChooser::setPreviewOrientation(Qt::Orientation orientation) { d->splitter->setOrientation(orientation); } void KoResourceItemChooser::setPreviewTiled(bool tiled) { d->tiledPreview = tiled; } void KoResourceItemChooser::setGrayscalePreview(bool grayscale) { d->grayscalePreview = grayscale; } void KoResourceItemChooser::setCurrentItem(int row, int column) { QModelIndex index = d->model->index(row, column); if (!index.isValid()) return; d->view->setCurrentIndex(index); if (index.isValid()) { updatePreview(resourceFromModelIndex(index)); } } void KoResourceItemChooser::setProxyModel(QAbstractProxyModel *proxyModel) { proxyModel->setSourceModel(d->model); d->view->setModel(proxyModel); } void KoResourceItemChooser::activated(const QModelIndex &/*index*/) { KoResource *resource = currentResource(); if (resource) { d->updatesBlocked = true; emit resourceSelected(resource); d->updatesBlocked = false; updatePreview(resource); updateButtonState(); } } void KoResourceItemChooser::updateButtonState() { QAbstractButton *removeButton = d->buttonGroup->button(Button_Remove); if (! removeButton) return; KoResource *resource = currentResource(); if (resource) { - removeButton->setEnabled(true); + removeButton->setEnabled(!resource->permanent()); return; } removeButton->setEnabled(false); } void KoResourceItemChooser::updatePreview(KoResource *resource) { if (!d->usePreview || !resource) return; QImage image = resource->image(); if (image.format() != QImage::Format_RGB32 || image.format() != QImage::Format_ARGB32 || image.format() != QImage::Format_ARGB32_Premultiplied) { image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); } if (d->tiledPreview) { int width = d->previewScroller->width() * 4; int height = d->previewScroller->height() * 4; QImage img(width, height, image.format()); QPainter gc(&img); gc.fillRect(img.rect(), Qt::white); gc.setPen(Qt::NoPen); gc.setBrush(QBrush(image)); gc.drawRect(img.rect()); image = img; } // Only convert to grayscale if it is rgb. Otherwise, it's gray already. if (d->grayscalePreview && !image.isGrayscale()) { QRgb *pixel = reinterpret_cast(image.bits()); for (int row = 0; row < image.height(); ++row) { for (int col = 0; col < image.width(); ++col) { const QRgb currentPixel = pixel[row * image.width() + col]; const int red = qRed(currentPixel); const int green = qGreen(currentPixel); const int blue = qBlue(currentPixel); const int grayValue = (red * 11 + green * 16 + blue * 5) / 32; pixel[row * image.width() + col] = qRgb(grayValue, grayValue, grayValue); } } } d->previewLabel->setPixmap(QPixmap::fromImage(image)); } KoResource *KoResourceItemChooser::resourceFromModelIndex(const QModelIndex &index) const { if (!index.isValid()) return 0; const QAbstractProxyModel *proxyModel = dynamic_cast(index.model()); if (proxyModel) { //Get original model index, because proxy models destroy the internalPointer QModelIndex originalIndex = proxyModel->mapToSource(index); return static_cast(originalIndex.internalPointer()); } return static_cast(index.internalPointer()); } QSize KoResourceItemChooser::viewSize() const { return d->view->size(); } KoResourceItemView *KoResourceItemChooser::itemView() const { return d->view; } void KoResourceItemChooser::contextMenuRequested(const QPoint &pos) { d->tagManager->contextMenuRequested(currentResource(), pos); } void KoResourceItemChooser::setViewModeButtonVisible(bool visible) { d->viewModeButton->setVisible(visible); } QToolButton *KoResourceItemChooser::viewModeButton() const { return d->viewModeButton; } void KoResourceItemChooser::setSynced(bool sync) { if (d->synced == sync) return; d->synced = sync; KoResourceItemChooserSync *chooserSync = KoResourceItemChooserSync::instance(); if (sync) { connect(chooserSync, SIGNAL(baseLenghtChanged(int)), SLOT(baseLengthChanged(int))); baseLengthChanged(chooserSync->baseLength()); } else { chooserSync->disconnect(this); } } void KoResourceItemChooser::baseLengthChanged(int length) { if (d->synced) { int resourceCount = d->model->resourcesCount(); int width = d->view->width(); int maxColums = width / length; int cols = width / (2 * length) + 1; while (cols <= maxColums) { int size = width / cols; int rows = ceil(resourceCount / (double)cols); if (rows * size < (d->view->height() - 5)) { break; } cols++; } setColumnCount(cols); } d->view->updateView(); } bool KoResourceItemChooser::eventFilter(QObject *object, QEvent *event) { if (d->synced && event->type() == QEvent::Wheel) { KoResourceItemChooserSync *chooserSync = KoResourceItemChooserSync::instance(); QWheelEvent *qwheel = static_cast(event); if (qwheel->modifiers() & Qt::ControlModifier) { int degrees = qwheel->delta() / 8; int newBaseLength = chooserSync->baseLength() + degrees / 15 * 10; chooserSync->setBaseLength(newBaseLength); return true; } } return QObject::eventFilter(object, event); } void KoResourceItemChooser::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); updateView(); } void KoResourceItemChooser::showEvent(QShowEvent *event) { QWidget::showEvent(event); updateView(); } void KoResourceItemChooser::updateView() { if (d->synced) { KoResourceItemChooserSync *chooserSync = KoResourceItemChooserSync::instance(); baseLengthChanged(chooserSync->baseLength()); } } diff --git a/libs/widgets/KoResourceServerProvider.cpp b/libs/widgets/KoResourceServerProvider.cpp index 53edd41cfd..3bc1cc73e2 100644 --- a/libs/widgets/KoResourceServerProvider.cpp +++ b/libs/widgets/KoResourceServerProvider.cpp @@ -1,240 +1,242 @@ /* This file is part of the KDE project Copyright (c) 1999 Matthias Elter Copyright (c) 2003 Patrick Julien Copyright (c) 2005 Sven Langkamp Copyright (C) 2011 Srikanth Tiyyagura This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "KoResourceServerProvider.h" #include #include #include #include #include #include #include #include #include "KoColorSpaceRegistry.h" #include "KoResourcePaths.h" #include using namespace std; class GradientResourceServer : public KoResourceServer { public: GradientResourceServer(const QString& type, const QString& extensions) : KoResourceServer(type, extensions) , m_foregroundToTransparent(0) , m_foregroundToBackground(0) { insertSpecialGradients(); } void insertSpecialGradients() { const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8(); QList stops; KoStopGradient* gradient = new KoStopGradient(""); gradient->setType(QGradient::LinearGradient); gradient->setName("Foreground to Transparent"); stops << KoGradientStop(0.0, KoColor(Qt::black, cs)) << KoGradientStop(1.0, KoColor(QColor(0, 0, 0, 0), cs)); gradient->setStops(stops); gradient->setValid(true); + gradient->setPermanent(true); addResource(gradient, false, true); m_foregroundToTransparent = gradient; gradient = new KoStopGradient(""); gradient->setType(QGradient::LinearGradient); gradient->setName("Foreground to Background"); stops.clear(); stops << KoGradientStop(0.0, KoColor(Qt::black, cs)) << KoGradientStop(1.0, KoColor(Qt::white, cs)); gradient->setStops(stops); gradient->setValid(true); + gradient->setPermanent(true); addResource(gradient, false, true); m_foregroundToBackground = gradient; } private: friend class KoResourceBundle; virtual KoAbstractGradient* createResource( const QString & filename ) { QString fileExtension; int index = filename.lastIndexOf('.'); if (index != -1) fileExtension = filename.mid(index).toLower(); KoAbstractGradient* grad = 0; if(fileExtension == ".svg" || fileExtension == ".kgr") grad = new KoStopGradient(filename); else if(fileExtension == ".ggr" ) grad = new KoSegmentGradient(filename); return grad; } virtual QList< KoAbstractGradient* > sortedResources() { QList< KoAbstractGradient* > resources = KoResourceServer::sortedResources(); QList< KoAbstractGradient* > sorted; if (m_foregroundToTransparent && resources.contains(m_foregroundToTransparent)) { sorted.append(resources.takeAt(resources.indexOf(m_foregroundToTransparent))); } if (m_foregroundToBackground && resources.contains(m_foregroundToBackground)) { sorted.append(resources.takeAt(resources.indexOf(m_foregroundToBackground))); } return sorted + resources; } KoAbstractGradient* m_foregroundToTransparent; KoAbstractGradient* m_foregroundToBackground; }; KoResourceLoaderThread::KoResourceLoaderThread(KoResourceServerBase * server) : QThread() , m_server(server) { m_fileNames = m_server->fileNames(); QStringList fileNames = m_server->blackListedFiles(); if (!fileNames.isEmpty()) { foreach (const QString &s, fileNames) { if (m_fileNames.contains(s)) { m_fileNames.removeAll(s); } } } connect(qApp, SIGNAL(aboutToQuit()), SLOT(barrier())); } KoResourceLoaderThread::~KoResourceLoaderThread() { } void KoResourceLoaderThread::loadSynchronously() { m_server->loadResources(m_fileNames); } void KoResourceLoaderThread::run() { m_server->loadResources(m_fileNames); } void KoResourceLoaderThread::barrier() { if(isRunning()) { wait(); } } struct Q_DECL_HIDDEN KoResourceServerProvider::Private { KoResourceServer* patternServer; KoResourceServer* gradientServer; KoResourceServer* paletteServer; KoResourceLoaderThread *paletteThread; KoResourceLoaderThread *gradientThread; KoResourceLoaderThread *patternThread; }; KoResourceServerProvider::KoResourceServerProvider() : d(new Private) { d->patternServer = new KoResourceServerSimpleConstruction("ko_patterns", "*.pat:*.jpg:*.gif:*.png:*.tif:*.xpm:*.bmp" ); if (!QFileInfo(d->patternServer->saveLocation()).exists()) { QDir().mkpath(d->patternServer->saveLocation()); } d->patternThread = new KoResourceLoaderThread(d->patternServer); d->patternThread->loadSynchronously(); // if (qApp->applicationName().contains(QLatin1String("test"), Qt::CaseInsensitive)) { // d->patternThread->barrier(); // } d->gradientServer = new GradientResourceServer("ko_gradients", "*.kgr:*.svg:*.ggr"); if (!QFileInfo(d->gradientServer->saveLocation()).exists()) { QDir().mkpath(d->gradientServer->saveLocation()); } d->gradientThread = new KoResourceLoaderThread(d->gradientServer); d->gradientThread->loadSynchronously(); // if (qApp->applicationName().contains(QLatin1String("test"), Qt::CaseInsensitive)) { // d->gradientThread->barrier(); // } d->paletteServer = new KoResourceServerSimpleConstruction("ko_palettes", "*.gpl:*.pal:*.act:*.aco:*.css:*.colors"); if (!QFileInfo(d->paletteServer->saveLocation()).exists()) { QDir().mkpath(d->paletteServer->saveLocation()); } d->paletteThread = new KoResourceLoaderThread(d->paletteServer); d->paletteThread->loadSynchronously(); // if (qApp->applicationName().contains(QLatin1String("test"), Qt::CaseInsensitive)) { // d->paletteThread->barrier(); // } } KoResourceServerProvider::~KoResourceServerProvider() { delete d->patternThread; delete d->gradientThread; delete d->paletteThread; delete d->patternServer; delete d->gradientServer; delete d->paletteServer; delete d; } Q_GLOBAL_STATIC(KoResourceServerProvider, s_instance); KoResourceServerProvider* KoResourceServerProvider::instance() { return s_instance; } KoResourceServer* KoResourceServerProvider::patternServer(bool block) { if (block) d->patternThread->barrier(); return d->patternServer; } KoResourceServer* KoResourceServerProvider::gradientServer(bool block) { if (block) d->gradientThread->barrier(); return d->gradientServer; } KoResourceServer* KoResourceServerProvider::paletteServer(bool block) { if (block) d->paletteThread->barrier(); return d->paletteServer; } diff --git a/packaging/linux/snap/snapcraft.yaml b/packaging/linux/snap/snapcraft.yaml index 5512c8d631..9bdb37aafa 100644 --- a/packaging/linux/snap/snapcraft.yaml +++ b/packaging/linux/snap/snapcraft.yaml @@ -1,142 +1,142 @@ name: krita -version: 3.0.1-beta-snap12 +version: 3.0.1-beta-snap13 summary: Krita is the digital painting studio for artists description: Krita is a creative application for raster images. Whether you want to create from scratch or work with existing images, Krita is for you. You can work with photos or scanned images, or start with a blank slate. Krita supports most graphics tablets out of the box. 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 - libkf5crash5 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: ../../../ + source: http://files.kde.org/krita/3/source/krita-3.0.99.91.tar.gz # 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 - 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 - 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/package.bat b/packaging/windows/package.bat index 9bb3eb688c..40eef5b9fc 100644 --- a/packaging/windows/package.bat +++ b/packaging/windows/package.bat @@ -1,103 +1,103 @@ :: This batch script is meant to prepare a Krita package folder to be zipped or :: to be a base for the installer. :: :: Just drop it next to the "i" install folder where the dependencies and Krita binaries are. :: :: TODO: Ask if the user want to make an archive and with which tool :: TODO: Maybe ask for a custom install folder name? set ZIP="c:\Program Files\7-Zip" set MINGW_GCC_BIN=c:\TDM-GCC-64\bin\ set BUILDROOT=%CD% set BUILDDIR_INSTALL=%BUILDROOT%\i set PATH=%MINGW_GCC_BIN%;%PATH% @echo off if not exist %BUILDDIR_INSTALL% ( echo Cannot find the install folder! pause exit /B ) set /P pkg_root=Insert krita package name: if [%pkg_root%] == [] ( echo You entered an empty name! pause exit /B ) :: Initial folder setup mkdir %pkg_root% mkdir %pkg_root%\bin mkdir %pkg_root%\lib mkdir %pkg_root%\share :: Bin folder copy %MINGW_GCC_BIN%\lib*.dll %pkg_root%\bin copy %BUILDDIR_INSTALL%\bin\krita.exe %pkg_root%\bin copy %BUILDDIR_INSTALL%\bin\*.dll %pkg_root%\bin copy %BUILDDIR_INSTALL%\lib\*.dll %pkg_root%\bin xcopy /S /Y /I %BUILDDIR_INSTALL%\plugins\imageformats %pkg_root%\bin\imageformats xcopy /S /Y /I %BUILDDIR_INSTALL%\plugins\kf5 %pkg_root%\bin\kf5 xcopy /S /Y /I %BUILDDIR_INSTALL%\plugins\platforms\qwindows.dll %pkg_root%\bin\platforms\ xcopy /S /Y /I %BUILDDIR_INSTALL%\plugins\printsupport %pkg_root%\bin\printsupport xcopy /Y %BUILDDIR_INSTALL%\plugins\iconengines\*.dll %pkg_root%\bin\iconengines\ :: Translations mkdir %pkg_root%\bin\translations copy %BUILDDIR_INSTALL%\translations\qt_ca.qm %pkg_root%\bin\translations\qt_ca.qm copy %BUILDDIR_INSTALL%\translations\qt_cs.qm %pkg_root%\bin\translations\qt_cs.qm copy %BUILDDIR_INSTALL%\translations\qt_de.qm %pkg_root%\bin\translations\qt_de.qm copy %BUILDDIR_INSTALL%\translations\qt_en.qm %pkg_root%\bin\translations\qt_en.qm copy %BUILDDIR_INSTALL%\translations\qt_fi.qm %pkg_root%\bin\translations\qt_fi.qm copy %BUILDDIR_INSTALL%\translations\qt_he.qm %pkg_root%\bin\translations\qt_hu.qm copy %BUILDDIR_INSTALL%\translations\qt_it.qm %pkg_root%\bin\translations\qt_it.qm copy %BUILDDIR_INSTALL%\translations\qt_ja.qm %pkg_root%\bin\translations\qt_ja.qm copy %BUILDDIR_INSTALL%\translations\qt_ko.qm %pkg_root%\bin\translations\qt_ko.qm copy %BUILDDIR_INSTALL%\translations\qt_lv.qm %pkg_root%\bin\translations\qt_lv.qm copy %BUILDDIR_INSTALL%\translations\qt_ru.qm %pkg_root%\bin\translations\qt_ru.qm copy %BUILDDIR_INSTALL%\translations\qt_sk.qm %pkg_root%\bin\translations\qt_sk.qm copy %BUILDDIR_INSTALL%\translations\qt_uk.qm %pkg_root%\bin\translations\qt_uk.qm copy %BUILDDIR_INSTALL%\translations\qt_fr.qm %pkg_root%\bin\translations\qt_fr.qm :: Lib xcopy /Y %BUILDDIR_INSTALL%\lib\kritaplugins\*.dll %pkg_root%\lib\kritaplugins\ strip %pkg_root%\lib\kritaplugins\*.dll :: Share xcopy /Y /S /I %BUILDDIR_INSTALL%\share\appdata %pkg_root%\share\appdata xcopy /Y /S /I %BUILDDIR_INSTALL%\share\applications %pkg_root%\share\applications xcopy /Y /S /I %BUILDDIR_INSTALL%\share\color %pkg_root%\share\color xcopy /Y /S /I %BUILDDIR_INSTALL%\share\color-schemes %pkg_root%\share\color-schemes xcopy /Y /S /I %BUILDDIR_INSTALL%\share\doc %pkg_root%\share\doc xcopy /Y /S /I %BUILDDIR_INSTALL%\share\icons %pkg_root%\share\icons xcopy /Y /S /I %BUILDDIR_INSTALL%\share\kf5 %pkg_root%\share\kf5 xcopy /Y /S /I %BUILDDIR_INSTALL%\share\krita %pkg_root%\share\krita xcopy /Y /S /I %BUILDDIR_INSTALL%\share\kritaplugins %pkg_root%\share\kritaplugins xcopy /Y /S /I %BUILDDIR_INSTALL%\share\kservices5 %pkg_root%\share\kservices5 -xcopy /Y /S /I %BUILDDIR_INSTALL%\share\locale %pkg_root%\share\locale +xcopy /Y /S /I %BUILDDIR_INSTALL%\share\locale %pkg_root%\bin\locale xcopy /Y /S /I %BUILDDIR_INSTALL%\share\man %pkg_root%\share\man xcopy /Y /S /I %BUILDDIR_INSTALL%\share\mime %pkg_root%\share\mime xcopy /Y /S /I %BUILDDIR_INSTALL%\share\ocio %pkg_root%\share\ocio ::Link copy %BUILDROOT%\krita\packaging\windows\krita.lnk %pkg_root% %BUILDDIR_INSTALL%\bin\windeployqt.exe %pkg_root%\bin\krita.exe :: Debug build %ZIP%\7z.exe a -tzip %pkg_root%-dbg.zip %pkg_root% :: Bin folder strip %pkg_root%\bin\krita.exe strip %pkg_root%\bin\*.dll strip %pkg_root%\bin\imageformats\*.dll strip %pkg_root%\bin\kf5\*.dll strip %pkg_root%\bin\kf5\org.kde.kwindowsystem.platforms\*.dll strip %pkg_root%\bin\platforms\*.dll strip %pkg_root%\bin\iconengines\*.dll %ZIP%\7z.exe a -tzip %pkg_root%.zip %pkg_root% diff --git a/plugins/color/lcms2engine/tests/TestKoLcmsColorProfile.cpp b/plugins/color/lcms2engine/tests/TestKoLcmsColorProfile.cpp index 6b1617861b..192bde1e24 100644 --- a/plugins/color/lcms2engine/tests/TestKoLcmsColorProfile.cpp +++ b/plugins/color/lcms2engine/tests/TestKoLcmsColorProfile.cpp @@ -1,214 +1,214 @@ #include "TestKoLcmsColorProfile.h" #include #include #include #include #include #include #include qreal testRounding(qreal value) { qreal factor; int temp; const int numPlaces = 3; factor = pow(10.0, numPlaces); temp = (int)(value * factor + 0.5); return temp / factor; } void TestKoLcmsColorProfile::testChromaticitiesFromProfile() { #if 0 cmsHPROFILE profile = cmsCreate_sRGBProfile(); KoLcmsRGBColorProfile::Chromaticities chromaticities = KoLcmsRGBColorProfile::chromaticitiesFromProfile(profile); const cmsCIExyY profileRed = {0.6400f, 0.3300f, 0.212656f}; const cmsCIExyY profileGreen = {0.3000f, 0.6000f, 0.715158f}; const cmsCIExyY profileBlue = {0.1500f, 0.0600f, 0.072186f}; const cmsCIExyY profileWhite = {0.3127f, 0.3290f, 1.000000f}; QCOMPARE(testRounding(chromaticities.primaries.Red.x), testRounding(profileRed.x)); QCOMPARE(testRounding(chromaticities.primaries.Red.y), testRounding(profileRed.y)); QCOMPARE(testRounding(chromaticities.primaries.Red.Y), testRounding(profileRed.Y)); QCOMPARE(testRounding(chromaticities.primaries.Green.x), testRounding(profileGreen.x)); QCOMPARE(testRounding(chromaticities.primaries.Green.y), testRounding(profileGreen.y)); QCOMPARE(testRounding(chromaticities.primaries.Green.Y), testRounding(profileGreen.Y)); QCOMPARE(testRounding(chromaticities.primaries.Blue.x), testRounding(profileBlue.x)); QCOMPARE(testRounding(chromaticities.primaries.Blue.y), testRounding(profileBlue.y)); QCOMPARE(testRounding(chromaticities.primaries.Blue.Y), testRounding(profileBlue.Y)); QCOMPARE(testRounding(chromaticities.whitePoint.x), testRounding(profileWhite.x)); QCOMPARE(testRounding(chromaticities.whitePoint.y), testRounding(profileWhite.y)); cmsCloseProfile(profile); #endif } void TestKoLcmsColorProfile::testProfileCreationFromChromaticities() { #if 0 KoLcmsRGBColorProfile::Chromaticities chromaticities; chromaticities.primaries.Red.x = 0.7347f; chromaticities.primaries.Red.y = 0.2653f; chromaticities.primaries.Red.Y = 1.0f; chromaticities.primaries.Green.x = 0.1596f; chromaticities.primaries.Green.y = 0.8404f; chromaticities.primaries.Green.Y = 1.0f; chromaticities.primaries.Blue.x = 0.0366f; chromaticities.primaries.Blue.y = 0.0001f; chromaticities.primaries.Blue.Y = 1.0f; chromaticities.whitePoint.x = 0.34567f; chromaticities.whitePoint.y = 0.35850f; chromaticities.whitePoint.Y = 1.0f; qreal gamma = 1.75f; KoLcmsRGBColorProfile *profile = new KoLcmsRGBColorProfile(chromaticities, gamma); QVERIFY(profile != 0); QCOMPARE(profile->colorSpaceSignature(), icSigRgbData); cmsHPROFILE lcmsProfile = profile->lcmsProfile(); KoLcmsRGBColorProfile::Chromaticities profileChromaticities = KoLcmsRGBColorProfile::chromaticitiesFromProfile(lcmsProfile); QCOMPARE(testRounding(profileChromaticities.primaries.Red.x), testRounding(chromaticities.primaries.Red.x)); QCOMPARE(testRounding(profileChromaticities.primaries.Red.y), testRounding(chromaticities.primaries.Red.y)); QCOMPARE(testRounding(profileChromaticities.primaries.Green.x), testRounding(chromaticities.primaries.Green.x)); QCOMPARE(testRounding(profileChromaticities.primaries.Green.y), testRounding(chromaticities.primaries.Green.y)); QCOMPARE(testRounding(profileChromaticities.primaries.Blue.x), testRounding(chromaticities.primaries.Blue.x)); QCOMPARE(testRounding(profileChromaticities.primaries.Blue.y), testRounding(chromaticities.primaries.Blue.y)); QCOMPARE(testRounding(profileChromaticities.whitePoint.x), testRounding(chromaticities.whitePoint.x)); QCOMPARE(testRounding(profileChromaticities.whitePoint.y), testRounding(chromaticities.whitePoint.y)); LPGAMMATABLE redGamma = cmsReadICCGamma(lcmsProfile, icSigRedTRCTag); LPGAMMATABLE greenGamma = cmsReadICCGamma(lcmsProfile, icSigGreenTRCTag); LPGAMMATABLE blueGamma = cmsReadICCGamma(lcmsProfile, icSigBlueTRCTag); QCOMPARE(testRounding(cmsEstimateGamma(redGamma)), gamma); QCOMPARE(testRounding(cmsEstimateGamma(greenGamma)), gamma); QCOMPARE(testRounding(cmsEstimateGamma(blueGamma)), gamma); QString expectedProfileName = QString("lcms virtual RGB profile - R(%1, %2) G(%3, %4) B(%5, %6) W(%7, %8) gamma %9") .arg(chromaticities.primaries.Red.x) .arg(chromaticities.primaries.Red.y) .arg(chromaticities.primaries.Green.x) .arg(chromaticities.primaries.Green.y) .arg(chromaticities.primaries.Blue.x) .arg(chromaticities.primaries.Blue.y) .arg(chromaticities.whitePoint.x) .arg(chromaticities.whitePoint.y) .arg(gamma); QCOMPARE(profile->name(), expectedProfileName); QCOMPARE(QString(cmsTakeProductDesc(lcmsProfile)), expectedProfileName); profileChromaticities = profile->chromaticities(); QCOMPARE(profileChromaticities.primaries.Red.x, chromaticities.primaries.Red.x); QCOMPARE(profileChromaticities.primaries.Red.y, chromaticities.primaries.Red.y); QCOMPARE(profileChromaticities.primaries.Green.x, chromaticities.primaries.Green.x); QCOMPARE(profileChromaticities.primaries.Green.y, chromaticities.primaries.Green.y); QCOMPARE(profileChromaticities.primaries.Blue.x, chromaticities.primaries.Blue.x); QCOMPARE(profileChromaticities.primaries.Blue.y, chromaticities.primaries.Blue.y); QCOMPARE(profileChromaticities.whitePoint.x, chromaticities.whitePoint.x); QCOMPARE(profileChromaticities.whitePoint.y, chromaticities.whitePoint.y); const QString testProfileName = "Test Profile Name"; profile = new KoLcmsRGBColorProfile(chromaticities, gamma, testProfileName); lcmsProfile = profile->lcmsProfile(); QCOMPARE(profile->name(), testProfileName); QCOMPARE(QString(cmsTakeProductDesc(lcmsProfile)), testProfileName); #endif } void TestKoLcmsColorProfile::testConversion() { const KoColorSpace *sRgb = KoColorSpaceRegistry::instance()->rgb16("sRGB built-in"); Q_ASSERT(sRgb); const KoColorSpace *linearRgb = KoColorSpaceRegistry::instance()->rgb16("scRGB (linear)"); Q_ASSERT(linearRgb); quint16 src[4]; src[0] = 257; src[1] = 257; src[2] = 257; src[3] = 65535; quint16 dst[4]; memset(&dst, 0, 8); linearRgb->convertPixelsTo((quint8 *)&src, (quint8 *)&dst, sRgb, 1, KoColorConversionTransformation::IntentRelativeColorimetric, KoColorConversionTransformation::BlackpointCompensation); quint16 dst2[4]; memset(&dst2, 0, 8); cmsHPROFILE sRgbProfile = cmsCreate_sRGBProfile(); QByteArray rawData = linearRgb->profile()->rawData(); cmsHPROFILE linearRgbProfile = cmsOpenProfileFromMem((void *)rawData.constData(), rawData.size()); cmsHTRANSFORM tf = cmsCreateTransform(linearRgbProfile, TYPE_BGRA_16, sRgbProfile, TYPE_BGRA_16, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE); cmsDoTransform(tf, (quint8 *)&src, (quint8 *)&dst2, 1); Q_ASSERT(dst[0] == dst2[0]); } void TestKoLcmsColorProfile::testProofingConversion() { const KoColorSpace *sRgb = KoColorSpaceRegistry::instance()->rgb16("sRGB built-in"); Q_ASSERT(sRgb); const KoColorSpace *lab = KoColorSpaceRegistry::instance()->lab16();//there's only one lab profile, replace with it's name. Q_ASSERT(lab); quint16 src[4];//the following ought to give us a purple only possible in lab. I can't seem to proof this away, somehow... src[0] = 32896; src[1] = 65535; src[2] = 0; src[3] = 65535; quint16 dst[4]; memset(&dst, 0, 8); cmsHPROFILE sRgbProfile = cmsCreate_sRGBProfile(); cmsHPROFILE LabProfile = cmsCreateLab4Profile(NULL); - quint16 alarm[4];//cyan! + quint16 alarm[cmsMAXCHANNELS]={0}; alarm[0] = 65535; alarm[1] = 0; alarm[2] = 0; alarm[3] = 65535; cmsSetAlarmCodes(alarm); cmsHTRANSFORM tf = cmsCreateProofingTransform(LabProfile, TYPE_Lab_16, LabProfile, TYPE_Lab_16, sRgbProfile, INTENT_ABSOLUTE_COLORIMETRIC, INTENT_ABSOLUTE_COLORIMETRIC, cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK); cmsDoTransform(tf, (quint8 *)&src, (quint8 *)&dst, 1); qDebug()< * * 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 "histogramdockerwidget.h" #include #include #include #include #include #include +#include #include "KoChannelInfo.h" #include "kis_paint_device.h" #include "KoColorSpace.h" #include "kis_iterator_ng.h" #include "kis_canvas2.h" HistogramDockerWidget::HistogramDockerWidget(QWidget *parent, const char *name, Qt::WindowFlags f) : QLabel(parent, f), m_paintDevice(nullptr), m_smoothHistogram(true) { setObjectName(name); } HistogramDockerWidget::~HistogramDockerWidget() { } void HistogramDockerWidget::setPaintDevice(KisCanvas2* canvas) { if (canvas) { m_paintDevice = canvas->image()->projection(); m_bounds = canvas->image()->bounds(); } else { m_paintDevice.clear(); m_bounds = QRect(); m_histogramData.clear(); } } void HistogramDockerWidget::updateHistogram() { if (!m_paintDevice.isNull()) { KisPaintDeviceSP m_devClone = new KisPaintDevice(m_paintDevice->colorSpace()); m_devClone->makeCloneFrom(m_paintDevice, m_bounds); HistogramComputationThread *workerThread = new HistogramComputationThread(m_devClone, m_bounds); connect(workerThread, &HistogramComputationThread::resultReady, this, &HistogramDockerWidget::receiveNewHistogram); connect(workerThread, &HistogramComputationThread::finished, workerThread, &QObject::deleteLater); workerThread->start(); } else { m_histogramData.clear(); update(); } } void HistogramDockerWidget::receiveNewHistogram(HistVector *histogramData) { m_histogramData = *histogramData; update(); } void HistogramDockerWidget::paintEvent(QPaintEvent *event) { if (!m_histogramData.empty()) { int nBins = m_histogramData.at(0).size(); const KoColorSpace* cs = m_paintDevice->colorSpace(); QLabel::paintEvent(event); QPainter painter(this); painter.setPen(this->palette().light().color()); const int NGRID = 4; for (int i = 0; i <= NGRID; ++i) { painter.drawLine(this->width()*i / NGRID, 0., this->width()*i / NGRID, this->height()); painter.drawLine(0., this->height()*i / NGRID, this->width(), this->height()*i / NGRID); } unsigned int nChannels = cs->channelCount(); QList channels = cs->channels(); unsigned int highest = 0; //find the most populous bin in the histogram to scale it properly for (int chan = 0; chan < channels.size(); chan++) { if (channels.at(chan)->channelType() != KoChannelInfo::ALPHA) { std::vector histogramTemp = m_histogramData.at(chan); //use 98th percentile, rather than max for better visual appearance int nthPercentile = 2 * histogramTemp.size() / 100; //unsigned int max = *std::max_element(m_histogramData.at(chan).begin(),m_histogramData.at(chan).end()); std::nth_element(histogramTemp.begin(), histogramTemp.begin() + nthPercentile, histogramTemp.end(), std::greater()); unsigned int max = *(histogramTemp.begin() + nthPercentile); highest = std::max(max, highest); } } painter.setWindow(QRect(-1, 0, nBins + 1, highest)); painter.setCompositionMode(QPainter::CompositionMode_Plus); for (int chan = 0; chan < nChannels; chan++) { if (channels.at(chan)->channelType() != KoChannelInfo::ALPHA) { QColor color = channels.at(chan)->color(); //special handling of grayscale color spaces. can't use color returned above. if(cs->colorChannelCount()==1){ color = QColor(Qt::gray); } QColor fill_color = color; fill_color.setAlphaF(.25); painter.setBrush(fill_color); QPen pen = QPen(color); pen.setWidth(0); painter.setPen(pen); if (m_smoothHistogram) { QPainterPath path; path.moveTo(QPointF(-1, highest)); for (qint32 i = 0; i < nBins; ++i) { float v = std::max((float)highest - m_histogramData[chan][i], 0.f); path.lineTo(QPointF(i, v)); } path.lineTo(QPointF(nBins + 1, highest)); path.closeSubpath(); painter.drawPath(path); } else { pen.setWidth(1); painter.setPen(pen); for (qint32 i = 0; i < nBins; ++i) { float v = std::max((float)highest - m_histogramData[chan][i], 0.f); painter.drawLine(QPointF(i, highest), QPointF(i, v)); } } } } } } void HistogramComputationThread::run() { const KoColorSpace *cs = m_dev->colorSpace(); quint32 channelCount = m_dev->channelCount(); quint32 pixelSize = m_dev->pixelSize(); quint32 imageSize = m_bounds.width() * m_bounds.height(); quint32 nSkip = 1 + (imageSize >> 20); //for speed use about 1M pixels for computing histograms //allocate space for the histogram data bins.resize((int)channelCount); for (auto &bin : bins) { bin.resize(std::numeric_limits::max() + 1); } QRect bounds = m_dev->exactBounds(); if (bounds.isEmpty()) return; KisSequentialConstIterator it(m_dev, m_dev->exactBounds()); int i; quint32 toSkip = nSkip; do { i = it.nConseqPixels(); const quint8* pixel = it.rawDataConst(); for (int k = 0; k < i; ++k) { if (--toSkip == 0) { for (int chan = 0; chan < (int)channelCount; ++chan) { bins[chan][cs->scaleToU8(pixel, chan)]++; } toSkip = nSkip; } pixel += pixelSize; } } while (it.nextPixels(i)); emit resultReady(&bins); } diff --git a/plugins/impex/csv/csv_saver.cpp b/plugins/impex/csv/csv_saver.cpp index ab8606c931..d1858cae6f 100644 --- a/plugins/impex/csv/csv_saver.cpp +++ b/plugins/impex/csv/csv_saver.cpp @@ -1,472 +1,481 @@ /* * Copyright (c) 2016 Laszlo Fazekas * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "csv_saver.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "csv_layer_record.h" CSVSaver::CSVSaver(KisDocument *doc, bool batchMode) : m_image(doc->image()) , m_doc(doc) , m_batchMode(batchMode) , m_stop(false) { } CSVSaver::~CSVSaver() { } KisImageWSP CSVSaver::image() { return m_image; } KisImageBuilder_Result CSVSaver::encode(const QString &filename) { int idx; int start, end; KisNodeSP node; QByteArray ba; KisKeyframeSP keyframe; QVector layers; KisImageAnimationInterface *animation = m_image->animationInterface(); //open the csv file for writing QFile f(filename); if (!f.open(QIODevice::WriteOnly)) { return KisImageBuilder_RESULT_NOT_LOCAL; } QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); //DataStream instead of TextStream for correct line endings QDataStream stream(&f); - QString path = filename; + //Using the original local path + QString path = m_doc->localFilePath(); if (path.right(4).toUpper() == ".CSV") path = path.left(path.size() - 4); + else { + //something is wrong: the local file name is not .csv! + //trying the given (probably temporary) filename as well + path= filename; + + if (path.right(4).toUpper() == ".CSV") + path = path.left(path.size() - 4); + } path.append(".frames"); //create directory QDir dir(path); if (!dir.exists()) { dir.mkpath("."); } //according to the QT docs, the slash is a universal directory separator path.append("/"); node = m_image->rootLayer()->firstChild(); //TODO: correct handling of the layer tree. //for now, only top level paint layers are saved idx = 0; while (node) { if (node->inherits("KisPaintLayer")) { KisPaintLayer* paintLayer = dynamic_cast(node.data()); CSVLayerRecord* layerRecord = new CSVLayerRecord(); layers.prepend(layerRecord); //reverse order! layerRecord->name = paintLayer->name(); layerRecord->name.replace(QRegExp("[\"\\r\\n]"), "_"); if (layerRecord->name.isEmpty()) layerRecord->name= QString("Unnamed-%1").arg(idx); layerRecord->visible = (paintLayer->visible()) ? 1 : 0; layerRecord->density = (float)(paintLayer->opacity()) / OPACITY_OPAQUE_U8; layerRecord->blending = convertToBlending(paintLayer->compositeOpId()); layerRecord->layer = paintLayer; layerRecord->channel = paintLayer->projection()->keyframeChannel(); layerRecord->last = ""; layerRecord->frame = 0; idx++; } node = node->nextSibling(); } KisTimeRange range = animation->fullClipRange(); start = (range.isValid()) ? range.start() : 0; if (!range.isInfinite()) { end = range.end(); if (end < start) end = start; } else { //undefined length, searching for the last keyframe end = start; for (idx = 0; idx < layers.size(); idx++) { KisRasterKeyframeChannel *channel = layers.at(idx)->channel; if (channel) { keyframe = channel->lastKeyframe(); if ( (!keyframe.isNull()) && (keyframe->time() > end) ) end = keyframe->time(); } } } //create temporary doc for exporting QScopedPointer exportDoc(KisPart::instance()->createDocument()); createTempImage(exportDoc.data()); KisImageBuilder_Result retval= KisImageBuilder_RESULT_OK; if (!m_batchMode) { emit m_doc->statusBarMessage(i18n("Saving CSV file...")); emit m_doc->sigProgress(0); connect(m_doc, SIGNAL(sigProgressCanceled()), this, SLOT(cancel())); } int frame = start; int step = 0; do { qApp->processEvents(); if (m_stop) { retval = KisImageBuilder_RESULT_CANCEL; break; } switch(step) { case 0 : //first row if (f.write("UTF-8, TVPaint, \"CSV 1.0\"\r\n") < 0) { retval = KisImageBuilder_RESULT_FAILURE; } break; case 1 : //scene header names if (f.write("Project Name, Width, Height, Frame Count, Layer Count, Frame Rate, Pixel Aspect Ratio, Field Mode\r\n") < 0) { retval = KisImageBuilder_RESULT_FAILURE; } break; case 2 : //scene header values ba = QString("\"%1\", ").arg(m_image->objectName()).toUtf8(); if (f.write(ba.data()) < 0) { retval = KisImageBuilder_RESULT_FAILURE; break; } ba = QString("%1, %2, ").arg(m_image->width()).arg(m_image->height()).toUtf8(); if (f.write(ba.data()) < 0) { retval = KisImageBuilder_RESULT_FAILURE; break; } ba = QString("%1, %2, ").arg(end - start + 1).arg(layers.size()).toUtf8(); if (f.write(ba.data()) < 0) { retval = KisImageBuilder_RESULT_FAILURE; break; } //the framerate is an integer here ba = QString("%1, ").arg((double)(animation->framerate()),0,'f',6).toUtf8(); if (f.write(ba.data()) < 0) { retval = KisImageBuilder_RESULT_FAILURE; break; } ba = QString("%1, Progressive\r\n").arg((double)(m_image->xRes() / m_image->yRes()),0,'f',6).toUtf8(); if (f.write(ba.data()) < 0) { retval = KisImageBuilder_RESULT_FAILURE; break; } break; case 3 : //layer header values if (f.write("#Layers") < 0) { //Layers retval = KisImageBuilder_RESULT_FAILURE; break; } for (idx = 0; idx < layers.size(); idx++) { ba = QString(", \"%1\"").arg(layers.at(idx)->name).toUtf8(); if (f.write(ba.data()) < 0) break; } break; case 4 : if (f.write("\r\n#Density") < 0) { //Density retval = KisImageBuilder_RESULT_FAILURE; break; } for (idx = 0; idx < layers.size(); idx++) { ba = QString(", %1").arg((double)(layers.at(idx)->density), 0, 'f', 6).toUtf8(); if (f.write(ba.data()) < 0) break; } break; case 5 : if (f.write("\r\n#Blending") < 0) { //Blending retval = KisImageBuilder_RESULT_FAILURE; break; } for (idx = 0; idx < layers.size(); idx++) { ba = QString(", \"%1\"").arg(layers.at(idx)->blending).toUtf8(); if (f.write(ba.data()) < 0) break; } break; case 6 : if (f.write("\r\n#Visible") < 0) { //Visible retval = KisImageBuilder_RESULT_FAILURE; break; } for (idx = 0; idx < layers.size(); idx++) { ba = QString(", %1").arg(layers.at(idx)->visible).toUtf8(); if (f.write(ba.data()) < 0) break; } if (idx < layers.size()) { retval = KisImageBuilder_RESULT_FAILURE; } break; default : //frames if (frame > end) { if (f.write("\r\n") < 0) retval = KisImageBuilder_RESULT_FAILURE; step = 8; break; } ba = QString("\r\n#%1").arg(frame, 5, 10, QChar('0')).toUtf8(); if (f.write(ba.data()) < 0) { retval = KisImageBuilder_RESULT_FAILURE; break; } for (idx = 0; idx < layers.size(); idx++) { CSVLayerRecord *layer = layers.at(idx); KisRasterKeyframeChannel *channel = layer->channel; if (channel) { if (frame == start) { keyframe = channel->activeKeyframeAt(frame); } else { keyframe = channel->keyframeAt(frame); } } else { keyframe.clear(); // without animation } if ( !keyframe.isNull() || (frame == start) ) { if (!m_batchMode) { emit m_doc->sigProgress(((frame - start) * layers.size() + idx) * 100 / ((end - start) * layers.size())); } retval = getLayer(layer, exportDoc.data(), keyframe, path, frame, idx); if (retval != KisImageBuilder_RESULT_OK) break; } ba = QString(", \"%1\"").arg(layer->last).toUtf8(); if (f.write(ba.data()) < 0) break; } if (idx < layers.size()) retval = KisImageBuilder_RESULT_FAILURE; frame++; step = 6; //keep step here break; } step++; } while((retval == KisImageBuilder_RESULT_OK) && (step < 8)); qDeleteAll(layers); f.close(); if (!m_batchMode) { disconnect(m_doc, SIGNAL(sigProgressCanceled()), this, SLOT(cancel())); emit m_doc->sigProgress(100); emit m_doc->clearStatusBarMessage(); } QApplication::restoreOverrideCursor(); return retval; } QString CSVSaver::convertToBlending(const QString &opid) { if (opid == COMPOSITE_OVER) return "Color"; if (opid == COMPOSITE_BEHIND) return "Behind"; if (opid == COMPOSITE_ERASE) return "Erase"; // "Shade" if (opid == COMPOSITE_LINEAR_LIGHT) return "Light"; if (opid == COMPOSITE_COLORIZE) return "Colorize"; if (opid == COMPOSITE_HUE) return "Hue"; if ((opid == COMPOSITE_ADD) || (opid == COMPOSITE_LINEAR_DODGE)) return "Add"; if (opid == COMPOSITE_INVERSE_SUBTRACT) return "Sub"; if (opid == COMPOSITE_MULT) return "Multiply"; if (opid == COMPOSITE_SCREEN) return "Screen"; // "Replace" // "Subtitute" if (opid == COMPOSITE_DIFF) return "Difference"; if (opid == COMPOSITE_DIVIDE) return "Divide"; if (opid == COMPOSITE_OVERLAY) return "Overlay"; if (opid == COMPOSITE_DODGE) return "Light2"; if (opid == COMPOSITE_BURN) return "Shade2"; if (opid == COMPOSITE_HARD_LIGHT) return "HardLight"; if ((opid == COMPOSITE_SOFT_LIGHT_PHOTOSHOP) || (opid == COMPOSITE_SOFT_LIGHT_SVG)) return "SoftLight"; if (opid == COMPOSITE_GRAIN_EXTRACT) return "GrainExtract"; if (opid == COMPOSITE_GRAIN_MERGE) return "GrainMerge"; if (opid == COMPOSITE_SUBTRACT) return "Sub2"; if (opid == COMPOSITE_DARKEN) return "Darken"; if (opid == COMPOSITE_LIGHTEN) return "Lighten"; if (opid == COMPOSITE_SATURATION) return "Saturation"; return "Color"; } KisImageBuilder_Result CSVSaver::getLayer(CSVLayerRecord* layer, KisDocument* exportDoc, KisKeyframeSP keyframe, const QString &path, int frame, int idx) { //render to the temp layer KisImageWSP image = exportDoc->image(); KisPaintDeviceSP device = image->rootLayer()->firstChild()->projection(); if (!keyframe.isNull()) { layer->channel->fetchFrame(keyframe, device); } else { device->makeCloneFrom(layer->layer->projection(),image->bounds()); // without animation } QRect bounds = device->exactBounds(); if (bounds.isEmpty()) { layer->last = ""; //empty frame return KisImageBuilder_RESULT_OK; } layer->last = QString("frame%1-%2.png").arg(idx + 1,5,10,QChar('0')).arg(frame,5,10,QChar('0')); QString filename = path; filename.append(layer->last); //save to PNG KisSequentialConstIterator it(device, image->bounds()); const KoColorSpace* cs = device->colorSpace(); bool isThereAlpha = false; do { if (cs->opacityU8(it.oldRawData()) != OPACITY_OPAQUE_U8) { isThereAlpha = true; break; } } while (it.nextPixel()); if (!KisPNGConverter::isColorSpaceSupported(cs)) { device = new KisPaintDevice(*device.data()); KUndo2Command *cmd= device->convertTo(KoColorSpaceRegistry::instance()->rgb8()); delete cmd; } KisPNGOptions options; options.alpha = isThereAlpha; options.interlace = false; options.compression = 8; options.tryToSaveAsIndexed = false; options.transparencyFillColor = QColor(0,0,0); options.saveSRGBProfile = true; //TVPaint can use only sRGB options.forceSRGB = false; KisPNGConverter kpc(exportDoc); KisImageBuilder_Result result = kpc.buildFile(filename, image->bounds(), image->xRes(), image->yRes(), device, image->beginAnnotations(), image->endAnnotations(), options, (KisMetaData::Store* )0 ); return result; } void CSVSaver::createTempImage(KisDocument* exportDoc) { exportDoc->setAutoSave(0); exportDoc->setOutputMimeType("image/png"); exportDoc->setFileBatchMode(true); KisImageWSP exportImage = new KisImage(exportDoc->createUndoStore(), m_image->width(), m_image->height(), m_image->colorSpace(), QString()); exportImage->setResolution(m_image->xRes(), m_image->yRes()); exportDoc->setCurrentImage(exportImage); KisPaintLayer* paintLayer = new KisPaintLayer(exportImage, "paint device", OPACITY_OPAQUE_U8); exportImage->addNode(paintLayer, exportImage->rootLayer(), KisLayerSP(0)); } KisImageBuilder_Result CSVSaver::buildAnimation(const QString &filename) { if (!m_image) return KisImageBuilder_RESULT_EMPTY; return encode(filename); } void CSVSaver::cancel() { m_stop = true; } diff --git a/plugins/paintops/dynadraw/wdgdynaoptions.ui b/plugins/paintops/dynadraw/wdgdynaoptions.ui index 6934f1a51e..7ee834bd11 100644 --- a/plugins/paintops/dynadraw/wdgdynaoptions.ui +++ b/plugins/paintops/dynadraw/wdgdynaoptions.ui @@ -1,344 +1,353 @@ WdgDynaOptions 0 0 337 359 210 60 337 359 - 1 + 0 Dynamics settings Initial width: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - -99.000000000000000 + 0.000000000000000 + + + 15.000000000000000 0.050000000000000 1.500000000000000 Mass: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - -99.000000000000000 + 0.000000000000000 + + + 15.000000000000000 0.050000000000000 0.500000000000000 Drag: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - -99.000000000000000 + 0.000000000000000 + + + 15.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 C&ircle true Two Lines Line spacing 20.000000000000000 Line count 10 Poly&gon Wi&re Paint connection true Qt::Vertical 20 40 + + KisDoubleSliderSpinBox + QWidget +
kis_slider_spin_box.h
+ 1 +
KisIntParseSpinBox QSpinBox
kis_int_parse_spin_box.h
KisDoubleParseSpinBox QDoubleSpinBox
kis_double_parse_spin_box.h
- - KisDoubleSliderSpinBox - QWidget -
kis_slider_spin_box.h
- 1 -